Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# sh can't read this file with CRLF line endings
plugin/hooks/check-node.cmd text eol=lf
15 changes: 15 additions & 0 deletions .github/workflows/prerelease-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,21 @@ jobs:
- name: Prerelease checks
run: npm run prerelease:check

plugin-session-check-windows:
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 22

- name: Run the plugin session check
shell: pwsh
run: ./scripts/smoke-plugin-session-check.ps1

responsive-smoke:
runs-on: ubuntu-latest
needs: checks
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ This repo is both the framework source and the template for new course projects.
- `template/` — what `coursecode create` gives course authors (their `vite.config.js`, `course/`)
- `template/course/` — doubles as the test course for framework development
- `vite.framework-dev.config.js` — framework dev build (references `lib/` directly); `template/vite.config.js` is the course-author build (imports from the `coursecode` package)
- `plugin/` — one plugin for Claude Code (`.claude-plugin/plugin.json`, `.mcp.json`) and Codex (`plugin.json`, `mcp.json`), sharing `skills/` and the `mcp/coursecode-mcp.mjs` launcher. Both repo marketplaces (`.claude-plugin/marketplace.json`, `.agents/plugins/marketplace.json`) point at it. `npm run plugin:pack` builds the Codex directory package with a bundled runtime in ignored `artifacts/codex-plugin/`; see `plugin/CODEX.md`.
- `plugin/` — one plugin for Claude Code (`.claude-plugin/plugin.json`, `.mcp.json`, `hooks/`) and Codex (`plugin.json`, `mcp.json`), sharing `skills/` and the `mcp/coursecode-mcp.mjs` launcher. Both repo marketplaces (`.claude-plugin/marketplace.json`, `.agents/plugins/marketplace.json`) point at it. `npm run plugin:pack` builds the Codex directory package with a bundled runtime in ignored `artifacts/codex-plugin/`; see `plugin/CODEX.md`.
- `lib/agent-setup.js` — MCP settings and `AGENTS.md` that `coursecode create` and `coursecode agent-setup` write into course projects

Changes under `template/` ship to every new course project. Keep framework-dev-only content out of it.
Expand Down
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- **Node.js check at session start:** In Claude Code, the plugin now tells the AI when Node.js is missing or older than 22.15, so it can run the `setup` skill, which asks before installing anything. Before, the CourseCode tools just failed to appear, because the MCP server needs Node.js to start. The check prints nothing when Node.js is fine, and it isn't included in the Codex directory package.

### Changed
- **First-run guidance:** `coursecode create` now tells you to open the project in your AI tool and describe the course. Projects created through the plugin skip that step because the AI tool is already running.
- **README:** Quick Start leads with the Claude Code and Codex plugin, then the `npx` path.
- **New project tooling:** New projects use ESLint 10, and `create` no longer prints npm's audit and funding notices.
- **Package homepage:** The npm `homepage` points to coursecodeframework.com.

### Fixed
- **Commands that failed without a global install:** The commands `create` prints now use `npx coursecode`, so they work after `npx coursecode create`.
- **New course metadata:** New projects start at version `1.0.0` instead of `2.0.0` and no longer carry the template author's name. Their first package is named `<course>_v1.0.0_<format>.zip`.
- **`narration` help:** The command description lists all five text-to-speech providers instead of only ElevenLabs.

## [0.4.3] - 2026-10-29

### Added
Expand Down
46 changes: 24 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,27 +33,29 @@ Framework documentation lives at [coursecodeframework.com/docs](https://courseco

---

## Installation
## Quick Start

### Required
### With the plugin (recommended)

[Node.js](https://nodejs.org/) 22.15 or later. You can run every command through `npx coursecode`, or install the CLI globally for the shorter `coursecode` command:
The Claude Code and Codex plugin guides your AI tool from a new project to an LMS package, and it works before a project exists. It is not yet in the Claude Code or Codex plugin directories, so install it from this repository:

```bash
npm install -g coursecode
```
# Claude Code
claude plugin marketplace add course-code-framework/coursecode
claude plugin install coursecode@coursecode

Learner courses support Chrome 111+, Edge 111+, Firefox 114+, and Safari 16.4+. SCORM compatibility describes LMS communication and packaging; it does not imply Internet Explorer support.
# Codex
codex plugin marketplace add course-code-framework/coursecode
codex plugin add coursecode@coursecode
```

### Recommended
Then start your AI tool in an empty folder and describe the course you want.

- A code or text editor, such as [VS Code](https://code.visualstudio.com/), [Cursor](https://cursor.com/), or similar
- An AI coding tool with MCP support, such as [Claude Code](https://claude.com/claude-code), [Codex](https://developers.openai.com/codex), [Cursor](https://cursor.com/), or VS Code with GitHub Copilot
- [GitHub Desktop](https://desktop.github.com/) for version control
The plugin needs [Node.js](https://nodejs.org/) 22.15 or later but no global `coursecode` install. If Node.js is missing, its `setup` skill offers to install it. The plugin bundles the MCP server below and six skills: setup, new-course, outline, build-slides, review, and publish. See [plugin/README.md](plugin/README.md).

---
### With npx

## Quick Start
With [Node.js](https://nodejs.org/) 22.15 or later installed, create a project:

```bash
npx coursecode create my-course
Expand All @@ -76,21 +78,21 @@ Start with the [User Guide](framework/docs/USER_GUIDE.md) for a complete walkthr

---

## Claude Code and Codex Plugin
## Requirements

The plugin adds guided skills for each stage, from a new project to an LMS package, and works before a project exists. Start your AI tool in an empty folder and describe the course you want. It is not yet in the Claude Code or Codex plugin directories, so install it from this repository:
[Node.js](https://nodejs.org/) 22.15 or later. You can run every command through `npx coursecode`, or install the CLI globally for the shorter `coursecode` command used in the rest of this guide:

```bash
# Claude Code
claude plugin marketplace add course-code-framework/coursecode
claude plugin install coursecode@coursecode

# Codex
codex plugin marketplace add course-code-framework/coursecode
codex plugin add coursecode@coursecode
npm install -g coursecode
```

The plugin bundles the MCP server below and six skills: setup, new-course, outline, build-slides, review, and publish. It needs Node.js but no global `coursecode` install. See [plugin/README.md](plugin/README.md).
Learner courses support Chrome 111+, Edge 111+, Firefox 114+, and Safari 16.4+. SCORM compatibility describes LMS communication and packaging; it does not imply Internet Explorer support.

Also recommended:

- A code or text editor, such as [VS Code](https://code.visualstudio.com/), [Cursor](https://cursor.com/), or similar
- An AI coding tool with MCP support, such as [Claude Code](https://claude.com/claude-code), [Codex](https://developers.openai.com/codex), [Cursor](https://cursor.com/), or VS Code with GitHub Copilot
- [GitHub Desktop](https://desktop.github.com/) for version control

---

Expand Down
2 changes: 1 addition & 1 deletion bin/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ program
// Narration command
program
.command('narration')
.description('Generate audio narration from text using ElevenLabs')
.description('Generate audio narration from text using ElevenLabs, OpenAI, Azure, Google Cloud, or Deepgram')
.option('-f, --force', 'Regenerate all narration (ignore cache)')
.option('-s, --slide <id>', 'Generate narration for a specific slide only')
.option('--dry-run', 'Preview what would be generated')
Expand Down
2 changes: 1 addition & 1 deletion framework/scripts/tts-providers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* 3. Default to deepgram
*
* Environment Variables:
* TTS_PROVIDER - Explicit provider selection: elevenlabs, openai, azure
* TTS_PROVIDER - Explicit provider selection: elevenlabs, openai, azure, google, deepgram
*/

import { ElevenLabsProvider } from './elevenlabs-provider.js';
Expand Down
31 changes: 22 additions & 9 deletions lib/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const PACKAGE_ROOT = path.join(__dirname, '..');
const REQUIRED_PROJECT_DIRECTORIES = [
{
path: path.join('course', 'references'),
placeholder: '# Place source documents here (docx, pptx, pdf)\n# Run `coursecode convert` to convert them to markdown\n'
placeholder: '# Place source documents here (docx, pptx, pdf)\n# Run `npx coursecode convert` to convert them to markdown\n'
},
{
path: path.join('course', 'references', 'converted'),
Expand Down Expand Up @@ -169,7 +169,8 @@ function npmInstall(cwd) {
return new Promise((resolve, reject) => {
const npmCli = process.env.COURSECODE_NPM_CLI;
const command = npmCli ? process.execPath : 'npm';
const args = npmCli ? [npmCli, 'install'] : ['install'];
const installArgs = ['install', '--no-audit', '--no-fund'];
const args = npmCli ? [npmCli, ...installArgs] : installArgs;
const child = spawn(command, args, {
cwd,
stdio: 'inherit',
Expand All @@ -187,6 +188,19 @@ function npmInstall(cwd) {
});
}

/**
* Next steps after create. Commands use npx because the project's own CLI
* works whether or not coursecode is installed globally.
*/
export function formatNextSteps({ directoryName, currentDirectory = false, plugin = false }) {
const changeDirectory = currentDirectory ? '' : `cd ${directoryName}\n `;
const preview = `To preview your course:\n\n ${plugin ? changeDirectory : ''}npx coursecode preview\n\n Then open http://localhost:4173\n`;

if (plugin) return `\n ${preview}`;

return `\n Open the project in your AI tool and describe the course you want:\n\n ${changeDirectory}claude # or codex, or open the folder in Cursor or VS Code\n\n ${preview}`;
}

/**
* Initialize git repository
*/
Expand Down Expand Up @@ -350,8 +364,8 @@ export async function create(name, options = {}) {

Next steps:
- Edit course-config.js with your course metadata
- Create slides with: coursecode new slide <id>
- Create assessments with: coursecode new assessment <id>
- Create slides with: npx coursecode new slide <id>
- Create assessments with: npx coursecode new assessment <id>
`);
} else {
console.log(`
Expand All @@ -365,8 +379,8 @@ export async function create(name, options = {}) {

The project includes example slides (prefixed with "example-") that
demonstrate framework features. When ready to build your own course:
- Run: coursecode clean
- Or create a blank project: coursecode create <name> --blank
- Run: npx coursecode clean
- Or create a blank project: npx coursecode create <name> --blank
`);
}

Expand All @@ -384,11 +398,10 @@ export async function create(name, options = {}) {

child.on('error', () => {
console.warn(' ⚠️ Failed to start preview server. Run manually:');
console.log(` cd ${directoryName} && coursecode preview\n`);
console.log(` cd ${directoryName} && npx coursecode preview\n`);
});
} else {
const changeDirectory = currentDirectory ? '' : `cd ${directoryName}\n `;
console.log(`\n To preview your course:\n\n ${changeDirectory}coursecode preview\n\n Then open http://localhost:4173\n`);
console.log(formatNextSteps({ directoryName, currentDirectory, plugin: options.plugin === true }));
}

return {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@
"bugs": {
"url": "https://github.com/course-code-framework/coursecode/issues"
},
"homepage": "https://github.com/course-code-framework/coursecode/blob/main/framework/docs/USER_GUIDE.md",
"homepage": "https://coursecodeframework.com",
"engines": {
"node": ">=22.15.0"
},
Expand Down
2 changes: 1 addition & 1 deletion plugin/CODEX.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ npm run plugin:pack
npm run plugin:smoke
```

Packaging copies `plugin/` without the Claude Code files and README, adds a `runtime/` folder with the framework's production dependencies, and writes `artifacts/codex-plugin/coursecode/`, `artifacts/codex-plugin/coursecode-<version>-codex.zip`, and a local marketplace at `artifacts/codex-plugin/`. The launcher uses `runtime/` when the project has no `coursecode` dependency, and the `new-course` skill runs the first `create` through the launcher when `runtime/` exists. The ZIP excludes the repository's dependencies, credentials, Cloud code, and marketplace catalogs. Third-party source maps, type declarations, tests, and examples are omitted; executable code and license notices remain. The ZIP has no directory entries, and packaging fails above the portal's 5,000-entry limit.
Packaging copies `plugin/` without the Claude Code files (including the `hooks/` session check) and README, adds a `runtime/` folder with the framework's production dependencies, and writes `artifacts/codex-plugin/coursecode/`, `artifacts/codex-plugin/coursecode-<version>-codex.zip`, and a local marketplace at `artifacts/codex-plugin/`. The launcher uses `runtime/` when the project has no `coursecode` dependency, and the `new-course` skill runs the first `create` through the launcher when `runtime/` exists. The ZIP excludes the repository's dependencies, credentials, Cloud code, and marketplace catalogs. Third-party source maps, type declarations, tests, and examples are omitted; executable code and license notices remain. The ZIP has no directory entries, and packaging fails above the portal's 5,000-entry limit.

The runtime is built from this checkout through `npm pack`, not fetched as a published release. `BUILD.json` records its provenance and package integrity; `runtime/package-lock.json` records resolved runtime dependencies. Framework and plugin versions must match. Do not present a development ZIP with unreleased changes as the published npm version. Before public release, choose an aligned release version and rerun the framework's release checks and plugin smoke test.

Expand Down
3 changes: 2 additions & 1 deletion plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ codex plugin add coursecode@coursecode

## Requirements

[Node.js](https://nodejs.org/) 22.15 or later. If it's missing, the `setup` skill offers to install it (Homebrew or the official installer on macOS, `winget` on Windows). Nothing else needs installing first:
[Node.js](https://nodejs.org/) 22.15 or later. If it's missing, the `setup` skill offers to install it (Homebrew or the official installer on macOS, `winget` on Windows). In Claude Code, a check at the start of each session tells the AI when Node.js is missing or too old, so it can run that skill. The check installs nothing itself and says nothing when Node.js is fine. Nothing else needs installing first:

- **No global `coursecode` install.** Inside a course project the plugin runs the project's own CourseCode version. Elsewhere it runs the matching published version through `npx`.
- **No browser setup.** Previews and screenshots use a headless copy of Chrome, Edge, Chromium, or Brave if one is installed. If none is, the first screenshot downloads Chrome Headless Shell (about 100 MB, once) into `~/.cache/puppeteer`, shared with other Puppeteer-based tools. Set `CHROME_PATH` to use a specific browser.
Expand All @@ -50,6 +50,7 @@ The preview runs at http://localhost:4173, so you can watch the course change as

- **MCP server** (`coursecode`): headless preview, screenshots, navigation, interaction testing, lint, live diagnostics, builds, and catalogs of components, interactions, CSS classes, and icons. Projects the plugin creates leave out `.mcp.json` so Claude Code doesn't list each tool twice; run `npx coursecode agent-setup` in a project to add it for people who don't have the plugin.
- **Skills**: the six workflows above. They follow the guides in each project's `framework/docs/`, so they stay in step with the framework version the course uses.
- **Session check** (Claude Code only): a `SessionStart` hook, `hooks/check-node.cmd`, that needs no Node.js and works in `sh`, Git Bash, and PowerShell. The same file holds a shell version and a Windows batch version of the check, so update both.

## What it downloads and sends

Expand Down
36 changes: 36 additions & 0 deletions plugin/hooks/check-node.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
: << 'BATCH'
@echo off
rem SessionStart check. It can't use Node.js, because Node.js is what it checks.
rem This one file is a batch file and a shell script. The hook runs it with
rem ". <file>": sh and Git Bash source it and skip this block as a heredoc, and
rem PowerShell runs it through cmd.exe, so execution policy doesn't apply.
rem Keep it LF-only (.gitattributes) and free of batch labels.
rem Minimum version: keep both halves in step with the launcher in
rem mcp/coursecode-mcp.mjs (tests/lib/plugin-manifests.test.js checks this).
setlocal
set "MAJOR=0"
set "MINOR=0"
set "FOUND=no Node.js"
for /f "tokens=1,2 delims=v." %%a in ('node --version 2^>nul') do (
set "MAJOR=%%a"
set "MINOR=%%b"
set "FOUND=Node.js %%a.%%b"
)
if %MAJOR% GTR 22 exit /b 0
if %MAJOR% EQU 22 if %MINOR% GEQ 15 exit /b 0
echo CourseCode: the tools can't start because they need Node.js 22.15 or later and this computer has %FOUND%. Use the coursecode:setup skill before any CourseCode work; it asks before installing anything.
exit /b 0
BATCH

version=$(node --version 2>/dev/null) || version=
version=${version#v}
major=${version%%.*}
minor=${version#*.}
minor=${minor%%.*}
case "$major$minor" in
'' | *[!0-9]*) major=0 minor=0 found='no Node.js' ;;
*) found="Node.js $major.$minor" ;;
esac
if [ "$major" -gt 22 ] || { [ "$major" -eq 22 ] && [ "$minor" -ge 15 ]; }; then exit 0; fi
echo "CourseCode: the tools can't start because they need Node.js 22.15 or later and this computer has $found. Use the coursecode:setup skill before any CourseCode work; it asks before installing anything."
exit 0
16 changes: 16 additions & 0 deletions plugin/hooks/hooks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"description": "Tells the AI tool at session start when Node.js is missing or too old for the CourseCode tools.",
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": ". \"${CLAUDE_PLUGIN_ROOT}/hooks/check-node.cmd\"",
"timeout": 10
}
]
}
]
}
}
3 changes: 2 additions & 1 deletion plugin/mcp/coursecode-mcp.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
* FALLBACK_VERSION must match package.json and both plugin manifests
* (tests/lib/claude-plugin.test.js checks this). Spawning node/npx from here
* rather than from the MCP config keeps the plugin working on Windows, where npx
* is a .cmd shim.
* is a .cmd shim. The minimum Node.js version in main() must match package.json
* engines and hooks/check-node.cmd (tests/lib/plugin-manifests.test.js checks this).
*/
import { spawn } from 'node:child_process';
import fs from 'node:fs';
Expand Down
2 changes: 1 addition & 1 deletion scripts/package-codex-plugin.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const outputRoot = path.join(root, 'artifacts', 'codex-plugin');
const source = path.join(root, 'plugin');
// Claude Code files and repository docs stay out of the Codex package
const CLAUDE_ONLY = new Set(['.claude-plugin', '.mcp.json', 'README.md', 'CODEX.md']);
const CLAUDE_ONLY = new Set(['.claude-plugin', '.mcp.json', 'hooks', 'README.md', 'CODEX.md']);
const manifest = JSON.parse(fs.readFileSync(path.join(source, 'plugin.json'), 'utf8'));
const pkg = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));
if (manifest.version !== pkg.version) throw new Error('Keep the Codex plugin and framework package versions aligned.');
Expand Down
2 changes: 1 addition & 1 deletion scripts/smoke-codex-plugin.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ try {
assert.equal(review.negative.length, 3);
const skills = fs.readdirSync(path.join(plugin, 'skills'));
assert.deepEqual(skills.sort(), ['build-slides', 'new-course', 'outline', 'publish', 'review', 'setup']);
assert(!fs.existsSync(path.join(plugin, '.claude-plugin')) && !fs.existsSync(path.join(plugin, '.mcp.json')));
assert(!['.claude-plugin', '.mcp.json', 'hooks'].some(name => fs.existsSync(path.join(plugin, name))));
const icon = fs.readFileSync(path.join(plugin, ui.logo));
assert.equal(icon.subarray(1, 4).toString(), 'PNG');
assert.equal(icon.readUInt32BE(16), 256);
Expand Down
Loading
Loading