coursecode 0.1.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.
- package/LICENSE +21 -0
- package/README.md +322 -0
- package/THIRD_PARTY_NOTICES.md +22 -0
- package/bin/cli.js +331 -0
- package/framework/assets/logo-coursecode-black.svg +14 -0
- package/framework/assets/logo-coursecode-white.svg +14 -0
- package/framework/assets/logo-coursecode.svg +14 -0
- package/framework/css/01-base.css +160 -0
- package/framework/css/02-layout.css +499 -0
- package/framework/css/accessibility.css +834 -0
- package/framework/css/components/accordions.css +710 -0
- package/framework/css/components/assessments.css +520 -0
- package/framework/css/components/audio-player.css +570 -0
- package/framework/css/components/badges.css +80 -0
- package/framework/css/components/breadcrumbs.css +87 -0
- package/framework/css/components/buttons.css +707 -0
- package/framework/css/components/callouts.css +1280 -0
- package/framework/css/components/cards.css +475 -0
- package/framework/css/components/carousel.css +193 -0
- package/framework/css/components/checkbox-group.css +123 -0
- package/framework/css/components/checklist.css +203 -0
- package/framework/css/components/collapse.css +96 -0
- package/framework/css/components/comparison.css +33 -0
- package/framework/css/components/content-image.css +36 -0
- package/framework/css/components/document-gallery.css +425 -0
- package/framework/css/components/dropdown.css +115 -0
- package/framework/css/components/embed-frame.css +142 -0
- package/framework/css/components/engagement.css +412 -0
- package/framework/css/components/features.css +35 -0
- package/framework/css/components/flip-cards.css +253 -0
- package/framework/css/components/footer.css +353 -0
- package/framework/css/components/forms.css +294 -0
- package/framework/css/components/hero.css +216 -0
- package/framework/css/components/images.css +528 -0
- package/framework/css/components/interactive-timeline.css +274 -0
- package/framework/css/components/intro-cards.css +30 -0
- package/framework/css/components/lightbox.css +666 -0
- package/framework/css/components/loading.css +65 -0
- package/framework/css/components/modals.css +235 -0
- package/framework/css/components/notifications.css +107 -0
- package/framework/css/components/quote.css +150 -0
- package/framework/css/components/sidebar.css +684 -0
- package/framework/css/components/slide-header.css +52 -0
- package/framework/css/components/spinner.css +62 -0
- package/framework/css/components/stats.css +44 -0
- package/framework/css/components/steps.css +232 -0
- package/framework/css/components/tables.css +90 -0
- package/framework/css/components/tabs.css +347 -0
- package/framework/css/components/timeline.css +154 -0
- package/framework/css/components/toggle.css +95 -0
- package/framework/css/components/tooltip.css +226 -0
- package/framework/css/components/video-player.css +438 -0
- package/framework/css/design-tokens.css +707 -0
- package/framework/css/framework.css +86 -0
- package/framework/css/interactions/accessibility.css +75 -0
- package/framework/css/interactions/base.css +92 -0
- package/framework/css/interactions/drag-drop.css +295 -0
- package/framework/css/interactions/fill-in-the-blank.css +236 -0
- package/framework/css/interactions/hotspots.css +69 -0
- package/framework/css/interactions/index.css +45 -0
- package/framework/css/interactions/interactive-image.css +359 -0
- package/framework/css/interactions/likert.css +126 -0
- package/framework/css/interactions/matching.css +354 -0
- package/framework/css/interactions/numeric-input.css +78 -0
- package/framework/css/interactions/sequencing.css +378 -0
- package/framework/css/interactions/true-false.css +177 -0
- package/framework/css/layouts/article.css +258 -0
- package/framework/css/layouts/base.css +30 -0
- package/framework/css/layouts/canvas.css +38 -0
- package/framework/css/layouts/focused.css +236 -0
- package/framework/css/layouts/index.css +29 -0
- package/framework/css/layouts/presentation.css +191 -0
- package/framework/css/layouts/traditional.css +52 -0
- package/framework/css/responsive.css +439 -0
- package/framework/css/utilities/accessibility-utils.css +59 -0
- package/framework/css/utilities/animations.css +419 -0
- package/framework/css/utilities/borders.css +72 -0
- package/framework/css/utilities/colors.css +76 -0
- package/framework/css/utilities/container.css +46 -0
- package/framework/css/utilities/decorative.css +442 -0
- package/framework/css/utilities/display.css +257 -0
- package/framework/css/utilities/flexbox.css +80 -0
- package/framework/css/utilities/grid.css +69 -0
- package/framework/css/utilities/icons.css +534 -0
- package/framework/css/utilities/lists.css +190 -0
- package/framework/css/utilities/spacing.css +167 -0
- package/framework/css/utilities/tables.css +81 -0
- package/framework/css/utilities/typography.css +159 -0
- package/framework/css/utilities/visibility.css +117 -0
- package/framework/docs/COURSE_AUTHORING_GUIDE.md +1773 -0
- package/framework/docs/COURSE_OUTLINE_GUIDE.md +725 -0
- package/framework/docs/COURSE_OUTLINE_TEMPLATE.md +161 -0
- package/framework/docs/DATA_MODEL.md +409 -0
- package/framework/docs/FRAMEWORK_GUIDE.md +1088 -0
- package/framework/docs/USER_GUIDE.md +583 -0
- package/framework/docs/examples/cloudflare-channel-relay.js +169 -0
- package/framework/docs/examples/cloudflare-data-worker.js +102 -0
- package/framework/docs/examples/cloudflare-error-worker.js +228 -0
- package/framework/index.html +175 -0
- package/framework/js/app/AppActions.js +410 -0
- package/framework/js/app/AppState.js +225 -0
- package/framework/js/app/AppUI.js +616 -0
- package/framework/js/assessment/AssessmentActions.js +615 -0
- package/framework/js/assessment/AssessmentFactory.js +471 -0
- package/framework/js/assessment/AssessmentState.js +322 -0
- package/framework/js/assessment/AssessmentUI.js +451 -0
- package/framework/js/automation/api-engagement.js +196 -0
- package/framework/js/automation/api-interactions.js +167 -0
- package/framework/js/automation/api.js +242 -0
- package/framework/js/automation/index.js +41 -0
- package/framework/js/components/interactions/drag-drop.js +884 -0
- package/framework/js/components/interactions/fill-in.js +535 -0
- package/framework/js/components/interactions/hotspot.js +702 -0
- package/framework/js/components/interactions/interaction-base.js +511 -0
- package/framework/js/components/interactions/likert.js +301 -0
- package/framework/js/components/interactions/matching.js +699 -0
- package/framework/js/components/interactions/multiple-choice.js +377 -0
- package/framework/js/components/interactions/numeric.js +271 -0
- package/framework/js/components/interactions/sequencing.js +423 -0
- package/framework/js/components/interactions/true-false.js +241 -0
- package/framework/js/components/ui-components/accordion.js +442 -0
- package/framework/js/components/ui-components/alert.js +88 -0
- package/framework/js/components/ui-components/audio-player.js +1193 -0
- package/framework/js/components/ui-components/callout.js +121 -0
- package/framework/js/components/ui-components/carousel.js +145 -0
- package/framework/js/components/ui-components/checkbox-group.js +87 -0
- package/framework/js/components/ui-components/checklist.js +40 -0
- package/framework/js/components/ui-components/collapse.js +114 -0
- package/framework/js/components/ui-components/comparison.js +30 -0
- package/framework/js/components/ui-components/conditional-display.js +150 -0
- package/framework/js/components/ui-components/content-image.js +41 -0
- package/framework/js/components/ui-components/dropdown.js +262 -0
- package/framework/js/components/ui-components/embed-frame.js +274 -0
- package/framework/js/components/ui-components/features.js +33 -0
- package/framework/js/components/ui-components/flip-card.js +230 -0
- package/framework/js/components/ui-components/form-validator.js +76 -0
- package/framework/js/components/ui-components/hero.js +49 -0
- package/framework/js/components/ui-components/index.js +12 -0
- package/framework/js/components/ui-components/interactive-image.js +235 -0
- package/framework/js/components/ui-components/interactive-timeline.js +285 -0
- package/framework/js/components/ui-components/intro-cards.js +35 -0
- package/framework/js/components/ui-components/lightbox.js +652 -0
- package/framework/js/components/ui-components/modal.js +386 -0
- package/framework/js/components/ui-components/notifications.js +145 -0
- package/framework/js/components/ui-components/progress.js +88 -0
- package/framework/js/components/ui-components/quote.js +41 -0
- package/framework/js/components/ui-components/stats.js +33 -0
- package/framework/js/components/ui-components/steps.js +41 -0
- package/framework/js/components/ui-components/tabs.js +255 -0
- package/framework/js/components/ui-components/timeline.js +42 -0
- package/framework/js/components/ui-components/toggle-group.js +73 -0
- package/framework/js/components/ui-components/tooltip.js +458 -0
- package/framework/js/components/ui-components/value-display.js +133 -0
- package/framework/js/components/ui-components/video-player.js +686 -0
- package/framework/js/core/component-catalog.js +121 -0
- package/framework/js/core/event-bus.js +178 -0
- package/framework/js/core/interaction-catalog.js +149 -0
- package/framework/js/dev/runtime-linter.js +1725 -0
- package/framework/js/drivers/cmi5-driver.js +768 -0
- package/framework/js/drivers/driver-factory.js +77 -0
- package/framework/js/drivers/driver-interface.js +110 -0
- package/framework/js/drivers/http-driver-base.js +241 -0
- package/framework/js/drivers/lti-driver.js +508 -0
- package/framework/js/drivers/proxy-driver.js +444 -0
- package/framework/js/drivers/scorm-12-driver.js +560 -0
- package/framework/js/drivers/scorm-2004-driver.js +775 -0
- package/framework/js/drivers/scorm-driver-base.js +112 -0
- package/framework/js/engagement/engagement-manager.js +404 -0
- package/framework/js/engagement/engagement-progress.js +191 -0
- package/framework/js/engagement/engagement-trackers.js +215 -0
- package/framework/js/engagement/requirement-strategies.js +268 -0
- package/framework/js/main.js +727 -0
- package/framework/js/managers/accessibility-manager.js +499 -0
- package/framework/js/managers/assessment-manager.js +230 -0
- package/framework/js/managers/audio-manager.js +944 -0
- package/framework/js/managers/comment-manager.js +88 -0
- package/framework/js/managers/flag-manager.js +86 -0
- package/framework/js/managers/interaction-manager.js +254 -0
- package/framework/js/managers/interaction-registry.js +96 -0
- package/framework/js/managers/objective-manager.js +423 -0
- package/framework/js/managers/score-manager.js +441 -0
- package/framework/js/managers/video-manager.js +536 -0
- package/framework/js/navigation/Breadcrumbs.js +234 -0
- package/framework/js/navigation/NavigationActions.js +1132 -0
- package/framework/js/navigation/NavigationState.js +276 -0
- package/framework/js/navigation/NavigationUI.js +574 -0
- package/framework/js/navigation/document-gallery.js +357 -0
- package/framework/js/navigation/navigation-helpers.js +175 -0
- package/framework/js/navigation/navigation-validators.js +174 -0
- package/framework/js/state/index.js +8 -0
- package/framework/js/state/lms-connection.js +482 -0
- package/framework/js/state/lms-error-utils.js +58 -0
- package/framework/js/state/state-commits.js +200 -0
- package/framework/js/state/state-domains.js +86 -0
- package/framework/js/state/state-manager.js +502 -0
- package/framework/js/state/state-validation.js +311 -0
- package/framework/js/state/transaction-log.js +41 -0
- package/framework/js/state/xapi-statement-service.js +325 -0
- package/framework/js/utilities/access-control.js +99 -0
- package/framework/js/utilities/breakpoint-manager.js +315 -0
- package/framework/js/utilities/canvas-slide.js +35 -0
- package/framework/js/utilities/conditional-display.js +388 -0
- package/framework/js/utilities/course-channel.js +214 -0
- package/framework/js/utilities/course-helpers.js +420 -0
- package/framework/js/utilities/data-reporter.js +273 -0
- package/framework/js/utilities/error-reporter.js +313 -0
- package/framework/js/utilities/hotspot-helper.js +341 -0
- package/framework/js/utilities/icons.js +348 -0
- package/framework/js/utilities/logger.js +92 -0
- package/framework/js/utilities/markdown-renderer.js +45 -0
- package/framework/js/utilities/scroll-tracker.js +68 -0
- package/framework/js/utilities/ui-initializer.js +146 -0
- package/framework/js/utilities/utilities.js +293 -0
- package/framework/js/utilities/view-manager.js +227 -0
- package/framework/js/validation/html-validators.js +422 -0
- package/framework/js/validation/scorm-validators.js +438 -0
- package/framework/js/vendor/pipwerks.js +931 -0
- package/framework/scripts/generate-narration.js +629 -0
- package/framework/scripts/tts-providers/azure-provider.js +178 -0
- package/framework/scripts/tts-providers/base-provider.js +81 -0
- package/framework/scripts/tts-providers/deepgram-provider.js +135 -0
- package/framework/scripts/tts-providers/elevenlabs-provider.js +148 -0
- package/framework/scripts/tts-providers/google-provider.js +272 -0
- package/framework/scripts/tts-providers/index.js +158 -0
- package/framework/scripts/tts-providers/openai-provider.js +143 -0
- package/framework/version.json +63 -0
- package/lib/authoring-api.js +919 -0
- package/lib/build-linter.js +450 -0
- package/lib/build-packaging.js +186 -0
- package/lib/build.js +88 -0
- package/lib/cloud.js +691 -0
- package/lib/convert.js +341 -0
- package/lib/course-parser.js +936 -0
- package/lib/course-writer.js +258 -0
- package/lib/create.js +248 -0
- package/lib/css-index.js +237 -0
- package/lib/dev.js +51 -0
- package/lib/export-content.js +1246 -0
- package/lib/headless-browser.js +413 -0
- package/lib/import.js +377 -0
- package/lib/index.js +80 -0
- package/lib/info.js +79 -0
- package/lib/interaction-formatters.js +568 -0
- package/lib/manifest/cmi5-manifest.js +63 -0
- package/lib/manifest/lti-tool-config.js +53 -0
- package/lib/manifest/manifest-factory.js +99 -0
- package/lib/manifest/scorm-12-manifest.js +61 -0
- package/lib/manifest/scorm-2004-manifest.js +94 -0
- package/lib/manifest/scorm-proxy-manifest.js +104 -0
- package/lib/manifest-parser.js +96 -0
- package/lib/mcp-prompts.js +753 -0
- package/lib/mcp-server.js +316 -0
- package/lib/narration.js +53 -0
- package/lib/pdf-structure.js +142 -0
- package/lib/preview-export.js +231 -0
- package/lib/preview-routes-api.js +662 -0
- package/lib/preview-routes-editing.js +159 -0
- package/lib/preview-routes-lms.js +230 -0
- package/lib/preview-server.js +564 -0
- package/lib/project-utils.js +269 -0
- package/lib/proxy-templates/proxy.html +68 -0
- package/lib/proxy-templates/scorm-bridge.js +112 -0
- package/lib/scaffold.js +193 -0
- package/lib/schema-extractor.js +361 -0
- package/lib/slide-source-editor.js +586 -0
- package/lib/stub-player/app-viewer.js +195 -0
- package/lib/stub-player/app.js +370 -0
- package/lib/stub-player/catalog-panel.js +312 -0
- package/lib/stub-player/config-panel.js +1303 -0
- package/lib/stub-player/content-generator.js +586 -0
- package/lib/stub-player/content-viewer.js +173 -0
- package/lib/stub-player/debug-panel.js +420 -0
- package/lib/stub-player/edit-mode.js +922 -0
- package/lib/stub-player/edit-utils.js +400 -0
- package/lib/stub-player/header-bar.js +354 -0
- package/lib/stub-player/interaction-editor.js +210 -0
- package/lib/stub-player/interactions-panel.js +565 -0
- package/lib/stub-player/lms-api.js +1094 -0
- package/lib/stub-player/login-screen.js +74 -0
- package/lib/stub-player/outline-mode.js +689 -0
- package/lib/stub-player/styles/_assessments-panel.css +245 -0
- package/lib/stub-player/styles/_base.css +89 -0
- package/lib/stub-player/styles/_catalog-icons.css +96 -0
- package/lib/stub-player/styles/_catalog-panel.css +291 -0
- package/lib/stub-player/styles/_config-panel.css +636 -0
- package/lib/stub-player/styles/_content-viewer.css +834 -0
- package/lib/stub-player/styles/_debug-panel.css +576 -0
- package/lib/stub-player/styles/_edit-mode.css +128 -0
- package/lib/stub-player/styles/_header-bar.css +343 -0
- package/lib/stub-player/styles/_interaction-editor.css +140 -0
- package/lib/stub-player/styles/_interactions-panel.css +1038 -0
- package/lib/stub-player/styles/_login-screen.css +102 -0
- package/lib/stub-player/styles/_outline-mode.css +752 -0
- package/lib/stub-player/styles.css +15 -0
- package/lib/stub-player.js +160 -0
- package/lib/test-data-reporting.js +176 -0
- package/lib/test-error-reporting.js +146 -0
- package/lib/token.js +86 -0
- package/lib/upgrade.js +257 -0
- package/lib/validation-rules.js +517 -0
- package/lib/vite-plugin-content-discovery.js +296 -0
- package/package.json +108 -0
- package/schemas/XMLSchema.dtd +402 -0
- package/schemas/adlcp_v1p3.xsd +111 -0
- package/schemas/adlnav_v1p3.xsd +61 -0
- package/schemas/adlseq_v1p3.xsd +93 -0
- package/schemas/common/anyElement.xsd +27 -0
- package/schemas/common/dataTypes.xsd +138 -0
- package/schemas/common/elementNames.xsd +767 -0
- package/schemas/common/elementTypes.xsd +786 -0
- package/schemas/common/rootElement.xsd +31 -0
- package/schemas/common/vocabTypes.xsd +345 -0
- package/schemas/common/vocabValues.xsd +257 -0
- package/schemas/datatypes.dtd +203 -0
- package/schemas/ims_xml.xsd +35 -0
- package/schemas/imscp_v1p1.xsd +368 -0
- package/schemas/imsss_v1p0.xsd +67 -0
- package/schemas/imsss_v1p0auxresource.xsd +19 -0
- package/schemas/imsss_v1p0control.xsd +20 -0
- package/schemas/imsss_v1p0delivery.xsd +17 -0
- package/schemas/imsss_v1p0limit.xsd +47 -0
- package/schemas/imsss_v1p0objective.xsd +67 -0
- package/schemas/imsss_v1p0random.xsd +16 -0
- package/schemas/imsss_v1p0rollup.xsd +46 -0
- package/schemas/imsss_v1p0seqrule.xsd +108 -0
- package/schemas/imsss_v1p0util.xsd +94 -0
- package/schemas/license.txt +17 -0
- package/schemas/lom.xsd +102 -0
- package/schemas/lomCustom.xsd +62 -0
- package/schemas/lomLoose.xsd +62 -0
- package/schemas/lomStrict.xsd +62 -0
- package/schemas/xml.xsd +81 -0
- package/template/.env.example +92 -0
- package/template/course/assets/audio/example-intro.mp3 +0 -0
- package/template/course/assets/audio/example-ui-demo--compact-player.mp3 +0 -0
- package/template/course/assets/audio/example-ui-demo--demo-modal.mp3 +0 -0
- package/template/course/assets/audio/example-ui-demo--full-player.mp3 +0 -0
- package/template/course/assets/docs/example_md_1.md +39 -0
- package/template/course/assets/docs/example_md_2.md +41 -0
- package/template/course/assets/docs/example_pdf_1_thumbnail.png +0 -0
- package/template/course/assets/docs/example_pdf_2.pdf +0 -0
- package/template/course/assets/images/course-architecture.svg +36 -0
- package/template/course/assets/images/logo.svg +14 -0
- package/template/course/assets/widgets/counter-demo.html +190 -0
- package/template/course/assets/widgets/gravity-painter.html +384 -0
- package/template/course/course-config.js +539 -0
- package/template/course/icons.js +19 -0
- package/template/course/interactions/PLUGIN_GUIDE.md +97 -0
- package/template/course/slides/example-course-structure.js +138 -0
- package/template/course/slides/example-final-exam.js +144 -0
- package/template/course/slides/example-finishing.js +127 -0
- package/template/course/slides/example-interactions-showcase.js +615 -0
- package/template/course/slides/example-preview-tour.js +129 -0
- package/template/course/slides/example-remedial.js +143 -0
- package/template/course/slides/example-summary.js +103 -0
- package/template/course/slides/example-ui-showcase.js +1805 -0
- package/template/course/slides/example-welcome.js +123 -0
- package/template/course/slides/example-workflow.js +140 -0
- package/template/course/theme.css +165 -0
- package/template/eslint.config.js +47 -0
- package/template/package.json +28 -0
- package/template/vite.config.js +339 -0
|
@@ -0,0 +1,753 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Prompts & Tool Configuration
|
|
3
|
+
*
|
|
4
|
+
* Single source of truth for all MCP tool definitions, descriptions,
|
|
5
|
+
* and dynamic instruction generation. mcp-server.js imports from here.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import { fileURLToPath } from 'url';
|
|
11
|
+
import { getWorkflowStatus } from './authoring-api.js';
|
|
12
|
+
|
|
13
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
|
|
15
|
+
// ========================================
|
|
16
|
+
// Tool Definitions
|
|
17
|
+
// ========================================
|
|
18
|
+
|
|
19
|
+
export const TOOLS = [
|
|
20
|
+
// --- Runtime Tools (require headless browser) ---
|
|
21
|
+
{
|
|
22
|
+
name: 'coursecode_state',
|
|
23
|
+
description: `Get course state, runtime errors, and warnings in one call. This is the primary tool for checking errors.
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
- slide: current slide ID (string)
|
|
27
|
+
- toc: course structure [{id, type, title, file?}]
|
|
28
|
+
- interactions: interactions on current slide [{id, type, hasResponse, isChecked}]
|
|
29
|
+
- engagement: slide engagement {complete, percentage, requirements}
|
|
30
|
+
- lmsState: LMS data {score, completion, success, bookmark, format, objectives, state}
|
|
31
|
+
- apiLog: last 20 LMS API calls [{timestamp, method, args, result}]
|
|
32
|
+
- errors: runtime errors/warnings [{type, message, hint, isWarning}]
|
|
33
|
+
- frameworkLogs: structured framework log events [{level, domain, operation, message, stack?, timestamp}]
|
|
34
|
+
- consoleLogs: browser console warnings/errors [{type, text, time}]
|
|
35
|
+
|
|
36
|
+
Use this first to understand the course state before taking actions.
|
|
37
|
+
Requires preview server to be running.`,
|
|
38
|
+
inputSchema: {
|
|
39
|
+
type: 'object',
|
|
40
|
+
properties: {},
|
|
41
|
+
required: []
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: 'coursecode_navigate',
|
|
46
|
+
description: `Navigate to a specific slide by ID. Returns:
|
|
47
|
+
- slide: current slide ID
|
|
48
|
+
- interactions: interactions on the new slide [{id, type, hasResponse, isChecked}]
|
|
49
|
+
- engagement: slide engagement {complete, percentage, requirements}
|
|
50
|
+
- accessibility: current accessibility state {theme, highContrast, largeFont, reducedMotion}
|
|
51
|
+
|
|
52
|
+
Optionally set theme or highContrast before navigating. Use this to toggle dark mode for visual inspection.
|
|
53
|
+
|
|
54
|
+
Use coursecode_state first to get the structure and find valid slide IDs.
|
|
55
|
+
Requires preview server to be running.`,
|
|
56
|
+
inputSchema: {
|
|
57
|
+
type: 'object',
|
|
58
|
+
properties: {
|
|
59
|
+
slideId: {
|
|
60
|
+
type: 'string',
|
|
61
|
+
description: 'The slide ID to navigate to'
|
|
62
|
+
},
|
|
63
|
+
theme: {
|
|
64
|
+
type: 'string',
|
|
65
|
+
description: 'Set theme before navigating: "light" or "dark"',
|
|
66
|
+
enum: ['light', 'dark']
|
|
67
|
+
},
|
|
68
|
+
highContrast: {
|
|
69
|
+
type: 'boolean',
|
|
70
|
+
description: 'Enable or disable high contrast mode before navigating'
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
required: ['slideId']
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: 'coursecode_interact',
|
|
78
|
+
description: `Set a response for an interaction AND evaluate it in one call. Returns:
|
|
79
|
+
- correct: boolean
|
|
80
|
+
- score: 0-1
|
|
81
|
+
- feedback: feedback message if any
|
|
82
|
+
- state: updated course state
|
|
83
|
+
|
|
84
|
+
Response format depends on interaction type:
|
|
85
|
+
- multiple-choice: 'a', 'b', 'c', etc.
|
|
86
|
+
- true-false: true or false
|
|
87
|
+
- fill-in-blank: {blankId: 'answer'}
|
|
88
|
+
- drag-drop: {itemId: 'zoneId'}
|
|
89
|
+
- numeric: number
|
|
90
|
+
- sequencing: ['id1', 'id2', 'id3']
|
|
91
|
+
|
|
92
|
+
Requires preview server to be running.`,
|
|
93
|
+
inputSchema: {
|
|
94
|
+
type: 'object',
|
|
95
|
+
properties: {
|
|
96
|
+
interactionId: {
|
|
97
|
+
type: 'string',
|
|
98
|
+
description: 'The interaction ID to answer'
|
|
99
|
+
},
|
|
100
|
+
response: {
|
|
101
|
+
description: 'The response value (format depends on interaction type)'
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
required: ['interactionId', 'response']
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
name: 'coursecode_reset',
|
|
109
|
+
description: `Clear learner state and restart the course. Use this to test from a fresh state.
|
|
110
|
+
|
|
111
|
+
Clears localStorage and fully reloads the headless browser.
|
|
112
|
+
|
|
113
|
+
Requires preview server to be running.`,
|
|
114
|
+
inputSchema: {
|
|
115
|
+
type: 'object',
|
|
116
|
+
properties: {},
|
|
117
|
+
required: []
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
name: 'coursecode_screenshot',
|
|
122
|
+
description: `Take a screenshot of the course preview. Returns a JPEG image.
|
|
123
|
+
|
|
124
|
+
Two modes optimized for token efficiency:
|
|
125
|
+
- normal (default): 800×450 (~30-60KB) — layout checks
|
|
126
|
+
- detailed: 1280×720 (~100-200KB) — close text/element inspection
|
|
127
|
+
|
|
128
|
+
Use scrollY to scroll course content before capturing (useful for long slides).
|
|
129
|
+
fullPage captures the course iframe's full content area.
|
|
130
|
+
|
|
131
|
+
Use to visually inspect slide layout, design, and component rendering.
|
|
132
|
+
Requires preview server to be running.`,
|
|
133
|
+
inputSchema: {
|
|
134
|
+
type: 'object',
|
|
135
|
+
properties: {
|
|
136
|
+
slideId: {
|
|
137
|
+
type: 'string',
|
|
138
|
+
description: 'Navigate to this slide ID before taking screenshot'
|
|
139
|
+
},
|
|
140
|
+
fullPage: {
|
|
141
|
+
type: 'boolean',
|
|
142
|
+
description: 'Capture full scrollable page instead of viewport'
|
|
143
|
+
},
|
|
144
|
+
detailed: {
|
|
145
|
+
type: 'boolean',
|
|
146
|
+
description: 'Use higher-res mode (1280×720) for close inspection'
|
|
147
|
+
},
|
|
148
|
+
scrollY: {
|
|
149
|
+
type: 'number',
|
|
150
|
+
description: 'Scroll course content to this Y position (pixels) before capturing'
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
required: []
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
// --- Workflow & Build Tools ---
|
|
157
|
+
{
|
|
158
|
+
name: 'coursecode_workflow_status',
|
|
159
|
+
description: `Detect the current authoring stage and get stage-specific instructions.
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
- stage: human-readable stage name
|
|
163
|
+
- stageNumber: 1-5
|
|
164
|
+
- checklist: {hasRawRefs, hasConvertedRefs, hasOutline, hasSlides, hasCourseConfig, previewRunning}
|
|
165
|
+
- nextAction: recommended next step
|
|
166
|
+
- recommendedTool: MCP tool to use next
|
|
167
|
+
- instructions: detailed guidance for the current stage
|
|
168
|
+
|
|
169
|
+
Stages:
|
|
170
|
+
1. Source Ingestion - create project and convert reference docs
|
|
171
|
+
2. Outline Creation - build COURSE_OUTLINE.md from references
|
|
172
|
+
3. Course Building - create slides and course-config.js
|
|
173
|
+
4. Preview & Polish - iterate on visual quality and correctness
|
|
174
|
+
5. Export Ready - lint passes, ready for LMS deployment
|
|
175
|
+
|
|
176
|
+
Call this after completing a major milestone to get updated guidance for the next stage.`,
|
|
177
|
+
inputSchema: {
|
|
178
|
+
type: 'object',
|
|
179
|
+
properties: {},
|
|
180
|
+
required: []
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
name: 'coursecode_build',
|
|
185
|
+
description: `Build the course for deployment. Runs the Vite production build.
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
- success: boolean
|
|
189
|
+
- format: LMS format used
|
|
190
|
+
- outputDir: path to built output
|
|
191
|
+
- errors: any build errors
|
|
192
|
+
- warnings: any build warnings
|
|
193
|
+
- duration: build time
|
|
194
|
+
|
|
195
|
+
Use in Stage 5 when the course is ready for export.`,
|
|
196
|
+
inputSchema: {
|
|
197
|
+
type: 'object',
|
|
198
|
+
properties: {
|
|
199
|
+
format: {
|
|
200
|
+
type: 'string',
|
|
201
|
+
description: 'LMS format: cmi5, scorm2004, scorm1.2, or lti (default: cmi5)',
|
|
202
|
+
enum: ['cmi5', 'scorm2004', 'scorm1.2', 'lti']
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
required: []
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
// --- Catalog & Validation Tools (filesystem, no preview needed) ---
|
|
209
|
+
{
|
|
210
|
+
name: 'coursecode_css_catalog',
|
|
211
|
+
description: `Get CSS class information extracted from real CSS source files.
|
|
212
|
+
|
|
213
|
+
Without 'category': returns all classes grouped by category with abbreviated declarations.
|
|
214
|
+
With 'category': returns full detail for that category only.
|
|
215
|
+
|
|
216
|
+
Categories are derived from CSS file paths (e.g., "utilities/borders", "layout", "patterns").
|
|
217
|
+
Use to discover available CSS classes before authoring slides. Lint catches invalid classes.`,
|
|
218
|
+
inputSchema: {
|
|
219
|
+
type: 'object',
|
|
220
|
+
properties: {
|
|
221
|
+
category: {
|
|
222
|
+
type: 'string',
|
|
223
|
+
description: 'Optional category to get full details for (e.g., "utilities/colors", "layout", "patterns")'
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
required: []
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
name: 'coursecode_component_catalog',
|
|
231
|
+
description: `Get UI component information.
|
|
232
|
+
|
|
233
|
+
Without 'type': returns compact list of all components (name + description + engagement tracking type).
|
|
234
|
+
With 'type': returns full schema, metadata, and HTML usage template for that specific component.
|
|
235
|
+
|
|
236
|
+
Components are declarative HTML (data-component attributes). No imports needed—just use the HTML patterns in your slide template.
|
|
237
|
+
Use to discover available components before authoring slides.`,
|
|
238
|
+
inputSchema: {
|
|
239
|
+
type: 'object',
|
|
240
|
+
properties: {
|
|
241
|
+
type: {
|
|
242
|
+
type: 'string',
|
|
243
|
+
description: 'Optional component type to get full details for (e.g., "tabs", "accordion")'
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
required: []
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
name: 'coursecode_interaction_catalog',
|
|
251
|
+
description: `Get interaction type information.
|
|
252
|
+
|
|
253
|
+
Without 'type': returns compact list of all interaction types (name + description).
|
|
254
|
+
With 'type': returns full schema, properties, and factory name for that specific interaction.
|
|
255
|
+
|
|
256
|
+
Interactions use factory functions from the global CourseCode object (e.g., const { createMultipleChoiceQuestion } = CourseCode). No import statements needed.
|
|
257
|
+
Use to discover available interactions before creating assessments.`,
|
|
258
|
+
inputSchema: {
|
|
259
|
+
type: 'object',
|
|
260
|
+
properties: {
|
|
261
|
+
type: {
|
|
262
|
+
type: 'string',
|
|
263
|
+
description: 'Optional interaction type to get full details for (e.g., "multiple-choice", "drag-drop")'
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
required: []
|
|
267
|
+
}
|
|
268
|
+
},
|
|
269
|
+
|
|
270
|
+
{
|
|
271
|
+
name: 'coursecode_lint',
|
|
272
|
+
description: `Run the build-time linter and get structured results.
|
|
273
|
+
|
|
274
|
+
Returns:
|
|
275
|
+
- errors: [{slideId, rule, message, severity}]
|
|
276
|
+
- warnings: [{slideId, rule, message, severity, class?, suggestion?}]
|
|
277
|
+
- passed: boolean
|
|
278
|
+
|
|
279
|
+
Rules detected:
|
|
280
|
+
- undefined-css-class: hallucinated or stale class names (with fix suggestions)
|
|
281
|
+
- unknown-component: unregistered data-component types
|
|
282
|
+
- requirement-missing-component: engagement requirement without matching component
|
|
283
|
+
- missing-slide-file: slide references non-existent file
|
|
284
|
+
- slide-id-filename-mismatch: slide ID doesn't match component filename
|
|
285
|
+
- assessment-id-mismatch: config ID doesn't match assessment ID
|
|
286
|
+
- invalid-gating: bad gating condition configuration
|
|
287
|
+
|
|
288
|
+
The runtime linter (visible via coursecode_state errors/warnings) also checks:
|
|
289
|
+
- Spacing: flex/grid containers without gap, adjacent elements with no margin, containers with no padding
|
|
290
|
+
- Contrast, touch targets, text proximity to borders, element overlap, styled lists
|
|
291
|
+
|
|
292
|
+
Suppression: Add data-lint-ignore to any HTML element to suppress warnings for it and children.
|
|
293
|
+
data-lint-ignore — suppress all warnings
|
|
294
|
+
data-lint-ignore="spacing" — suppress only spacing warnings
|
|
295
|
+
data-lint-ignore="spacing,contrast" — suppress multiple categories
|
|
296
|
+
Categories: spacing, contrast, target-size, proximity, overlap, list-style, css-class
|
|
297
|
+
|
|
298
|
+
Use AFTER making changes to validate the course.`,
|
|
299
|
+
inputSchema: {
|
|
300
|
+
type: 'object',
|
|
301
|
+
properties: {},
|
|
302
|
+
required: []
|
|
303
|
+
}
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
name: 'coursecode_icon_catalog',
|
|
307
|
+
description: `Get icon information.
|
|
308
|
+
|
|
309
|
+
Without 'name': returns all available icon names grouped by category, with counts and usage syntax.
|
|
310
|
+
With 'name': returns the icon's SVG content, category, and usage examples (JS, config, HTML).
|
|
311
|
+
|
|
312
|
+
Use to discover available icons before authoring slides or configuring menus.`,
|
|
313
|
+
inputSchema: {
|
|
314
|
+
type: 'object',
|
|
315
|
+
properties: {
|
|
316
|
+
name: {
|
|
317
|
+
type: 'string',
|
|
318
|
+
description: 'Optional icon name to get full details for (e.g., "check", "book-open")'
|
|
319
|
+
}
|
|
320
|
+
},
|
|
321
|
+
required: []
|
|
322
|
+
}
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
name: 'coursecode_export_content',
|
|
326
|
+
description: `Extract course content as structured Markdown or JSON for review.
|
|
327
|
+
|
|
328
|
+
Returns the full text content of the course: slide headers, body text, tabs, accordions, callouts, cards, interactions, assessment questions, narration, config, and structure overview.
|
|
329
|
+
|
|
330
|
+
Use cases:
|
|
331
|
+
- Compare built course against COURSE_OUTLINE.md for accuracy
|
|
332
|
+
- Review all interactions and assessment questions at once
|
|
333
|
+
- Audit content wording and consistency across slides
|
|
334
|
+
- Generate content for localization or SME review
|
|
335
|
+
|
|
336
|
+
Filtering options keep output manageable:
|
|
337
|
+
- slides: scope to specific slide IDs
|
|
338
|
+
- interactionsOnly: just Q&A, no slide content
|
|
339
|
+
- excludeInteractions: content only, no Q&A
|
|
340
|
+
- format: 'md' (default) or 'json' for structured data
|
|
341
|
+
|
|
342
|
+
Does not require preview server.`,
|
|
343
|
+
inputSchema: {
|
|
344
|
+
type: 'object',
|
|
345
|
+
properties: {
|
|
346
|
+
slides: {
|
|
347
|
+
type: 'string',
|
|
348
|
+
description: 'Comma-separated slide IDs to export (default: all slides)'
|
|
349
|
+
},
|
|
350
|
+
interactionsOnly: {
|
|
351
|
+
type: 'boolean',
|
|
352
|
+
description: 'Export only interactions and assessment questions (no slide content)'
|
|
353
|
+
},
|
|
354
|
+
includeNarration: {
|
|
355
|
+
type: 'boolean',
|
|
356
|
+
description: 'Include narration transcripts (default: false)'
|
|
357
|
+
},
|
|
358
|
+
includeAnswers: {
|
|
359
|
+
type: 'boolean',
|
|
360
|
+
description: 'Include correct answers for interactions (default: true)'
|
|
361
|
+
},
|
|
362
|
+
includeFeedback: {
|
|
363
|
+
type: 'boolean',
|
|
364
|
+
description: 'Include feedback text (default: true)'
|
|
365
|
+
},
|
|
366
|
+
excludeInteractions: {
|
|
367
|
+
type: 'boolean',
|
|
368
|
+
description: 'Exclude all interactions from output (default: false)'
|
|
369
|
+
},
|
|
370
|
+
format: {
|
|
371
|
+
type: 'string',
|
|
372
|
+
description: 'Output format: md or json (default: md)',
|
|
373
|
+
enum: ['md', 'json']
|
|
374
|
+
}
|
|
375
|
+
},
|
|
376
|
+
required: []
|
|
377
|
+
}
|
|
378
|
+
},
|
|
379
|
+
];
|
|
380
|
+
|
|
381
|
+
// ========================================
|
|
382
|
+
// Dynamic Instructions Builder
|
|
383
|
+
// ========================================
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Build a compact directory tree string for a given directory.
|
|
387
|
+
*/
|
|
388
|
+
function buildDirTree(dirPath, prefix = '', maxDepth = 3, depth = 0) {
|
|
389
|
+
if (depth >= maxDepth || !fs.existsSync(dirPath)) return '';
|
|
390
|
+
|
|
391
|
+
const entries = fs.readdirSync(dirPath, { withFileTypes: true })
|
|
392
|
+
.filter(e => !e.name.startsWith('.'))
|
|
393
|
+
.sort((a, b) => {
|
|
394
|
+
// directories first, then files
|
|
395
|
+
if (a.isDirectory() !== b.isDirectory()) return a.isDirectory() ? -1 : 1;
|
|
396
|
+
return a.name.localeCompare(b.name);
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
const lines = [];
|
|
400
|
+
entries.forEach((entry, i) => {
|
|
401
|
+
const isLast = i === entries.length - 1;
|
|
402
|
+
const connector = isLast ? '└── ' : '├── ';
|
|
403
|
+
const childPrefix = isLast ? ' ' : '│ ';
|
|
404
|
+
|
|
405
|
+
if (entry.isDirectory()) {
|
|
406
|
+
const children = fs.readdirSync(path.join(dirPath, entry.name))
|
|
407
|
+
.filter(e => !e.startsWith('.'));
|
|
408
|
+
lines.push(`${prefix}${connector}${entry.name}/ (${children.length} files)`);
|
|
409
|
+
if (depth < maxDepth - 1) {
|
|
410
|
+
lines.push(buildDirTree(path.join(dirPath, entry.name), prefix + childPrefix, maxDepth, depth + 1));
|
|
411
|
+
}
|
|
412
|
+
} else {
|
|
413
|
+
lines.push(`${prefix}${connector}${entry.name}`);
|
|
414
|
+
}
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
return lines.filter(Boolean).join('\n');
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Try to read course title and description from course-config.js
|
|
422
|
+
*/
|
|
423
|
+
function getCourseInfo(courseDir) {
|
|
424
|
+
const configPath = path.join(courseDir, 'course-config.js');
|
|
425
|
+
if (!fs.existsSync(configPath)) return { title: null, description: null };
|
|
426
|
+
|
|
427
|
+
try {
|
|
428
|
+
const content = fs.readFileSync(configPath, 'utf-8');
|
|
429
|
+
const titleMatch = content.match(/title:\s*['"`]([^'"`]+)['"`]/);
|
|
430
|
+
const descMatch = content.match(/description:\s*['"`]([^'"`]+)['"`]/);
|
|
431
|
+
return {
|
|
432
|
+
title: titleMatch ? titleMatch[1] : null,
|
|
433
|
+
description: descMatch ? descMatch[1] : null
|
|
434
|
+
};
|
|
435
|
+
} catch {
|
|
436
|
+
return { title: null, description: null };
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Build stage-specific instructions for the current authoring state.
|
|
442
|
+
* Returns instruction text appropriate for the detected stage.
|
|
443
|
+
*/
|
|
444
|
+
function buildStageInstructions(stageNumber, courseDir, _checklist) {
|
|
445
|
+
const frameworkDocs = path.join(__dirname, '..', 'framework', 'docs');
|
|
446
|
+
const outlineGuidePath = path.join(frameworkDocs, 'COURSE_OUTLINE_GUIDE.md');
|
|
447
|
+
const outlineTemplatePath = path.join(frameworkDocs, 'COURSE_OUTLINE_TEMPLATE.md');
|
|
448
|
+
const authoringGuidePath = path.join(frameworkDocs, 'COURSE_AUTHORING_GUIDE.md');
|
|
449
|
+
const refsDir = path.join(courseDir, 'references');
|
|
450
|
+
const convertedDir = path.join(refsDir, 'converted');
|
|
451
|
+
|
|
452
|
+
switch (stageNumber) {
|
|
453
|
+
case 0: // Not initialized
|
|
454
|
+
return `## What to Do
|
|
455
|
+
|
|
456
|
+
No course project found. Help the author create one:
|
|
457
|
+
coursecode create <project-name>
|
|
458
|
+
|
|
459
|
+
This creates a course/ directory with the starter template.
|
|
460
|
+
After creating the project, the author should add reference files (PDF, DOCX, PPTX) to course/references/ for conversion.
|
|
461
|
+
|
|
462
|
+
## Next Stage
|
|
463
|
+
Once the project exists and reference files are added, Stage 1 (Source Ingestion) begins. Run workflow_status to refresh.`;
|
|
464
|
+
|
|
465
|
+
case 1: { // Source Ingestion
|
|
466
|
+
const rawFiles = listSafe(refsDir, ['.pdf', '.docx', '.doc', '.pptx', '.ppt']);
|
|
467
|
+
const convertedFiles = listSafe(convertedDir, ['.md']);
|
|
468
|
+
|
|
469
|
+
let refStatus = '';
|
|
470
|
+
if (rawFiles.length > 0 && convertedFiles.length === 0) {
|
|
471
|
+
refStatus = `\nFound ${rawFiles.length} unconverted reference file(s):\n${rawFiles.map(f => ` - ${f}`).join('\n')}\n\nConvert them: coursecode convert`;
|
|
472
|
+
} else if (convertedFiles.length > 0) {
|
|
473
|
+
refStatus = `\n${convertedFiles.length} converted reference(s) ready in course/references/converted/`;
|
|
474
|
+
if (rawFiles.length > convertedFiles.length) {
|
|
475
|
+
refStatus += `\n${rawFiles.length - convertedFiles.length} file(s) still need conversion. Run: coursecode convert`;
|
|
476
|
+
}
|
|
477
|
+
} else {
|
|
478
|
+
refStatus = '\nNo reference files found. The author should add source documents (PDF, DOCX, PPTX) to course/references/';
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
return `## What to Do
|
|
482
|
+
|
|
483
|
+
Help the author prepare reference materials for course creation.
|
|
484
|
+
${refStatus}
|
|
485
|
+
|
|
486
|
+
Reference files go in course/references/ (PDF, DOCX, PPTX).
|
|
487
|
+
Convert to markdown: coursecode convert
|
|
488
|
+
Converted files appear in course/references/converted/
|
|
489
|
+
|
|
490
|
+
## Next Stage
|
|
491
|
+
Once references are converted to markdown, Stage 2 (Outline Creation) begins. Call workflow_status after conversion to get updated guidance.`;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
case 2: { // Outline Creation
|
|
495
|
+
const convertedFiles = listSafe(convertedDir, ['.md']);
|
|
496
|
+
const refList = convertedFiles.length <= 10
|
|
497
|
+
? convertedFiles.map(f => ` - ${path.join(convertedDir, f)}`).join('\n')
|
|
498
|
+
: convertedFiles.slice(0, 10).map(f => ` - ${path.join(convertedDir, f)}`).join('\n') + `\n ... and ${convertedFiles.length - 10} more`;
|
|
499
|
+
|
|
500
|
+
return `## What to Do
|
|
501
|
+
|
|
502
|
+
Create COURSE_OUTLINE.md from the reference materials.
|
|
503
|
+
|
|
504
|
+
1. Read the outline guide (explains format, rules, section structure):
|
|
505
|
+
${outlineGuidePath}
|
|
506
|
+
|
|
507
|
+
2. Copy and modify the template to create course/COURSE_OUTLINE.md:
|
|
508
|
+
${outlineTemplatePath}
|
|
509
|
+
|
|
510
|
+
3. Use these reference materials as source content:
|
|
511
|
+
${refList}
|
|
512
|
+
|
|
513
|
+
The outline is a DESIGN document. Define content, interactions, structure, engagement requirements, and objectives using template terminology. Do NOT include code or config syntax.
|
|
514
|
+
|
|
515
|
+
Pause for author review after creating the outline.
|
|
516
|
+
|
|
517
|
+
## Next Stage
|
|
518
|
+
Once the outline is approved, Stage 3 (Course Building) begins. Call workflow_status to get updated guidance.`;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
case 3: { // Course Building
|
|
522
|
+
const convertedFiles = listSafe(convertedDir, ['.md']);
|
|
523
|
+
const refList = convertedFiles.length > 0
|
|
524
|
+
? convertedFiles.map(f => ` - ${path.join(convertedDir, f)}`).join('\n')
|
|
525
|
+
: ' (none found)';
|
|
526
|
+
|
|
527
|
+
return `## What to Do
|
|
528
|
+
|
|
529
|
+
Build slides (course/slides/*.js) and course-config.js from the outline.
|
|
530
|
+
|
|
531
|
+
1. READ THESE FIRST (essential for slide format, config, interactions, CSS):
|
|
532
|
+
Authoring Guide: ${authoringGuidePath}
|
|
533
|
+
Outline: ${path.join(courseDir, 'COURSE_OUTLINE.md')}
|
|
534
|
+
|
|
535
|
+
2. REFERENCE MATERIALS (converted source content for slide writing):
|
|
536
|
+
${refList}
|
|
537
|
+
|
|
538
|
+
3. SLIDE FILE FORMAT (course/slides/*.js):
|
|
539
|
+
export const meta = { title: 'Slide Title' };
|
|
540
|
+
export default \`<section class="slide">...</section>\`;
|
|
541
|
+
Each file exports meta (title) + default HTML string.
|
|
542
|
+
⚠️ NO import statements needed. Components, interactions, CSS classes, and icons are all globally available.
|
|
543
|
+
The only valid import is for local assets: import myImg from '../assets/images/photo.png';
|
|
544
|
+
|
|
545
|
+
4. RULES:
|
|
546
|
+
- ⚠️ NEVER add import statements for components, interactions, CSS, or icons. They are globally available.
|
|
547
|
+
Only import local assets (images, SVGs): import myImage from '../assets/images/photo.png';
|
|
548
|
+
Interactions: const { createMultipleChoiceQuestion } = CourseCode; (destructure from global, NOT import)
|
|
549
|
+
Components: use data-component="tabs" in HTML (declarative, no JS needed)
|
|
550
|
+
- Use css_catalog to discover available CSS classes by category. Lint catches invalid classes with fix suggestions.
|
|
551
|
+
- Use component_catalog and interaction_catalog to discover available components and interactions.
|
|
552
|
+
- Run lint after each batch of file changes. Fix all errors before proceeding.
|
|
553
|
+
- Never modify files in framework/ — all work goes in course/ only.
|
|
554
|
+
- No em-dashes in sentence structure. Use alternative phrasing.
|
|
555
|
+
|
|
556
|
+
Pause for author review after the initial slide build.
|
|
557
|
+
|
|
558
|
+
## Next Stage
|
|
559
|
+
Once slides and config are built, Stage 4 (Preview & Polish) begins. Start the preview and call workflow_status for updated guidance.`;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
case 4: { // Preview & Polish
|
|
563
|
+
const convertedFiles = listSafe(convertedDir, ['.md']);
|
|
564
|
+
const refList = convertedFiles.length > 0
|
|
565
|
+
? convertedFiles.map(f => ` - ${path.join(convertedDir, f)}`).join('\n')
|
|
566
|
+
: '';
|
|
567
|
+
|
|
568
|
+
// Import-specific guidance
|
|
569
|
+
if (_checklist.source === 'powerpoint-import') {
|
|
570
|
+
return `## Imported from PowerPoint
|
|
571
|
+
|
|
572
|
+
This course was imported from a PowerPoint presentation. Each slide is currently a static image. Your job is to enhance it into an interactive course.
|
|
573
|
+
|
|
574
|
+
1. Ensure the preview server is running (\`coursecode preview\` in a terminal, or AI uses run_command)
|
|
575
|
+
2. Review slides: screenshot each to understand the content
|
|
576
|
+
3. Enhancement priorities:
|
|
577
|
+
- **Replace image slides** with interactive HTML — use the extracted text from references/converted/ as source content
|
|
578
|
+
- **Add engagement tracking** — require interaction before advancing (tabs, accordions, etc.)
|
|
579
|
+
- **Insert assessments** — add knowledge checks with multiple-choice, drag-drop, etc.
|
|
580
|
+
- **Group into sections** — organize slides into logical modules with section headers in course-config.js
|
|
581
|
+
- **Customize theme** — update colors in course/theme.css
|
|
582
|
+
${refList ? `\n4. REFERENCE MATERIALS (extracted text from presentation):\n${refList}\n` : ''}
|
|
583
|
+
5. RULES:
|
|
584
|
+
- Use css_catalog, component_catalog, interaction_catalog to discover available tools
|
|
585
|
+
- Run lint after changes. Fix all errors before proceeding.
|
|
586
|
+
- Efficient loop: edit files → lint → fix errors → screenshot to verify
|
|
587
|
+
|
|
588
|
+
## Next Stage
|
|
589
|
+
Once polished and lint passes, Stage 5 (Export Ready) begins.`;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
return `## What to Do
|
|
593
|
+
|
|
594
|
+
Visually verify and polish the course using the preview server.
|
|
595
|
+
|
|
596
|
+
1. Ensure the preview server is running (\`coursecode preview\` in a terminal, or AI uses run_command)
|
|
597
|
+
2. Do NOT open a browser yourself — the MCP has its own headless Chrome
|
|
598
|
+
3. Workflow (all tools execute instantly via internal headless browser):
|
|
599
|
+
- state — get course structure, current slide, interactions, engagement
|
|
600
|
+
- navigate — go to any slide by ID (get IDs from state)
|
|
601
|
+
- screenshot — capture visual state (accepts slideId to navigate+capture in one call)
|
|
602
|
+
- interact — test interactions with responses
|
|
603
|
+
- export_content — extract all text content to review or compare against outline
|
|
604
|
+
- lint — validate after file changes
|
|
605
|
+
|
|
606
|
+
4. Efficient iteration loop:
|
|
607
|
+
Edit files → lint → fix errors → screenshot to verify visual result
|
|
608
|
+
Do NOT screenshot every slide sequentially. Target specific slides.
|
|
609
|
+
${refList ? `\n5. REFERENCE MATERIALS (for verifying content accuracy):\n${refList}\n` : ''}
|
|
610
|
+
Run lint and ensure zero errors before moving to export.
|
|
611
|
+
|
|
612
|
+
## Next Stage
|
|
613
|
+
Once the course is polished and lint passes, Stage 5 (Export Ready) begins. Call workflow_status for export guidance.`;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
case 5: // Export Ready
|
|
617
|
+
return `## What to Do
|
|
618
|
+
|
|
619
|
+
Export the finished course for LMS deployment.
|
|
620
|
+
|
|
621
|
+
1. Run lint one final time to confirm zero errors
|
|
622
|
+
2. Build with: coursecode_build (format: cmi5, scorm2004, scorm1.2, or lti)
|
|
623
|
+
- cmi5 (default) for modern LMS
|
|
624
|
+
- scorm1.2 for legacy systems
|
|
625
|
+
|
|
626
|
+
The build produces a ZIP file in dist/ ready for LMS upload.`;
|
|
627
|
+
|
|
628
|
+
default:
|
|
629
|
+
return 'Call workflow_status to determine the current authoring stage.';
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Safely list files in a directory matching given extensions.
|
|
635
|
+
*/
|
|
636
|
+
function listSafe(dirPath, extensions) {
|
|
637
|
+
try {
|
|
638
|
+
if (!fs.existsSync(dirPath)) return [];
|
|
639
|
+
return fs.readdirSync(dirPath).filter(f =>
|
|
640
|
+
extensions.some(ext => f.toLowerCase().endsWith(ext))
|
|
641
|
+
);
|
|
642
|
+
} catch {
|
|
643
|
+
return [];
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/**
|
|
648
|
+
* Build the browser architecture and rules section.
|
|
649
|
+
* Critical for preventing agents from using external browsers.
|
|
650
|
+
*/
|
|
651
|
+
function buildBrowserRules() {
|
|
652
|
+
return `## Browser Architecture (CRITICAL)
|
|
653
|
+
|
|
654
|
+
The MCP server runs its OWN headless Chrome internally via puppeteer-core.
|
|
655
|
+
All runtime tools (state, navigate, interact, screenshot, reset) execute instantly inside this headless browser.
|
|
656
|
+
|
|
657
|
+
### Preview Server Ownership
|
|
658
|
+
- The MCP does NOT start or manage the preview server
|
|
659
|
+
- The preview must be started externally: run \`coursecode preview\` in a terminal (human) or via run_command (AI agent)
|
|
660
|
+
- If preview is not running, runtime tools will fail with a clear error message
|
|
661
|
+
- The headless browser auto-reconnects when Vite rebuilds (file changes)
|
|
662
|
+
|
|
663
|
+
### Navigation API
|
|
664
|
+
- coursecode_state → get slim TOC with slide IDs, current slide, interactions, engagement, lmsState, apiLog, errors, frameworkLogs, consoleLogs
|
|
665
|
+
- coursecode_navigate(slideId) → go to any slide instantly by ID
|
|
666
|
+
- coursecode_screenshot(slideId) → navigate + capture in one call
|
|
667
|
+
- coursecode_interact(interactionId, response) → set response + evaluate in one call
|
|
668
|
+
- NEVER click nav buttons or menu items via browser tools. Use these MCP tools.
|
|
669
|
+
|
|
670
|
+
### RULES
|
|
671
|
+
- Do NOT use browser_subagent, open_browser_url, or any external browser tool to view the course
|
|
672
|
+
- Do NOT add manual waits or setTimeout delays — all tool calls return instantly
|
|
673
|
+
- Do NOT screenshot every slide sequentially — target specific slides to verify
|
|
674
|
+
- NEVER modify files in framework/ or lib/ — these are the framework internals. All authoring goes in course/ only.
|
|
675
|
+
- Efficient loop: edit files → lint → fix errors → screenshot specific slides to verify
|
|
676
|
+
|
|
677
|
+
### Customization (all in course/, never in framework/)
|
|
678
|
+
- **CSS overrides**: Edit \`course/theme.css\` — override palette tokens to rebrand (all colors cascade via color-mix). Use framework utility classes first (css_catalog), theme.css only for brand-specific overrides.
|
|
679
|
+
- **Custom components**: Add \`.js\` files to \`course/components/\` — auto-discovered at build time. Use component_catalog for built-in options first.
|
|
680
|
+
- **Custom interactions**: Add \`.js\` files to \`course/interactions/\` — auto-discovered. See \`course/interactions/PLUGIN_GUIDE.md\` for the template.
|
|
681
|
+
- **Custom icons**: Add SVG definitions to \`course/icons.js\` — merged with built-in icons. Use icon_catalog to check existing icons first.`;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
const __packageRoot = path.dirname(__dirname); // lib/ -> repo root
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* Find the course/ directory, trying cwd first then package root.
|
|
688
|
+
* Returns the path to the course/ dir, or null if not found.
|
|
689
|
+
*/
|
|
690
|
+
function findCourseDir() {
|
|
691
|
+
const candidates = [process.cwd(), __packageRoot];
|
|
692
|
+
for (const root of candidates) {
|
|
693
|
+
if (fs.existsSync(path.join(root, 'course'))) {
|
|
694
|
+
return path.join(root, 'course');
|
|
695
|
+
}
|
|
696
|
+
if (fs.existsSync(path.join(root, 'template', 'course'))) {
|
|
697
|
+
return path.join(root, 'template', 'course');
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
return null;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
/**
|
|
704
|
+
* Build the full MCP instructions string — called at server startup.
|
|
705
|
+
* Also used by workflow_status to provide refreshed instructions mid-session.
|
|
706
|
+
*/
|
|
707
|
+
export async function buildInstructions(port = 4173) {
|
|
708
|
+
const status = await getWorkflowStatus(port);
|
|
709
|
+
const { stageNumber, stage, checklist } = status;
|
|
710
|
+
|
|
711
|
+
const courseDir = findCourseDir();
|
|
712
|
+
const courseInfo = courseDir ? getCourseInfo(courseDir) : { title: null, description: null };
|
|
713
|
+
|
|
714
|
+
// Header: framework identity + course info + stage
|
|
715
|
+
const header = [
|
|
716
|
+
'CourseCode: AI-assisted e-learning course authoring for LMS (cmi5/SCORM).',
|
|
717
|
+
courseInfo.title ? `Course: "${courseInfo.title}"${courseInfo.description ? ` — ${courseInfo.description}` : ''}` : '',
|
|
718
|
+
`Stage ${stageNumber}/5: ${stage}`,
|
|
719
|
+
].filter(Boolean).join('\n');
|
|
720
|
+
|
|
721
|
+
// Directory tree (if course exists)
|
|
722
|
+
let tree = '';
|
|
723
|
+
if (courseDir) {
|
|
724
|
+
tree = `\ncourse/\n${buildDirTree(courseDir, '', 2)}`;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
// Browser rules (always included)
|
|
728
|
+
const browserRules = buildBrowserRules();
|
|
729
|
+
|
|
730
|
+
// Automation warning (when config exists but automation is off)
|
|
731
|
+
let automationWarning = '';
|
|
732
|
+
if (checklist.hasCourseConfig && !checklist.hasAutomationEnabled) {
|
|
733
|
+
automationWarning = `\n\n## ⚠️ Automation Disabled
|
|
734
|
+
MCP runtime tools (state, navigate, interact, screenshot, reset) require \`environment.automation.enabled: true\` in course-config.js. Without it, the headless browser cannot access the course API and these tools will fail.
|
|
735
|
+
You MUST notify the author about this and let them decide whether to enable it. Do not silently modify the config.`;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// Stage-specific body
|
|
739
|
+
const stageBody = buildStageInstructions(stageNumber, courseDir || '', checklist);
|
|
740
|
+
|
|
741
|
+
return `${header}${tree}\n\n${browserRules}${automationWarning}\n\n${stageBody}`;
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
/**
|
|
745
|
+
* Get the enhanced workflow status with stage-specific instructions.
|
|
746
|
+
* Called by the coursecode_workflow_status tool handler.
|
|
747
|
+
*/
|
|
748
|
+
export async function getWorkflowStatusWithInstructions(port = 4173) {
|
|
749
|
+
const status = await getWorkflowStatus(port);
|
|
750
|
+
const courseDir = findCourseDir() || '';
|
|
751
|
+
status.instructions = buildStageInstructions(status.stageNumber, courseDir, status.checklist);
|
|
752
|
+
return status;
|
|
753
|
+
}
|