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 +1 -1
- package/bin/coursecast.mjs +7 -5
- package/package.json +1 -1
- package/src/engines/gemini.mjs +2 -2
- package/src/index.mjs +16 -0
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
|
package/bin/coursecast.mjs
CHANGED
|
@@ -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 (
|
|
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
|
|
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
|
|
82
|
+
log(`Deploy: coursecast "${topic}" --deploy`);
|
|
81
83
|
}
|
|
82
84
|
} catch (e) {
|
|
83
85
|
log(`\n❌ ${e.message}`);
|
package/package.json
CHANGED
package/src/engines/gemini.mjs
CHANGED
|
@@ -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.`); }
|