Agent skill¶
cass ships a Claude Code skill. It tells an agent which cass command fits a Canvas task, how to preview writes safely, and which Canvas quirks to expect. The skill is bundled with cass, so it describes the commands of the version you have installed.
Install¶
cass skill install # copies the skill to ~/.claude/skills/cass
cass skill install --dir .claude/skills # project-level instead
cass skill install --yes # replace an existing copy without asking
Rerun it after upgrading cass to get the new skill.
Contents¶
The skill is SKILL.md, reproduced below, plus reference files the agent reads on demand:
| File | Covers |
|---|---|
references/quirks.md |
Canvas behaviors cass has verified, and what cass does about each one |
references/files-and-modules.md |
Uploads, folders, duplicate handling, module items, sync and --prune |
references/quizzes.md |
Quiz file format, question editing, previews, responses |
references/late-enrollments.md |
Finding late-enrolled students, excuse and clear-late |
The sources are in cass/skill/ in the repository. A test checks that every cass … command they mention exists and accepts the options shown.
The skill¶
cass is a CLI over the Canvas REST API for one course, set by cass.toml. --help on any command is the full truth; this skill tells you which command fits a task and what Canvas does that --help won't say.
Before you act¶
- Run
cass statusfirst. It shows the config path, course, auth source and pending local edits.cass status --jsongives the same thing as one object. - To target another course, pass
--config PATHbefore the subcommand, as incass --config ../f25/cass.toml canvas quizzes list, or setCASS_CONFIG. Credentials andcass.dbresolve beside that file. - On an error, rerun with
cass --debug …for the traceback. Exit 1 means the command failed. Exit 2 means a usage error.
Safety rules¶
- Dry run every write first. Every Canvas write takes
--dry-run, which prints the exact requests and sends none. The one exception iscass canvas quizzes preview, which leaves a teacher preview attempt on Canvas. Reconcile commands are dry runs unless you pass--apply:cass canvas sync,cass canvas quizzes update QUIZ --from FILE,cass canvas submissions excuseandcass canvas submissions clear-late. - Show the plan and wait. Paste the dry-run output to the user. Run the real command, or add
--apply, only after they agree. - Deletes need
--yes. You have no TTY, so a prompting command exits 1 without it. Pass--yesonly after the user approved that dry run. - Never publish unless asked. Don't pass
--publishor runpublishverbs unless the user asked.cass canvas files uploadmakes the file visible to students unless you pass--unpublished, so pass it by default. Announcements and calendar events are visible to students as soon as they're created; there is no unpublished state. - Leave out what you don't mean to change. In
cass.tomland quiz files, an omittedpublished, or any other omitted quiz setting, leaves the live value as it is. Only new content gets defaults: unpublished, one attempt. Writepublishedonly when the user asked to publish or unpublish. - Use the public API only. In Python, use
CanvasClientpublic methods. Never_client.
Task → command¶
| Task | Command |
|---|---|
| Course overview | cass canvas |
| List or inspect anything | cass canvas assignments list --json; show for one module, assignment or quiz, by ID or name |
| Roster, enrollment dates | cass canvas people list --json (--state completed, --enrolled-after DATE) |
| Module contents | cass canvas modules show "Week 1" --json |
| Add, edit, reorder or remove module items | cass canvas modules items add, update, move, remove |
| Declare modules, items, assignments and quizzes | edit cass.toml, then cass canvas sync, then cass canvas sync --apply |
| Remove undeclared module items | cass canvas sync --prune --apply |
| Readings and files | cass canvas files upload notes.pdf --folder readings/week1 --unpublished --dry-run |
| File tree, folders | cass canvas files list --json; cass canvas files folders list |
| Move, rename, hide or show a file | cass canvas files mv, rename, unpublish, publish |
| Create a quiz or survey | write a quiz file, then cass canvas quizzes create --from quizzes/survey.toml --dry-run |
| Edit quiz settings | cass canvas quizzes update QUIZ --from FILE, then add --apply |
| Edit quiz questions | cass canvas quizzes export QUIZ -o FILE, edit it, then cass canvas quizzes update QUIZ --from FILE --questions |
| See question IDs and groups, or a student's view | cass canvas quizzes questions QUIZ --json; cass canvas quizzes preview QUIZ |
| Pull survey or quiz answers | cass canvas quizzes responses QUIZ --csv responses.csv |
| Submissions and scores | cass canvas submissions list HW1 --json |
| Late-enrolled students | cass canvas submissions excuse --enrolled-after DATE; cass canvas submissions clear-late --enrolled-after DATE |
| Assignments, groups | cass canvas assignments create "HW 3" --group Labs --due "2026-10-20 23:59" --dry-run |
| Announcements | cass canvas announcements create "Title" -m "<p>Body</p>" --dry-run |
| Calendar | cass canvas calendar create "Review" --start "2026-10-20 10:00" --dry-run |
| Navigation tabs | cass canvas tabs list; cass canvas tabs hide Grades --dry-run |
| Read grades | cass pull, then cass query submissions --where "score < 50"; cass query gradebook for the student × assignment matrix |
Push grade edits made in cass view |
cass push --dry-run, then cass push --yes |
| Anything else | cass canvas api (see below) |
Commands take an ID or a name, and an ambiguous name fails and lists the matching IDs. Calendar events take IDs only. Times are course-local (YYYY-MM-DD HH:MM) unless they carry an offset.
Output¶
--jsonprints JSON Lines onlistcommands and a single object onshowandcass status --json, using Canvas field names. It can't be combined with--csvor--save.- stdout carries only data. Hints, progress, prompts and errors go to stderr, so pipes get clean output.
cass canvas files list --jsonaddskind(fileorfolder).cass canvas modules list --jsonhas no items, so usemodules show --jsonfor those.
Escape hatch¶
When no command fits, use the raw API. {course} expands to the course ID:
cass canvas api GET /courses/{course}/pages --all
cass canvas api GET /courses/{course}/quizzes --param 'include[]=assignment'
cass canvas api PUT /courses/{course}/modules/5 --data 'module[name]=Week 1' --dry-run
cass canvas api POST /courses/{course}/pages --json-body '{"wiki_page": {"title": "X"}}' --dry-run
cass canvas api writes don't prompt, so always --dry-run first. In Python, use the same client the CLI uses:
from cass.apis.canvas.client import CanvasAPIError, CanvasClient
from cass.apis.canvas.dry_run import format_plan
with CanvasClient.from_config("cass.toml", dry_run=True) as c: # or CanvasClient()
pages = c.get_all(c.course_path("/pages")) # follows pagination
c.request("PUT", c.course_path("/modules/5"), data=[("module[name]", "Week 1")])
print("\n".join(format_plan(c.planned_requests))) # writes recorded, not sent
CanvasAPIError has .status_code and .canvas_message. Pass repeated keys as lists of pairs.
Canvas quirks to remember¶
- New
ExternalUrlandSubHeadermodule items start unpublished. - Canvas refuses to unpublish an item whose content can't be unpublished, such as an assignment with submissions, and answers
403 item can't be unpublished. - Quiz questions: positions sent at creation are ignored, and
question_countupdates only after a re-save. cass handles both. Concluded courses return 403 on the questions endpoint. quizzes responseswaits about 30 seconds for Canvas's report. The Test Student is never in it.
References¶
Read these when the task needs them:
references/quirks.md: every verified Canvas quirk, with what cass does about it.references/files-and-modules.md: uploads, folders, duplicates, module items,syncand--prune.references/quizzes.md: quiz file format, question editing, previews, responses.references/late-enrollments.md:people list --enrolled-after,excuse,clear-late.