@codio-ai/opencode-authoring-agent 0.1.2 → 0.1.3

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,17 @@
1
+ import { z } from 'zod';
2
+ export declare const fetchCourseInputSchema: z.ZodObject<{
3
+ workspace: z.ZodString;
4
+ courseId: z.ZodString;
5
+ outputDir: z.ZodOptional<z.ZodString>;
6
+ domain: z.ZodOptional<z.ZodString>;
7
+ }, z.core.$strip>;
8
+ export type FetchCourseInput = z.infer<typeof fetchCourseInputSchema>;
9
+ export interface FetchCourseResult {
10
+ outputDir: string;
11
+ manifestPath: string;
12
+ assignmentsCsvPath: string;
13
+ moduleCount: number;
14
+ assignmentCount: number;
15
+ }
16
+ export declare function fetchCourseHandler(raw: unknown): Promise<FetchCourseResult>;
17
+ //# sourceMappingURL=fetch-course.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch-course.d.ts","sourceRoot":"","sources":["../../src/tools/fetch-course.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,eAAO,MAAM,sBAAsB;;;;;iBAQjC,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;CACzB;AAoED,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAoGjF"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=fetch-course.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch-course.test.d.ts","sourceRoot":"","sources":["../../src/tools/fetch-course.test.ts"],"names":[],"mappings":""}
@@ -58,5 +58,41 @@ export declare const validate_guide: {
58
58
  workspace: string;
59
59
  }, context: import("@opencode-ai/plugin").ToolContext): Promise<import("@opencode-ai/plugin").ToolResult>;
60
60
  };
61
+ export declare const fetch_course: {
62
+ description: string;
63
+ args: {
64
+ workspace: import("zod").ZodString;
65
+ courseId: import("zod").ZodString;
66
+ outputDir: import("zod").ZodOptional<import("zod").ZodString>;
67
+ domain: import("zod").ZodOptional<import("zod").ZodString>;
68
+ };
69
+ execute(args: {
70
+ workspace: string;
71
+ courseId: string;
72
+ outputDir?: string | undefined;
73
+ domain?: string | undefined;
74
+ }, context: import("@opencode-ai/plugin").ToolContext): Promise<import("@opencode-ai/plugin").ToolResult>;
75
+ };
76
+ export declare const publish_course: {
77
+ description: string;
78
+ args: {
79
+ manifestPath: import("zod").ZodString;
80
+ dryRun: import("zod").ZodOptional<import("zod").ZodBoolean>;
81
+ force: import("zod").ZodOptional<import("zod").ZodBoolean>;
82
+ module: import("zod").ZodOptional<import("zod").ZodString>;
83
+ assignment: import("zod").ZodOptional<import("zod").ZodString>;
84
+ changelog: import("zod").ZodOptional<import("zod").ZodString>;
85
+ stack: import("zod").ZodOptional<import("zod").ZodString>;
86
+ };
87
+ execute(args: {
88
+ manifestPath: string;
89
+ dryRun?: boolean | undefined;
90
+ force?: boolean | undefined;
91
+ module?: string | undefined;
92
+ assignment?: string | undefined;
93
+ changelog?: string | undefined;
94
+ stack?: string | undefined;
95
+ }, context: import("@opencode-ai/plugin").ToolContext): Promise<import("@opencode-ai/plugin").ToolResult>;
96
+ };
61
97
  export declare const tools: Record<string, unknown>;
62
98
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAOtB,CAAC;AAOH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;CAkB5B,CAAC;AAIH,eAAO,MAAM,cAAc;;;;;;;;CAOzB,CAAC;AAQH,eAAO,MAAM,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAIzC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAOtB,CAAC;AAOH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;CAkB5B,CAAC;AAIH,eAAO,MAAM,cAAc;;;;;;;;CAOzB,CAAC;AAIH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;CAOvB,CAAC;AAIH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;CAOzB,CAAC;AAQH,eAAO,MAAM,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAMzC,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { z } from 'zod';
2
+ export declare const publishCourseInputSchema: z.ZodObject<{
3
+ manifestPath: z.ZodString;
4
+ dryRun: z.ZodOptional<z.ZodBoolean>;
5
+ force: z.ZodOptional<z.ZodBoolean>;
6
+ module: z.ZodOptional<z.ZodString>;
7
+ assignment: z.ZodOptional<z.ZodString>;
8
+ changelog: z.ZodOptional<z.ZodString>;
9
+ stack: z.ZodOptional<z.ZodString>;
10
+ }, z.core.$strip>;
11
+ export type PublishCourseInput = z.infer<typeof publishCourseInputSchema>;
12
+ export interface PublishCourseResult {
13
+ published: number;
14
+ created: number;
15
+ unchanged: number;
16
+ skipped: number;
17
+ dryRun: boolean;
18
+ }
19
+ export declare function publishCourseHandler(raw: unknown): Promise<PublishCourseResult>;
20
+ //# sourceMappingURL=publish-course.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publish-course.d.ts","sourceRoot":"","sources":["../../src/tools/publish-course.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,eAAO,MAAM,wBAAwB;;;;;;;;iBAQnC,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;CACjB;AA6DD,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAoHrF"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=publish-course.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publish-course.test.d.ts","sourceRoot":"","sources":["../../src/tools/publish-course.test.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codio-ai/opencode-authoring-agent",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Opencode plugin for authoring Codio guide assignments — orchestrator, subagents, tools, and skills.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -17,7 +17,7 @@
17
17
  "codio-authoring-unregister": "./bin/codio-authoring-unregister.js"
18
18
  },
19
19
  "scripts": {
20
- "build": "bun build src/index.ts --outdir dist --target node --format esm --external @opencode-ai/plugin --external @opencode-ai/sdk --external zod && bun build src/codio-scripts/codio-fetch.js src/codio-scripts/codio-publish.js --outdir dist/codio-scripts --target node --format esm && tsc --emitDeclarationOnly",
20
+ "build": "bun build src/index.ts --outdir dist --target node --format esm --external @opencode-ai/plugin --external @opencode-ai/sdk --external zod && tsc --emitDeclarationOnly",
21
21
  "postinstall": "node ./bin/codio-authoring-register.js || true",
22
22
  "preuninstall": "node ./bin/codio-authoring-unregister.js || true",
23
23
  "test": "bun test",
@@ -0,0 +1,149 @@
1
+ ---
2
+ name: reference-codio-api
3
+ description: Reference for the Codio platform API tools — fetch_course and publish_course. Course manifest format, output structure, credential requirements, and troubleshooting. Use when working with fetched courses or publishing assignments.
4
+ ---
5
+
6
+ # Codio API Tools Reference
7
+
8
+ ## Credentials
9
+
10
+ Both tools read credentials from environment variables. Set these on the Codio box before using either tool:
11
+
12
+ | Variable | Required | Description |
13
+ |---|---|---|
14
+ | `CODIO_CLIENT_ID` | Yes | Codio API client ID |
15
+ | `CODIO_CLIENT_SECRET` | Yes | Codio API client secret |
16
+ | `CODIO_DOMAIN` | No | Codio domain, default `codio.com` |
17
+
18
+ ---
19
+
20
+ ## `fetch_course` — Pull a course from Codio
21
+
22
+ Authenticates with the Codio API, downloads the course export, and writes an AI-ready project to the workspace.
23
+
24
+ ### Inputs
25
+
26
+ | Field | Required | Description |
27
+ |---|---|---|
28
+ | `workspace` | Yes | Absolute path to the Codio workspace root |
29
+ | `courseId` | Yes | Codio course ID (32-char hex string) |
30
+ | `outputDir` | No | Output folder name, default `course-project` |
31
+ | `domain` | No | Codio domain, default `codio.com` |
32
+
33
+ ### Output
34
+
35
+ Returns `{ outputDir, manifestPath, assignmentsCsvPath, moduleCount, assignmentCount }`.
36
+
37
+ Writes to `<outputDir>/` inside the workspace:
38
+
39
+ ```
40
+ <outputDir>/
41
+ ├── course-manifest.json ← machine-readable index; source of truth for publish_course
42
+ ├── course-assignments.csv ← flat CSV for review
43
+ └── course-content/
44
+ └── <module-folder>/
45
+ └── <assignment-folder>/
46
+ └── .guides/ ← guide pages, assessments, images
47
+ ```
48
+
49
+ ---
50
+
51
+ ## `publish_course` — Push assignments back to Codio
52
+
53
+ Reads `course-manifest.json` from the project folder and publishes each assignment to Codio.
54
+
55
+ ### Inputs
56
+
57
+ | Field | Required | Description |
58
+ |---|---|---|
59
+ | `manifestPath` | Yes | Absolute path to `course-manifest.json` |
60
+ | `dryRun` | No | Preview actions without making changes |
61
+ | `force` | No | Publish all, bypassing change detection |
62
+ | `module` | No | Only process this module (name or folder) |
63
+ | `assignment` | No | Only process this assignment (name or folder) |
64
+ | `changelog` | No | Changelog text applied to all publishes |
65
+ | `stack` | No | Force a specific stack ID for all assignments |
66
+
67
+ ### Output
68
+
69
+ Returns `{ published, created, unchanged, skipped }`.
70
+
71
+ Writes back any new IDs to `course-manifest.json` immediately after each resource is created.
72
+
73
+ ### Change detection
74
+
75
+ The tool skips existing assignments whose content hash matches the stored `contentHash` in the manifest. Pass `force: true` to bypass. New assignments (no `id`) are always published.
76
+
77
+ ### Stacks
78
+
79
+ - **New assignments** always use the default stack.
80
+ - **Existing assignments** preserve their current stack unless `stack` is supplied.
81
+
82
+ ---
83
+
84
+ ## course-manifest.json format
85
+
86
+ Created by `fetch_course`, consumed by `publish_course`. Both tools write back IDs as resources are created/resolved.
87
+
88
+ ```jsonc
89
+ {
90
+ "schemaVersion": 2,
91
+ "courseId": "c87b4540de108faad55c8cefe7847492",
92
+ "exportedAt": "2026-06-12T10:00:00.000Z",
93
+ "counts": { "modules": 2, "assignments": 6 },
94
+ "paths": {
95
+ "assignmentsCsv": "course-assignments.csv",
96
+ "courseContent": "course-content"
97
+ },
98
+ "modules": [
99
+ {
100
+ "id": "...", // Resolved or created by publish_course; written back
101
+ "name": "Module Name",
102
+ "folder": "module-name",
103
+ "path": "course-content/module-name",
104
+ "assignmentCount": 3,
105
+ "assignments": [
106
+ {
107
+ "id": "...", // Created by publish_course if empty; written back
108
+ "name": "Assignment Name",
109
+ "folder": "assignment-name",
110
+ "path": "course-content/module-name/assignment-name",
111
+ "contentHash": "..." // Written after each successful publish; used for change detection
112
+ }
113
+ ]
114
+ }
115
+ ]
116
+ }
117
+ ```
118
+
119
+ ### Folder slug rules
120
+
121
+ Codio uses lowercase slugs: normalise to ASCII, lowercase, replace each non-alphanumeric character with `-`, trim leading/trailing hyphens. Example: `"1.1 – Intro"` → `"1-1---intro"`.
122
+
123
+ ---
124
+
125
+ ## Typical workflow
126
+
127
+ ```
128
+ fetch_course(courseId)
129
+ → course-project/course-manifest.json + course-content/...
130
+
131
+ [edit assignment pages using authoring tools]
132
+
133
+ publish_course(manifestPath, dryRun: true) ← preview first
134
+ publish_course(manifestPath) ← publish for real
135
+ ```
136
+
137
+ ---
138
+
139
+ ## Troubleshooting
140
+
141
+ | Error | Fix |
142
+ |---|---|
143
+ | `CODIO_CLIENT_ID ... must be set` | Set env vars on the box |
144
+ | `course-manifest.json not found` | Pass the absolute path to the manifest; run from or inside the project folder |
145
+ | `path not found` for an assignment | The folder referenced in the manifest doesn't exist — generate content there first |
146
+ | `Required command not found: unzip` | Install `unzip` via the box package manager |
147
+ | API 401 / auth error | Credentials are invalid or expired |
148
+ | API 429 rate limit | Library retries automatically; wait and re-run if still failing |
149
+ | `no module ID available` | Module creation failed earlier in the run — check the error above it |
@@ -1,153 +0,0 @@
1
- ---
2
- name: codio-fetch
3
- description: Fetch and export an existing Codio course into an AI-ready local project. Use when the user asks to fetch, export, download, pull, or import a course from Codio, or to create a local AI-ready project from an existing Codio course.
4
- argument-hint: [--course-id <id>] [--output-dir <path>] [--domain <domain>]
5
- allowed-tools: [Bash, Read, Write, Edit]
6
- ---
7
-
8
- # Codio Fetch
9
-
10
- Export an existing Codio course into a clean, AI-ready local project that can be edited and re-published with `codio-publish`.
11
-
12
- ## What this skill does
13
-
14
- 1. Loads credentials from `.env` at the workspace root (the Codio assignment directory).
15
- 2. Authenticates with the Codio API using `CODIO_CLIENT_ID` and `CODIO_CLIENT_SECRET`.
16
- 3. Fetches course metadata and builds a `course-manifest.json` with all module and assignment IDs.
17
- 4. Downloads the course source export ZIP from Codio.
18
- 5. Extracts the ZIP into `course-content/`, expanding nested `.tar.zst` assignment archives.
19
- 6. Writes `course-assignments.csv` as a flat index for easy review.
20
- 7. Cleans up temporary download files.
21
-
22
- The output is a self-contained project folder ready for `codio-publish` to read back.
23
-
24
- ## Output structure
25
-
26
- ```
27
- <output-dir>/ ← default: ./course-project
28
- ├── course-manifest.json ← machine-readable index with all IDs and paths
29
- ├── course-assignments.csv ← flat CSV listing all modules and assignments
30
- └── course-content/
31
- └── <module-folder>/
32
- └── <assignment-folder>/
33
- └── .guides/ ← guide pages, assessments, images
34
- ```
35
-
36
- The `<output-dir>` is always a named subfolder, never at the workspace root itself.
37
-
38
- ## Prerequisites
39
-
40
- Before running, verify:
41
-
42
- 1. **`.env` file** — must exist at the workspace root:
43
- ```
44
- CODIO_CLIENT_ID=your-client-id
45
- CODIO_CLIENT_SECRET=your-client-secret
46
- CODIO_COURSE_ID=your-course-id # optional if passing --course-id
47
- CODIO_DOMAIN=codio.com # optional, defaults to codio.com
48
- ```
49
- The script finds `.env` by walking up from the current directory, so it works from anywhere inside the workspace.
50
-
51
- 2. **Check `.env` is in place:**
52
- ```bash
53
- [ -f .env ] && echo "found" || echo "missing — create .env before running"
54
- ```
55
-
56
- 3. **Required system tools:** `node`, `unzip`, `tar`, `find`
57
-
58
- ## Running the export
59
-
60
- Run from the workspace root (the Codio assignment directory):
61
-
62
- ```bash
63
- bash ~/.config/opencode/skills/codio-fetch/scripts/run-course-export.sh --course-id <id>
64
- ```
65
-
66
- ### Options
67
-
68
- | Option | Description |
69
- |---|---|
70
- | `--course-id <id>` | Codio course ID (overrides `CODIO_COURSE_ID` from `.env`) |
71
- | `--output-dir <path>` | Output project folder (default: `./course-project`) |
72
- | `--domain <domain>` | Codio domain (default: `codio.com` or `CODIO_DOMAIN` from `.env`) |
73
-
74
- ### Examples
75
-
76
- ```bash
77
- # Use course ID from .env, output to ./course-project
78
- bash ~/.config/opencode/skills/codio-fetch/scripts/run-course-export.sh
79
-
80
- # Specify course ID and custom output folder
81
- bash ~/.config/opencode/skills/codio-fetch/scripts/run-course-export.sh \
82
- --course-id c87b4540de108faad55c8cefe7847492 \
83
- --output-dir ./my-java-course
84
-
85
- # Use a staging Codio domain
86
- bash ~/.config/opencode/skills/codio-fetch/scripts/run-course-export.sh \
87
- --course-id c87b4540de108faad55c8cefe7847492 \
88
- --domain codio.co.uk
89
- ```
90
-
91
- ## How credentials are loaded
92
-
93
- The shell script walks up from the current directory looking for `.env`, then sources it with `set -a; . .env; set +a`. Values in `.env` **override** anything already in the environment.
94
-
95
- The `.env` is shared across all Codio skills (`codio-fetch`, `codio-publish`). There is one `.env` at the workspace root, not one per skill.
96
-
97
- ## Environment variables
98
-
99
- | Variable | Required | Description |
100
- |---|---|---|
101
- | `CODIO_CLIENT_ID` | Yes | Codio API client ID |
102
- | `CODIO_CLIENT_SECRET` | Yes | Codio API client secret |
103
- | `CODIO_COURSE_ID` | No | Course ID (can be passed as `--course-id` instead) |
104
- | `CODIO_DOMAIN` | No | Codio domain, default `codio.com` |
105
-
106
- ## After the export
107
-
108
- - Open `<output-dir>/course-manifest.json` to see the full module/assignment hierarchy with IDs.
109
- - Open `<output-dir>/course-assignments.csv` for a flat spreadsheet view.
110
- - Assignment content is in `<output-dir>/course-content/<module>/<assignment>/`.
111
- - Use `codio-publish` to push edits back to Codio — the manifest with existing IDs is already in place.
112
-
113
- ## Troubleshooting
114
-
115
- | Error | Fix |
116
- |---|---|
117
- | `Missing required environment variable: CODIO_CLIENT_ID` | Create or fix `.env` at workspace root |
118
- | `Missing course id` | Pass `--course-id <id>` or set `CODIO_COURSE_ID` in `.env` |
119
- | `Required command not found: unzip` | Install `unzip` via your package manager |
120
- | API 401 / auth error | Credentials in `.env` are invalid or expired |
121
-
122
- ## Manifest format
123
-
124
- `course-manifest.json` produced by this skill is directly compatible with `codio-publish`. All assignment IDs are pre-populated, so a subsequent publish run will update existing assignments rather than create new ones.
125
-
126
- ```jsonc
127
- {
128
- "schemaVersion": 2,
129
- "courseId": "...",
130
- "exportedAt": "...",
131
- "counts": { "modules": 2, "assignments": 6 },
132
- "paths": {
133
- "assignmentsCsv": "course-assignments.csv",
134
- "courseContent": "course-content"
135
- },
136
- "modules": [
137
- {
138
- "name": "Module Name",
139
- "folder": "module-name",
140
- "path": "course-content/module-name",
141
- "assignmentCount": 3,
142
- "assignments": [
143
- {
144
- "id": "...",
145
- "name": "Assignment Name",
146
- "folder": "assignment-name",
147
- "path": "course-content/module-name/assignment-name"
148
- }
149
- ]
150
- }
151
- ]
152
- }
153
- ```
@@ -1,69 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -Eeuo pipefail
3
- shopt -s nullglob
4
-
5
- log() {
6
- printf '[%s] %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$*"
7
- }
8
-
9
- fail() {
10
- log "ERROR: $*"
11
- exit 1
12
- }
13
-
14
- require_command() {
15
- command -v "$1" >/dev/null 2>&1 || fail "Required command not found: $1"
16
- }
17
-
18
- extract_assignment_archives() {
19
- local root_dir="$1"
20
- local archive_count=0
21
-
22
- while IFS= read -r -d '' archive_path; do
23
- archive_count=$((archive_count + 1))
24
-
25
- local archive_dir archive_name destination_dir
26
- archive_dir="$(dirname "$archive_path")"
27
- archive_name="$(basename "$archive_path")"
28
- destination_dir="$archive_dir/${archive_name%.tar.zst}"
29
-
30
- rm -rf "$destination_dir"
31
- mkdir -p "$destination_dir"
32
-
33
- log "Extracting nested archive ${archive_name}"
34
- tar --zstd -xf "$archive_path" -C "$destination_dir"
35
- rm -f "$archive_path"
36
- done < <(find "$root_dir" -type f -name '*.tar.zst' -print0)
37
-
38
- log "Expanded ${archive_count} nested assignment archive(s)"
39
- }
40
-
41
- main() {
42
- require_command unzip
43
- require_command tar
44
- require_command find
45
- require_command cp
46
-
47
- local export_zip_path="${1:-}"
48
- local staging_dir="${2:-}"
49
- local visible_output_dir="${3:-}"
50
-
51
- [[ -n "$export_zip_path" ]] || fail 'Usage: extract-course-export.sh <export-zip-path> <staging-dir> <visible-output-dir>'
52
- [[ -n "$staging_dir" ]] || fail 'Missing staging directory'
53
- [[ -n "$visible_output_dir" ]] || fail 'Missing visible output directory'
54
- [[ -f "$export_zip_path" ]] || fail "Export ZIP not found: $export_zip_path"
55
-
56
- rm -rf "$staging_dir" "$visible_output_dir"
57
- mkdir -p "$staging_dir" "$visible_output_dir"
58
-
59
- log "Unzipping $(basename "$export_zip_path") into hidden staging"
60
- unzip -q "$export_zip_path" -d "$staging_dir"
61
-
62
- extract_assignment_archives "$staging_dir"
63
-
64
- cp -R "$staging_dir"/. "$visible_output_dir"
65
-
66
- log "Published extracted course content to $visible_output_dir"
67
- }
68
-
69
- main "$@"