coursecast 0.2.2 → 0.2.4

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.
package/README.md CHANGED
@@ -42,7 +42,7 @@ The engine is what writes the content. Pick with `--engine`:
42
42
  coursecast "learn rust" --engine gemini
43
43
  ```
44
44
 
45
- Gemini extras: `GEMINI_MODEL` overrides the model (default `gemini-3.5-flash`);
45
+ Gemini extras: `GEMINI_MODEL` overrides the model (default `gemini-3.5-flash-lite`);
46
46
  `COURSECAST_RESEARCH=0` skips the web-search research pass (faster, cheaper, less current).
47
47
 
48
48
  ## All flags
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  // coursecast — one prompt → a whole interactive course → deployed live.
3
3
 
4
- import { createCourse, deployDir } from '../src/index.mjs';
4
+ import { createCourse, deployDir, detectProvider } from '../src/index.mjs';
5
5
 
6
6
  const argv = process.argv.slice(2);
7
7
 
@@ -25,9 +25,9 @@ USAGE
25
25
 
26
26
  OPTIONS
27
27
  --engine <id> generation engine: claude-code (default), gemini, codex, stub
28
- --model <id> model hint passed to the engine (e.g. gemini-3.5-flash)
28
+ --model <id> model hint passed to the engine (e.g. gemini-3.5-flash-lite)
29
29
  --storage <id> progress storage: local (default, no setup), cloud (sync across devices)
30
- --provider <id> deploy target: vercel (default), netlify
30
+ --provider <id> deploy target: vercel | netlify (auto-detects what you have installed)
31
31
  --lessons <n> target number of lessons (default ~12)
32
32
  --concurrency <n> how many lessons to generate at once (default 4)
33
33
  --deploy deploy after generating (otherwise generate only)
@@ -57,7 +57,7 @@ async function main() {
57
57
  }
58
58
 
59
59
  const engine = getFlag('engine', 'claude-code');
60
- const provider = getFlag('provider', 'vercel');
60
+ const providerFlag = getFlag('provider', undefined);
61
61
  const model = getFlag('model', undefined);
62
62
  const storage = getFlag('storage', 'local');
63
63
  const out = getFlag('out', undefined);
@@ -70,6 +70,8 @@ async function main() {
70
70
  log(`✅ Course ready: ${res.lessons.length} lessons${res.failed.length ? ` (${res.failed.length} failed: ${res.failed.join(', ')})` : ''}`);
71
71
 
72
72
  if (has('deploy')) {
73
+ const provider = providerFlag || await detectProvider();
74
+ if (!providerFlag) log(`🔎 Provider: ${provider} (auto-detected — use --provider to override)`);
73
75
  const dep = await deployDir({
74
76
  dir, provider, prod: !has('preview'), dryRun: has('dry-run'), log,
75
77
  });
@@ -77,7 +79,7 @@ async function main() {
77
79
  else log(`\n🎉 Live at: ${dep.url}`);
78
80
  } else {
79
81
  log(`\nPreview locally: npx serve "${dir}" (or open ${dir}/index.html)`);
80
- log(`Deploy: coursecast "${topic}" --deploy --provider ${provider}`);
82
+ log(`Deploy: coursecast "${topic}" --deploy`);
81
83
  }
82
84
  } catch (e) {
83
85
  log(`\n❌ ${e.message}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "coursecast",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "One prompt → a full interactive course — live widgets, quizzes, a mastery dashboard — rendered to self-contained HTML and deployed live.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -4,14 +4,14 @@
4
4
  // schema rather than inventing them from stale training data.
5
5
  //
6
6
  // Requires: GEMINI_API_KEY (free tier works — https://aistudio.google.com/apikey)
7
- // Optional: GEMINI_MODEL (default "gemini-3.5-flash"), COURSECAST_RESEARCH=0 to skip search.
7
+ // Optional: GEMINI_MODEL (default "gemini-3.5-flash-lite"), COURSECAST_RESEARCH=0 to skip search.
8
8
 
9
9
  import { buildSyllabusPrompt, buildLessonPrompt, finalizeLesson, extractJsonObject } from './shared-prompts.mjs'
10
10
 
11
11
  export const name = 'gemini'
12
12
 
13
13
  const API = 'https://generativelanguage.googleapis.com/v1beta/models'
14
- const DEFAULT_MODEL = process.env.GEMINI_MODEL || 'gemini-3.5-flash'
14
+ const DEFAULT_MODEL = process.env.GEMINI_MODEL || 'gemini-3.5-flash-lite'
15
15
  const RESEARCH_ON = process.env.COURSECAST_RESEARCH !== '0'
16
16
 
17
17
  function apiKey() {
package/src/index.mjs CHANGED
@@ -19,6 +19,22 @@ async function loadEngine(id) {
19
19
  try { return await import(`./engines/${id}.mjs`); }
20
20
  catch { throw new Error(`Unknown engine "${id}". Available: claude-code, gemini, codex, stub.`); }
21
21
  }
22
+ // Pick a deploy provider by what's actually installed: vercel first, then netlify.
23
+ // Auto-detection beats a silent default (which pretended netlify didn't exist) and
24
+ // beats prompting (which would break CI/scripted use).
25
+ export async function detectProvider() {
26
+ for (const id of ['vercel', 'netlify']) {
27
+ const p = await loadProvider(id);
28
+ if (p.isAvailable()) return id;
29
+ }
30
+ throw new Error(
31
+ 'No deploy CLI found. Install one and log in:\n' +
32
+ ' vercel: npm i -g vercel && vercel login\n' +
33
+ ' netlify: npm i -g netlify-cli && netlify login\n' +
34
+ '…or skip --deploy and host the folder anywhere that serves static files.'
35
+ );
36
+ }
37
+
22
38
  async function loadProvider(id) {
23
39
  try { return await import(`./providers/${id}.mjs`); }
24
40
  catch { throw new Error(`Unknown provider "${id}". Available: vercel, netlify.`); }