get-fsa-training-done 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (294) hide show
  1. get_fsa_training_done-1.0.0/.gitignore +14 -0
  2. get_fsa_training_done-1.0.0/LICENSE +21 -0
  3. get_fsa_training_done-1.0.0/PKG-INFO +138 -0
  4. get_fsa_training_done-1.0.0/README.md +117 -0
  5. get_fsa_training_done-1.0.0/pyproject.toml +69 -0
  6. get_fsa_training_done-1.0.0/scripts/assessment/gen_levels_md.py +158 -0
  7. get_fsa_training_done-1.0.0/scripts/material/gen_structure_md.py +155 -0
  8. get_fsa_training_done-1.0.0/scripts/program/gen_rules_md.py +106 -0
  9. get_fsa_training_done-1.0.0/scripts/program/gen_schemas_md.py +112 -0
  10. get_fsa_training_done-1.0.0/scripts/sync_version.py +70 -0
  11. get_fsa_training_done-1.0.0/src/get_fsa_training_done/__about__.py +10 -0
  12. get_fsa_training_done-1.0.0/src/get_fsa_training_done/__init__.py +13 -0
  13. get_fsa_training_done-1.0.0/src/get_fsa_training_done/__main__.py +4 -0
  14. get_fsa_training_done-1.0.0/src/get_fsa_training_done/cli.py +94 -0
  15. get_fsa_training_done-1.0.0/src/get_fsa_training_done/errors.py +50 -0
  16. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/__init__.py +31 -0
  17. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/README.md +270 -0
  18. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/__init__.py +24 -0
  19. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/commands/__init__.py +0 -0
  20. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/commands/emit.py +59 -0
  21. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/commands/grade.py +413 -0
  22. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/commands/levels.py +115 -0
  23. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/commands/render.py +91 -0
  24. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/commands/sandbox.py +76 -0
  25. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/commands/sprint_kit.py +91 -0
  26. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/commands/verify.py +160 -0
  27. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/__init__.py +1 -0
  28. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/budget.py +93 -0
  29. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/emit/__init__.py +5 -0
  30. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/emit/blooket.py +69 -0
  31. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/emit/coderbyte.py +66 -0
  32. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/emit/master.py +107 -0
  33. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/fonts/DejaVuSans-Bold.ttf +0 -0
  34. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/fonts/DejaVuSans.ttf +0 -0
  35. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/fonts/LICENSE-DejaVu.txt +99 -0
  36. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/fonts/LICENSE-RobotoMono.txt +202 -0
  37. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/fonts/README.md +31 -0
  38. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/fonts/RobotoMono-Bold.ttf +0 -0
  39. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/fonts/RobotoMono-Regular.ttf +0 -0
  40. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/grading/__init__.py +32 -0
  41. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/grading/aggregate.py +244 -0
  42. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/grading/ai_cheat.py +574 -0
  43. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/grading/batches.py +97 -0
  44. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/grading/plagiarism.py +338 -0
  45. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/grading/preprocess.py +251 -0
  46. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/grading/quiz_merge.py +83 -0
  47. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/grading/quiz_scores.py +289 -0
  48. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/grading/retake.py +151 -0
  49. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/grading/roster.py +146 -0
  50. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/html.py +81 -0
  51. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/markdown.py +253 -0
  52. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/pdf.py +122 -0
  53. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/sandbox.py +424 -0
  54. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/sprintkit.py +636 -0
  55. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/verify/__init__.py +32 -0
  56. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/verify/answer_template.py +154 -0
  57. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/verify/calibration.py +93 -0
  58. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/verify/capstone.py +342 -0
  59. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/verify/common.py +160 -0
  60. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/verify/long_form.py +432 -0
  61. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/verify/question_set.py +272 -0
  62. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/common/__init__.py +2 -0
  63. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/common/findings.py +98 -0
  64. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/common/levels.py +296 -0
  65. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/material/README.md +168 -0
  66. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/material/__init__.py +24 -0
  67. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/material/commands/__init__.py +0 -0
  68. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/material/commands/coverage.py +61 -0
  69. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/material/commands/derive.py +80 -0
  70. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/material/commands/verify.py +91 -0
  71. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/material/core/__init__.py +1 -0
  72. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/material/core/appendix.py +98 -0
  73. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/material/core/checks.py +239 -0
  74. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/material/core/coverage.py +183 -0
  75. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/material/core/grammar.py +262 -0
  76. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/material/core/notes.py +161 -0
  77. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/material/core/rules.py +219 -0
  78. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/README.md +175 -0
  79. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/__init__.py +24 -0
  80. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/commands/__init__.py +0 -0
  81. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/commands/derive.py +156 -0
  82. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/commands/export.py +163 -0
  83. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/commands/verify.py +127 -0
  84. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/__init__.py +1 -0
  85. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/checks/__init__.py +33 -0
  86. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/checks/crosslink.py +90 -0
  87. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/checks/program.py +256 -0
  88. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/checks/topic.py +298 -0
  89. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/codes.py +105 -0
  90. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/contracts.py +127 -0
  91. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/csvio.py +50 -0
  92. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/derive/__init__.py +8 -0
  93. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/derive/allocation.py +78 -0
  94. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/derive/skeleton.py +103 -0
  95. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/loader.py +113 -0
  96. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/mdtable.py +79 -0
  97. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/program.py +127 -0
  98. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/rules.py +327 -0
  99. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/schedule.py +48 -0
  100. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/schemas.py +114 -0
  101. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/syllabus.py +240 -0
  102. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/xlsx/__init__.py +14 -0
  103. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/xlsx/package.py +278 -0
  104. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/xlsx/probe.py +84 -0
  105. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/xlsx/refs.py +62 -0
  106. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/xlsx/sheetxml.py +190 -0
  107. get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/xlsx/syllabus_layout.py +270 -0
  108. get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/__init__.py +3 -0
  109. get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/commands/__init__.py +1 -0
  110. get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/commands/common.py +154 -0
  111. get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/commands/doctor.py +129 -0
  112. get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/commands/env.py +69 -0
  113. get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/commands/install.py +225 -0
  114. get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/commands/status.py +110 -0
  115. get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/commands/uninstall.py +111 -0
  116. get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/commands/update.py +128 -0
  117. get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/envmgr/__init__.py +5 -0
  118. get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/envmgr/bootstrap.py +198 -0
  119. get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/envmgr/reexec.py +69 -0
  120. get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/envmgr/requirements/core.txt +13 -0
  121. get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/envmgr/stamp.py +141 -0
  122. get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/install/__init__.py +5 -0
  123. get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/install/fsops.py +107 -0
  124. get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/install/migrations.py +52 -0
  125. get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/install/planner.py +159 -0
  126. get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/install/receipt.py +147 -0
  127. get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/install/removal.py +64 -0
  128. get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/platforms/__init__.py +5 -0
  129. get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/platforms/base.py +63 -0
  130. get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/platforms/claude.py +32 -0
  131. get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/platforms/codex.py +129 -0
  132. get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/platforms/registry.py +41 -0
  133. get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/skillmeta.py +97 -0
  134. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/SKILL.md +165 -0
  135. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/VERSION +1 -0
  136. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/blooket_import_template.csv +4 -0
  137. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/capstone_spec_template.md +100 -0
  138. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/coderbyte_mc_import_template.json +32 -0
  139. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/examples/brief_example.md +165 -0
  140. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/examples/rubric_example.md +212 -0
  141. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/grading_contract.md +124 -0
  142. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/levels.md +122 -0
  143. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/overview.md +72 -0
  144. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/question_guidelines.md +111 -0
  145. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/score_sheet_schema.md +68 -0
  146. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/sprint_checkpoint_template.md +182 -0
  147. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/tasks/design/author_mcq.md +54 -0
  148. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/tasks/design/author_oe_questions.md +60 -0
  149. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/tasks/design/build_fixtures.md +56 -0
  150. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/tasks/design/emit_imports.md +41 -0
  151. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/tasks/design/plan_scope.md +51 -0
  152. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/tasks/design/render_pdf.md +34 -0
  153. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/tasks/design/verify.md +44 -0
  154. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/tasks/design/write_answer_template.md +61 -0
  155. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/tasks/design/write_brief.md +140 -0
  156. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/tasks/design/write_rubric.md +72 -0
  157. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/tasks/grade/aggregate.md +34 -0
  158. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/tasks/grade/ai_signals.md +40 -0
  159. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/tasks/grade/learner_remarks.md +33 -0
  160. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/tasks/grade/merge_quizzes.md +32 -0
  161. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/tasks/grade/merge_retake.md +46 -0
  162. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/tasks/grade/plan_batches.md +35 -0
  163. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/tasks/grade/preprocess.md +60 -0
  164. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/tasks/grade/score_quiz.md +49 -0
  165. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/tasks/grade/score_submissions.md +55 -0
  166. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/tasks/grade/similarity.md +37 -0
  167. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/verifiers/capstone_project.md +133 -0
  168. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/verifiers/long_assignment.md +99 -0
  169. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/verifiers/practice_exam.md +107 -0
  170. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/verifiers/quiz.md +77 -0
  171. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/verifiers/short_assignment.md +89 -0
  172. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/verifiers/theory_exam.md +91 -0
  173. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/workflows/design/capstone_project.md +173 -0
  174. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/workflows/design/long_assignment.md +34 -0
  175. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/workflows/design/practice_exam.md +39 -0
  176. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/workflows/design/quiz.md +30 -0
  177. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/workflows/design/short_assignment.md +15 -0
  178. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/workflows/design/theory_exam.md +33 -0
  179. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/workflows/grade/cheat_check.md +30 -0
  180. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/workflows/grade/quiz_report.md +14 -0
  181. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/workflows/grade/retake.md +14 -0
  182. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/workflows/grade/submissions.md +20 -0
  183. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/common/language_vn.md +46 -0
  184. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/common/style.md +48 -0
  185. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/common/tasks/run_in_sandbox.md +53 -0
  186. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/common/tasks/translate_vn.md +41 -0
  187. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/material/conventions.md +101 -0
  188. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/material/examples/mini/00_Study_Guide_Handbook.md +30 -0
  189. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/material/examples/mini/01_Relational_Modelling.md +91 -0
  190. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/material/examples/mini/02_Querying.md +77 -0
  191. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/material/examples/mini/99_Appendix.md +19 -0
  192. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/material/examples/mini/dbf_lab_01.md +35 -0
  193. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/material/examples/plan/AA_FR_TT_DBF_ScheduleDetail.csv +9 -0
  194. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/material/examples/plan/AA_FR_TT_DBF_Syllabus.md +21 -0
  195. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/material/overview.md +56 -0
  196. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/material/structure.md +238 -0
  197. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/material/tasks/coverage.md +41 -0
  198. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/material/tasks/derive_appendix.md +39 -0
  199. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/material/tasks/review_for_trainee.md +35 -0
  200. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/material/tasks/verify.md +39 -0
  201. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/material/tasks/write_handbook.md +32 -0
  202. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/material/tasks/write_lab.md +42 -0
  203. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/material/tasks/write_lecture.md +41 -0
  204. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/material/tasks/write_worksheet.md +32 -0
  205. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/material/templates/appendix.md +13 -0
  206. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/material/templates/handbook.md +15 -0
  207. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/material/templates/lab_guide.md +24 -0
  208. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/material/templates/lecture_note.md +59 -0
  209. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/material/workflows/fill_gaps.md +10 -0
  210. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/material/workflows/module_pack.md +18 -0
  211. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/program/artifact_map.md +53 -0
  212. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/program/examples/capstone/curriculum/AA_FR_TT_CAPSTONE_DetailedSchedule.csv +2 -0
  213. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/program/examples/capstone/curriculum/AA_FR_TT_CAPSTONE_MasterSchedule.csv +2 -0
  214. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/program/examples/capstone/curriculum/AA_FR_TT_CAPSTONE_OSTModuleMapping.csv +2 -0
  215. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/program/examples/capstone/curriculum/AA_FR_TT_CAPSTONE_TopicList.csv +2 -0
  216. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/program/examples/capstone/curriculum/AA_FR_TT_CAPSTONE_TrainingProgramCurriculum.md +24 -0
  217. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/program/examples/capstone/curriculum/syllabi/AA_FR_TT_PRJ_ScheduleDetail.csv +9 -0
  218. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/program/examples/capstone/curriculum/syllabi/AA_FR_TT_PRJ_Syllabus.md +91 -0
  219. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/program/examples/mini/curriculum/AA_FR_TT_DEMO_DetailedSchedule.csv +3 -0
  220. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/program/examples/mini/curriculum/AA_FR_TT_DEMO_MasterSchedule.csv +3 -0
  221. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/program/examples/mini/curriculum/AA_FR_TT_DEMO_OSTModuleMapping.csv +3 -0
  222. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/program/examples/mini/curriculum/AA_FR_TT_DEMO_TopicList.csv +3 -0
  223. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/program/examples/mini/curriculum/AA_FR_TT_DEMO_TrainingProgramCurriculum.md +25 -0
  224. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/program/examples/mini/curriculum/syllabi/AA_FR_TT_DBF_ScheduleDetail.csv +13 -0
  225. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/program/examples/mini/curriculum/syllabi/AA_FR_TT_DBF_Syllabus.md +94 -0
  226. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/program/examples/mini/curriculum/syllabi/AA_FR_TT_FND_ScheduleDetail.csv +10 -0
  227. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/program/examples/mini/curriculum/syllabi/AA_FR_TT_FND_Syllabus.md +94 -0
  228. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/program/overview.md +61 -0
  229. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/program/rules.md +226 -0
  230. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/program/schemas.md +81 -0
  231. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/program/tasks/agree_module_table.md +42 -0
  232. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/program/tasks/derive_allocation.md +32 -0
  233. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/program/tasks/derive_skeleton.md +29 -0
  234. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/program/tasks/export_workbook.md +40 -0
  235. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/program/tasks/fill_schedules.md +33 -0
  236. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/program/tasks/revise_cascade.md +42 -0
  237. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/program/tasks/verify.md +43 -0
  238. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/program/tasks/write_syllabus.md +82 -0
  239. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/program/workflows/add_topic.md +9 -0
  240. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/program/workflows/new_programme.md +12 -0
  241. get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/program/workflows/revise.md +8 -0
  242. get_fsa_training_done-1.0.0/src/get_fsa_training_done/skill.py +45 -0
  243. get_fsa_training_done-1.0.0/tests/__init__.py +0 -0
  244. get_fsa_training_done-1.0.0/tests/conftest.py +24 -0
  245. get_fsa_training_done-1.0.0/tests/features/__init__.py +0 -0
  246. get_fsa_training_done-1.0.0/tests/features/assessment/__init__.py +0 -0
  247. get_fsa_training_done-1.0.0/tests/features/assessment/conftest.py +58 -0
  248. get_fsa_training_done-1.0.0/tests/features/assessment/test_consistency.py +23 -0
  249. get_fsa_training_done-1.0.0/tests/features/assessment/test_emit.py +138 -0
  250. get_fsa_training_done-1.0.0/tests/features/assessment/test_grading.py +453 -0
  251. get_fsa_training_done-1.0.0/tests/features/assessment/test_levels.py +91 -0
  252. get_fsa_training_done-1.0.0/tests/features/assessment/test_pdf.py +94 -0
  253. get_fsa_training_done-1.0.0/tests/features/assessment/test_quiz_scores.py +199 -0
  254. get_fsa_training_done-1.0.0/tests/features/assessment/test_render.py +44 -0
  255. get_fsa_training_done-1.0.0/tests/features/assessment/test_retake_and_quiz_merge.py +164 -0
  256. get_fsa_training_done-1.0.0/tests/features/assessment/test_sandbox.py +177 -0
  257. get_fsa_training_done-1.0.0/tests/features/assessment/test_sprint_kit.py +188 -0
  258. get_fsa_training_done-1.0.0/tests/features/assessment/test_verify.py +540 -0
  259. get_fsa_training_done-1.0.0/tests/features/assessment/test_written.py +211 -0
  260. get_fsa_training_done-1.0.0/tests/features/material/__init__.py +0 -0
  261. get_fsa_training_done-1.0.0/tests/features/material/conftest.py +102 -0
  262. get_fsa_training_done-1.0.0/tests/features/material/test_consistency.py +62 -0
  263. get_fsa_training_done-1.0.0/tests/features/material/test_derive.py +93 -0
  264. get_fsa_training_done-1.0.0/tests/features/material/test_rules.py +307 -0
  265. get_fsa_training_done-1.0.0/tests/features/program/__init__.py +0 -0
  266. get_fsa_training_done-1.0.0/tests/features/program/conftest.py +119 -0
  267. get_fsa_training_done-1.0.0/tests/features/program/synthetic.py +255 -0
  268. get_fsa_training_done-1.0.0/tests/features/program/test_consistency.py +60 -0
  269. get_fsa_training_done-1.0.0/tests/features/program/test_derive.py +230 -0
  270. get_fsa_training_done-1.0.0/tests/features/program/test_export.py +298 -0
  271. get_fsa_training_done-1.0.0/tests/features/program/test_parsers.py +118 -0
  272. get_fsa_training_done-1.0.0/tests/features/program/test_rules.py +316 -0
  273. get_fsa_training_done-1.0.0/tests/features/program/test_schemas.py +103 -0
  274. get_fsa_training_done-1.0.0/tests/features/program/test_syllabus.py +175 -0
  275. get_fsa_training_done-1.0.0/tests/features/program/test_xlsx.py +409 -0
  276. get_fsa_training_done-1.0.0/tests/fixtures/assessment/capstone/mkp_capstone_project_01.md +81 -0
  277. get_fsa_training_done-1.0.0/tests/fixtures/assessment/capstone/mkp_capstone_project_01_rubric.md +156 -0
  278. get_fsa_training_done-1.0.0/tests/fixtures/assessment/capstone/mkp_capstone_project_01_spec.md +108 -0
  279. get_fsa_training_done-1.0.0/tests/fixtures/assessment/long_form/sbf_practice_exam_02.md +161 -0
  280. get_fsa_training_done-1.0.0/tests/fixtures/assessment/long_form/sbf_practice_exam_02_rubric.md +270 -0
  281. get_fsa_training_done-1.0.0/tests/fixtures/assessment/question_set/quiz_blooket.csv +8 -0
  282. get_fsa_training_done-1.0.0/tests/fixtures/assessment/question_set/quiz_master.csv +7 -0
  283. get_fsa_training_done-1.0.0/tests/fixtures/assessment/question_set/theory_coderbyte.json +58 -0
  284. get_fsa_training_done-1.0.0/tests/fixtures/assessment/question_set/theory_master.csv +7 -0
  285. get_fsa_training_done-1.0.0/tests/fixtures/assessment/written/dbf_quiz_02.md +97 -0
  286. get_fsa_training_done-1.0.0/tests/fixtures/assessment/written/dbf_quiz_02_rubric.md +221 -0
  287. get_fsa_training_done-1.0.0/tests/fixtures/assessment/written/jcf_theory_exam_01.md +88 -0
  288. get_fsa_training_done-1.0.0/tests/fixtures/assessment/written/jcf_theory_exam_01_answer_template.md +104 -0
  289. get_fsa_training_done-1.0.0/tests/fixtures/assessment/written/jcf_theory_exam_01_rubric.md +107 -0
  290. get_fsa_training_done-1.0.0/tests/lifecycle/__init__.py +0 -0
  291. get_fsa_training_done-1.0.0/tests/lifecycle/test_cli.py +34 -0
  292. get_fsa_training_done-1.0.0/tests/lifecycle/test_install.py +214 -0
  293. get_fsa_training_done-1.0.0/tests/lifecycle/test_reexec.py +45 -0
  294. get_fsa_training_done-1.0.0/tests/test_consistency.py +268 -0
@@ -0,0 +1,14 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ .eggs/
5
+ build/
6
+ dist/
7
+ .venv/
8
+ venv/
9
+ .pytest_cache/
10
+ .ruff_cache/
11
+ .mypy_cache/
12
+ node_modules/
13
+ .DS_Store
14
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Phuong Bui
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,138 @@
1
+ Metadata-Version: 2.5
2
+ Name: get-fsa-training-done
3
+ Version: 1.0.0
4
+ Summary: One agent skill, and the CLI behind it, for getting FSA training done: programmes, teaching material and assessments. Installs into Claude Code and Codex.
5
+ Project-URL: Homepage, https://github.com/phuongbv00/get-fsa-training-done
6
+ Project-URL: Issues, https://github.com/phuongbv00/get-fsa-training-done/issues
7
+ Author: Phuong Bui
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: agent-skill,assessment,claude-code,codex,curriculum,grading
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Education
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Education :: Testing
19
+ Requires-Python: >=3.12
20
+ Description-Content-Type: text/markdown
21
+
22
+ # get-fsa-training-done
23
+
24
+ One agent skill, and the CLI behind it, for getting FSA training done:
25
+ designing a programme, writing its teaching material, and designing and grading
26
+ its assessments. It installs into [Claude Code](https://claude.com/claude-code)
27
+ and the [Codex CLI](https://developers.openai.com/codex/cli).
28
+
29
+ The skill has three features, each with its own CLI namespace:
30
+
31
+ | Namespace | What it does | Docs |
32
+ | --- | --- | --- |
33
+ | `program` | design a training programme and export its workbooks | [README](src/get_fsa_training_done/features/program/README.md) |
34
+ | `material` | write lecture notes, handbooks, lab guides and worksheets | [README](src/get_fsa_training_done/features/material/README.md) |
35
+ | `assessment` | design and grade quizzes, assignments, exams and capstones | [README](src/get_fsa_training_done/features/assessment/README.md) |
36
+
37
+ They divide one job. A programme's session plan names, in its
38
+ `Training Materials` column, the file that serves each session — so `program`
39
+ declares that a slot exists and what it weighs, `material` writes the lecture
40
+ note or lab guide, and `assessment` writes the quiz, brief or exam. No feature
41
+ authors another's artifact, and each checks its own half of that manifest.
42
+
43
+ The skill is built from **tasks** — one step each, such as writing a rubric,
44
+ rendering a brief, or merging a retake — composed into **workflows**. Ask for a
45
+ whole job and the agent follows the workflow; ask for one step and it runs that
46
+ task on its own.
47
+
48
+ The skill is **stateless**. It assumes nothing about the directory layout it
49
+ was installed into and never scans a project on a hunch — it asks for its
50
+ inputs first, echoes back the resolved paths, and waits for a go-ahead.
51
+
52
+ ## Install
53
+
54
+ ```bash
55
+ pip install get-fsa-training-done
56
+ ```
57
+
58
+ This gives you the `get-fsa-training-done` command and its short alias `gftd`.
59
+ It needs Python 3.12+.
60
+
61
+ Then install the skill into your agent:
62
+
63
+ ```bash
64
+ get-fsa-training-done install --platform all
65
+ ```
66
+
67
+ | Command | Effect |
68
+ | --- | --- |
69
+ | `get-fsa-training-done install --platform claude --scope user` | `~/.claude/skills/get-fsa-training-done` |
70
+ | `get-fsa-training-done install --platform claude --scope project` | `./.claude/skills/get-fsa-training-done` |
71
+ | `get-fsa-training-done install --platform codex --scope user` | `~/.codex/skills/get-fsa-training-done` |
72
+ | `get-fsa-training-done install --platform codex --scope project` | `./.codex/skills/get-fsa-training-done` |
73
+ | `get-fsa-training-done update` | upgrade in place, preserving files you edited |
74
+ | `get-fsa-training-done uninstall --platform all` | remove exactly what was installed |
75
+ | `get-fsa-training-done status` | what is installed where, and whether it drifted |
76
+ | `get-fsa-training-done doctor` | check this machine for the skill's prerequisites |
77
+
78
+ Add `--dry-run` to any of `install`, `update`, or `uninstall` to see the exact
79
+ file-by-file plan without touching anything.
80
+
81
+ ## Dependencies
82
+
83
+ Installing the package pulls nothing: `dependencies = []` is deliberate, so the
84
+ lifecycle commands run on a bare interpreter. The libraries the worker commands
85
+ need live in the **managed virtualenv** instead — declared once in
86
+ `src/get_fsa_training_done/lifecycle/envmgr/requirements/core.txt`:
87
+
88
+ | Library | Used by |
89
+ | --- | --- |
90
+ | `xhtml2pdf` | `assessment render`, for the brief PDF |
91
+ | `py7zr` | `assessment grade preprocess`, for `.7z` submissions |
92
+
93
+ `get-fsa-training-done install` builds that environment, so the first install
94
+ needs a network and everything after it does not — which is the property that
95
+ matters on an exam machine.
96
+
97
+ Two external tools are optional, and `doctor` reports each without failing for
98
+ its absence:
99
+
100
+ - **A `.rar` extractor** (`unar`, `7z` or `bsdtar`). `.rar` is proprietary and
101
+ has no pure-Python reader; every other archive format is read in-process.
102
+ - **Docker**, for `assessment sandbox`: running a seed script, a JUnit suite or
103
+ a pytest run that is not ours — an exam's supplied files, or a submission —
104
+ in a disposable container with no network.
105
+
106
+ Rendering embeds its own fonts (see
107
+ [`core/fonts/`](src/get_fsa_training_done/features/assessment/core/fonts)), so a
108
+ Vietnamese brief comes out identical on every machine.
109
+
110
+ ## Layout
111
+
112
+ ```
113
+ src/get_fsa_training_done/
114
+ cli.py skill.py the CLI, and the one skill it installs
115
+ lifecycle/ install, update, uninstall, status, doctor, env
116
+ features/ program, material, assessment (+ common)
117
+ payload/get-fsa-training-done/
118
+ SKILL.md the router: workflows and tasks per feature
119
+ references/<feature>/ overview, tasks/, workflows/, generated references
120
+ references/common/ writing style, Vietnamese conventions, shared tasks
121
+ ```
122
+
123
+ ## Development
124
+
125
+ ```bash
126
+ pip install -e .
127
+ pytest
128
+ ruff check . && ruff format --check .
129
+ python scripts/sync_version.py --check
130
+ for generator in scripts/*/gen_*.py; do python "$generator" --check; done
131
+ ```
132
+
133
+ `src/get_fsa_training_done/__about__.py` holds the canonical version;
134
+ `sync_version.py` propagates it to the payload's `VERSION`.
135
+
136
+ ## Licence
137
+
138
+ MIT
@@ -0,0 +1,117 @@
1
+ # get-fsa-training-done
2
+
3
+ One agent skill, and the CLI behind it, for getting FSA training done:
4
+ designing a programme, writing its teaching material, and designing and grading
5
+ its assessments. It installs into [Claude Code](https://claude.com/claude-code)
6
+ and the [Codex CLI](https://developers.openai.com/codex/cli).
7
+
8
+ The skill has three features, each with its own CLI namespace:
9
+
10
+ | Namespace | What it does | Docs |
11
+ | --- | --- | --- |
12
+ | `program` | design a training programme and export its workbooks | [README](src/get_fsa_training_done/features/program/README.md) |
13
+ | `material` | write lecture notes, handbooks, lab guides and worksheets | [README](src/get_fsa_training_done/features/material/README.md) |
14
+ | `assessment` | design and grade quizzes, assignments, exams and capstones | [README](src/get_fsa_training_done/features/assessment/README.md) |
15
+
16
+ They divide one job. A programme's session plan names, in its
17
+ `Training Materials` column, the file that serves each session — so `program`
18
+ declares that a slot exists and what it weighs, `material` writes the lecture
19
+ note or lab guide, and `assessment` writes the quiz, brief or exam. No feature
20
+ authors another's artifact, and each checks its own half of that manifest.
21
+
22
+ The skill is built from **tasks** — one step each, such as writing a rubric,
23
+ rendering a brief, or merging a retake — composed into **workflows**. Ask for a
24
+ whole job and the agent follows the workflow; ask for one step and it runs that
25
+ task on its own.
26
+
27
+ The skill is **stateless**. It assumes nothing about the directory layout it
28
+ was installed into and never scans a project on a hunch — it asks for its
29
+ inputs first, echoes back the resolved paths, and waits for a go-ahead.
30
+
31
+ ## Install
32
+
33
+ ```bash
34
+ pip install get-fsa-training-done
35
+ ```
36
+
37
+ This gives you the `get-fsa-training-done` command and its short alias `gftd`.
38
+ It needs Python 3.12+.
39
+
40
+ Then install the skill into your agent:
41
+
42
+ ```bash
43
+ get-fsa-training-done install --platform all
44
+ ```
45
+
46
+ | Command | Effect |
47
+ | --- | --- |
48
+ | `get-fsa-training-done install --platform claude --scope user` | `~/.claude/skills/get-fsa-training-done` |
49
+ | `get-fsa-training-done install --platform claude --scope project` | `./.claude/skills/get-fsa-training-done` |
50
+ | `get-fsa-training-done install --platform codex --scope user` | `~/.codex/skills/get-fsa-training-done` |
51
+ | `get-fsa-training-done install --platform codex --scope project` | `./.codex/skills/get-fsa-training-done` |
52
+ | `get-fsa-training-done update` | upgrade in place, preserving files you edited |
53
+ | `get-fsa-training-done uninstall --platform all` | remove exactly what was installed |
54
+ | `get-fsa-training-done status` | what is installed where, and whether it drifted |
55
+ | `get-fsa-training-done doctor` | check this machine for the skill's prerequisites |
56
+
57
+ Add `--dry-run` to any of `install`, `update`, or `uninstall` to see the exact
58
+ file-by-file plan without touching anything.
59
+
60
+ ## Dependencies
61
+
62
+ Installing the package pulls nothing: `dependencies = []` is deliberate, so the
63
+ lifecycle commands run on a bare interpreter. The libraries the worker commands
64
+ need live in the **managed virtualenv** instead — declared once in
65
+ `src/get_fsa_training_done/lifecycle/envmgr/requirements/core.txt`:
66
+
67
+ | Library | Used by |
68
+ | --- | --- |
69
+ | `xhtml2pdf` | `assessment render`, for the brief PDF |
70
+ | `py7zr` | `assessment grade preprocess`, for `.7z` submissions |
71
+
72
+ `get-fsa-training-done install` builds that environment, so the first install
73
+ needs a network and everything after it does not — which is the property that
74
+ matters on an exam machine.
75
+
76
+ Two external tools are optional, and `doctor` reports each without failing for
77
+ its absence:
78
+
79
+ - **A `.rar` extractor** (`unar`, `7z` or `bsdtar`). `.rar` is proprietary and
80
+ has no pure-Python reader; every other archive format is read in-process.
81
+ - **Docker**, for `assessment sandbox`: running a seed script, a JUnit suite or
82
+ a pytest run that is not ours — an exam's supplied files, or a submission —
83
+ in a disposable container with no network.
84
+
85
+ Rendering embeds its own fonts (see
86
+ [`core/fonts/`](src/get_fsa_training_done/features/assessment/core/fonts)), so a
87
+ Vietnamese brief comes out identical on every machine.
88
+
89
+ ## Layout
90
+
91
+ ```
92
+ src/get_fsa_training_done/
93
+ cli.py skill.py the CLI, and the one skill it installs
94
+ lifecycle/ install, update, uninstall, status, doctor, env
95
+ features/ program, material, assessment (+ common)
96
+ payload/get-fsa-training-done/
97
+ SKILL.md the router: workflows and tasks per feature
98
+ references/<feature>/ overview, tasks/, workflows/, generated references
99
+ references/common/ writing style, Vietnamese conventions, shared tasks
100
+ ```
101
+
102
+ ## Development
103
+
104
+ ```bash
105
+ pip install -e .
106
+ pytest
107
+ ruff check . && ruff format --check .
108
+ python scripts/sync_version.py --check
109
+ for generator in scripts/*/gen_*.py; do python "$generator" --check; done
110
+ ```
111
+
112
+ `src/get_fsa_training_done/__about__.py` holds the canonical version;
113
+ `sync_version.py` propagates it to the payload's `VERSION`.
114
+
115
+ ## Licence
116
+
117
+ MIT
@@ -0,0 +1,69 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.25"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "get-fsa-training-done"
7
+ dynamic = ["version"]
8
+ description = "One agent skill, and the CLI behind it, for getting FSA training done: programmes, teaching material and assessments. Installs into Claude Code and Codex."
9
+ readme = "README.md"
10
+ requires-python = ">=3.12"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Phuong Bui" }]
13
+ keywords = ["assessment", "curriculum", "grading", "claude-code", "codex", "agent-skill"]
14
+ classifiers = [
15
+ "Development Status :: 5 - Production/Stable",
16
+ "Environment :: Console",
17
+ "Intended Audience :: Education",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.12",
21
+ "Programming Language :: Python :: 3.13",
22
+ "Topic :: Education :: Testing",
23
+ ]
24
+
25
+ # The package itself declares nothing, deliberately: `pip install
26
+ # get-fsa-training-done` pulls no transitive tree, and every lifecycle command
27
+ # (install, update, status, doctor) runs on a bare interpreter.
28
+ #
29
+ # The libraries the *workers* need live in the managed venv instead, listed in
30
+ # `envmgr/requirements/core.txt` and pre-warmed by `install`. So the offline
31
+ # guarantee still holds — after the first install, which needs a network.
32
+ dependencies = []
33
+
34
+ [project.urls]
35
+ Homepage = "https://github.com/phuongbv00/get-fsa-training-done"
36
+ Issues = "https://github.com/phuongbv00/get-fsa-training-done/issues"
37
+
38
+ [project.scripts]
39
+ get-fsa-training-done = "get_fsa_training_done.cli:run"
40
+ gftd = "get_fsa_training_done.cli:run"
41
+
42
+ [tool.hatch.version]
43
+ path = "src/get_fsa_training_done/__about__.py"
44
+
45
+ [tool.hatch.build.targets.wheel]
46
+ # The skill's payload is data inside this one package, so the default
47
+ # packages declaration already covers everything — no force-include needed.
48
+ packages = ["src/get_fsa_training_done"]
49
+
50
+ [tool.hatch.build.targets.sdist]
51
+ include = [
52
+ "/src",
53
+ "/tests",
54
+ "/scripts",
55
+ "/README.md",
56
+ "/LICENSE",
57
+ ]
58
+
59
+ [tool.ruff]
60
+ line-length = 100
61
+ target-version = "py312"
62
+
63
+ [tool.ruff.lint]
64
+ select = ["E", "F", "I", "UP", "B", "W"]
65
+ ignore = ["B008"]
66
+
67
+ [tool.pytest.ini_options]
68
+ testpaths = ["tests"]
69
+ addopts = "-q"
@@ -0,0 +1,158 @@
1
+ #!/usr/bin/env python3
2
+ """Generate the assessment skill's `references/levels.md` from the shared level table.
3
+
4
+ The model reads the Markdown; `verify` and the CLI read the Python. Writing the
5
+ Markdown by hand would guarantee they drift, and drift here is invisible — the
6
+ workflow would quietly calibrate to numbers the verifier does not check against.
7
+ So the table is generated, and `--check` fails CI when it is stale.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import argparse
13
+ import sys
14
+ from pathlib import Path
15
+
16
+ ROOT = Path(__file__).resolve().parents[2]
17
+ sys.path.insert(0, str(ROOT / "src"))
18
+
19
+ from get_fsa_training_done.features.common import levels as L # noqa: E402
20
+
21
+ TARGET = (
22
+ ROOT
23
+ / "src"
24
+ / "get_fsa_training_done"
25
+ / "payload"
26
+ / "get-fsa-training-done"
27
+ / "references"
28
+ / "assessment"
29
+ / "levels.md"
30
+ )
31
+
32
+ HEADER = """# Levels
33
+
34
+ Every assessment is calibrated for exactly one level. It is a required input
35
+ when planning, and it changes the Bloom mix, the difficulty mix, the duration,
36
+ how much of the specification is handed over, and how strict the rubric is.
37
+
38
+ Two axes, because they answer different questions:
39
+
40
+ - **UP_SKILL** — same technology stack, greater depth. The candidate already
41
+ works in this stack; the assessment probes how far.
42
+ - **RE_SKILL** — an experienced engineer arriving from a *different* stack.
43
+ Transferable reasoning is high but stack-specific recall is not assumed, so
44
+ these assessments use migration, integration, and modernisation framing and
45
+ never reward knowing an idiom by heart.
46
+
47
+ `CPL` and `FR` have no bands. `UP_SKILL` and `RE_SKILL` each split into
48
+ `junior` / `mid` / `senior`.
49
+
50
+ The level goes in the display code and a `Level:` banner line — **never in the
51
+ filename stem**, which would break rubric discovery and the submission archive
52
+ naming the grading pipeline depends on.
53
+
54
+ > These are defaults. Confirm them with the user when planning and adjust when
55
+ > the subject warrants it; state any deviation in the plan echo.
56
+ > `get-fsa-training-done assessment verify` reports drift from the level default
57
+ > as a warning, not an error.
58
+
59
+ <!-- Generated by scripts/assessment/gen_levels_md.py from
60
+ src/get_fsa_training_done/features/common/levels.py.
61
+ Do not edit by hand; edit the Python and regenerate. -->
62
+
63
+ ## At a glance
64
+
65
+ """
66
+
67
+
68
+ def render() -> str:
69
+ lines = [HEADER.rstrip("\n"), ""]
70
+
71
+ bloom_cols = " | ".join(name[:2] for name in L.BLOOM_ORDER)
72
+ difficulty_cols = " | ".join(L.DIFFICULTY_ORDER)
73
+ lines.append(f"| Level | Audience | {bloom_cols} | {difficulty_cols} | Tasks | Duration |")
74
+ numeric = len(L.BLOOM_ORDER) + len(L.DIFFICULTY_ORDER)
75
+ lines.append("|---|---|" + "---:|" * numeric + "---:|---:|")
76
+ for level in L.LEVELS:
77
+ bloom = " | ".join(str(level.bloom.get(name, 0)) for name in L.BLOOM_ORDER)
78
+ difficulty = " | ".join(str(level.difficulty.get(name, 0)) for name in L.DIFFICULTY_ORDER)
79
+ tasks = f"{level.task_range[0]}-{level.task_range[1]}"
80
+ lines.append(
81
+ f"| **{level.display}** | {level.audience} | {bloom} | {difficulty} | "
82
+ f"{tasks} | {level.duration_factor}x |"
83
+ )
84
+
85
+ lines.append("")
86
+ lines.append(
87
+ f"Bloom columns are {', '.join(L.BLOOM_ORDER)}, as percentages of the question set. "
88
+ "Duration is a multiplier on the baseline you would set for a fresher."
89
+ )
90
+ lines.append("")
91
+ lines.append("## Per level")
92
+ lines.append("")
93
+
94
+ for level in L.LEVELS:
95
+ lines.append(f"### {level.display}")
96
+ lines.append("")
97
+ lines.append(f"- **Audience** — {level.audience}")
98
+ lines.append(f"- **Scope** — {level.scope}")
99
+ lines.append(f"- **Rubric posture** — {level.rubric_posture}")
100
+ lines.append(
101
+ "- **Mechanisms** — "
102
+ + (
103
+ "may be named directly in the brief"
104
+ if level.may_name_mechanism
105
+ else "state the outcome and let the candidate choose the mechanism; "
106
+ "the rubric still names it so criteria stay observable"
107
+ )
108
+ )
109
+ seconds = ", ".join(
110
+ f"{name} {level.time_map[name]}s"
111
+ for name in L.DIFFICULTY_ORDER
112
+ if name in level.time_map
113
+ )
114
+ lines.append(f"- **Seconds per question** — {seconds} (quiz always uses 5/10/20)")
115
+ if level.notes:
116
+ lines.append(f"- **Note** — {level.notes}")
117
+ lines.append("")
118
+
119
+ lines.append("## Question counts")
120
+ lines.append("")
121
+ lines.append(
122
+ "Percentages become whole questions by largest remainder, so the counts always "
123
+ "sum to the requested total. Ask the CLI rather than doing the arithmetic:"
124
+ )
125
+ lines.append("")
126
+ lines.append("```bash")
127
+ lines.append(
128
+ "get-fsa-training-done assessment levels show --level UP_SKILL --band mid --count 40"
129
+ )
130
+ lines.append("```")
131
+ lines.append("")
132
+ return "\n".join(lines)
133
+
134
+
135
+ def main() -> int:
136
+ parser = argparse.ArgumentParser(description=__doc__)
137
+ parser.add_argument("--check", action="store_true", help="fail if the file is stale")
138
+ args = parser.parse_args()
139
+
140
+ content = render()
141
+ current = TARGET.read_text(encoding="utf-8") if TARGET.is_file() else ""
142
+
143
+ if args.check:
144
+ if current != content:
145
+ print(f"ERROR: {TARGET.relative_to(ROOT)} is out of date", file=sys.stderr)
146
+ print("run `python scripts/assessment/gen_levels_md.py` to regenerate", file=sys.stderr)
147
+ return 1
148
+ print("levels.md is up to date")
149
+ return 0
150
+
151
+ TARGET.parent.mkdir(parents=True, exist_ok=True)
152
+ TARGET.write_text(content, encoding="utf-8")
153
+ print(f"wrote {TARGET.relative_to(ROOT)} ({len(L.LEVELS)} levels)")
154
+ return 0
155
+
156
+
157
+ if __name__ == "__main__":
158
+ raise SystemExit(main())
@@ -0,0 +1,155 @@
1
+ #!/usr/bin/env python3
2
+ """Generate the material skill's `references/structure.md` from `core/grammar.py`.
3
+
4
+ The model writes documents from this file; `verify` checks them against the
5
+ Python. Prose describing a required section the checker does not enforce would
6
+ calibrate the model to a constraint nothing holds it to — and prose omitting one
7
+ it does enforce produces documents that fail a check the docs never mentioned.
8
+ So the templates and the rulebook are both generated, and `--check` fails CI
9
+ when either is stale.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import argparse
15
+ import sys
16
+ from pathlib import Path
17
+
18
+ ROOT = Path(__file__).resolve().parents[2]
19
+ sys.path.insert(0, str(ROOT / "src"))
20
+
21
+ from get_fsa_training_done.features.material.core import grammar as G # noqa: E402
22
+ from get_fsa_training_done.features.material.core import rules as R # noqa: E402
23
+
24
+ TARGET = (
25
+ ROOT
26
+ / "src"
27
+ / "get_fsa_training_done"
28
+ / "payload"
29
+ / "get-fsa-training-done"
30
+ / "references"
31
+ / "material"
32
+ / "structure.md"
33
+ )
34
+
35
+ HEADER = """# Document structure
36
+
37
+ What each kind of teaching document is shaped like, and every rule
38
+ `FSA material verify` applies. Both come from the same definitions the checker
39
+ executes, so nothing here is a constraint the tool does not enforce.
40
+
41
+ **Write new lecture notes as a unit.** The chapter form is older, still used by
42
+ two modules, and is recognised so those files can be checked rather than
43
+ rewritten — not because it is a choice for new work.
44
+
45
+ Universal, whatever the kind:
46
+
47
+ - Exactly one `#` heading, on line 1. No YAML front matter.
48
+ - Every fenced block declares a language. `text` is the right tag for the blocks
49
+ that are not code — diagrams, console output, directory trees.
50
+ - Relative links resolve, and so do their `#anchors`.
51
+ - English, unless the filename ends `_vn` to mark a translation.
52
+
53
+ <!-- Generated by scripts/material/gen_structure_md.py from
54
+ src/get_fsa_training_done/features/material/core/{grammar,rules}.py.
55
+ Do not edit by hand; edit the Python and regenerate. -->
56
+ """
57
+
58
+
59
+ def render() -> str:
60
+ lines = [HEADER, "\n## Naming\n"]
61
+ lines.append(f"- Lecture notes, handbooks and appendices: `{G.NOTE_FILENAME.pattern}`")
62
+ lines.append(f"- Lab guides: `{G.LAB_FILENAME.pattern}`")
63
+ lines.append(f"- Index `{G.HANDBOOK_INDEX}` is the module handbook.")
64
+ lines.append(f"- Index `{G.APPENDIX_INDEX}` is the module appendix.")
65
+ lines.append(
66
+ "- A trailing letter marks an aside outside the syllabus (`02b_...`).\n"
67
+ f"- A translation appends `{G.TRANSLATION_SUFFIX}` to the stem.\n"
68
+ )
69
+
70
+ for template in G.TEMPLATES:
71
+ lines.append(f"\n## {template.title}\n")
72
+ lines.append(f"{template.summary}\n")
73
+ if template.numbered_sections:
74
+ lines.append(
75
+ "Sections between the opening and the closing ones are numbered "
76
+ "`1..n`, contiguously, and named by the author.\n"
77
+ )
78
+ if template.slots:
79
+ lines.append("| Section | Required | Holds |")
80
+ lines.append("|---|---|---|")
81
+ for slot in template.slots:
82
+ required = "yes" if slot.required else "optional"
83
+ lines.append(f"| `## {slot.label}` | {required} | {slot.description} |")
84
+ lines.append("")
85
+ for slot in template.slots:
86
+ if slot.opening is not None:
87
+ lines.append(
88
+ f"`## {slot.label}` must open with `{slot.opening.pattern.lstrip('^')}`.\n"
89
+ )
90
+ if template.footer_description:
91
+ lines.append(f"Ends with {template.footer_description}\n")
92
+
93
+ lines.append("\n## Fence languages\n")
94
+ lines.append(
95
+ "Anything outside this set is reported as a warning rather than an "
96
+ "error — a module may legitimately introduce a language.\n"
97
+ )
98
+ lines.append(", ".join(f"`{name}`" for name in G.FENCE_LANGUAGES) + "\n")
99
+
100
+ for scope, title, blurb in (
101
+ ("document", "Rules — one document", "Applied to every file `verify` reads."),
102
+ (
103
+ "coverage",
104
+ "Rules — material against the session plan",
105
+ "Applied by `coverage`, comparing a folder with the plan that asks for it.",
106
+ ),
107
+ ):
108
+ in_scope = [rule for rule in R.RULES if rule.scope == scope]
109
+ lines.append(f"\n## {title}\n")
110
+ lines.append(f"{blurb}\n")
111
+ lines.append("| Rule | Severity | Checks |")
112
+ lines.append("|---|---|---|")
113
+ for rule in in_scope:
114
+ lines.append(f"| `{rule.id}` | {rule.severity} | {rule.title} |")
115
+ lines.append("")
116
+ for rule in in_scope:
117
+ lines.append(f"### `{rule.id}` — {rule.title}\n")
118
+ lines.append(f"{rule.rationale}\n")
119
+
120
+ counts = {
121
+ severity: sum(1 for rule in R.RULES if rule.severity == severity)
122
+ for severity in ("error", "warning")
123
+ }
124
+ lines.append(
125
+ f"---\n\n{len(R.RULES)} rules: {counts['error']} errors, {counts['warning']} warnings.\n"
126
+ )
127
+ return "\n".join(lines)
128
+
129
+
130
+ def main() -> int:
131
+ parser = argparse.ArgumentParser(description=__doc__)
132
+ parser.add_argument("--check", action="store_true", help="fail if the file is stale")
133
+ args = parser.parse_args()
134
+
135
+ content = render()
136
+ current = TARGET.read_text(encoding="utf-8") if TARGET.is_file() else ""
137
+
138
+ if args.check:
139
+ if current != content:
140
+ print(f"ERROR: {TARGET.relative_to(ROOT)} is out of date", file=sys.stderr)
141
+ print(
142
+ "run `python scripts/material/gen_structure_md.py` to regenerate", file=sys.stderr
143
+ )
144
+ return 1
145
+ print("structure.md is up to date")
146
+ return 0
147
+
148
+ TARGET.parent.mkdir(parents=True, exist_ok=True)
149
+ TARGET.write_text(content, encoding="utf-8")
150
+ print(f"wrote {TARGET.relative_to(ROOT)} ({len(G.TEMPLATES)} templates, {len(R.RULES)} rules)")
151
+ return 0
152
+
153
+
154
+ if __name__ == "__main__":
155
+ raise SystemExit(main())