coursecode 0.1.24 → 0.1.25

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.
@@ -662,6 +662,10 @@ Runs **in Node.js** during build (via `vite.framework-dev.config.js` `closeBundl
662
662
 
663
663
  **Errors fail the build; warnings print but don't block.**
664
664
 
665
+ ### MCP `coursecode_lint` — Unified Results
666
+
667
+ The MCP `coursecode_lint` tool always runs the build linter. When the preview server is running and the headless browser is connected, it also merges runtime lint results (contrast, touch targets, spacing, layout) into the same response. Runtime-sourced items are tagged with `source: 'runtime'` and `rule: 'runtime-lint'`. The `runtimeLintIncluded` flag in the response indicates whether runtime checks were included. This gives AI agents a single, low-token-cost tool for all lint results without needing to pull the full course state via `coursecode_state`.
668
+
665
669
  ### Shared Rules (`lib/validation-rules.js`)
666
670
 
667
671
  Pure validation functions used by **both** linters. No environment-specific code (no DOM, no `fs`). Includes assessment validation, engagement validation, and result formatting.
@@ -306,14 +306,17 @@ Use to discover available interactions before creating assessments.`,
306
306
 
307
307
  {
308
308
  name: 'coursecode_lint',
309
- description: `Run the build-time linter and get structured results.
309
+ description: `Run the course linter and get structured results.
310
+
311
+ Always runs build-time lint (config, CSS classes, structure). When the preview server is running and the headless browser is connected, also includes runtime lint results (contrast, touch targets, spacing, layout).
310
312
 
311
313
  Returns:
312
- - errors: [{slideId, rule, message, severity}]
313
- - warnings: [{slideId, rule, message, severity, class?, suggestion?}]
314
+ - errors: [{slideId?, rule, message, severity, source?, hint?}]
315
+ - warnings: [{slideId?, rule, message, severity, source?, class?, suggestion?, hint?}]
314
316
  - passed: boolean
317
+ - runtimeLintIncluded: boolean (true when runtime checks were included)
315
318
 
316
- Rules detected:
319
+ Build-time rules (always checked):
317
320
  - undefined-css-class: hallucinated or stale class names (with fix suggestions)
318
321
  - unknown-component: unregistered data-component types
319
322
  - requirement-missing-component: engagement requirement without matching component
@@ -322,9 +325,11 @@ Rules detected:
322
325
  - assessment-id-mismatch: config ID doesn't match assessment ID
323
326
  - invalid-gating: bad gating condition configuration
324
327
 
325
- The runtime linter (visible via coursecode_state errors/warnings) also checks:
326
- - Spacing: flex/grid containers without gap, adjacent elements with no margin, containers with no padding
327
- - Contrast, touch targets, text proximity to borders, element overlap, styled lists
328
+ Runtime rules (included when preview is running, source='runtime'):
329
+ - Contrast ratio violations
330
+ - Touch target size violations
331
+ - Spacing issues (missing gap, margin, padding)
332
+ - Text proximity to borders, element overlap, styled lists
328
333
 
329
334
  Suppression: Add data-lint-ignore to any HTML element to suppress warnings for it and children.
330
335
  data-lint-ignore — suppress all warnings
package/lib/mcp-server.js CHANGED
@@ -247,6 +247,46 @@ export async function startMcpServer(options = {}) {
247
247
  break;
248
248
  case 'coursecode_lint':
249
249
  result = await lintCourse();
250
+ // Merge runtime lint errors if headless browser is already connected
251
+ if (headless.isRunning()) {
252
+ try {
253
+ const runtimeErrors = await headless.evaluateParent(() => {
254
+ return window._stubPlayerState?.errorLog || [];
255
+ });
256
+ if (runtimeErrors.length > 0) {
257
+ const runtimeWarnings = runtimeErrors.filter(e => e.isWarning);
258
+ const runtimeErrs = runtimeErrors.filter(e => !e.isWarning);
259
+ if (runtimeWarnings.length > 0) {
260
+ result.warnings = (result.warnings || []).concat(
261
+ runtimeWarnings.map(e => ({
262
+ severity: 'warning',
263
+ message: e.message,
264
+ hint: e.hint,
265
+ rule: 'runtime-lint',
266
+ source: 'runtime'
267
+ }))
268
+ );
269
+ result.warningCount = result.warnings.length;
270
+ }
271
+ if (runtimeErrs.length > 0) {
272
+ result.errors = (result.errors || []).concat(
273
+ runtimeErrs.map(e => ({
274
+ severity: 'error',
275
+ message: e.message,
276
+ hint: e.hint,
277
+ rule: 'runtime-lint',
278
+ source: 'runtime'
279
+ }))
280
+ );
281
+ result.errorCount = result.errors.length;
282
+ result.passed = result.errors.length === 0;
283
+ }
284
+ result.runtimeLintIncluded = true;
285
+ }
286
+ } catch {
287
+ // Headless browser may have disconnected — non-fatal, build lint still valid
288
+ }
289
+ }
250
290
  break;
251
291
 
252
292
  case 'coursecode_export_content':
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "coursecode",
3
- "version": "0.1.24",
3
+ "version": "0.1.25",
4
4
  "description": "Multi-format course authoring framework with CLI tools (SCORM 2004, SCORM 1.2, cmi5, LTI 1.3)",
5
5
  "type": "module",
6
6
  "bin": {