@lukeguo12210/canvas-cli 0.0.1

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/redaction.ts","../../src/core/output.ts","../../src/core/errors.ts","../../src/core/pagination.ts","../../src/core/canvas-client.ts","../../src/core/config-store.ts","../../src/core/paths.ts","../../src/core/browser.ts","../../src/core/prompt.ts","../../src/registry/schools.ts","../../src/workflows/context-bootstrap.ts","../../src/commands/auth.ts","../../src/commands/config.ts","../../src/commands/shared.ts","../../src/commands/courses.ts","../../src/commands/me.ts","../../src/commands/tabs.ts","../../src/bin/canvas.ts"],"sourcesContent":["const REDACTED = \"[REDACTED]\";\n\nconst SECRET_KEY_PATTERN = /(^|[_-])(authorization|access[_-]?token|token|api[_-]?key|secret)$/i;\nconst BEARER_PATTERN = /Bearer\\s+[A-Za-z0-9._~+/=-]+/gi;\nconst QUERY_SECRET_PATTERN = /([?&](?:access_token|token|api_key|verifier|code)=)[^&#\\s]+/gi;\n\nexport function redactSecrets(value: unknown): unknown {\n if (typeof value === \"string\") {\n return redactString(value);\n }\n\n if (Array.isArray(value)) {\n return value.map((item) => redactSecrets(item));\n }\n\n if (value && typeof value === \"object\") {\n const output: Record<string, unknown> = {};\n for (const [key, nested] of Object.entries(value)) {\n output[key] = SECRET_KEY_PATTERN.test(key) ? REDACTED : redactSecrets(nested);\n }\n return output;\n }\n\n return value;\n}\n\nexport function redactString(value: string): string {\n return value\n .replace(BEARER_PATTERN, `Bearer ${REDACTED}`)\n .replace(QUERY_SECRET_PATTERN, `$1${REDACTED}`);\n}\n\nexport function redactedValue(): string {\n return REDACTED;\n}\n","import { redactSecrets } from \"./redaction.js\";\n\nexport type OutputFormat = \"json\" | \"pretty\" | \"table\" | \"ndjson\";\n\nexport type Success<T> = {\n ok: true;\n data: T;\n meta?: Record<string, unknown>;\n};\n\nexport type Failure = {\n ok: false;\n error: {\n code: string;\n message: string;\n status?: number;\n retryable: boolean;\n };\n};\n\nexport type ResultEnvelope<T> = Success<T> | Failure;\n\nexport function formatOutput<T>(\n result: ResultEnvelope<T>,\n options: { format?: OutputFormat } = {}\n): string {\n const format = options.format ?? \"json\";\n const safeResult = redactSecrets(result) as ResultEnvelope<T>;\n\n if (format === \"json\") {\n return `${JSON.stringify(safeResult, null, 2)}\\n`;\n }\n\n if (format === \"ndjson\") {\n return `${JSON.stringify(safeResult)}\\n`;\n }\n\n if (!safeResult.ok) {\n const status = safeResult.error.status ? ` (${safeResult.error.status})` : \"\";\n return `Error ${safeResult.error.code}${status}: ${safeResult.error.message}\\n`;\n }\n\n if (format === \"table\") {\n return tableOutput(safeResult.data);\n }\n\n return prettyOutput(safeResult.data);\n}\n\nexport async function writeOutput<T>(\n result: ResultEnvelope<T>,\n options: { format?: OutputFormat } = {}\n): Promise<void> {\n const stream = result.ok ? process.stdout : process.stderr;\n stream.write(formatOutput(result, options));\n}\n\nfunction prettyOutput(data: unknown): string {\n if (typeof data === \"string\") {\n return `${data}\\n`;\n }\n return `${JSON.stringify(data, null, 2)}\\n`;\n}\n\nfunction tableOutput(data: unknown): string {\n if (!Array.isArray(data)) {\n return prettyOutput(data);\n }\n\n if (data.length === 0) {\n return \"\\n\";\n }\n\n const rows = data.filter((row): row is Record<string, unknown> => {\n return row !== null && typeof row === \"object\" && !Array.isArray(row);\n });\n\n if (rows.length === 0) {\n return prettyOutput(data);\n }\n\n const columns = Object.keys(rows[0] ?? {});\n const widths = columns.map((column) =>\n Math.max(column.length, ...rows.map((row) => String(row[column] ?? \"\").length))\n );\n\n const line = (values: string[]) =>\n `${values.map((value, index) => value.padEnd(widths[index] ?? value.length)).join(\" \")}\\n`;\n\n let output = line(columns);\n output += line(widths.map((width) => \"-\".repeat(width)));\n for (const row of rows) {\n output += line(columns.map((column) => String(row[column] ?? \"\")));\n }\n return output;\n}\n","export class CanvasCliError extends Error {\n readonly code: string;\n readonly status?: number;\n readonly retryable: boolean;\n\n constructor(\n code: string,\n message: string,\n options: { status?: number; retryable?: boolean; cause?: unknown } = {}\n ) {\n super(message, { cause: options.cause });\n this.name = \"CanvasCliError\";\n this.code = code;\n this.status = options.status;\n this.retryable = options.retryable ?? false;\n }\n}\n\nexport function toErrorEnvelope(error: unknown) {\n if (error instanceof CanvasCliError) {\n return {\n ok: false as const,\n error: {\n code: error.code,\n message: error.message,\n status: error.status,\n retryable: error.retryable\n }\n };\n }\n\n const message = error instanceof Error ? error.message : String(error);\n return {\n ok: false as const,\n error: {\n code: \"UNEXPECTED_ERROR\",\n message,\n retryable: false\n }\n };\n}\n","export type LinkRelation = \"current\" | \"next\" | \"prev\" | \"first\" | \"last\";\n\nexport type ParsedLinks = Partial<Record<LinkRelation, string>>;\n\nexport function parseLinkHeader(header: string | null | undefined): ParsedLinks {\n if (!header) {\n return {};\n }\n\n const links: ParsedLinks = {};\n for (const part of splitHeader(header)) {\n const match = part.match(/^\\s*<([^>]+)>\\s*;\\s*rel=\"([^\"]+)\"\\s*$/i);\n if (!match) {\n continue;\n }\n const [, url, rel] = match;\n if (isLinkRelation(rel)) {\n links[rel] = url;\n }\n }\n return links;\n}\n\nfunction splitHeader(header: string): string[] {\n const parts: string[] = [];\n let current = \"\";\n let inQuotes = false;\n\n for (const char of header) {\n if (char === \"\\\"\") {\n inQuotes = !inQuotes;\n }\n if (char === \",\" && !inQuotes) {\n parts.push(current);\n current = \"\";\n continue;\n }\n current += char;\n }\n\n if (current.trim()) {\n parts.push(current);\n }\n\n return parts;\n}\n\nfunction isLinkRelation(value: string): value is LinkRelation {\n return [\"current\", \"next\", \"prev\", \"first\", \"last\"].includes(value);\n}\n","import { CanvasCliError } from \"./errors.js\";\nimport { parseLinkHeader } from \"./pagination.js\";\nimport { redactSecrets } from \"./redaction.js\";\n\nexport type CanvasClientOptions = {\n baseUrl: string;\n token: string;\n fetchImpl?: typeof fetch;\n};\n\nexport type RequestOptions = {\n query?: Record<string, string | number | boolean | Array<string | number | boolean> | undefined>;\n pageAll?: boolean;\n pageLimit?: number;\n};\n\nexport type CanvasResponse<T> = {\n data: T;\n meta: {\n request: {\n method: \"GET\";\n path: string;\n };\n pagination: {\n pagesFetched: number;\n hasNext: boolean;\n };\n };\n};\n\nexport class CanvasClient {\n private readonly baseUrl: string;\n private readonly token: string;\n private readonly fetchImpl: typeof fetch;\n\n constructor(options: CanvasClientOptions) {\n this.baseUrl = normalizeBaseUrl(options.baseUrl);\n this.token = options.token;\n this.fetchImpl = options.fetchImpl ?? fetch;\n }\n\n async get<T>(path: string, options: RequestOptions = {}): Promise<CanvasResponse<T>> {\n if (!path.startsWith(\"/api/v1/\")) {\n throw new CanvasCliError(\"INVALID_API_PATH\", \"Canvas API paths must start with /api/v1/.\");\n }\n\n let nextUrl: string | null = buildUrl(this.baseUrl, path, options.query);\n const pages: unknown[] = [];\n let pagesFetched = 0;\n const pageLimit = options.pageLimit ?? 50;\n\n let hasNext = false;\n\n while (nextUrl) {\n pagesFetched += 1;\n if (pagesFetched > pageLimit) {\n throw new CanvasCliError(\n \"PAGE_LIMIT_EXCEEDED\",\n `Stopped after ${pageLimit} pages. Increase --page-limit if needed.`\n );\n }\n\n const response = await this.fetchImpl(nextUrl, {\n method: \"GET\",\n headers: {\n Authorization: `Bearer ${this.token}`,\n Accept: \"application/json+canvas-string-ids\"\n }\n }).catch((error: unknown) => {\n throw new CanvasCliError(\"CANVAS_NETWORK_ERROR\", \"Could not reach Canvas.\", {\n retryable: true,\n cause: error\n });\n });\n\n if (!response.ok) {\n throw new CanvasCliError(\n mapStatusCode(response.status),\n `Canvas request failed with status ${response.status}.`,\n { status: response.status, retryable: response.status === 429 || response.status >= 500 }\n );\n }\n\n const data = (await response.json()) as unknown;\n pages.push(data);\n\n const links = parseLinkHeader(response.headers.get(\"link\"));\n hasNext = Boolean(links.next);\n nextUrl = options.pageAll ? links.next ?? null : null;\n }\n\n const merged = mergePages(pages) as T;\n return {\n data: redactSecrets(merged) as T,\n meta: {\n request: {\n method: \"GET\",\n path\n },\n pagination: {\n pagesFetched,\n hasNext\n }\n }\n };\n }\n}\n\nexport function normalizeBaseUrl(input: string): string {\n const trimmed = input.trim().replace(/\\/+$/, \"\");\n const url = new URL(trimmed);\n\n if (url.protocol !== \"https:\") {\n throw new CanvasCliError(\"INVALID_BASE_URL\", \"Canvas base URL must use https://.\");\n }\n\n if (url.pathname.includes(\"/api/\")) {\n throw new CanvasCliError(\"INVALID_BASE_URL\", \"Canvas base URL should not include /api/.\");\n }\n\n url.pathname = url.pathname.replace(/\\/+$/, \"\");\n url.search = \"\";\n url.hash = \"\";\n return url.toString().replace(/\\/+$/, \"\");\n}\n\nfunction buildUrl(\n baseUrl: string,\n path: string,\n query: RequestOptions[\"query\"] = {}\n): string {\n const url = new URL(path, baseUrl);\n for (const [key, value] of Object.entries(query)) {\n if (value === undefined) {\n continue;\n }\n if (Array.isArray(value)) {\n for (const item of value) {\n url.searchParams.append(key, String(item));\n }\n continue;\n }\n url.searchParams.set(key, String(value));\n }\n return url.toString();\n}\n\nfunction mergePages(pages: unknown[]): unknown {\n if (pages.length === 1) {\n return pages[0];\n }\n\n if (pages.every(Array.isArray)) {\n return pages.flat();\n }\n\n return pages;\n}\n\nfunction mapStatusCode(status: number): string {\n if (status === 401) return \"CANVAS_UNAUTHORIZED\";\n if (status === 403) return \"CANVAS_FORBIDDEN\";\n if (status === 404) return \"CANVAS_NOT_FOUND\";\n if (status === 429) return \"CANVAS_RATE_LIMITED\";\n if (status >= 500) return \"CANVAS_SERVER_ERROR\";\n return \"CANVAS_REQUEST_FAILED\";\n}\n","import { mkdir, readFile, rm, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport { CanvasCliError } from \"./errors.js\";\nimport { configPath } from \"./paths.js\";\nimport { redactSecrets } from \"./redaction.js\";\n\nexport type CanvasProfile = {\n schoolName: string;\n baseUrl: string;\n token: string;\n createdAt: string;\n validatedAt?: string;\n user?: {\n id?: string;\n name?: string;\n };\n};\n\nexport type CanvasConfig = {\n version: 1;\n activeProfile: string;\n profiles: Record<string, CanvasProfile>;\n};\n\nexport class ConfigStore {\n constructor(private readonly filePath = configPath()) {}\n\n async read(): Promise<CanvasConfig | null> {\n try {\n const raw = await readFile(this.filePath, \"utf8\");\n return JSON.parse(raw) as CanvasConfig;\n } catch (error) {\n if (isNotFound(error)) {\n return null;\n }\n throw error;\n }\n }\n\n async write(config: CanvasConfig): Promise<void> {\n await mkdir(dirname(this.filePath), { recursive: true, mode: 0o700 });\n await writeFile(this.filePath, `${JSON.stringify(config, null, 2)}\\n`, {\n encoding: \"utf8\",\n mode: 0o600\n });\n }\n\n async remove(): Promise<void> {\n await rm(this.filePath, { force: true });\n }\n\n async readRedacted(): Promise<CanvasConfig | null> {\n const config = await this.read();\n return config ? (redactSecrets(config) as CanvasConfig) : null;\n }\n\n async activeProfile(): Promise<CanvasProfile> {\n const config = await this.read();\n if (!config) {\n throw new CanvasCliError(\"NO_AUTH_CONFIG\", \"No Canvas auth config found. Run canvas auth login.\");\n }\n\n const profile = config.profiles[config.activeProfile];\n if (!profile) {\n throw new CanvasCliError(\n \"NO_ACTIVE_PROFILE\",\n `Active Canvas profile not found: ${config.activeProfile}`\n );\n }\n\n return profile;\n }\n}\n\nfunction isNotFound(error: unknown): boolean {\n return Boolean(error && typeof error === \"object\" && \"code\" in error && error.code === \"ENOENT\");\n}\n","import os from \"node:os\";\nimport path from \"node:path\";\n\nexport function canvasHome(): string {\n return process.env.CANVAS_HOME || path.join(os.homedir(), \".canvas\");\n}\n\nexport function configPath(): string {\n return path.join(canvasHome(), \"config.json\");\n}\n\nexport function contextPath(): string {\n return path.join(canvasHome(), \"context.json\");\n}\n\nexport function resolveInside(baseDir: string, targetPath: string): string {\n const resolvedBase = path.resolve(baseDir);\n const resolvedTarget = path.resolve(resolvedBase, targetPath);\n const relative = path.relative(resolvedBase, resolvedTarget);\n\n if (relative.startsWith(\"..\") || path.isAbsolute(relative)) {\n throw new Error(`Path escapes target directory: ${targetPath}`);\n }\n\n return resolvedTarget;\n}\n","import { spawn } from \"node:child_process\";\n\nexport async function openBrowser(url: string): Promise<void> {\n const command = openCommand(url);\n const child = spawn(command.command, command.args, {\n detached: true,\n stdio: \"ignore\"\n });\n child.unref();\n}\n\nfunction openCommand(url: string): { command: string; args: string[] } {\n if (process.platform === \"darwin\") {\n return { command: \"open\", args: [url] };\n }\n if (process.platform === \"win32\") {\n return { command: \"cmd\", args: [\"/c\", \"start\", \"\", url] };\n }\n return { command: \"xdg-open\", args: [url] };\n}\n","import { createInterface } from \"node:readline/promises\";\nimport { stdin as input, stdout as output } from \"node:process\";\n\nexport type PromptIO = {\n question(prompt: string): Promise<string>;\n close(): void;\n};\n\nexport function createPrompt(): PromptIO {\n return createInterface({ input, output });\n}\n\nexport async function promptHidden(prompt: string): Promise<string> {\n if (!process.stdin.isTTY) {\n const io = createPrompt();\n try {\n return (await io.question(prompt)).trim();\n } finally {\n io.close();\n }\n }\n\n return new Promise((resolve, reject) => {\n const stdin = process.stdin;\n const onData = (char: Buffer) => {\n const value = char.toString(\"utf8\");\n if (value === \"\\n\" || value === \"\\r\" || value === \"\\r\\n\") {\n stdin.setRawMode(false);\n stdin.pause();\n stdin.off(\"data\", onData);\n process.stdout.write(\"\\n\");\n resolve(buffer.trim());\n return;\n }\n if (value === \"\\u0003\") {\n stdin.setRawMode(false);\n stdin.pause();\n stdin.off(\"data\", onData);\n process.stdout.write(\"\\n\");\n reject(new Error(\"Prompt cancelled.\"));\n return;\n }\n if (value === \"\\u007f\") {\n buffer = buffer.slice(0, -1);\n return;\n }\n buffer += value;\n };\n\n let buffer = \"\";\n process.stdout.write(prompt);\n stdin.setRawMode(true);\n stdin.resume();\n stdin.on(\"data\", onData);\n });\n}\n","import { normalizeBaseUrl } from \"../core/canvas-client.js\";\n\nexport type School = {\n name: string;\n url: string;\n};\n\nexport const SCHOOLS: School[] = [\n { name: \"Brown University\", url: \"https://canvas.brown.edu\" },\n { name: \"Carnegie Mellon University\", url: \"https://canvas.cmu.edu\" },\n { name: \"Columbia University (CourseWorks)\", url: \"https://courseworks2.columbia.edu\" },\n { name: \"Cornell University\", url: \"https://canvas.cornell.edu\" },\n { name: \"Dartmouth College\", url: \"https://canvas.dartmouth.edu\" },\n { name: \"Duke University\", url: \"https://go.canvas.duke.edu\" },\n { name: \"Emory University\", url: \"https://canvas.emory.edu\" },\n { name: \"Georgetown University\", url: \"https://canvas.georgetown.edu\" },\n { name: \"Georgia Institute of Technology\", url: \"https://canvas.gatech.edu\" },\n { name: \"Harvard University\", url: \"https://canvas.harvard.edu\" },\n { name: \"Massachusetts Institute of Technology (MIT)\", url: \"https://canvas.mit.edu\" },\n { name: \"Northeastern University\", url: \"https://canvas.northeastern.edu\" },\n { name: \"Northwestern University\", url: \"https://canvas.northwestern.edu\" },\n { name: \"Ohio State University\", url: \"https://canvas.osu.edu\" },\n { name: \"Pennsylvania State University\", url: \"https://canvas.psu.edu\" },\n { name: \"Princeton University\", url: \"https://canvas.princeton.edu\" },\n { name: \"Rice University\", url: \"https://canvas.rice.edu\" },\n { name: \"Stanford University\", url: \"https://canvas.stanford.edu\" },\n { name: \"Tufts University\", url: \"https://canvas.tufts.edu\" },\n { name: \"University of California, Berkeley (bCourses)\", url: \"https://bcourses.berkeley.edu\" },\n { name: \"University of California, Davis\", url: \"https://canvas.ucdavis.edu\" },\n { name: \"University of California, Irvine\", url: \"https://canvas.eee.uci.edu\" },\n { name: \"University of California, Los Angeles (Bruin Learn)\", url: \"https://bruinlearn.ucla.edu\" },\n { name: \"University of California, San Diego\", url: \"https://canvas.ucsd.edu\" },\n { name: \"University of California, Santa Barbara\", url: \"https://canvas.ucsb.edu\" },\n { name: \"University of California, Santa Cruz\", url: \"https://canvas.ucsc.edu\" },\n { name: \"University of Chicago\", url: \"https://canvas.uchicago.edu\" },\n { name: \"University of Michigan\", url: \"https://canvas.umich.edu\" },\n { name: \"University of North Carolina at Chapel Hill\", url: \"https://canvas.unc.edu\" },\n { name: \"University of Notre Dame\", url: \"https://canvas.nd.edu\" },\n { name: \"University of Pennsylvania\", url: \"https://canvas.upenn.edu\" },\n { name: \"University of Southern California\", url: \"https://canvas.usc.edu\" },\n { name: \"University of Virginia\", url: \"https://canvas.its.virginia.edu\" },\n { name: \"University of Washington\", url: \"https://canvas.uw.edu\" },\n { name: \"Yale University\", url: \"https://canvas.yale.edu\" }\n];\n\nexport function searchSchools(query: string, limit = 10): School[] {\n const normalized = query.trim().toLowerCase();\n if (!normalized) {\n return SCHOOLS.slice(0, limit);\n }\n\n return SCHOOLS.filter((school) => {\n return school.name.toLowerCase().includes(normalized) || school.url.toLowerCase().includes(normalized);\n }).slice(0, limit);\n}\n\nexport function makeCustomSchool(name: string, url: string): School {\n return {\n name: name.trim() || \"Custom Canvas School\",\n url: normalizeBaseUrl(url)\n };\n}\n","export type BootstrapSummary = {\n skipped: true;\n reason: string;\n};\n\nexport async function runPostLoginBootstrap(): Promise<BootstrapSummary> {\n return {\n skipped: true,\n reason: \"Context bootstrap is planned for Phase 4.\"\n };\n}\n","import { CanvasClient, normalizeBaseUrl } from \"../core/canvas-client.js\";\nimport { ConfigStore, type CanvasConfig } from \"../core/config-store.js\";\nimport { CanvasCliError, toErrorEnvelope } from \"../core/errors.js\";\nimport type { OutputFormat } from \"../core/output.js\";\nimport { writeOutput } from \"../core/output.js\";\nimport { openBrowser } from \"../core/browser.js\";\nimport { createPrompt, promptHidden, type PromptIO } from \"../core/prompt.js\";\nimport { makeCustomSchool, searchSchools, type School } from \"../registry/schools.js\";\nimport { runPostLoginBootstrap } from \"../workflows/context-bootstrap.js\";\n\nconst TOKEN_PURPOSE = \"Hyperknow\";\n\nexport async function handleAuthCommand(\n argv: string[],\n options: { format: OutputFormat }\n): Promise<number> {\n const [subcommand] = argv;\n\n if (subcommand === \"login\") {\n return authLogin(options);\n }\n\n if (subcommand === \"status\") {\n return authStatus(options);\n }\n\n if (subcommand === \"logout\") {\n return authLogout(options);\n }\n\n await writeOutput(\n {\n ok: false,\n error: {\n code: \"UNKNOWN_COMMAND\",\n message: `Unknown auth command: ${argv.join(\" \")}`,\n retryable: false\n }\n },\n options\n );\n return 1;\n}\n\nasync function authLogin(options: { format: OutputFormat }): Promise<number> {\n const io = createPrompt();\n\n try {\n const school = await chooseSchool(io);\n const settingsUrl = `${school.url}/profile/settings`;\n\n process.stdout.write(tokenInstructions(school, settingsUrl));\n await io.question(\"Press Enter to open Canvas settings in your browser...\");\n await openBrowser(settingsUrl);\n process.stdout.write(\"\\nWaiting for your Canvas personal access token.\\n\");\n\n const token = await promptHidden(\"Paste token: \");\n if (!token) {\n throw new CanvasCliError(\"EMPTY_TOKEN\", \"No token entered.\");\n }\n\n const client = new CanvasClient({ baseUrl: school.url, token });\n const user = await validateToken(client);\n const now = new Date().toISOString();\n const config: CanvasConfig = {\n version: 1,\n activeProfile: \"default\",\n profiles: {\n default: {\n schoolName: school.name,\n baseUrl: school.url,\n token,\n createdAt: now,\n validatedAt: now,\n user\n }\n }\n };\n\n await new ConfigStore().write(config);\n const bootstrap = await runPostLoginBootstrap();\n\n await writeOutput(\n {\n ok: true,\n data: {\n authenticated: true,\n school: {\n name: school.name,\n baseUrl: school.url\n },\n user,\n contextBootstrap: bootstrap,\n next: \"canvas context show\"\n },\n meta: {\n command: \"auth login\"\n }\n },\n options\n );\n return 0;\n } catch (error) {\n await writeOutput(toErrorEnvelope(error), options);\n return 1;\n } finally {\n io.close();\n }\n}\n\nasync function authStatus(options: { format: OutputFormat }): Promise<number> {\n const store = new ConfigStore();\n const config = await store.readRedacted();\n\n if (!config) {\n await writeOutput(\n {\n ok: true,\n data: {\n authenticated: false,\n message: \"No Canvas auth config found. Run canvas auth login.\"\n },\n meta: {\n command: \"auth status\"\n }\n },\n options\n );\n return 0;\n }\n\n await writeOutput(\n {\n ok: true,\n data: {\n authenticated: true,\n activeProfile: config.activeProfile,\n profile: config.profiles[config.activeProfile]\n },\n meta: {\n command: \"auth status\"\n }\n },\n options\n );\n return 0;\n}\n\nasync function authLogout(options: { format: OutputFormat }): Promise<number> {\n await new ConfigStore().remove();\n await writeOutput(\n {\n ok: true,\n data: {\n authenticated: false,\n message: \"Canvas auth config removed.\"\n },\n meta: {\n command: \"auth logout\"\n }\n },\n options\n );\n return 0;\n}\n\nexport async function chooseSchool(\n io: PromptIO,\n write: (message: string) => void = (message) => process.stdout.write(message)\n): Promise<School> {\n write(\"Search for your school, or press Enter to browse the first matches.\\n\");\n const query = await io.question(\"School: \");\n const matches = searchSchools(query);\n\n if (matches.length === 1) {\n const school = matches[0];\n const answer = (\n await io.question(`Is this your school: ${school.name} (${school.url})? Choose: y/n `)\n )\n .trim()\n .toLowerCase();\n\n if (answer === \"y\" || answer === \"yes\") {\n return {\n name: school.name,\n url: normalizeBaseUrl(school.url)\n };\n }\n\n if (answer === \"n\" || answer === \"no\") {\n return promptCustomSchool(io);\n }\n\n throw new CanvasCliError(\"INVALID_SELECTION\", \"Please answer y or n.\");\n }\n\n for (const [index, school] of matches.entries()) {\n write(`${index + 1}. ${school.name}\\n ${school.url}\\n`);\n }\n write(`${matches.length + 1}. Not found? Add your own\\n`);\n\n const selected = Number.parseInt(await io.question(\"Choose: \"), 10);\n if (!Number.isFinite(selected) || selected < 1 || selected > matches.length + 1) {\n throw new CanvasCliError(\"INVALID_SELECTION\", \"Invalid school selection.\");\n }\n\n if (selected === matches.length + 1) {\n return promptCustomSchool(io);\n }\n\n const school = matches[selected - 1];\n return {\n name: school.name,\n url: normalizeBaseUrl(school.url)\n };\n}\n\nasync function promptCustomSchool(io: PromptIO): Promise<School> {\n const name = await io.question(\"School display name: \");\n const url = await io.question(\"Canvas base URL: \");\n return makeCustomSchool(name, url);\n}\n\nfunction tokenInstructions(school: School, settingsUrl: string): string {\n return `\nCanvas token setup for ${school.name}\n\n1. Go to ${settingsUrl}\n2. Click \"+ New Access Token\"\n3. Enter \"${TOKEN_PURPOSE}\" as the purpose\n4. Optionally set an expiration date\n5. Click \"Generate Token\"\n6. Copy the token and paste it back here\n\n`;\n}\n\nasync function validateToken(client: CanvasClient): Promise<{ id?: string; name?: string }> {\n try {\n const response = await client.get<{ id?: string; name?: string; short_name?: string }>(\n \"/api/v1/users/self/profile\"\n );\n return {\n id: response.data.id,\n name: response.data.name ?? response.data.short_name\n };\n } catch (error) {\n if (error instanceof CanvasCliError && error.status === 404) {\n await client.get(\"/api/v1/courses\");\n return {};\n }\n throw error;\n }\n}\n","import { ConfigStore } from \"../core/config-store.js\";\nimport type { OutputFormat } from \"../core/output.js\";\nimport { writeOutput } from \"../core/output.js\";\n\nexport async function handleConfigCommand(\n argv: string[],\n options: { format: OutputFormat }\n): Promise<number> {\n const [subcommand] = argv;\n\n if (subcommand !== \"show\") {\n await writeOutput(\n {\n ok: false,\n error: {\n code: \"UNKNOWN_COMMAND\",\n message: `Unknown config command: ${argv.join(\" \")}`,\n retryable: false\n }\n },\n options\n );\n return 1;\n }\n\n const config = await new ConfigStore().readRedacted();\n await writeOutput(\n {\n ok: true,\n data: config ?? {\n configured: false,\n message: \"No Canvas config found. Run canvas auth login.\"\n },\n meta: {\n command: \"config show\"\n }\n },\n options\n );\n return 0;\n}\n","import { CanvasClient } from \"../core/canvas-client.js\";\nimport { ConfigStore, type CanvasProfile } from \"../core/config-store.js\";\n\nexport type ActiveCanvas = {\n profile: CanvasProfile;\n client: CanvasClient;\n};\n\nexport async function activeCanvas(): Promise<ActiveCanvas> {\n const profile = await new ConfigStore().activeProfile();\n return {\n profile,\n client: new CanvasClient({\n baseUrl: profile.baseUrl,\n token: profile.token\n })\n };\n}\n\nexport function flagValue(argv: string[], flag: string): string | undefined {\n const index = argv.indexOf(flag);\n if (index === -1) {\n return undefined;\n }\n return argv[index + 1];\n}\n\nexport function hasFlag(argv: string[], flag: string): boolean {\n return argv.includes(flag);\n}\n\nexport function pageOptions(argv: string[]): { pageAll: boolean; pageLimit?: number } {\n const pageLimitRaw = flagValue(argv, \"--page-limit\");\n return {\n pageAll: hasFlag(argv, \"--page-all\"),\n pageLimit: pageLimitRaw ? Number.parseInt(pageLimitRaw, 10) : undefined\n };\n}\n\nexport function positionalArgs(argv: string[]): string[] {\n const valueFlags = new Set([\n \"--course-id\",\n \"--module-id\",\n \"--item-id\",\n \"--assignment-id\",\n \"--quiz-id\",\n \"--topic-id\",\n \"--page\",\n \"--path\",\n \"--out\",\n \"--format\",\n \"--page-limit\",\n \"--page-size\",\n \"--page-delay\",\n \"--enrollment-state\",\n \"--state\",\n \"--include\",\n \"--params\"\n ]);\n const values: string[] = [];\n for (let index = 0; index < argv.length; index += 1) {\n const arg = argv[index];\n if (arg.startsWith(\"--\")) {\n if (valueFlags.has(arg)) {\n index += 1;\n }\n continue;\n }\n values.push(arg);\n }\n return values;\n}\n","import { toErrorEnvelope } from \"../core/errors.js\";\nimport type { OutputFormat } from \"../core/output.js\";\nimport { writeOutput } from \"../core/output.js\";\nimport { activeCanvas, flagValue, hasFlag, pageOptions, positionalArgs } from \"./shared.js\";\n\nexport type CanvasCourse = {\n id: string;\n name?: string;\n course_code?: string;\n workflow_state?: string;\n enrollment_term_id?: string;\n term?: {\n id?: string;\n name?: string;\n start_at?: string;\n end_at?: string;\n };\n enrollments?: Array<{\n type?: string;\n role?: string;\n enrollment_state?: string;\n }>;\n};\n\nexport async function handleCoursesCommand(\n argv: string[],\n options: { format: OutputFormat }\n): Promise<number> {\n const [subcommand] = argv;\n\n try {\n if (subcommand === \"list\") {\n return await listCourses(argv.slice(1), options);\n }\n if (subcommand === \"search\") {\n return await searchCourses(argv.slice(1), options);\n }\n if (subcommand === \"show\") {\n return await showCourse(argv.slice(1), options);\n }\n if (subcommand === \"overview\") {\n return await overviewCourse(argv.slice(1), options);\n }\n\n await writeOutput(\n {\n ok: false,\n error: {\n code: \"UNKNOWN_COMMAND\",\n message: `Unknown courses command: ${argv.join(\" \")}`,\n retryable: false\n }\n },\n options\n );\n return 1;\n } catch (error) {\n await writeOutput(toErrorEnvelope(error), options);\n return 1;\n }\n}\n\nasync function listCourses(argv: string[], options: { format: OutputFormat }): Promise<number> {\n const { client, profile } = await activeCanvas();\n const response = await client.get<CanvasCourse[]>(\"/api/v1/courses\", {\n query: courseListQuery(argv),\n ...pageOptions(argv)\n });\n\n await writeOutput(\n {\n ok: true,\n data: response.data.map(normalizeCourse),\n meta: {\n ...response.meta,\n baseUrl: profile.baseUrl\n }\n },\n options\n );\n return 0;\n}\n\nasync function searchCourses(argv: string[], options: { format: OutputFormat }): Promise<number> {\n const query = positionalArgs(argv).join(\" \").trim();\n const { client, profile } = await activeCanvas();\n const response = await client.get<CanvasCourse[]>(\"/api/v1/courses\", {\n query: courseListQuery([\"--active\"]),\n pageAll: true\n });\n\n const matches = response.data\n .map(normalizeCourse)\n .filter((course) => {\n const haystack = `${course.name ?? \"\"} ${course.courseCode ?? \"\"}`.toLowerCase();\n return haystack.includes(query.toLowerCase());\n });\n\n await writeOutput(\n {\n ok: true,\n data: matches,\n meta: {\n ...response.meta,\n baseUrl: profile.baseUrl,\n query\n }\n },\n options\n );\n return 0;\n}\n\nasync function showCourse(argv: string[], options: { format: OutputFormat }): Promise<number> {\n const courseId = positionalArgs(argv)[0];\n if (!courseId) {\n throw new Error(\"Usage: canvas courses show <course-id>\");\n }\n\n const { client, profile } = await activeCanvas();\n const response = await client.get<CanvasCourse>(`/api/v1/courses/${courseId}`, {\n query: {\n \"include[]\": [\"term\", \"course_image\", \"total_scores\", \"teachers\"]\n }\n });\n\n await writeOutput(\n {\n ok: true,\n data: normalizeCourse(response.data),\n meta: {\n ...response.meta,\n baseUrl: profile.baseUrl\n }\n },\n options\n );\n return 0;\n}\n\nasync function overviewCourse(argv: string[], options: { format: OutputFormat }): Promise<number> {\n const courseId = positionalArgs(argv)[0];\n if (!courseId) {\n throw new Error(\"Usage: canvas courses overview <course-id>\");\n }\n\n const { client, profile } = await activeCanvas();\n const [course, tabs, modules, assignments] = await Promise.all([\n client.get<CanvasCourse>(`/api/v1/courses/${courseId}`, {\n query: { \"include[]\": [\"term\", \"course_image\"] }\n }),\n client.get<Array<{ id: string; label?: string; visibility?: string }>>(\n `/api/v1/courses/${courseId}/tabs`\n ),\n client.get<Array<{ id: string; name?: string; position?: number }>>(\n `/api/v1/courses/${courseId}/modules`,\n { query: { per_page: 100 } }\n ),\n client.get<Array<{ id: string; name?: string; due_at?: string }>>(\n `/api/v1/courses/${courseId}/assignments`,\n { query: { bucket: \"upcoming\", per_page: 20 } }\n )\n ]);\n\n await writeOutput(\n {\n ok: true,\n data: {\n course: normalizeCourse(course.data),\n tabs: tabs.data,\n setup: {\n hasModules: modules.data.length > 0,\n hasAssignments: assignments.data.length > 0,\n hasFilesTab: tabs.data.some((tab) => tab.id === \"files\"),\n isModuleHeavy: modules.data.length > 0\n },\n counts: {\n tabs: tabs.data.length,\n modules: modules.data.length,\n upcomingAssignments: assignments.data.length\n },\n modules: modules.data.map((module) => ({\n id: module.id,\n name: module.name,\n position: module.position\n })),\n upcomingAssignments: assignments.data.map((assignment) => ({\n id: assignment.id,\n name: assignment.name,\n dueAt: assignment.due_at\n }))\n },\n meta: {\n baseUrl: profile.baseUrl,\n request: {\n method: \"GET\",\n path: `/api/v1/courses/${courseId}/overview`\n }\n }\n },\n options\n );\n return 0;\n}\n\nexport function courseListQuery(argv: string[]) {\n return {\n enrollment_state: hasFlag(argv, \"--active\") ? \"active\" : flagValue(argv, \"--enrollment-state\"),\n state: flagValue(argv, \"--state\"),\n per_page: flagValue(argv, \"--page-size\"),\n \"include[]\": [\"term\", \"total_scores\"]\n };\n}\n\nexport function normalizeCourse(course: CanvasCourse) {\n return {\n id: String(course.id),\n name: course.name,\n courseCode: course.course_code,\n workflowState: course.workflow_state,\n enrollmentTermId: course.enrollment_term_id,\n term: course.term\n ? {\n id: course.term.id,\n name: course.term.name,\n startAt: course.term.start_at,\n endAt: course.term.end_at\n }\n : undefined,\n enrollments: course.enrollments?.map((enrollment) => ({\n type: enrollment.type,\n role: enrollment.role,\n state: enrollment.enrollment_state\n }))\n };\n}\n","import { CanvasClient } from \"../core/canvas-client.js\";\nimport { ConfigStore } from \"../core/config-store.js\";\nimport { toErrorEnvelope } from \"../core/errors.js\";\nimport type { OutputFormat } from \"../core/output.js\";\nimport { writeOutput } from \"../core/output.js\";\n\nexport async function handleMeCommand(options: { format: OutputFormat }): Promise<number> {\n try {\n const profile = await new ConfigStore().activeProfile();\n const client = new CanvasClient({\n baseUrl: profile.baseUrl,\n token: profile.token\n });\n const response = await client.get(\"/api/v1/users/self/profile\");\n\n await writeOutput(\n {\n ok: true,\n data: response.data,\n meta: {\n ...response.meta,\n baseUrl: profile.baseUrl\n }\n },\n options\n );\n return 0;\n } catch (error) {\n await writeOutput(toErrorEnvelope(error), options);\n return 1;\n }\n}\n","import { toErrorEnvelope } from \"../core/errors.js\";\nimport type { OutputFormat } from \"../core/output.js\";\nimport { writeOutput } from \"../core/output.js\";\nimport { activeCanvas, flagValue } from \"./shared.js\";\n\nexport type CanvasTab = {\n id: string;\n html_url?: string;\n full_url?: string;\n position?: number;\n label?: string;\n type?: string;\n hidden?: boolean;\n visibility?: string;\n};\n\nexport async function handleTabsCommand(\n argv: string[],\n options: { format: OutputFormat }\n): Promise<number> {\n const [subcommand] = argv;\n\n if (subcommand !== \"list\") {\n await writeOutput(\n {\n ok: false,\n error: {\n code: \"UNKNOWN_COMMAND\",\n message: `Unknown tabs command: ${argv.join(\" \")}`,\n retryable: false\n }\n },\n options\n );\n return 1;\n }\n\n try {\n const courseId = flagValue(argv, \"--course-id\");\n if (!courseId) {\n throw new Error(\"Usage: canvas tabs list --course-id <course-id>\");\n }\n\n const { client, profile } = await activeCanvas();\n const response = await client.get<CanvasTab[]>(`/api/v1/courses/${courseId}/tabs`);\n\n await writeOutput(\n {\n ok: true,\n data: response.data.map(normalizeTab),\n meta: {\n ...response.meta,\n baseUrl: profile.baseUrl\n }\n },\n options\n );\n return 0;\n } catch (error) {\n await writeOutput(toErrorEnvelope(error), options);\n return 1;\n }\n}\n\nexport function normalizeTab(tab: CanvasTab) {\n return {\n id: tab.id,\n label: tab.label,\n type: tab.type,\n position: tab.position,\n hidden: tab.hidden,\n visibility: tab.visibility,\n htmlUrl: tab.html_url,\n fullUrl: tab.full_url\n };\n}\n","import { writeOutput } from \"../core/output.js\";\nimport type { OutputFormat } from \"../core/output.js\";\nimport { handleAuthCommand } from \"../commands/auth.js\";\nimport { handleConfigCommand } from \"../commands/config.js\";\nimport { handleCoursesCommand } from \"../commands/courses.js\";\nimport { handleMeCommand } from \"../commands/me.js\";\nimport { handleTabsCommand } from \"../commands/tabs.js\";\n\nconst VERSION = \"0.0.0\";\n\nfunction helpText(): string {\n return `canvas — Canvas LMS CLI for students and agents.\n\nUSAGE:\n canvas <command> [options]\n\nCOMMANDS:\n auth login Interactive Canvas PAT setup\n auth status Show redacted auth status\n auth logout Remove local Canvas auth config\n config show Show redacted local config\n me Show current Canvas user profile\n context show Show cached post-login context\n courses list List active Canvas courses\n review pack Create a local course review pack\n version Print CLI version\n\nFLAGS:\n -h, --help Show help\n --format <fmt> Output format: json | pretty | table | ndjson\n\nMVP STATUS:\n Auth/config/me/courses/tabs are in progress. Review commands are planned next.\n`;\n}\n\nasync function main(argv: string[]): Promise<number> {\n const parsed = parseGlobalOptions(argv);\n const [command] = parsed.argv;\n\n if (!command || command === \"--help\" || command === \"-h\" || command === \"help\") {\n process.stdout.write(helpText());\n return 0;\n }\n\n if (command === \"version\" || command === \"--version\" || command === \"-v\") {\n await writeOutput(\n {\n ok: true,\n data: { version: VERSION },\n meta: { command: \"version\" }\n },\n { format: parsed.format === \"json\" ? \"json\" : \"pretty\" }\n );\n return 0;\n }\n\n if (command === \"auth\") {\n return handleAuthCommand(parsed.argv.slice(1), { format: parsed.format });\n }\n\n if (command === \"config\") {\n return handleConfigCommand(parsed.argv.slice(1), { format: parsed.format });\n }\n\n if (command === \"me\") {\n return handleMeCommand({ format: parsed.format });\n }\n\n if (command === \"courses\") {\n return handleCoursesCommand(parsed.argv.slice(1), { format: parsed.format });\n }\n\n if (command === \"tabs\") {\n return handleTabsCommand(parsed.argv.slice(1), { format: parsed.format });\n }\n\n await writeOutput(\n {\n ok: false,\n error: {\n code: \"UNKNOWN_COMMAND\",\n message: `Unknown command: ${parsed.argv.join(\" \")}`,\n retryable: false\n }\n },\n { format: parsed.format }\n );\n return 1;\n}\n\nfunction parseGlobalOptions(argv: string[]): { argv: string[]; format: OutputFormat } {\n const nextArgv: string[] = [];\n let format: OutputFormat = \"json\";\n\n for (let index = 0; index < argv.length; index += 1) {\n const arg = argv[index];\n if (arg === \"--format\") {\n const value = argv[index + 1];\n if (isOutputFormat(value)) {\n format = value;\n index += 1;\n continue;\n }\n }\n nextArgv.push(arg);\n }\n\n return { argv: nextArgv, format };\n}\n\nfunction isOutputFormat(value: string | undefined): value is OutputFormat {\n return value === \"json\" || value === \"pretty\" || value === \"table\" || value === \"ndjson\";\n}\n\nmain(process.argv.slice(2))\n .then((code) => {\n process.exitCode = code;\n })\n .catch((error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n process.stderr.write(`canvas: ${message}\\n`);\n process.exitCode = 1;\n });\n"],"mappings":"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","names":["output","output","path","input","school"]}
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@lukeguo12210/canvas-cli",
3
+ "version": "0.0.1",
4
+ "description": "Canvas LMS CLI and AI agent skills bundle for technical students.",
5
+ "type": "module",
6
+ "bin": {
7
+ "canvas": "dist/bin/canvas.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "skills",
12
+ "README.md"
13
+ ],
14
+ "scripts": {
15
+ "dev": "tsx src/bin/canvas.ts",
16
+ "build": "tsup",
17
+ "test": "vitest run",
18
+ "test:watch": "vitest",
19
+ "typecheck": "tsc --noEmit"
20
+ },
21
+ "engines": {
22
+ "node": ">=20"
23
+ },
24
+ "keywords": [
25
+ "canvas",
26
+ "canvas-lms",
27
+ "cli",
28
+ "agents",
29
+ "skills",
30
+ "students",
31
+ "education"
32
+ ],
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "git+https://github.com/lukeguo12210/canvas-cli.git"
36
+ },
37
+ "bugs": {
38
+ "url": "https://github.com/lukeguo12210/canvas-cli/issues"
39
+ },
40
+ "homepage": "https://github.com/lukeguo12210/canvas-cli#readme",
41
+ "license": "BUSL-1.1",
42
+ "devDependencies": {
43
+ "@types/node": "^24.0.0",
44
+ "tsup": "^8.3.5",
45
+ "tsx": "^4.19.2",
46
+ "typescript": "^5.6.3",
47
+ "vitest": "^2.1.4"
48
+ }
49
+ }
@@ -0,0 +1,30 @@
1
+ ---
2
+ name: canvas-assignments
3
+ version: 0.1.0
4
+ description: "Canvas LMS assignments: list assignments, inspect assignment details, due dates, submission types, descriptions, visible attachments, and assignment groups."
5
+ metadata:
6
+ requires:
7
+ bins: ["canvas"]
8
+ cliHelp: "canvas assignments --help"
9
+ ---
10
+
11
+ # canvas-assignments
12
+
13
+ Before using this skill, read `../canvas-shared/SKILL.md`.
14
+
15
+ ## Commands
16
+
17
+ ```bash
18
+ canvas assignments list --course-id <course-id>
19
+ canvas assignments list --course-id <course-id> --bucket upcoming
20
+ canvas assignments show --course-id <course-id> --assignment-id <assignment-id>
21
+ canvas assignments export --course-id <course-id> --assignment-id <assignment-id> --out <dir>
22
+ canvas assignment-groups list --course-id <course-id>
23
+ ```
24
+
25
+ ## Notes
26
+
27
+ - Canvas assignment descriptions are HTML; export them as Markdown when writing local files.
28
+ - Due dates may be overridden per student; use Canvas-returned dates as authoritative.
29
+ - Submission data is sensitive and should not be part of default review workflows.
30
+ - External tools should be linked, not scraped.
@@ -0,0 +1,121 @@
1
+ ---
2
+ name: canvas-courses
3
+ version: 0.1.0
4
+ description: "Canvas LMS courses: list active courses, disambiguate course names, inspect course overview, tabs, and top-level student-visible course structure."
5
+ metadata:
6
+ requires:
7
+ bins: ["canvas"]
8
+ cliHelp: "canvas courses --help"
9
+ ---
10
+
11
+ # canvas-courses
12
+
13
+ Before using this skill, read `../canvas-shared/SKILL.md`.
14
+
15
+ ## Quick Decisions
16
+
17
+ - If auth state is uncertain, start with `canvas auth status --format json`.
18
+ - If the user names a course ambiguously, use `canvas courses search <query>`.
19
+ - Once a course ID is known, prefer the ID.
20
+ - `canvas context show` is planned but not implemented yet. Until it exists, use `canvas courses list --active --page-all`.
21
+ - Use tabs to understand whether the course is module-heavy, assignment-heavy, file-heavy, or page-heavy.
22
+ - For "pull this class" or "help me study", switch to `canvas-review`, but note that review packs are not implemented yet.
23
+
24
+ ## Commands
25
+
26
+ ```bash
27
+ canvas courses list --active --page-all --format json
28
+ canvas courses search <query>
29
+ canvas courses show <course-id>
30
+ canvas courses overview <course-id>
31
+ canvas tabs list --course-id <course-id>
32
+ ```
33
+
34
+ ### List Active Courses
35
+
36
+ Use this to discover what courses the student can currently access:
37
+
38
+ ```bash
39
+ canvas courses list --active --page-all --format json
40
+ ```
41
+
42
+ Important output fields:
43
+
44
+ - `id`: stable course ID; use this for later commands.
45
+ - `name`: Canvas course name.
46
+ - `courseCode`: school/course code when Canvas exposes it.
47
+ - `workflowState`: availability state.
48
+ - `term.name`, `term.startAt`, `term.endAt`: useful for semester context when present.
49
+
50
+ ### Search Courses
51
+
52
+ Use search when the user gives a name, nickname, department code, or partial course title:
53
+
54
+ ```bash
55
+ canvas courses search "algorithms" --format json
56
+ ```
57
+
58
+ If multiple courses match, show the user the course names and IDs and ask which one they mean.
59
+
60
+ ### Show One Course
61
+
62
+ ```bash
63
+ canvas courses show <course-id> --format json
64
+ ```
65
+
66
+ Use this after a course is selected and before deeper course traversal.
67
+
68
+ ### Course Overview
69
+
70
+ ```bash
71
+ canvas courses overview <course-id> --format json
72
+ ```
73
+
74
+ This fetches course metadata, tabs, module summary, and upcoming assignment summary. Use it to understand how the course is organized.
75
+
76
+ Overview output includes:
77
+
78
+ - `course`
79
+ - `tabs`
80
+ - `setup.hasModules`
81
+ - `setup.hasFilesTab`
82
+ - `setup.hasAssignments`
83
+ - `setup.isModuleHeavy`
84
+ - `counts.modules`
85
+ - `counts.upcomingAssignments`
86
+ - `modules`
87
+ - `upcomingAssignments`
88
+
89
+ ### Tabs
90
+
91
+ ```bash
92
+ canvas tabs list --course-id <course-id> --format json
93
+ ```
94
+
95
+ Tabs help agents infer course structure:
96
+
97
+ - `modules`: likely module-first course.
98
+ - `assignments`: assignment-heavy course.
99
+ - `files`: course exposes a file library.
100
+ - `pages` or `wiki`: course may use pages outside modules.
101
+ - external tool tabs should be linked but not scraped.
102
+
103
+ ## Output Notes
104
+
105
+ Course names can be school-specific, abbreviated, or nicknamed. Preserve Canvas IDs in summaries.
106
+
107
+ When answering a user, prefer a compact list:
108
+
109
+ ```text
110
+ 1. COMS 3134 Algorithms — course id 12345
111
+ 2. COMS 3157 Advanced Programming — course id 67890
112
+ ```
113
+
114
+ Do not invent term dates. If Canvas does not return term dates, say they are unavailable.
115
+
116
+ ## Common Errors
117
+
118
+ - `NO_AUTH_CONFIG`: ask the user to run `canvas auth login`.
119
+ - `CANVAS_NETWORK_ERROR`: network or sandbox cannot reach Canvas.
120
+ - `CANVAS_UNAUTHORIZED`: token invalid or expired.
121
+ - `CANVAS_FORBIDDEN`: course exists but is not visible to this user.
@@ -0,0 +1,32 @@
1
+ ---
2
+ name: canvas-files
3
+ version: 0.1.0
4
+ description: "Canvas LMS files: list course files and folders, inspect file metadata, download accessible files safely, and resolve files linked from modules, pages, and assignments."
5
+ metadata:
6
+ requires:
7
+ bins: ["canvas"]
8
+ cliHelp: "canvas files --help"
9
+ ---
10
+
11
+ # canvas-files
12
+
13
+ Before using this skill, read `../canvas-shared/SKILL.md`.
14
+
15
+ ## Commands
16
+
17
+ ```bash
18
+ canvas files list --course-id <course-id>
19
+ canvas files show <file-id>
20
+ canvas files download <file-id> --out <dir>
21
+ canvas files download-linked --course-id <course-id> --out <dir>
22
+ canvas folders list --course-id <course-id>
23
+ canvas folders path --course-id <course-id> --path <path>
24
+ ```
25
+
26
+ ## Rules
27
+
28
+ - Prefer linked files for review packs.
29
+ - Use all-files export only when the user explicitly asks.
30
+ - Preserve Canvas display names in metadata.
31
+ - Record source API URL and source HTML URL in manifests.
32
+ - Never write downloaded files outside the requested output directory.
@@ -0,0 +1,42 @@
1
+ ---
2
+ name: canvas-modules
3
+ version: 0.1.0
4
+ description: "Canvas LMS modules: list modules, traverse module items, and resolve Canvas pages, files, assignments, quizzes, discussions, and external URLs in course order."
5
+ metadata:
6
+ requires:
7
+ bins: ["canvas"]
8
+ cliHelp: "canvas modules --help"
9
+ ---
10
+
11
+ # canvas-modules
12
+
13
+ Before using this skill, read `../canvas-shared/SKILL.md`.
14
+
15
+ ## Core Concepts
16
+
17
+ Modules are often the canonical student learning path. Preserve module order and item order.
18
+
19
+ Common module item types:
20
+
21
+ - `Page`
22
+ - `File`
23
+ - `Assignment`
24
+ - `Quiz`
25
+ - `Discussion`
26
+ - `ExternalUrl`
27
+ - `ExternalTool`
28
+ - `SubHeader`
29
+
30
+ ## Commands
31
+
32
+ ```bash
33
+ canvas modules list --course-id <course-id>
34
+ canvas modules list --course-id <course-id> --include items,content_details
35
+ canvas modules items --course-id <course-id> --module-id <module-id>
36
+ canvas modules item --course-id <course-id> --module-id <module-id> --item-id <item-id>
37
+ canvas modules export --course-id <course-id> --module-id <module-id> --out <dir>
38
+ ```
39
+
40
+ ## Notes
41
+
42
+ Canvas may omit inline module items. If that happens, call `canvas modules items` per module. Locked items should be recorded as locked, not treated as fatal.
@@ -0,0 +1,40 @@
1
+ ---
2
+ name: canvas-review
3
+ version: 0.1.0
4
+ description: "Canvas LMS review packs: create agent-first local course exports that preserve Canvas course/module structure, cite sources, and organize materials for study sessions."
5
+ metadata:
6
+ requires:
7
+ bins: ["canvas"]
8
+ cliHelp: "canvas review --help"
9
+ ---
10
+
11
+ # canvas-review
12
+
13
+ Before using this skill, read `../canvas-shared/SKILL.md`.
14
+
15
+ ## Workflow
16
+
17
+ Before creating a review pack:
18
+
19
+ 1. Inspect `canvas context show`.
20
+ 2. Resolve the target course ID.
21
+ 3. Use `canvas review pack`.
22
+
23
+ ## Commands
24
+
25
+ ```bash
26
+ canvas review pack --course-id <course-id> --out <dir>
27
+ canvas review pack --course "<course name or code>" --out <dir>
28
+ canvas review pack --course-id <course-id> --module <module-id-or-name> --out <dir>
29
+ canvas review index --path <pack-dir>
30
+ canvas review search --path <pack-dir> --query <query>
31
+ ```
32
+
33
+ ## Review Pack Rules
34
+
35
+ - Course is always the top-level unit.
36
+ - Preserve Canvas structure first: course, tabs, modules, module items, pages, assignments, files, discussions, announcements, and calendar.
37
+ - Use manifests and indexes to make preserved structure easy for agents to parse.
38
+ - Default excludes grades, submissions, conversations, and group member lists.
39
+ - Record unavailable, locked, external, or skipped resources as warnings.
40
+ - Always cite Canvas source URLs in `citations.json`.
@@ -0,0 +1,82 @@
1
+ ---
2
+ name: canvas-shared
3
+ version: 0.1.0
4
+ description: "Canvas LMS shared rules: PAT auth, school URL setup, token safety, pagination, output formats, raw read-only API usage, and common Canvas error handling."
5
+ metadata:
6
+ requires:
7
+ bins: ["canvas"]
8
+ cliHelp: "canvas --help"
9
+ ---
10
+
11
+ # canvas-shared
12
+
13
+ Read this skill before using any other Canvas skill.
14
+
15
+ ## Current CLI Surface
16
+
17
+ Implemented now:
18
+
19
+ - `canvas auth login`
20
+ - `canvas auth status`
21
+ - `canvas auth logout`
22
+ - `canvas config show`
23
+ - `canvas me`
24
+ - `canvas courses list`
25
+ - `canvas courses search <query>`
26
+ - `canvas courses show <course-id>`
27
+ - `canvas courses overview <course-id>`
28
+ - `canvas tabs list --course-id <course-id>`
29
+
30
+ Planned but not implemented yet:
31
+
32
+ - `canvas context show`
33
+ - `canvas review pack`
34
+ - `canvas api get`
35
+ - modules, pages, files, assignments, grades, submissions, discussions, calendar, groups, conversations
36
+
37
+ ## Core Rules
38
+
39
+ - Use `canvas auth status` first if auth state is uncertain.
40
+ - If no auth config exists, ask the user to run `canvas auth login`.
41
+ - Never print, paste, summarize, or expose a Canvas personal access token.
42
+ - Default output is JSON and should be preferred for agents.
43
+ - Use `--format pretty` only when presenting a compact human-facing result.
44
+ - Use `--page-all` when completeness matters.
45
+ - Canvas pagination follows `Link` headers; do not synthesize next-page URLs.
46
+ - MVP cannot write to Canvas.
47
+ - Do not pretend planned commands exist. If a command is marked planned, say it is not implemented yet.
48
+
49
+ ## References
50
+
51
+ - [Auth flow](references/auth.md)
52
+ - [Output format](references/output.md)
53
+ - [Pagination](references/pagination.md)
54
+
55
+ ## Privacy
56
+
57
+ Sensitive read-only data must be requested explicitly:
58
+
59
+ - grades
60
+ - submissions
61
+ - conversations
62
+ - group member lists
63
+
64
+ Do not include those in review packs unless the user explicitly asks.
65
+
66
+ ## Common Errors
67
+
68
+ - `NO_AUTH_CONFIG`: no local auth config; ask the user to run `canvas auth login`.
69
+ - `CANVAS_NETWORK_ERROR`: Canvas could not be reached; retry later or check network/sandbox access.
70
+ - `CANVAS_UNAUTHORIZED` / `401`: invalid or expired PAT; ask the user to run `canvas auth login`.
71
+ - `CANVAS_FORBIDDEN` / `403`: locked, unpublished, or permission-denied content.
72
+ - `CANVAS_NOT_FOUND` / `404`: content unavailable or not visible to this user.
73
+ - `CANVAS_RATE_LIMITED` / `429`: rate limited; retry later or use lower pagination concurrency.
74
+ - `CANVAS_SERVER_ERROR` / `5xx`: Canvas backend issue.
75
+
76
+ ## Good First Commands
77
+
78
+ ```bash
79
+ canvas auth status --format json
80
+ canvas courses list --active --page-all --format json
81
+ canvas courses search "course name" --format json
82
+ ```
@@ -0,0 +1,67 @@
1
+ # Canvas Auth Reference
2
+
3
+ Implemented auth commands:
4
+
5
+ ```bash
6
+ canvas auth login
7
+ canvas auth status --format json
8
+ canvas auth logout
9
+ canvas config show --format json
10
+ canvas me --format json
11
+ ```
12
+
13
+ ## Login Flow
14
+
15
+ `canvas auth login` is interactive.
16
+
17
+ Expected flow:
18
+
19
+ 1. User searches for a school.
20
+ 2. If exactly one school matches, confirm:
21
+
22
+ ```text
23
+ Is this your school: Columbia University (CourseWorks) (https://courseworks2.columbia.edu)? Choose: y/n
24
+ ```
25
+
26
+ 3. If user chooses `y`, continue with that school.
27
+ 4. If user chooses `n`, ask for custom school name and Canvas base URL.
28
+ 5. Print Canvas PAT creation instructions.
29
+ 6. Open `<baseUrl>/profile/settings` in the browser.
30
+ 7. Prompt for PAT using hidden input.
31
+ 8. Validate token.
32
+ 9. Store local config.
33
+
34
+ ## Token Safety
35
+
36
+ Never ask the user to paste a token into chat. The token should be pasted into the local terminal prompt from `canvas auth login`.
37
+
38
+ Never print:
39
+
40
+ - PAT values
41
+ - `Authorization` headers
42
+ - bearer tokens
43
+ - query params named `token`, `access_token`, `api_key`, `verifier`, or `code`
44
+
45
+ ## Auth Status
46
+
47
+ Use:
48
+
49
+ ```bash
50
+ canvas auth status --format json
51
+ ```
52
+
53
+ If `authenticated` is `false`, ask the user to run:
54
+
55
+ ```bash
56
+ canvas auth login
57
+ ```
58
+
59
+ ## Current User
60
+
61
+ Use:
62
+
63
+ ```bash
64
+ canvas me --format json
65
+ ```
66
+
67
+ This calls Canvas `GET /api/v1/users/self/profile`.