@tutorflow/cli 0.1.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +400 -89
  2. package/dist/client.d.ts +7 -1
  3. package/dist/client.d.ts.map +1 -1
  4. package/dist/client.js +53 -19
  5. package/dist/client.js.map +1 -1
  6. package/dist/commands/agent.d.ts +3 -0
  7. package/dist/commands/agent.d.ts.map +1 -0
  8. package/dist/commands/agent.js +123 -0
  9. package/dist/commands/agent.js.map +1 -0
  10. package/dist/commands/auth.d.ts.map +1 -1
  11. package/dist/commands/auth.js +48 -45
  12. package/dist/commands/auth.js.map +1 -1
  13. package/dist/commands/config.d.ts.map +1 -1
  14. package/dist/commands/config.js +34 -45
  15. package/dist/commands/config.js.map +1 -1
  16. package/dist/commands/course.d.ts.map +1 -1
  17. package/dist/commands/course.js +61 -75
  18. package/dist/commands/course.js.map +1 -1
  19. package/dist/commands/evaluate.d.ts.map +1 -1
  20. package/dist/commands/evaluate.js +49 -55
  21. package/dist/commands/evaluate.js.map +1 -1
  22. package/dist/commands/module.d.ts +3 -0
  23. package/dist/commands/module.d.ts.map +1 -0
  24. package/dist/commands/module.js +108 -0
  25. package/dist/commands/module.js.map +1 -0
  26. package/dist/commands/platform.d.ts +3 -0
  27. package/dist/commands/platform.d.ts.map +1 -0
  28. package/dist/commands/platform.js +36 -0
  29. package/dist/commands/platform.js.map +1 -0
  30. package/dist/commands/slide.d.ts.map +1 -1
  31. package/dist/commands/slide.js +65 -76
  32. package/dist/commands/slide.js.map +1 -1
  33. package/dist/commands/test.d.ts +3 -0
  34. package/dist/commands/test.d.ts.map +1 -0
  35. package/dist/commands/test.js +119 -0
  36. package/dist/commands/test.js.map +1 -0
  37. package/dist/commands/video.d.ts.map +1 -1
  38. package/dist/commands/video.js +66 -78
  39. package/dist/commands/video.js.map +1 -1
  40. package/dist/index.js +24 -2
  41. package/dist/index.js.map +1 -1
  42. package/dist/types.d.ts +75 -0
  43. package/dist/types.d.ts.map +1 -1
  44. package/dist/utils/output.d.ts +15 -2
  45. package/dist/utils/output.d.ts.map +1 -1
  46. package/dist/utils/output.js +66 -22
  47. package/dist/utils/output.js.map +1 -1
  48. package/dist/utils/runtime.d.ts +39 -0
  49. package/dist/utils/runtime.d.ts.map +1 -0
  50. package/dist/utils/runtime.js +105 -0
  51. package/dist/utils/runtime.js.map +1 -0
  52. package/package.json +18 -2
package/README.md CHANGED
@@ -1,17 +1,18 @@
1
1
  # @tutorflow/cli
2
2
 
3
- TutorFlow CLI for creating AI-powered educational content (slides, videos, courses) and evaluating student responses from the terminal. Wraps the TutorFlow Platform API (`/v1/platform/*`).
3
+ TutorFlow CLI for creating AI-powered educational content (slides, videos, courses) and evaluating student responses from the terminal or from AI agents. Wraps the TutorFlow Platform API (`/v1/platform/*`).
4
4
 
5
5
  - **Binary:** `tutorflow`
6
- - **Version:** `0.1.0`
7
- - **Node:** `>= 18`
8
- - **Package:** `@tutorflow/cli`
6
+ - **Package:** [`@tutorflow/cli`](https://www.npmjs.com/package/@tutorflow/cli) (current version in [`package.json`](package.json))
7
+ - **Source:** [tutorflow-io/cli](https://github.com/tutorflow-io/cli) (private)
8
+ - **Node:** `>= 18` for runtime, `>= 20` for development (Vitest 4 requirement)
9
+ - **Agent-friendly:** every command supports `--json` output, env-var auth (`TUTORFLOW_API_KEY`), and proper exit codes — see [Non-interactive / agent usage](#non-interactive--agent-usage)
9
10
 
10
11
  ## Installation
11
12
 
12
- ### A. End users (other machines)
13
+ ### A. End users (recommended)
13
14
 
14
- Once published to npm, install globally with any package manager:
15
+ Install globally with any package manager:
15
16
 
16
17
  ```bash
17
18
  npm install -g @tutorflow/cli
@@ -24,122 +25,225 @@ Then anywhere on the system:
24
25
  ```bash
25
26
  tutorflow --version
26
27
  tutorflow auth login
28
+ tutorflow agent register --name "My Agent Workspace" --slug my-agent-workspace --email me@example.com
29
+ tutorflow --json platform manifest
27
30
  tutorflow slide create "My first deck"
28
31
  ```
29
32
 
30
- > **One-shot use without installing** (npm 5.2+):
31
- >
32
- > ```bash
33
- > npx @tutorflow/cli slide create "My first deck"
34
- > ```
35
-
36
- If you publish to a private registry (e.g. GitHub Packages or a self-hosted Verdaccio), users first authenticate to that registry and scope the `@tutorflow` namespace to it:
33
+ **One-shot use without installing**:
37
34
 
38
35
  ```bash
39
- npm config set @tutorflow:registry https://npm.pkg.github.com
40
- npm login --scope=@tutorflow --registry=https://npm.pkg.github.com
41
- npm install -g @tutorflow/cli
36
+ npx @tutorflow/cli slide create "My first deck"
42
37
  ```
43
38
 
44
- ### B. Local development (this monorepo)
39
+ This is the recommended path for AI agents and CI environments — no install step, always pulls the latest published version.
45
40
 
46
- From the monorepo root:
41
+ ### B. Local development (this repo)
47
42
 
48
43
  ```bash
44
+ git clone https://github.com/tutorflow-io/cli.git
45
+ cd cli
49
46
  pnpm install
50
- pnpm --filter @tutorflow/cli build # tsc → ./dist
51
- pnpm --filter @tutorflow/cli link # npm link → exposes `tutorflow` globally
47
+ pnpm build
48
+ pnpm link # exposes the `tutorflow` binary globally
52
49
  ```
53
50
 
54
51
  Verify:
55
52
 
56
53
  ```bash
57
- tutorflow --version # 0.1.0
54
+ tutorflow --version # matches package.json
58
55
  tutorflow --help
59
56
  ```
60
57
 
61
58
  To unlink later: `npm unlink -g @tutorflow/cli`.
62
59
 
63
- ## Publishing
60
+ Run from source without building:
61
+
62
+ ```bash
63
+ pnpm dev -- <command> [options]
64
+ # e.g. pnpm dev -- slide list --limit 5
65
+ ```
64
66
 
65
- The package is **scoped** (`@tutorflow/...`), so the first publish needs `--access public` (or a paid org plan for private packages).
67
+ ## Releasing
66
68
 
67
- ### One-time setup
69
+ Releases are automated by GitHub Actions on pushes to `master` / `main`.
70
+ Auth uses **npm OIDC Trusted Publishing** — no `NPM_TOKEN` secret in CI.
68
71
 
69
- 1. Make sure you're a member of the `@tutorflow` npm org with publish rights.
70
- 2. Login from the CLI:
72
+ ```bash
73
+ git checkout master
74
+ git pull
71
75
 
72
- ```bash
73
- npm login # public registry
74
- # or:
75
- npm login --scope=@tutorflow --registry=<your-private-registry>
76
- ```
76
+ npm version patch --no-git-tag-version # 0.3.0 → 0.3.1 (or minor / major)
77
+ git push origin master # Release workflow publishes if that version is not on npm yet
78
+ ```
77
79
 
78
- 3. Confirm `apps/cli/package.json` has the right metadata. Recommended fields to add before the first release:
80
+ The [`Release` workflow](.github/workflows/release.yml):
79
81
 
80
- ```json
81
- {
82
- "files": ["dist", "README.md"],
83
- "publishConfig": { "access": "public" },
84
- "repository": "https://github.com/<org>/tutorflow-monorepo",
85
- "license": "UNLICENSED"
86
- }
87
- ```
82
+ 1. Reads `package.json` name and version
83
+ 2. Skips npm publish if that exact version already exists
84
+ 3. Runs `pnpm typecheck` + `pnpm build` + `pnpm test`
85
+ 4. Publishes new versions with `npm publish --access public` (auth via OIDC)
86
+ 5. Creates a GitHub Release only for `v*.*.*` tag pushes
88
87
 
89
- The `files` allowlist keeps `src/`, `node_modules/`, and `tsconfig.json` out of the tarball.
88
+ For full setup (npm org, Trusted Publisher config) see [RELEASING.md](RELEASING.md).
90
89
 
91
- ### Release flow
90
+ ### Pre-release / beta
92
91
 
93
92
  ```bash
94
- cd apps/cli
93
+ npm version prerelease --preid=beta --no-git-tag-version # 0.3.1 → 0.3.2-beta.0
94
+ git push origin master
95
+ ```
95
96
 
96
- # 1. bump version (creates a git tag)
97
- npm version patch # 0.1.0 → 0.1.1
98
- # or: npm version minor / major / prerelease --preid=beta
97
+ Pre-release versions publish to a dist-tag derived from the prerelease id
98
+ (`0.3.1-beta.0` publishes with `--tag beta`).
99
99
 
100
- # 2. clean build
101
- rm -rf dist
102
- pnpm build
100
+ ## ⚠️ Caveats & gotchas
101
+
102
+ These tripped us up during initial setup. Read before changing release infrastructure.
103
+
104
+ ### Versioning
105
+
106
+ - **Don't hardcode the version in `src/index.ts`.** It reads `version` from [`package.json`](package.json) via `resolveJsonModule`. Hardcoding will silently lie to users via `--version` after every `npm version` bump.
107
+ - The `prepublishOnly` script in `package.json` rebuilds `dist/` before publish. Don't skip it.
108
+
109
+ ### npm Trusted Publishing (OIDC)
110
+
111
+ - **npm CLI must be 11.5.1+** for OIDC auth. npm also requires a modern Node 22 runtime for trusted publishing, so the workflow uses Node 22.x and invokes `pnpm dlx npm@latest ...` for registry checks and publish. Do not replace that with the runner's bundled npm unless you verify its version supports OIDC trusted publishing.
112
+ - The Trusted Publisher binding at [npmjs.com/package/@tutorflow/cli/access](https://www.npmjs.com/package/@tutorflow/cli/access) hardcodes:
113
+ - GitHub org: `tutorflow-io`
114
+ - Repo: `cli`
115
+ - Workflow filename: `release.yml`
116
+ - Environment: (none)
117
+
118
+ Renaming the repo, moving the workflow file, or adding an `environment:` to the publish job will break OIDC auth until the binding is updated.
119
+ - Local manual publishes still need a token (set `NPM_TOKEN` in `.env` or run `npm login`). Use a Granular Access Token with **"Bypass two-factor authentication (2FA)"** checked, otherwise CI/scripts get `EOTP` errors.
120
+
121
+ ### Provenance
122
+
123
+ - `--provenance` is **disabled** in [release.yml](.github/workflows/release.yml). npm only accepts provenance attestations from **public** GitHub repos. Add `--provenance` back to the `npm publish` command when this repo goes public.
124
+
125
+ ### Node + tooling
126
+
127
+ - **Vitest 4.x requires Node 20+** (uses `node:util.styleText`). CI tests Node 20 and Node 22, while release publishing runs on Node 22 for npm OIDC. The runtime CLI itself still supports Node 18 (declared in `package.json#engines`).
128
+
129
+ ### Tarball hygiene
130
+
131
+ - `tsconfig.json` excludes `**/*.test.ts` so test files don't end up in `dist/` (and therefore not in the published tarball).
132
+ - `package.json#files` is an allowlist (`dist`, `README.md`, `LICENSE`). Anything not listed is excluded.
133
+ - Run `npm pack --dry-run` to inspect the tarball before publish.
134
+
135
+ ### Source maps and a private repo
136
+
137
+ The published tarball currently includes `.js.map` and `.d.ts.map` files which embed paths from the build environment. While the source itself isn't shipped, this leaks file structure. If that matters, set `sourceMap: false` and `declarationMap: false` in [tsconfig.json](tsconfig.json) before going public.
138
+
139
+ ## Development
140
+
141
+ Run with `tsx` (no build step):
142
+
143
+ ```bash
144
+ pnpm dev -- <command> [options]
145
+ # e.g. pnpm dev -- slide list --limit 5
146
+ ```
147
+
148
+ Build artifacts land in `dist/` (entry: `dist/index.js`, declared in `package.json#bin`).
149
+
150
+ ## Non-interactive / agent usage
103
151
 
104
- # 3. (optional) inspect what will be published
105
- npm pack --dry-run
152
+ Every command supports the contract below so AI agents and CI scripts can use the CLI without a TTY.
106
153
 
107
- # 4. publish
108
- npm publish --access public
154
+ ### Auth without prompts
155
+
156
+ ```bash
157
+ # Option 1: env var (preferred for one-shot calls)
158
+ TUTORFLOW_API_KEY=tf_xxx tutorflow --json slide list
159
+
160
+ # Option 2: persist to ~/.tutorflow/config.json once
161
+ tutorflow auth login --api-key tf_xxx
162
+ tutorflow --json slide create "Photosynthesis for 5th graders" -n 12
109
163
  ```
110
164
 
111
- For pre-release channels (so end users don't auto-pull untested versions):
165
+ `TUTORFLOW_API_KEY` always wins over the config file. `tutorflow auth whoami --json` reports `source: "env" | "config"` so an agent can verify which credential is active.
166
+
167
+ ### Machine-readable output
168
+
169
+ `--json` (or `TUTORFLOW_JSON=1`) makes every command emit a single JSON line on stdout. Errors emit JSON to stderr.
112
170
 
113
171
  ```bash
114
- npm version prerelease --preid=beta # 0.1.1 → 0.1.2-beta.0
115
- npm publish --tag beta --access public
116
- # users install with: npm install -g @tutorflow/cli@beta
172
+ $ tutorflow --json auth whoami
173
+ {"authenticated":true,"apiKeyPreview":"tf_xxxxxxx...","baseUrl":"https://api.tutorflow.io","source":"env"}
174
+
175
+ $ tutorflow --json auth whoami # without auth
176
+ # stdout: (empty)
177
+ # stderr: {"error":{"message":"Not authenticated","code":"EAUTH"}}
178
+ # exit: 1
117
179
  ```
118
180
 
119
- ### CI publishing (sketch)
181
+ List commands return raw arrays:
182
+
183
+ ```bash
184
+ $ tutorflow --json slide list --limit 3
185
+ [{"id":"...","title":"...","status":"ready","createdAt":"..."},...]
186
+ ```
120
187
 
121
- In GitHub Actions, after `pnpm build`:
188
+ Pipe into `jq` for downstream automation:
122
189
 
123
- ```yaml
124
- - run: echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
125
- - run: npm publish --access public
126
- working-directory: apps/cli
127
- env:
128
- NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
190
+ ```bash
191
+ tutorflow --json slide list --status ready | jq -r '.[].id' | xargs -I{} tutorflow --json slide get {}
129
192
  ```
130
193
 
131
- Trigger on tags matching `cli-v*` or use `changesets` for monorepo-aware versioning.
194
+ ### Exit codes
132
195
 
133
- ## Development
196
+ | Code | Meaning |
197
+ | ----- | ----------------------------------------------------------------- |
198
+ | `0` | Success |
199
+ | `1` | Generic failure (auth, network, API error, etc.) |
200
+ | `2` | Bad input (invalid flag value, missing required input in non-TTY) |
201
+ | `130` | User cancelled an interactive prompt (Ctrl+C) |
134
202
 
135
- Run with `tsx` (no build step):
203
+ The error JSON includes a stable `code`: `EAUTH`, `EINVAL`, `ENOINTERACTIVE`, `ENETWORK`, `EHTTP_<status>`, `ENOPREVIEW`, `ENOTFOUND`, `ECANCELLED`.
204
+
205
+ ### Required-input flags (no prompts)
206
+
207
+ Commands that have required input expose every prompt as a flag:
136
208
 
137
209
  ```bash
138
- pnpm --filter @tutorflow/cli dev -- <command> [options]
139
- # e.g. pnpm --filter @tutorflow/cli dev -- slide list --limit 5
210
+ # self-register a platform workspace and save the returned API key
211
+ tutorflow --json agent register \
212
+ --name "Agent Workspace" \
213
+ --slug agent-workspace \
214
+ --email agent@example.com \
215
+ --agent-identity "codex"
216
+
217
+ # slide / video / course create — accept positional [prompt]
218
+ tutorflow --json slide create "Photosynthesis" -n 12 -l en
219
+ tutorflow --json video create "Newton's laws" -s 6 -r 16:9 -d 90
220
+ tutorflow --json course create "Beginner Python" -l 10 -lang en
221
+
222
+ # evaluate — every input is a flag
223
+ tutorflow --json evaluate \
224
+ --type rubric \
225
+ --question "What is 2+2?" \
226
+ --student-answer "4" \
227
+ --expected-answer "4"
228
+
229
+ # logout (skip confirmation)
230
+ tutorflow auth logout --force
140
231
  ```
141
232
 
142
- Build artifacts land in `apps/cli/dist/` (entry: `dist/index.js`, declared in `package.json#bin`).
233
+ If a required input is missing **and** stdin is not a TTY, the command exits 2 with `ENOINTERACTIVE` and an error message naming the flag(s) to use. It never hangs waiting for input.
234
+
235
+ ### Globals
236
+
237
+ | Flag | Env var | Effect |
238
+ | --------------------------------- | -------------------------------- | ----------------------------------------------- |
239
+ | `--json` | `TUTORFLOW_JSON=1` | JSON output to stdout, JSON errors to stderr |
240
+ | `--debug` | `DEBUG=1` or `TUTORFLOW_DEBUG=1` | Print stack traces on errors |
241
+ | `--api-key <k>` (auth login only) | `TUTORFLOW_API_KEY` | API key |
242
+ | (n/a) | `TUTORFLOW_BASE_URL` | Override API base URL (staging, self-hosted) |
243
+
244
+ ### MCP server
245
+
246
+ A native MCP server (so agents can call tools directly without shelling out) is on the roadmap. For now, the `--json` shell contract is the recommended integration path.
143
247
 
144
248
  ## Authentication
145
249
 
@@ -194,6 +298,104 @@ tutorflow config set <key> <value> # update one key
194
298
  All resource commands require authentication. Without an API key they print:
195
299
  `✗ Not authenticated. Run "tutorflow auth login" first.`
196
300
 
301
+ ### `tutorflow agent`
302
+
303
+ Manage the agent-facing TutorFlow Platform account flow.
304
+
305
+ #### `agent register`
306
+
307
+ Public self-service registration. Creates a platform workspace through
308
+ `POST /v1/platform/agent/register`, returns the one-time API key, and saves it
309
+ to `~/.tutorflow/config.json` by default so the next CLI command can run
310
+ without a separate `auth login`.
311
+
312
+ | Option | Type | Default | Description |
313
+ | ------------------------------ | -------- | ------- | -------------------------------------------- |
314
+ | `--name <name>` | `string` | — | Workspace display name |
315
+ | `--slug <slug>` | `string` | — | Unique lowercase slug |
316
+ | `--email <email>` | `string` | none | Contact email for verification and billing |
317
+ | `--agent-identity <identity>` | `string` | none | Optional agent identity string |
318
+ | `--no-save` | boolean | false | Print the API key but do not persist it |
319
+
320
+ ```bash
321
+ tutorflow --json agent register \
322
+ --name "Agent Workspace" \
323
+ --slug agent-workspace \
324
+ --email agent@example.com \
325
+ --agent-identity "codex"
326
+ ```
327
+
328
+ #### `agent account`
329
+
330
+ GET `/v1/platform/agent/account`. Shows workspace status, credit balance,
331
+ email verification state, rate limits, and `restrictedActions`.
332
+
333
+ ```bash
334
+ tutorflow --json agent account
335
+ ```
336
+
337
+ #### `agent billing status`
338
+
339
+ GET `/v1/platform/agent/billing/status`. Shows payment-method state, credit
340
+ balance, billing mode, email verification state, and `restrictedActions`.
341
+
342
+ ```bash
343
+ tutorflow --json agent billing status
344
+ ```
345
+
346
+ #### `agent billing session`
347
+
348
+ POST `/v1/platform/agent/billing/session`. Returns a TutorFlow-hosted checkout
349
+ URL that an agent should forward to a human operator.
350
+
351
+ ```bash
352
+ tutorflow --json agent billing session
353
+ ```
354
+
355
+ #### `agent verify-email resend`
356
+
357
+ POST `/v1/platform/agent/verify-email/resend`. Resends the workspace email
358
+ verification message using the active API key.
359
+
360
+ ```bash
361
+ tutorflow --json agent verify-email resend
362
+ ```
363
+
364
+ #### `agent checkout info`
365
+
366
+ GET `/v1/platform/agent/checkout?token=<token>`. Reads workspace info for a
367
+ TutorFlow checkout token returned through the hosted billing-session flow.
368
+
369
+ ```bash
370
+ tutorflow --json agent checkout info --token checkout_123
371
+ ```
372
+
373
+ #### `agent checkout create`
374
+
375
+ POST `/v1/platform/agent/checkout`. Creates a Stripe Checkout URL from a
376
+ TutorFlow checkout token and amount. This is normally called by the hosted
377
+ checkout page; CLI support exists for complete automation and testing.
378
+
379
+ ```bash
380
+ tutorflow --json agent checkout create --token checkout_123 --amount-usd 25
381
+ ```
382
+
383
+ ### `tutorflow platform`
384
+
385
+ Inspect public discovery metadata and validate the active Platform API key.
386
+
387
+ | Subcommand | Endpoint | Auth |
388
+ | ---------- | -------- | ---- |
389
+ | `manifest` | `GET /v1/platform/.well-known/agent.json` | none |
390
+ | `pricing` | `GET /v1/platform/pricing-catalog` | none |
391
+ | `health` | `GET /v1/platform/health` | Platform API key |
392
+
393
+ ```bash
394
+ tutorflow --json platform manifest
395
+ tutorflow --json platform pricing
396
+ tutorflow --json platform health
397
+ ```
398
+
197
399
  ### `tutorflow slide`
198
400
 
199
401
  Create and manage slide decks.
@@ -335,6 +537,98 @@ Opens the course preview URL in the browser.
335
537
 
336
538
  ---
337
539
 
540
+ ### `tutorflow module`
541
+
542
+ Create and manage standalone lesson modules.
543
+
544
+ #### `module create [prompt]`
545
+
546
+ POST `/v1/platform/modules`. Generates one classroom-compatible standalone
547
+ lesson. `--idempotency-key` is sent as the `Idempotency-Key` header so agents
548
+ can safely retry a create call.
549
+
550
+ | Argument / Option | Type | Default | Description |
551
+ | ------------------------------ | --------------------- | -------- | -------------------------------- |
552
+ | `[prompt]` | `string` (positional) | — | Lesson/module prompt |
553
+ | `--title <title>` | `string` | none | Override generated title |
554
+ | `--description <description>` | `string` | none | Override generated description |
555
+ | `--target <target>` | `string` | none | Target learner audience |
556
+ | `--type <type>` | `string` | `lesson` | Module type |
557
+ | `--locale <locale>` | `string` | `en` | Locale code |
558
+ | `--language <language>` | `string` | `en` | Language code |
559
+ | `--slug <slug>` | `string` | none | Optional slug |
560
+ | `--private` | boolean | false | Set `isPublic: false` |
561
+ | `--no-quiz` | boolean | false | Set `hasQuiz: false` |
562
+ | `--idempotency-key <key>` | `string` | none | Safe retry key |
563
+
564
+ ```bash
565
+ tutorflow --json module create "Fractions for 5th grade" \
566
+ --title "Fractions" \
567
+ --target "5th grade" \
568
+ --no-quiz \
569
+ --idempotency-key lesson-001
570
+ ```
571
+
572
+ #### `module list`
573
+
574
+ GET `/v1/platform/modules?limit=<n>&offset=<n>[&search=<q>]`.
575
+
576
+ ```bash
577
+ tutorflow --json module list --limit 20 --offset 0 --search fractions
578
+ ```
579
+
580
+ #### `module get <id>` / `module open <id>`
581
+
582
+ Fetch a module by ID or open its `publicUrl`.
583
+
584
+ ---
585
+
586
+ ### `tutorflow test`
587
+
588
+ Create and manage generated assessments.
589
+
590
+ #### `test create [prompt]`
591
+
592
+ POST `/v1/platform/tests`. Generates a test/assessment. `--idempotency-key` is
593
+ sent as the `Idempotency-Key` header.
594
+
595
+ | Argument / Option | Type | Default | Description |
596
+ | ------------------------- | --------------------- | ---------- | ------------------------------ |
597
+ | `[prompt]` | `string` (positional) | — | Test generation prompt |
598
+ | `--title <title>` | `string` | none | Override generated title |
599
+ | `--description <text>` | `string` | none | Override generated description |
600
+ | `-l, --language <lang>` | `string` | `en` | Language code |
601
+ | `--level <level>` | `string` | none | Difficulty level |
602
+ | `--subject <subject>` | `string` | none | Subject area |
603
+ | `-n, --items <number>` | `string -> int` | none | Target item count |
604
+ | `--time-limit <minutes>` | `string -> int` | none | Time limit in minutes |
605
+ | `--classroom-id <id>` | `string` | none | Classroom ID |
606
+ | `--tier <tier>` | `string` | `standard` | Pricing tier |
607
+ | `--mode <mode>` | `string` | `sync` | `sync` or `async` |
608
+ | `--idempotency-key <key>` | `string` | none | Safe retry key |
609
+
610
+ ```bash
611
+ tutorflow --json test create "Algebra quiz" \
612
+ --title "Algebra Basics" \
613
+ --items 8 \
614
+ --time-limit 30 \
615
+ --idempotency-key test-001
616
+ ```
617
+
618
+ #### `test list`
619
+
620
+ GET `/v1/platform/tests?limit=<n>&offset=<n>[&status=<s>]`.
621
+
622
+ ```bash
623
+ tutorflow --json test list --limit 20 --status READY
624
+ ```
625
+
626
+ #### `test get <id>` / `test open <id>` / `test edit-token <id>`
627
+
628
+ Fetch a test, open its preview URL, or generate a fresh edit token.
629
+
630
+ ---
631
+
338
632
  ### `tutorflow evaluate`
339
633
 
340
634
  Evaluates a single student response. Fully interactive; only the type can be passed as a flag.
@@ -403,33 +697,49 @@ class ApiClient {
403
697
  ## Project layout
404
698
 
405
699
  ```
406
- apps/cli/
407
- ├── package.json # bin: { tutorflow: ./dist/index.js }
700
+ .
701
+ ├── package.json # bin: { tutorflow: ./dist/index.js }
408
702
  ├── tsconfig.json
703
+ ├── vitest.config.ts
704
+ ├── .github/workflows/
705
+ │ ├── ci.yml # typecheck + build + test on PRs and master
706
+ │ └── release.yml # OIDC publish on master/main pushes when version is new
707
+ ├── RELEASING.md # full release procedure + Trusted Publisher setup
409
708
  └── src/
410
- ├── index.ts # commander entrypoint, registers all commands
411
- ├── client.ts # undici-based ApiClient
412
- ├── config.ts # ~/.tutorflow/config.json read/write/update/delete
709
+ ├── index.ts # commander entrypoint, registers all commands
710
+ ├── client.ts # undici-based ApiClient
711
+ ├── client.test.ts
712
+ ├── config.ts # ~/.tutorflow/config.json read/write/update/delete
713
+ ├── config.test.ts
714
+ ├── types.ts # API response interfaces (SlideResource, etc.)
413
715
  ├── commands/
414
- │ ├── auth.ts # login / logout / whoami
415
- │ ├── config.ts # get / set
416
- │ ├── course.ts # create / list / open
417
- │ ├── evaluate.ts # interactive evaluation
418
- │ ├── slide.ts # create / list / open
419
- │ └── video.ts # create / list / open
716
+ │ ├── agent.ts # register / account / billing / email verification
717
+ │ ├── auth.ts # login / logout / whoami
718
+ │ ├── config.ts # get / set
719
+ │ ├── course.ts # create / list / open
720
+ │ ├── evaluate.ts # interactive evaluation
721
+ │ ├── module.ts # standalone lesson modules
722
+ │ ├── platform.ts # discovery / pricing / health
723
+ │ ├── slide.ts # create / list / open
724
+ │ ├── test.ts # generated assessments
725
+ │ └── video.ts # create / list / open
420
726
  └── utils/
421
- ├── output.ts # formatTable, formatJson
727
+ ├── output.ts # formatTable, formatJson
422
728
  └── spinner.ts
423
729
  ```
424
730
 
425
731
  ## npm scripts
426
732
 
427
- | Script | Command | Purpose |
428
- | ------- | ------------------- | ---------------------------- |
429
- | `dev` | `tsx src/index.ts` | Run from source |
430
- | `build` | `tsc --outDir dist` | Emit JS + `.d.ts` to `dist/` |
431
- | `link` | `npm link` | Symlink `tutorflow` globally |
432
- | `test` | `jest` | (No tests committed yet) |
733
+ | Script | Command | Purpose |
734
+ | ---------------- | ----------------------- | --------------------------------------- |
735
+ | `dev` | `tsx src/index.ts` | Run from source |
736
+ | `build` | `tsc --outDir dist` | Emit JS + `.d.ts` to `dist/` |
737
+ | `link` | `npm link` | Symlink `tutorflow` globally |
738
+ | `typecheck` | `tsc --noEmit` | Type-check without emitting |
739
+ | `test` | `vitest run` | Run all tests once |
740
+ | `test:watch` | `vitest` | Watch-mode tests |
741
+ | `test:coverage` | `vitest run --coverage` | Generate coverage report |
742
+ | `prepublishOnly` | `rm -rf dist && build` | Auto-runs before `npm publish` |
433
743
 
434
744
  ## Stack
435
745
 
@@ -438,4 +748,5 @@ apps/cli/
438
748
  - **HTTP:** [undici](https://github.com/nodejs/undici) `^5.27.0`
439
749
  - **Color:** [chalk](https://github.com/chalk/chalk) `^5.3.0`
440
750
  - **Browser open:** [open](https://github.com/sindresorhus/open) `^9.0.0`
751
+ - **Tests:** [Vitest](https://vitest.dev) `^4.1.5`
441
752
  - **Dev:** TypeScript `^5.0.0`, tsx `^3.0.0`, `@types/node` `^20.0.0`
package/dist/client.d.ts CHANGED
@@ -6,7 +6,13 @@ export declare class ApiClient {
6
6
  private config;
7
7
  constructor(config?: IClientConfig);
8
8
  static create(): Promise<ApiClient>;
9
- request<T>(method: string, endpoint: string, body?: Record<string, unknown>): Promise<T>;
9
+ /**
10
+ * Like `create` but throws CliError if no API key is resolvable. Use this
11
+ * in any command that talks to the API so we get a single, consistent
12
+ * "not authenticated" path.
13
+ */
14
+ static createAuthenticated(): Promise<ApiClient>;
15
+ request<T>(method: string, endpoint: string, body?: Record<string, unknown>, headers?: Record<string, string>): Promise<T>;
10
16
  setApiKey(apiKey: string): void;
11
17
  setBaseUrl(baseUrl: string): void;
12
18
  }
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAgB;gBAElB,MAAM,CAAC,EAAE,aAAa;WAOrB,MAAM,IAAI,OAAO,CAAC,SAAS,CAAC;IAQnC,OAAO,CAAC,CAAC,EACb,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,OAAO,CAAC,CAAC,CAAC;IA0Bb,SAAS,CAAC,MAAM,EAAE,MAAM;IAIxB,UAAU,CAAC,OAAO,EAAE,MAAM;CAG3B"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAqBD,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAgB;gBAElB,MAAM,CAAC,EAAE,aAAa;WAIrB,MAAM,IAAI,OAAO,CAAC,SAAS,CAAC;IAIzC;;;;OAIG;WACU,mBAAmB,IAAI,OAAO,CAAC,SAAS,CAAC;IAWhD,OAAO,CAAC,CAAC,EACb,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,CAAC,CAAC;IAwCb,SAAS,CAAC,MAAM,EAAE,MAAM;IAIxB,UAAU,CAAC,OAAO,EAAE,MAAM;CAG3B"}