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.
- get_fsa_training_done-1.0.0/.gitignore +14 -0
- get_fsa_training_done-1.0.0/LICENSE +21 -0
- get_fsa_training_done-1.0.0/PKG-INFO +138 -0
- get_fsa_training_done-1.0.0/README.md +117 -0
- get_fsa_training_done-1.0.0/pyproject.toml +69 -0
- get_fsa_training_done-1.0.0/scripts/assessment/gen_levels_md.py +158 -0
- get_fsa_training_done-1.0.0/scripts/material/gen_structure_md.py +155 -0
- get_fsa_training_done-1.0.0/scripts/program/gen_rules_md.py +106 -0
- get_fsa_training_done-1.0.0/scripts/program/gen_schemas_md.py +112 -0
- get_fsa_training_done-1.0.0/scripts/sync_version.py +70 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/__about__.py +10 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/__init__.py +13 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/__main__.py +4 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/cli.py +94 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/errors.py +50 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/__init__.py +31 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/README.md +270 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/__init__.py +24 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/commands/__init__.py +0 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/commands/emit.py +59 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/commands/grade.py +413 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/commands/levels.py +115 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/commands/render.py +91 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/commands/sandbox.py +76 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/commands/sprint_kit.py +91 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/commands/verify.py +160 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/__init__.py +1 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/budget.py +93 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/emit/__init__.py +5 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/emit/blooket.py +69 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/emit/coderbyte.py +66 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/emit/master.py +107 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/fonts/DejaVuSans-Bold.ttf +0 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/fonts/DejaVuSans.ttf +0 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/fonts/LICENSE-DejaVu.txt +99 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/fonts/LICENSE-RobotoMono.txt +202 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/fonts/README.md +31 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/fonts/RobotoMono-Bold.ttf +0 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/fonts/RobotoMono-Regular.ttf +0 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/grading/__init__.py +32 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/grading/aggregate.py +244 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/grading/ai_cheat.py +574 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/grading/batches.py +97 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/grading/plagiarism.py +338 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/grading/preprocess.py +251 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/grading/quiz_merge.py +83 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/grading/quiz_scores.py +289 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/grading/retake.py +151 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/grading/roster.py +146 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/html.py +81 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/markdown.py +253 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/pdf.py +122 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/sandbox.py +424 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/sprintkit.py +636 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/verify/__init__.py +32 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/verify/answer_template.py +154 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/verify/calibration.py +93 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/verify/capstone.py +342 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/verify/common.py +160 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/verify/long_form.py +432 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/assessment/core/verify/question_set.py +272 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/common/__init__.py +2 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/common/findings.py +98 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/common/levels.py +296 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/material/README.md +168 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/material/__init__.py +24 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/material/commands/__init__.py +0 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/material/commands/coverage.py +61 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/material/commands/derive.py +80 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/material/commands/verify.py +91 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/material/core/__init__.py +1 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/material/core/appendix.py +98 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/material/core/checks.py +239 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/material/core/coverage.py +183 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/material/core/grammar.py +262 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/material/core/notes.py +161 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/material/core/rules.py +219 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/README.md +175 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/__init__.py +24 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/commands/__init__.py +0 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/commands/derive.py +156 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/commands/export.py +163 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/commands/verify.py +127 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/__init__.py +1 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/checks/__init__.py +33 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/checks/crosslink.py +90 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/checks/program.py +256 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/checks/topic.py +298 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/codes.py +105 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/contracts.py +127 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/csvio.py +50 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/derive/__init__.py +8 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/derive/allocation.py +78 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/derive/skeleton.py +103 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/loader.py +113 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/mdtable.py +79 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/program.py +127 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/rules.py +327 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/schedule.py +48 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/schemas.py +114 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/syllabus.py +240 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/xlsx/__init__.py +14 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/xlsx/package.py +278 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/xlsx/probe.py +84 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/xlsx/refs.py +62 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/xlsx/sheetxml.py +190 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/features/program/core/xlsx/syllabus_layout.py +270 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/__init__.py +3 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/commands/__init__.py +1 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/commands/common.py +154 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/commands/doctor.py +129 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/commands/env.py +69 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/commands/install.py +225 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/commands/status.py +110 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/commands/uninstall.py +111 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/commands/update.py +128 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/envmgr/__init__.py +5 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/envmgr/bootstrap.py +198 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/envmgr/reexec.py +69 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/envmgr/requirements/core.txt +13 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/envmgr/stamp.py +141 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/install/__init__.py +5 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/install/fsops.py +107 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/install/migrations.py +52 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/install/planner.py +159 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/install/receipt.py +147 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/install/removal.py +64 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/platforms/__init__.py +5 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/platforms/base.py +63 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/platforms/claude.py +32 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/platforms/codex.py +129 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/platforms/registry.py +41 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/lifecycle/skillmeta.py +97 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/SKILL.md +165 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/VERSION +1 -0
- 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
- 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
- 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
- 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
- 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
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/grading_contract.md +124 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/levels.md +122 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/overview.md +72 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/question_guidelines.md +111 -0
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/assessment/verifiers/quiz.md +77 -0
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/common/language_vn.md +46 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/common/style.md +48 -0
- 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
- 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
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/material/conventions.md +101 -0
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/material/overview.md +56 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/material/structure.md +238 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/material/tasks/coverage.md +41 -0
- 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
- 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
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/material/tasks/verify.md +39 -0
- 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
- 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
- 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
- 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
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/material/templates/appendix.md +13 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/material/templates/handbook.md +15 -0
- 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
- 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
- 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
- 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
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/program/artifact_map.md +53 -0
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/program/overview.md +61 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/program/rules.md +226 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/program/schemas.md +81 -0
- 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
- 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
- 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
- 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
- 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
- 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
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/program/tasks/verify.md +43 -0
- 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
- 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
- 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
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/payload/get-fsa-training-done/references/program/workflows/revise.md +8 -0
- get_fsa_training_done-1.0.0/src/get_fsa_training_done/skill.py +45 -0
- get_fsa_training_done-1.0.0/tests/__init__.py +0 -0
- get_fsa_training_done-1.0.0/tests/conftest.py +24 -0
- get_fsa_training_done-1.0.0/tests/features/__init__.py +0 -0
- get_fsa_training_done-1.0.0/tests/features/assessment/__init__.py +0 -0
- get_fsa_training_done-1.0.0/tests/features/assessment/conftest.py +58 -0
- get_fsa_training_done-1.0.0/tests/features/assessment/test_consistency.py +23 -0
- get_fsa_training_done-1.0.0/tests/features/assessment/test_emit.py +138 -0
- get_fsa_training_done-1.0.0/tests/features/assessment/test_grading.py +453 -0
- get_fsa_training_done-1.0.0/tests/features/assessment/test_levels.py +91 -0
- get_fsa_training_done-1.0.0/tests/features/assessment/test_pdf.py +94 -0
- get_fsa_training_done-1.0.0/tests/features/assessment/test_quiz_scores.py +199 -0
- get_fsa_training_done-1.0.0/tests/features/assessment/test_render.py +44 -0
- get_fsa_training_done-1.0.0/tests/features/assessment/test_retake_and_quiz_merge.py +164 -0
- get_fsa_training_done-1.0.0/tests/features/assessment/test_sandbox.py +177 -0
- get_fsa_training_done-1.0.0/tests/features/assessment/test_sprint_kit.py +188 -0
- get_fsa_training_done-1.0.0/tests/features/assessment/test_verify.py +540 -0
- get_fsa_training_done-1.0.0/tests/features/assessment/test_written.py +211 -0
- get_fsa_training_done-1.0.0/tests/features/material/__init__.py +0 -0
- get_fsa_training_done-1.0.0/tests/features/material/conftest.py +102 -0
- get_fsa_training_done-1.0.0/tests/features/material/test_consistency.py +62 -0
- get_fsa_training_done-1.0.0/tests/features/material/test_derive.py +93 -0
- get_fsa_training_done-1.0.0/tests/features/material/test_rules.py +307 -0
- get_fsa_training_done-1.0.0/tests/features/program/__init__.py +0 -0
- get_fsa_training_done-1.0.0/tests/features/program/conftest.py +119 -0
- get_fsa_training_done-1.0.0/tests/features/program/synthetic.py +255 -0
- get_fsa_training_done-1.0.0/tests/features/program/test_consistency.py +60 -0
- get_fsa_training_done-1.0.0/tests/features/program/test_derive.py +230 -0
- get_fsa_training_done-1.0.0/tests/features/program/test_export.py +298 -0
- get_fsa_training_done-1.0.0/tests/features/program/test_parsers.py +118 -0
- get_fsa_training_done-1.0.0/tests/features/program/test_rules.py +316 -0
- get_fsa_training_done-1.0.0/tests/features/program/test_schemas.py +103 -0
- get_fsa_training_done-1.0.0/tests/features/program/test_syllabus.py +175 -0
- get_fsa_training_done-1.0.0/tests/features/program/test_xlsx.py +409 -0
- get_fsa_training_done-1.0.0/tests/fixtures/assessment/capstone/mkp_capstone_project_01.md +81 -0
- get_fsa_training_done-1.0.0/tests/fixtures/assessment/capstone/mkp_capstone_project_01_rubric.md +156 -0
- get_fsa_training_done-1.0.0/tests/fixtures/assessment/capstone/mkp_capstone_project_01_spec.md +108 -0
- get_fsa_training_done-1.0.0/tests/fixtures/assessment/long_form/sbf_practice_exam_02.md +161 -0
- get_fsa_training_done-1.0.0/tests/fixtures/assessment/long_form/sbf_practice_exam_02_rubric.md +270 -0
- get_fsa_training_done-1.0.0/tests/fixtures/assessment/question_set/quiz_blooket.csv +8 -0
- get_fsa_training_done-1.0.0/tests/fixtures/assessment/question_set/quiz_master.csv +7 -0
- get_fsa_training_done-1.0.0/tests/fixtures/assessment/question_set/theory_coderbyte.json +58 -0
- get_fsa_training_done-1.0.0/tests/fixtures/assessment/question_set/theory_master.csv +7 -0
- get_fsa_training_done-1.0.0/tests/fixtures/assessment/written/dbf_quiz_02.md +97 -0
- get_fsa_training_done-1.0.0/tests/fixtures/assessment/written/dbf_quiz_02_rubric.md +221 -0
- get_fsa_training_done-1.0.0/tests/fixtures/assessment/written/jcf_theory_exam_01.md +88 -0
- get_fsa_training_done-1.0.0/tests/fixtures/assessment/written/jcf_theory_exam_01_answer_template.md +104 -0
- get_fsa_training_done-1.0.0/tests/fixtures/assessment/written/jcf_theory_exam_01_rubric.md +107 -0
- get_fsa_training_done-1.0.0/tests/lifecycle/__init__.py +0 -0
- get_fsa_training_done-1.0.0/tests/lifecycle/test_cli.py +34 -0
- get_fsa_training_done-1.0.0/tests/lifecycle/test_install.py +214 -0
- get_fsa_training_done-1.0.0/tests/lifecycle/test_reexec.py +45 -0
- get_fsa_training_done-1.0.0/tests/test_consistency.py +268 -0
|
@@ -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())
|