@fullstackcraftllc/codevideo-mcp 1.0.2 → 1.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.
Files changed (44) hide show
  1. package/README.md +98 -5
  2. package/dist/index.d.ts +3 -0
  3. package/dist/index.js +3370 -1935
  4. package/dist/index.js.map +7 -0
  5. package/package.json +40 -33
  6. package/bin/darwin-arm64/codevideo-cli +0 -0
  7. package/bin/darwin-x64/codevideo-cli +0 -0
  8. package/bin/linux-x64/codevideo-cli +0 -0
  9. package/bin/win32-x64/codevideo-cli.exe +0 -0
  10. package/dist/src/constants/Constants.d.ts +0 -2
  11. package/dist/src/index.d.ts +0 -2
  12. package/dist/src/prompts/instructions_to_create_a_course.d.ts +0 -1
  13. package/dist/src/prompts/instructions_to_create_lesson_actions.d.ts +0 -1
  14. package/dist/src/prompts/instructions_to_create_lesson_video.d.ts +0 -1
  15. package/dist/src/prompts/instructions_to_translate_actions.d.ts +0 -2
  16. package/dist/src/resources/get_action_names.d.ts +0 -4
  17. package/dist/src/resources/get_example_actions_array_by_keyword.d.ts +0 -1
  18. package/dist/src/resources/get_example_lessons_by_keyword.d.ts +0 -1
  19. package/dist/src/rest-server/index.d.ts +0 -24
  20. package/dist/src/rest-server/job-manager.d.ts +0 -56
  21. package/dist/src/rest-server/mcp-client.d.ts +0 -23
  22. package/dist/src/rest-server/rest-server.d.ts +0 -24
  23. package/dist/src/rest-server/server.d.ts +0 -4
  24. package/dist/src/tools/add_actions_to_lesson.d.ts +0 -2
  25. package/dist/src/tools/add_final_snapshot_to_lesson.d.ts +0 -2
  26. package/dist/src/tools/add_initial_snapshot_to_lesson.d.ts +0 -2
  27. package/dist/src/tools/add_lesson_to_course.d.ts +0 -2
  28. package/dist/src/tools/create_course_with_initial_metadata.d.ts +0 -1
  29. package/dist/src/tools/critique_video_using_gemini.d.ts +0 -2
  30. package/dist/src/tools/get_current_actions.d.ts +0 -3
  31. package/dist/src/tools/get_current_lesson.d.ts +0 -3
  32. package/dist/src/tools/get_empty_lesson_snapshot.d.ts +0 -1
  33. package/dist/src/tools/get_final_lesson_snapshot.d.ts +0 -2
  34. package/dist/src/tools/get_initial_lesson_state_from_github_repo.d.ts +0 -1
  35. package/dist/src/tools/get_initial_lesson_state_from_local_repo.d.ts +0 -1
  36. package/dist/src/tools/is_repeatable_action.d.ts +0 -2
  37. package/dist/src/tools/make_blog_post.d.ts +0 -2
  38. package/dist/src/tools/make_html_web_page.d.ts +0 -2
  39. package/dist/src/tools/make_video_from_actions.d.ts +0 -8
  40. package/dist/src/tools/make_video_from_lesson.d.ts +0 -8
  41. package/dist/src/tools/set_current_actions.d.ts +0 -2
  42. package/dist/src/tools/set_current_lesson.d.ts +0 -2
  43. package/dist/src/tools/validate_actions.d.ts +0 -6
  44. package/dist/src/utils/getBinaryPath.d.ts +0 -1
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/index.ts", "../package.json", "../src/prompts/instructions_to_create_a_course.ts", "../src/prompts/actionReference.ts", "../src/prompts/authoringRules.ts", "../src/prompts/instructions_to_create_lesson_actions.ts", "../src/prompts/instructions_to_create_lesson_video.ts", "../src/constants/Constants.ts", "../src/store/db.ts", "../src/store/keyValueStore.ts", "../src/tools/get_current_actions.ts", "../src/prompts/instructions_to_translate_actions.ts", "../src/prompts/instructions_to_build_course_from_scratch.ts", "../src/tools/get_final_lesson_snapshot.ts", "../src/tools/get_initial_lesson_state_from_local_repo.ts", "../src/tools/get_initial_lesson_state_from_github_repo.ts", "../src/tools/is_repeatable_action.ts", "../src/tools/create_course_with_initial_metadata.ts", "../src/tools/add_lesson_to_course.ts", "../src/tools/add_actions_to_lesson.ts", "../src/tools/get_empty_lesson_snapshot.ts", "../src/tools/critique_video_using_gemini.ts", "../src/tools/set_current_actions.ts", "../src/tools/set_current_lesson.ts", "../src/tools/get_current_lesson.ts", "../src/tools/make_markdown_from_actions.ts", "../src/tools/make_markdown_from_lesson.ts", "../src/tools/make_html_web_page.ts", "../src/render/queue.ts", "../src/render/jobs.ts", "../src/render/cli.ts", "../src/render/options.ts", "../src/render/worker.ts", "../src/utils/getBinaryPath.ts", "../src/tools/make_video_from_actions.ts", "../src/tools/make_video_from_lesson.ts", "../src/tools/validate_actions.ts", "../src/validation/rejectedActions.ts", "../src/tools/courseTools.ts", "../src/store/courseStore.ts", "../src/utils/emptyCourseSnapshot.ts", "../src/tools/compileCheck.ts", "../src/tools/renderTools.ts", "../src/resources/get_action_names.ts", "../src/resources/get_example_actions_array_by_keyword.ts", "../src/resources/get_example_lessons_by_keyword.ts"],
4
+ "sourcesContent": ["import dotenv from 'dotenv';\nimport fs from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport * as packageJson from '../package.json';\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { IAction, ICourse, ICourseSnapshot, ILesson, AllActionStrings, isRepeatableAction } from \"@fullstackcraftllc/codevideo-types\";\n\n// prompts\nimport { instructionsToCreateACourse } from \"./prompts/instructions_to_create_a_course\";\nimport { instructionsToCreateLessonActions } from \"./prompts/instructions_to_create_lesson_actions\";\nimport { instructionsToCreateLessonVideo } from './prompts/instructions_to_create_lesson_video';\nimport { instructionsToTranslateActions } from './prompts/instructions_to_translate_actions';\nimport { instructionsToBuildCourseFromScratch } from \"./prompts/instructions_to_build_course_from_scratch\";\n\n// tools\nimport { getFinalLessonSnapshot } from \"./tools/get_final_lesson_snapshot\";\nimport { getInitialLessonStateFromLocalCodebase } from \"./tools/get_initial_lesson_state_from_local_repo\";\nimport { getInitialLessonStateFromGitHubRepo } from \"./tools/get_initial_lesson_state_from_github_repo\";\nimport { isRepeatable } from \"./tools/is_repeatable_action\";\nimport { createCourseWithInitialMetadata } from \"./tools/create_course_with_initial_metadata\";\nimport { addLessonToCourse } from \"./tools/add_lesson_to_course\";\nimport { addActionsToLesson } from \"./tools/add_actions_to_lesson\";\nimport { getEmptyLessonSnapshot } from \"./tools/get_empty_lesson_snapshot\";\nimport { critiqueVideoUsingGemini } from \"./tools/critique_video_using_gemini\";\nimport { setCurrentActions } from \"./tools/set_current_actions\";\nimport { getCurrentActions } from \"./tools/get_current_actions\";\nimport { setCurrentLesson } from \"./tools/set_current_lesson\";\nimport { getCurrentLesson } from \"./tools/get_current_lesson\";\nimport { makeMarkdownFromActions } from \"./tools/make_markdown_from_actions\";\nimport { makeMarkdownFromLesson } from \"./tools/make_markdown_from_lesson\";\nimport { makeHTMLWebPage } from \"./tools/make_html_web_page\";\nimport { queueVideoFromActions } from \"./tools/make_video_from_actions\";\nimport { queueVideoFromLesson } from \"./tools/make_video_from_lesson\";\nimport { validateActions } from \"./tools/validate_actions\";\n\n// course store tools (server-side state; snapshots stay out of the model's context)\nimport {\n createCourseTool,\n addLessonTool,\n setLessonActionsTool,\n getLessonFileTreeTool,\n getLessonFileTool,\n compileLessonTool,\n validateCourseTool,\n getCourseStatusTool,\n exportCourseTool,\n editLessonActionsTool,\n getLessonActionsTool,\n} from \"./tools/courseTools\";\n\n// phase 2: previews + gated video render\nimport {\n generateLessonPreviewTool,\n generateCoursePreviewsTool,\n renderLessonVideoTool,\n renderCourseVideosTool,\n} from \"./tools/renderTools\";\n\n// render jobs: a queue in the shared database, drained by a background worker\nimport { RenderOptions, RenderOptionsSchema } from \"./render/options\";\nimport { describeJob, describeJobById } from \"./render/queue\";\nimport { listJobs, requestCancel } from \"./render/jobs\";\nimport { runWorker } from \"./render/worker\";\n\n// resources\nimport { getActionNames } from \"./resources/get_action_names\";\nimport { getExampleActionsArrayByKeyword } from \"./resources/get_example_actions_array_by_keyword\";\nimport { getExampleLessonByKeyword } from './resources/get_example_lessons_by_keyword';\n\n// Load environment variables from .env file\ndotenv.config();\n\nconst ActionSchema = z.object({\n name: z.string(),\n value: z.string()\n});\n\n// Strict variant for the new course-authoring entry point: rejects unknown action names and\n// empty values at the MCP boundary, so the model gets immediate feedback instead of finding\n// out only at validate time. The looser ActionSchema above is kept for the existing\n// single-lesson tools that already work well.\nconst validActionNames = new Set<string>(AllActionStrings);\nconst StrictActionSchema = z.object({\n name: z.string().refine((name) => validActionNames.has(name), \"unknown CodeVideo action name\"),\n value: z.string().min(1, \"action 'value' must not be empty\")\n}).refine(\n (a) => !isRepeatableAction(a as IAction) || /^[0-9]+$/.test(a.value),\n { message: \"repeatable actions require a positive integer string value (usually \\\"1\\\")\" }\n);\n\nconst CourseSnapshotSchema = z.custom((value) => {\n if (!value || typeof value !== \"object\") return false;\n const snapshot = value as Partial<ICourseSnapshot>;\n return typeof snapshot.isUnsavedChangesDialogOpen === \"boolean\"\n && typeof snapshot.unsavedFileName === \"string\"\n && !!snapshot.fileExplorerSnapshot\n && !!snapshot.editorSnapshot\n && !!snapshot.terminalSnapshot\n && !!snapshot.mouseSnapshot\n && !!snapshot.authorSnapshot;\n}, \"Invalid CodeVideo course snapshot\");\n\nconst LessonSchema = z.object({\n id: z.string(),\n name: z.string(),\n description: z.string(),\n actions: z.array(ActionSchema),\n initialSnapshot: CourseSnapshotSchema,\n finalSnapshot: CourseSnapshotSchema\n})\n\nconst CourseSchema = z.object({\n id: z.string(),\n name: z.string(),\n description: z.string(),\n primaryLanguage: z.string(),\n lessons: z.array(LessonSchema)\n});\n\n// Every render tool takes the same options and an `open` switch.\nconst RenderOptionsParam = RenderOptionsSchema.optional()\n .describe(\"Render options: orientation, IDE layout (theme, font size, which panes and the cursor are visible), pacing, voice and render mode.\");\nconst openParam = (whenOmitted: boolean) => z.boolean().optional()\n .describe(`Open the finished video in the default player. Defaults to ${whenOmitted}.`);\n\nconst QUEUED_MESSAGE = \"Queued. It renders in the background, even if this client closes; check it with codevideo_get_render_job.\";\n\nconst jobReply = (result: { job: Parameters<typeof describeJob>[0]; estimatedSeconds: number }): TextToolResult => ({\n content: [{\n type: \"text\",\n text: JSON.stringify({ ...describeJob(result.job), estimatedSeconds: result.estimatedSeconds, message: QUEUED_MESSAGE }, null, 2)\n }]\n});\n\nconst errorReply = (prefix: string, error: unknown): TextToolResult => ({\n isError: true,\n content: [{ type: \"text\", text: `${prefix}: ${error instanceof Error ? error.message : String(error)}` }]\n});\n\ntype ToolShape = Record<string, z.ZodTypeAny>;\ntype ToolArguments<Shape extends ToolShape> = z.output<z.ZodObject<Shape>>;\ntype TextToolResult = {\n content: Array<{ type: \"text\"; text: string }>;\n isError?: boolean;\n};\ntype ToolHandler<Shape extends ToolShape> = (\n args: ToolArguments<Shape>,\n) => TextToolResult | Promise<TextToolResult>;\ninterface ToolRegistrar {\n <Shape extends ToolShape>(name: string, shape: Shape, handler: ToolHandler<Shape>): void;\n <Shape extends ToolShape>(name: string, description: string, shape: Shape, handler: ToolHandler<Shape>): void;\n}\n\nexport function createMcpServer(): McpServer {\n const server = new McpServer({\n name: \"codevideo-mcp\",\n version: packageJson.version,\n });\n\n setupMcpServerTools(server);\n return server;\n}\n\nfunction setupMcpServerTools(server: McpServer): void {\n const tool = server.tool.bind(server) as unknown as ToolRegistrar;\n\n// Commenting out prompts and resources that aren't showing up \n// - does Claude just use them internally?\n/*\n// add prompts\nserver.prompt(\n \"codevideo_create_lesson\",\n { additionalContext: z.string() },\n ({ additionalContext }) => ({\n messages: [{\n role: \"user\",\n content: {\n type: \"text\",\n text: createLesson(additionalContext)\n }\n }]\n })\n);\n\n// add resources\nserver.resource(\n \"codevideo_get_action_names\",\n \"codevideo_get_action_names://codevideo_get_action_names\",\n (uri) => ({\n contents: [{\n uri: uri.href,\n text: getActionNames()\n }]\n })\n);\n\nserver.resource(\n \"codevideo_get_example_lesson\",\n new ResourceTemplate(\"codevideo_get_example_lesson://{keyword}\", { list: undefined }),\n async (uri, { keyword }) => ({\n contents: [{\n uri: uri.href,\n text: getExampleLesson(keyword)\n }]\n })\n);\n*/\n\ntool(\n \"codevideo_instructions_to_create_a_course\",\n {\n additionalContext: z.string()\n },\n async ({ additionalContext }) => {\n return {\n content: [{\n type: \"text\",\n text: instructionsToCreateACourse(additionalContext)\n }]\n };\n }\n)\n\ntool(\n \"codevideo_instructions_to_create_lesson_actions\",\n {\n additionalContext: z.string()\n },\n async ({ additionalContext }) => {\n return {\n content: [{\n type: \"text\",\n text: instructionsToCreateLessonActions(additionalContext)\n }]\n };\n }\n);\n\ntool(\n \"codevideo_instructions_to_create_lesson_video\",\n {\n additionalContext: z.string()\n },\n async ({ additionalContext }) => {\n return {\n content: [{\n type: \"text\",\n text: instructionsToCreateLessonVideo(additionalContext)\n }]\n };\n }\n);\n\ntool(\n \"codevideo_instructions_to_translate_actions\",\n \"Generate translation instructions for CodeVideo actions. Pass actions array OR use previously stored actions from codevideo_set_current_actions (recommended workflow).\",\n {\n actionsToTranslation: z.array(ActionSchema).optional().describe(\"Array of CodeVideo actions to translate. Optional if actions were previously stored with codevideo_set_current_actions\"),\n targetLanguage: z.string().describe(\"Target language for translation (e.g., 'Spanish', 'French', 'German')\")\n },\n async ({ actionsToTranslation, targetLanguage }) => {\n // Cast the actions to IAction if provided\n const typedActions = actionsToTranslation as IAction[] | undefined;\n\n return {\n content: [{\n type: \"text\",\n text: instructionsToTranslateActions(typedActions, targetLanguage)\n }]\n };\n }\n);\n\ntool(\n \"codevideo_get_empty_lesson_snapshot\",\n {},\n async () => {\n return {\n content: [{\n type: \"text\",\n text: getEmptyLessonSnapshot()\n }]\n };\n }\n)\n\n\n\ntool(\n \"codevideo_create_course_with_initial_metadata\",\n {\n id: z.string(),\n name: z.string(),\n description: z.string(),\n primaryLanguage: z.string()\n },\n async ({ id, name, description, primaryLanguage }) => {\n return {\n content: [{\n type: \"text\",\n text: createCourseWithInitialMetadata(id, name, description, primaryLanguage)\n }]\n };\n }\n)\n\ntool(\n \"codevideo_add_actions_to_lesson\",\n {\n lesson: LessonSchema,\n actions: z.array(ActionSchema)\n },\n async ({ lesson, actions }) => {\n // Cast the lesson to ILesson\n const typedLesson = lesson as ILesson;\n\n // Cast the actions to IAction\n const typedActions = actions as IAction[];\n\n return {\n content: [{\n type: \"text\",\n text: addActionsToLesson(typedLesson, typedActions)\n }]\n };\n }\n)\n\ntool(\n \"codevideo_get_final_lesson_snapshot\",\n \"Get the final state snapshot after applying CodeVideo actions. Pass actions array OR use previously stored actions from codevideo_set_current_actions (recommended workflow).\",\n {\n actions: z.array(ActionSchema).optional().describe(\"Array of CodeVideo actions to apply. Optional if actions were previously stored with codevideo_set_current_actions\")\n },\n async ({ actions }) => {\n // Cast the actions to IAction if provided\n const typedActions = actions as IAction[] | undefined;\n\n return {\n content: [{\n type: \"text\",\n text: getFinalLessonSnapshot(typedActions)\n }]\n };\n }\n)\n\ntool(\n \"codevideo_add_lesson_to_course\",\n {\n course: CourseSchema,\n id: z.string(),\n name: z.string(),\n description: z.string()\n },\n async ({ course, id, name, description }) => {\n // Cast the course to ICourse\n const typedCourse = course as ICourse;\n\n return {\n content: [{\n type: \"text\",\n text: addLessonToCourse(typedCourse, id, name, description)\n }]\n };\n }\n);\n\ntool(\n \"codevideo_get_action_names\",\n \"Every CodeVideo action, one per line: what it does and what its value takes. Check the rows for the actions you plan to use.\",\n {},\n async () => {\n return {\n content: [{\n type: \"text\",\n text: getActionNames()\n }]\n };\n }\n);\n\ntool(\n \"codevideo_get_example_actions_array_by_keyword\",\n {\n keyword: z.string()\n },\n async ({ keyword }) => {\n return {\n content: [{\n type: \"text\",\n text: getExampleActionsArrayByKeyword(keyword)\n }]\n };\n }\n);\n\ntool(\n \"codevideo_get_example_lesson_by_keyword\",\n {\n keyword: z.string()\n },\n async ({ keyword }) => {\n return {\n content: [{\n type: \"text\",\n text: getExampleLessonByKeyword(keyword)\n }]\n };\n }\n);\n\ntool(\n \"codevideo_get_initial_lesson_state_from_local_repo\",\n \"Create an initial lesson state snapshot from a local repository/codebase directory. This captures the file structure and creates an empty lesson template ready for action recording.\",\n {\n repoLocation: z.string().describe(\"Local file system path to the repository/codebase directory\")\n },\n async ({ repoLocation }) => {\n return {\n content: [{\n type: \"text\",\n text: await getInitialLessonStateFromLocalCodebase(repoLocation)\n }]\n };\n }\n);\n\ntool(\n \"codevideo_get_initial_lesson_state_from_github_repo\",\n \"Create an initial lesson state snapshot from a GitHub repository URL. This fetches the repository content, captures the file structure, and creates an empty lesson template ready for action recording.\",\n {\n repoUrl: z.string().describe(\"GitHub repository URL (e.g., https://github.com/user/repo)\")\n },\n async ({ repoUrl }) => {\n return {\n content: [{\n type: \"text\",\n text: await getInitialLessonStateFromGitHubRepo(repoUrl)\n }]\n };\n }\n);\n\ntool(\n \"codevideo_make_markdown_from_actions\",\n \"Generate a markdown blog post from CodeVideo actions array. Pass actions array OR use previously stored actions from codevideo_set_current_actions (recommended workflow).\",\n {\n actions: z.array(ActionSchema).optional().describe(\"Array of CodeVideo actions. Optional if actions were previously stored with codevideo_set_current_actions\")\n },\n async ({ actions }) => {\n // Cast the actions to IAction if provided\n const typedActions = actions as IAction[] | undefined;\n\n return {\n content: [{\n type: \"text\",\n text: makeMarkdownFromActions(typedActions)\n }]\n };\n }\n);\n\ntool(\n \"codevideo_make_markdown_from_lesson\",\n \"Generate a markdown blog post from a complete lesson object. Pass a lesson object OR use previously stored lesson from lesson management tools. This extracts the actions from the lesson and generates markdown.\",\n {\n lesson: LessonSchema.optional().describe(\"Complete lesson object. Optional if lesson was previously stored with lesson management tools\")\n },\n async ({ lesson }) => {\n // Cast the lesson to ILesson if provided\n const typedLesson = lesson as ILesson | undefined;\n\n return {\n content: [{\n type: \"text\",\n text: makeMarkdownFromLesson(typedLesson)\n }]\n };\n }\n);\n\ntool(\n \"codevideo_make_html_web_page\",\n \"Generate an HTML web page from CodeVideo actions. Pass actions array OR use previously stored actions from codevideo_set_current_actions (recommended workflow).\",\n {\n actions: z.array(ActionSchema).optional().describe(\"Array of CodeVideo actions. Optional if actions were previously stored with codevideo_set_current_actions\")\n },\n async ({ actions }) => {\n // Cast the actions to IAction if provided\n const typedActions = actions as IAction[] | undefined;\n\n return {\n content: [{\n type: \"text\",\n text: await makeHTMLWebPage(typedActions)\n }]\n };\n }\n);\n\ntool(\n \"codevideo_make_video_from_actions\",\n \"Render a video from CodeVideo actions: pass actions, or omit them to use the ones stored with codevideo_set_current_actions. Queues a background render job and returns its jobId at once; follow it with codevideo_get_render_job.\",\n {\n actions: z.array(ActionSchema).optional().describe(\"Array of CodeVideo actions. Optional if actions were previously stored with codevideo_set_current_actions\"),\n options: RenderOptionsParam,\n open: openParam(true),\n },\n async ({ actions, options, open }) => {\n try {\n return jobReply(queueVideoFromActions(actions as IAction[] | undefined, (options ?? {}) as RenderOptions, open ?? true));\n } catch (error) {\n return errorReply(\"Couldn't queue the video\", error);\n }\n }\n);\n\ntool(\n \"codevideo_make_video_from_lesson\",\n \"Render a video from a complete lesson object: pass the lesson, or omit it to use the one stored with codevideo_set_current_lesson. Queues a background render job and returns its jobId at once; follow it with codevideo_get_render_job. For lessons in a stored course, use codevideo_render_lesson_video instead.\",\n {\n lesson: LessonSchema.optional().describe(\"Complete lesson object with snapshots. Optional if lesson was previously stored with lesson management tools\"),\n options: RenderOptionsParam,\n open: openParam(true),\n },\n async ({ lesson, options, open }) => {\n try {\n return jobReply(queueVideoFromLesson(lesson as ILesson | undefined, (options ?? {}) as RenderOptions, open ?? true));\n } catch (error) {\n return errorReply(\"Couldn't queue the video\", error);\n }\n }\n);\n\ntool(\n \"codevideo_validate_actions\",\n \"Checks a list of actions: unknown names, values of the wrong kind, and actions lessons can't use, each with what to do instead.\",\n {\n actions: z.array(ActionSchema).optional()\n },\n async ({ actions }) => {\n // Cast the actions to IAction if provided\n const typedActions = actions as IAction[] | undefined;\n\n return {\n content: [{\n type: \"text\",\n text: validateActions(typedActions)\n }]\n };\n }\n);\n\ntool(\n \"codevideo_is_repeatable_action\",\n {\n action: ActionSchema\n },\n async ({ action }) => {\n // Cast the action to IAction\n const typedAction = action as IAction;\n\n return {\n content: [{\n type: \"text\",\n text: isRepeatable(typedAction)\n }]\n };\n }\n)\n\ntool(\n \"codevideo_critique_video_using_gemini\",\n \"Critique a generated video using Google Gemini AI. Pass actions array OR use previously stored actions from codevideo_set_current_actions (recommended workflow).\",\n {\n videoPath: z.string().describe(\"Path to the video file to critique\"),\n actions: z.array(ActionSchema).optional().describe(\"Array of CodeVideo actions used to generate the video. Optional if actions were previously stored with codevideo_set_current_actions\")\n },\n async ({ videoPath, actions }) => {\n // Cast the actions to IAction if provided\n const typedActions = actions as IAction[] | undefined;\n\n try {\n const critique = await critiqueVideoUsingGemini(videoPath, typedActions);\n return {\n content: [{\n type: \"text\",\n text: critique\n }]\n };\n } catch (error: unknown) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.error(\"Error critiquing video:\", errorMessage);\n return {\n content: [{\n type: \"text\",\n text: `Error critiquing video: ${errorMessage}`\n }]\n };\n }\n }\n)\n\ntool(\n \"codevideo_set_current_actions\",\n \"Legacy single-slot store: keep one actions array for later tool calls, so they can run without re-sending it. Every conversation shares this one slot. For courses or several lessons, use codevideo_create_course and the course tools, which keep work apart by id.\",\n {\n actions: z.array(ActionSchema).describe(\"Array of CodeVideo actions to store for later use\")\n },\n async ({ actions }) => {\n // Cast the actions to IAction\n const typedActions = actions as IAction[];\n\n return {\n content: [{\n type: \"text\",\n text: setCurrentActions(typedActions)\n }]\n };\n }\n)\n\ntool(\n \"codevideo_get_current_actions\",\n \"Legacy single-slot store: read back the actions stored with codevideo_set_current_actions (shared by every conversation).\",\n {},\n async () => {\n const actions = getCurrentActions();\n if (!actions) {\n return {\n content: [{\n type: \"text\",\n text: \"No actions found in storage. Use codevideo_set_current_actions first to store actions.\"\n }]\n };\n }\n\n return {\n content: [{\n type: \"text\",\n text: JSON.stringify(actions, null, 2)\n }]\n };\n }\n)\n\ntool(\n \"codevideo_set_current_lesson\",\n \"Legacy single-slot store: keep one lesson object for later tool calls, so they can run without re-sending it. Every conversation shares this one slot. For courses or several lessons, use codevideo_create_course and the course tools, which keep work apart by id.\",\n {\n lesson: LessonSchema.describe(\"Complete lesson object to store for later use\")\n },\n async ({ lesson }) => {\n // Cast the lesson to ILesson\n const typedLesson = lesson as ILesson;\n\n return {\n content: [{\n type: \"text\",\n text: setCurrentLesson(typedLesson)\n }]\n };\n }\n)\n\ntool(\n \"codevideo_get_current_lesson\",\n \"Legacy single-slot store: read back the lesson stored with codevideo_set_current_lesson (shared by every conversation).\",\n {},\n async () => {\n const lesson = getCurrentLesson();\n if (!lesson) {\n return {\n content: [{\n type: \"text\",\n text: \"No lesson found in storage. Use codevideo_set_current_lesson first to store a lesson.\"\n }]\n };\n }\n\n return {\n content: [{\n type: \"text\",\n text: JSON.stringify(lesson, null, 2)\n }]\n };\n }\n)\n\n// ===== Build-from-scratch course flow (server-side state; snapshots stay out of context) =====\n\ntool(\n \"codevideo_instructions_to_build_course_from_scratch\",\n \"Get step-by-step instructions for building a COMPLETE multi-lesson 'build from scratch' course with the course-store tools (snapshots stay server-side). Use this before authoring a full course.\",\n { additionalContext: z.string().describe(\"What the course should teach / build\") },\n async ({ additionalContext }) => ({\n content: [{ type: \"text\", text: instructionsToBuildCourseFromScratch(additionalContext) }]\n })\n);\n\ntool(\n \"codevideo_create_course\",\n \"Create a new course (server-side, returns a courseId). Snapshots and lesson state live in the MCP, not in your context.\",\n {\n name: z.string(),\n description: z.string().optional(),\n primaryLanguage: z.string().optional(),\n id: z.string().optional()\n },\n async ({ name, description, primaryLanguage, id }) => ({\n content: [{ type: \"text\", text: createCourseTool({ name, description, primaryLanguage, id }) }]\n })\n);\n\ntool(\n \"codevideo_add_lesson\",\n \"Add a lesson to a course. By default it continues from the previous lesson's final state (chained server-side). Set mustCompile:false for slideshow/discussion/half-refactor lessons. The reply includes the previous lesson's name and description, for the recap that opens this lesson.\",\n {\n courseId: z.string(),\n name: z.string(),\n description: z.string().min(1, \"describe the lesson in one sentence; the next lesson's recap is written from it\"),\n mustCompile: z.boolean().optional(),\n continuesFromPrevious: z.boolean().optional()\n },\n async ({ courseId, name, description, mustCompile, continuesFromPrevious }) => ({\n content: [{ type: \"text\", text: addLessonTool(courseId, { name, description, mustCompile, continuesFromPrevious }) }]\n })\n);\n\ntool(\n \"codevideo_set_lesson_actions\",\n \"Store a lesson's actions, replacing any it had. The server recomputes the lesson's final snapshot and re-chains following lessons. Returns only a small ack (no snapshot). To change part of a lesson later, use codevideo_edit_lesson_actions instead of sending every action again.\",\n {\n courseId: z.string(),\n lessonId: z.string(),\n actions: z.array(StrictActionSchema)\n },\n async ({ courseId, lessonId, actions }) => {\n try {\n return { content: [{ type: \"text\", text: setLessonActionsTool(courseId, lessonId, actions as IAction[]) }] };\n } catch (error) {\n return errorReply(\"Couldn't store the actions\", error);\n }\n }\n);\n\ntool(\n \"codevideo_edit_lesson_actions\",\n \"Change part of a lesson without re-sending it: each edit removes deleteCount actions at start and inserts the given ones there, like Array.splice. Edits apply in order (each start refers to the actions as the previous edits left them), all or nothing, with one snapshot recompute. Use codevideo_get_lesson_actions to find indices.\",\n {\n courseId: z.string(),\n lessonId: z.string(),\n edits: z.array(z.object({\n start: z.number().int().min(0).describe(\"Index of the first action to change\"),\n deleteCount: z.number().int().min(0).describe(\"How many actions to remove at start (0 to only insert)\"),\n insert: z.array(StrictActionSchema).default([]).describe(\"Actions to insert at start\"),\n })).min(1),\n },\n async ({ courseId, lessonId, edits }) => {\n try {\n return { content: [{ type: \"text\", text: editLessonActionsTool(courseId, lessonId, edits as { start: number; deleteCount: number; insert: IAction[] }[]) }] };\n } catch (error) {\n return errorReply(\"Couldn't apply the edits\", error);\n }\n }\n);\n\ntool(\n \"codevideo_get_lesson_actions\",\n \"Read part of a lesson's actions with their indices (default: the first 50), e.g. to find what to change with codevideo_edit_lesson_actions.\",\n {\n courseId: z.string(),\n lessonId: z.string(),\n start: z.number().int().min(0).optional(),\n limit: z.number().int().min(1).max(500).optional(),\n },\n async ({ courseId, lessonId, start, limit }) => ({\n content: [{ type: \"text\", text: getLessonActionsTool(courseId, lessonId, start, limit) }]\n })\n);\n\ntool(\n \"codevideo_get_lesson_file_tree\",\n \"List the file paths present at the END of a lesson (cheap; no file contents, no snapshot).\",\n { courseId: z.string(), lessonId: z.string() },\n async ({ courseId, lessonId }) => ({\n content: [{ type: \"text\", text: getLessonFileTreeTool(courseId, lessonId) }]\n })\n);\n\ntool(\n \"codevideo_get_lesson_file\",\n \"Get the contents of ONE file at the END of a lesson (cheap targeted read instead of dumping the whole snapshot).\",\n { courseId: z.string(), lessonId: z.string(), path: z.string() },\n async ({ courseId, lessonId, path }) => ({\n content: [{ type: \"text\", text: getLessonFileTool(courseId, lessonId, path) }]\n })\n);\n\ntool(\n \"codevideo_compile_lesson\",\n \"Compile the code at the end of a lesson and return only the structured errors. Use this to check a lesson builds before moving on.\",\n { courseId: z.string(), lessonId: z.string() },\n async ({ courseId, lessonId }) => ({\n content: [{ type: \"text\", text: await compileLessonTool(courseId, lessonId) }]\n })\n);\n\ntool(\n \"codevideo_validate_course\",\n \"Validate the whole course: per-lesson action validity, snapshot continuity, and compilation (for mustCompile lessons + the final lesson). Returns done:true when the course is complete.\",\n { courseId: z.string() },\n async ({ courseId }) => ({\n content: [{ type: \"text\", text: await validateCourseTool(courseId) }]\n })\n);\n\ntool(\n \"codevideo_get_course_status\",\n \"Resumable checkpoint view of a course: lessons, action counts, estimated video length per lesson, flags \u2014 without pulling any snapshot.\",\n { courseId: z.string() },\n async ({ courseId }) => ({\n content: [{ type: \"text\", text: getCourseStatusTool(courseId) }]\n })\n);\n\ntool(\n \"codevideo_export_course\",\n \"Export the full course (ICourse with snapshots) to a JSON file under ~/.codevideo/exports and return its path. The course itself isn't returned, so it doesn't fill your context.\",\n { courseId: z.string() },\n async ({ courseId }) => ({\n content: [{ type: \"text\", text: exportCourseTool(courseId) }]\n })\n);\n\n// ===== Phase 2: previews + video render (gated behind validate_course) =====\n\ntool(\n \"codevideo_generate_lesson_preview\",\n \"Generate a server-side HTML preview of a single lesson (open in a browser to eyeball it). Cheap; no video.\",\n { courseId: z.string(), lessonId: z.string() },\n async ({ courseId, lessonId }) => ({\n content: [{ type: \"text\", text: await generateLessonPreviewTool(courseId, lessonId) }]\n })\n);\n\ntool(\n \"codevideo_generate_course_previews\",\n \"Generate HTML previews for every lesson \u2014 GATED: only works once codevideo_validate_course returns done. Use for human review before rendering video.\",\n { courseId: z.string() },\n async ({ courseId }) => ({\n content: [{ type: \"text\", text: await generateCoursePreviewsTool(courseId) }]\n })\n);\n\ntool(\n \"codevideo_render_lesson_video\",\n \"Render ONE lesson of a stored course to video: queues a background render job and returns its jobId. GATED: refuses until codevideo_validate_course returns done.\",\n { courseId: z.string(), lessonId: z.string(), options: RenderOptionsParam, open: openParam(true) },\n async ({ courseId, lessonId, options, open }) => {\n try {\n return { content: [{ type: \"text\", text: await renderLessonVideoTool(courseId, lessonId, (options ?? {}) as RenderOptions, open ?? true) }] };\n } catch (error) {\n return errorReply(\"Couldn't queue the video\", error);\n }\n }\n);\n\ntool(\n \"codevideo_render_course_videos\",\n \"Render every lesson of a stored course to video: queues one background render job per lesson, run one at a time in lesson order. GATED: refuses until codevideo_validate_course returns done. Run only after a human approves the previews.\",\n { courseId: z.string(), options: RenderOptionsParam, open: openParam(false) },\n async ({ courseId, options, open }) => {\n try {\n return { content: [{ type: \"text\", text: await renderCourseVideosTool(courseId, (options ?? {}) as RenderOptions, open ?? false) }] };\n } catch (error) {\n return errorReply(\"Couldn't queue the videos\", error);\n }\n }\n);\n\n// ===== Render jobs =====\n\ntool(\n \"codevideo_get_render_job\",\n \"Status of one render job: queued (with how many jobs are ahead), running (with the latest progress line), succeeded (the video exists at outputPath), failed (with the CLI's error), cancelled or interrupted. Includes the last lines of the CLI's log.\",\n { jobId: z.string() },\n async ({ jobId }) => {\n const job = describeJobById(jobId, true);\n return job\n ? { content: [{ type: \"text\", text: JSON.stringify(job, null, 2) }] }\n : { isError: true, content: [{ type: \"text\", text: `No render job ${jobId}.` }] };\n }\n);\n\ntool(\n \"codevideo_list_render_jobs\",\n \"Recent render jobs, newest first (default 20), optionally for one course or in one status.\",\n {\n courseId: z.string().optional(),\n status: z.enum([\"queued\", \"running\", \"succeeded\", \"failed\", \"cancelled\", \"interrupted\"]).optional(),\n limit: z.number().int().min(1).max(100).optional(),\n },\n async ({ courseId, status, limit }) => ({\n content: [{ type: \"text\", text: JSON.stringify(listJobs({ courseId, status, limit }).map((job) => describeJob(job)), null, 2) }]\n })\n);\n\ntool(\n \"codevideo_cancel_render_job\",\n \"Cancel one render job, or every unfinished render job of a course. Queued jobs are dropped; a running one is stopped along with its browser.\",\n { jobId: z.string().optional(), courseId: z.string().optional() },\n async ({ jobId, courseId }) => {\n if (!jobId === !courseId) {\n return { isError: true, content: [{ type: \"text\", text: \"Pass exactly one of jobId or courseId.\" }] };\n }\n const { cancelled, stopping } = requestCancel({ jobId, courseId });\n return {\n content: [{\n type: \"text\",\n text: JSON.stringify({ cancelled, stopping, message: stopping > 0 ? \"Stopping the running job; it shows as cancelled within a few seconds.\" : \"Done.\" }, null, 2)\n }]\n };\n }\n);\n\n}\n\n// start the server when running directly\nconst invokedPath = process.argv[1];\nconst isDirectExecution = invokedPath !== undefined\n && fs.realpathSync(fileURLToPath(import.meta.url)) === fs.realpathSync(invokedPath);\nif (isDirectExecution && process.argv.includes(\"--render-worker\")) {\n // The background render worker (src/render/worker.ts), started by the server.\n runWorker().then(\n () => process.exit(0),\n (error) => {\n console.error(error);\n process.exit(1);\n }\n );\n} else if (isDirectExecution) {\n const server = createMcpServer();\n const transport = new StdioServerTransport();\n (async () => {\n await server.connect(transport);\n })();\n}\n", "{\n \"name\": \"@fullstackcraftllc/codevideo-mcp\",\n \"version\": \"1.1.0\",\n \"description\": \"The MCP server for CodeVideo. Create software educational content using natural language.\",\n \"type\": \"module\",\n \"main\": \"dist/index.js\",\n \"types\": \"dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\"\n }\n },\n \"files\": [\n \"dist/\",\n \"README.md\"\n ],\n \"bin\": {\n \"codevideo-mcp\": \"dist/index.js\"\n },\n \"engines\": {\n \"node\": \">=22.13\"\n },\n \"scripts\": {\n \"clean\": \"rimraf dist\",\n \"build\": \"npm run clean && node scripts/build.mjs\",\n \"build-typescript\": \"tsc\",\n \"test\": \"node --import tsx --test test/*.test.ts\",\n \"type-check\": \"tsc --noEmit\",\n \"prepack\": \"npm run build\",\n \"prepublishOnly\": \"npm run release:check\",\n \"verify-package\": \"node scripts/verify-package.mjs\",\n \"smoke:packed\": \"node scripts/smoke-packed.mjs\",\n \"release:check\": \"npm run type-check && npm test && npm run build && npm run verify-package && npm run smoke:packed && npm audit --omit=dev --audit-level=critical\",\n \"download-binaries\": \"tsx scripts/download-binaries.ts\",\n \"rest-server\": \"tsx src/rest-server/server.ts\",\n \"rest-server:dev\": \"tsx --watch src/rest-server/server.ts\",\n \"client:example\": \"tsx examples/rest-client.ts\",\n \"client:agent-example\": \"tsx examples/agent-client.ts\",\n \"examples:curl\": \"./examples/curl-examples.sh\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/codevideo/codevideo-mcp.git\"\n },\n \"keywords\": [\n \"CodeVideo\",\n \"mcp\",\n \"video\",\n \"productivity\"\n ],\n \"author\": \"Chris Frewin\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/codevideo/codevideo-mcp/issues\"\n },\n \"homepage\": \"https://github.com/codevideo/codevideo-mcp#readme\",\n \"dependencies\": {\n \"@fullstackcraftllc/codevideo-adapters\": \"^1.0.12\",\n \"@fullstackcraftllc/codevideo-cli\": \"~0.0.13\",\n \"@fullstackcraftllc/codevideo-dynamic-ast\": \"^1.0.5\",\n \"@fullstackcraftllc/codevideo-exporters\": \"^0.0.15\",\n \"@fullstackcraftllc/codevideo-types\": \"^2.6.2\",\n \"@fullstackcraftllc/codevideo-virtual-ide\": \"^1.0.43\",\n \"@google/genai\": \"^2.10.0\",\n \"@modelcontextprotocol/sdk\": \"^1.32.2\",\n \"dotenv\": \"^16.5.0\",\n \"uuid\": \"^11.1.1\",\n \"zod\": \"^3.25.76\"\n },\n \"devDependencies\": {\n \"@types/cors\": \"^2.8.19\",\n \"@types/express\": \"^4.17.25\",\n \"@types/node\": \"^22.20.0\",\n \"@types/ws\": \"^8.18.1\",\n \"cors\": \"^2.8.6\",\n \"esbuild\": \"^0.28.1\",\n \"express\": \"^4.22.3\",\n \"node-fetch\": \"^3.3.2\",\n \"rimraf\": \"^6.1.3\",\n \"socket.io\": \"^4.8.4\",\n \"tsx\": \"^4.22.4\",\n \"typescript\": \"^5.9.3\",\n \"ws\": \"^8.22.1\"\n }\n}\n", "// this prompt describes how to create a course lesson using the CodeVideo framework\nexport const instructionsToCreateACourse = (additionalContext: string): string => {\n return `You are an expert senior software engineer and educator, and skilled in describing software concepts in a way that is easy for students to understand. You are going to create a course using the CodeVideo framework, a framework that uses simple JSON to generate software courses and lessons in a variety of formats (video, markdown, pdf, and more).\n\n There are a few rules to uphold when making a CodeVideo course:\n\n 1. The final course snapshot of the previous lesson must be equivalent to the initial course snapshot of the next lesson.\n 2. Beyond the first lesson, each lesson must start with an \\`author-speak-before\\` action that summarizes the previous lesson with a maximum of 3 sentences.\n 3. Beyond the first lesson, each lesson must have their second action be an \\`author-speak-during\\` action that summarizes the current lesson with a maximum of 3 sentences.\n 4. Beyond the first lesson, each lesson must end with an \\`author-speak-after\\` action that summarizes the current lesson with a maximum of 3 sentences.\n\n With these rules in mind, please create a course with the following lessons:\n \n ${additionalContext}\n\n Now, you should first think of a name and description for this course, they should be SEO friendly title and meta description respectively. \n \n To create the initial course object, you can call the MCP tool \\`codevideo_create_course_with_initial_metadata\\` with the following parameters:\n - id: a unique identifier for the course\n - name: the name of the course\n - description: the description of the course\n - primaryLanguage: the primary language of the course\n \n You should then plan out the number of lessons and what they will be about. To add a lesson to the course, you can call the MCP tool \\`codevideo_add_lesson_to_course\\` with the following parameters:\n - id: a unique identifier for the lesson. it should relate to the course id, for example if the course id is \\`generics-in-typescript\\`, the lesson id should be something like \\`generics-in-typescript-lesson-1-introduction\\`\n - name: the name of the lesson\n - description: the description of the lesson\n\n You then need to create the initial lesson snapshot. For the very first lesson, you can call the tool \\`codevideo_get_empty_lesson_snapshot\\` without any parameters to get an empty lesson snapshot object. For the rest of the lessons, you can copy the final lesson snapshot of the previous lesson and use that as the initial lesson snapshot.\n\n You then need to add actions to the lesson. First read the prompt in \\`codevideo_instructions_to_create_lesson_actions\\` to get the instructions on how to create lesson actions. \n \n When done creating actions, you can add actions to the lesson by calling the MCP tool \\`codevideo_add_actions_to_lesson\\` with the following parameters:\n - lesson: the ILesson object that you created earlier\n - actions: the array of IAction objects that you created earlier\n\n WORKFLOW TIP: For efficiency, after creating actions for each lesson, you can call \\`codevideo_set_current_actions\\` to store them, then use \\`codevideo_get_final_lesson_snapshot\\` without the actions parameter (it will use the stored actions automatically).\n\n For each lesson, you also need to create the final lesson snapshot. You can get the final snapshot the lesson by calling the MCP tool \\`codevideo_get_final_lesson_snapshot\\` with the following parameters:\n - actions: the array of IAction objects that you created earlier for the lesson (OR omit this parameter if you've called \\`codevideo_set_current_actions\\` first)\n\n In summary, the steps to create a course are:\n\n 1. Create the initial course object using the MCP tool \\`codevideo_create_course_with_initial_metadata\\`\n 2. Plan out the number of lessons needed and what they will be about\n 3. For each lesson, create a unique identifier, name, and description\n 4. Create the initial empty lesson snapshot for the first lesson using the MCP tool \\`codevideo_get_empty_lesson_snapshot\\`. In the case of any lesson beyond the first lesson, generate the final lesson snapshot and copy it as the initial lesson snapshot.\n 5. Create actions for the lesson using the MCP tool \\`codevideo_instructions_to_create_lesson_actions\\`\n 6. Add actions to the lesson using the MCP tool \\`codevideo_add_actions_to_lesson\\`\n 7. Create the final lesson snapshot using the MCP tool \\`codevideo_get_final_lesson_snapshot\\`\n 8. Add the lesson to the course using the MCP tool \\`codevideo_add_lesson_to_course\\`\n 9. Repeat steps 4-8 for each lesson\n\n When done, return the completed course object as a JSON string.\n \n Good luck, and enjoy creating your course!`;\n}", "import { AllActionDescriptions } from \"@fullstackcraftllc/codevideo-types\";\n\n/**\n * Every action with what it does and what its value takes, one per line. It\n * comes straight from codevideo-types, so the tools never restate the\n * vocabulary themselves and a new or reworded action reaches the model as is.\n */\nexport const actionReference = (): string =>\n AllActionDescriptions.map((action) => `${action.name}: ${action.description} Value: ${action.valueDescription}`).join(\"\\n\");\n", "// How to write CodeVideo actions. Every prompt that asks for actions includes\n// these rules, so an agent gets the same guidance whichever tool it starts from.\nexport const AUTHORING_RULES = `ACTION STYLE AND PACING (this is what makes lessons clear):\n- This JSON array is always an array of simple IAction objects: { name: AllActions, value: string }. codevideo_get_action_names lists every action with what it does and what its value takes, so check the rows for the actions you use; codevideo_get_example_actions_array_by_keyword / codevideo_get_example_lessons_by_keyword give verified human-made examples.\n- The \"value\" of any action is NEVER empty. Repeatable actions (e.g. editor-enter) take a numeric STRING value, usually \"1\"; check with codevideo_is_repeatable_action.\n- Interleave speech with typing \u2014 no long silent blocks of code. Narrate the WHY before you write, and keep individual speak actions to 2-3 sentences; break longer narration into multiple author-speak-before actions.\n- Mind the flow of time: speak about saving BEFORE the editor-save; speak about opening a terminal BEFORE terminal-open. REGARDLESS OF ORDER, only ever use the author-speak-before speaking action.\n- Show every new file being made, one of two ways:\n - The mouse (the default): mouse-move-file-explorer \"1\" -> mouse-right-click \"1\" -> mouse-move-file-explorer-context-menu-new-file \"1\" -> mouse-left-click \"1\" -> file-explorer-type-new-file-input \"<filename>\" -> file-explorer-enter-new-file-input \"1\". For a file inside a folder, start with mouse-move-file-explorer-folder \"<folder>\" and use mouse-move-file-explorer-folder-context-menu-new-file instead.\n - The terminal, for more technical lessons: terminal-open \"1\" (if the terminal isn't open yet) -> terminal-type \"touch <path>\" -> terminal-enter \"1\".\n Folders the same way: mouse-move-file-explorer-context-menu-new-folder with file-explorer-type-new-folder-input / file-explorer-enter-new-folder-input, or terminal-type \"mkdir <path>\".\n- Never use file-explorer-create-file, file-explorer-create-folder or file-explorer-set-file-contents: they change files with nothing on screen causing it, so things just appear. Don't use the composite-* actions or editor-highlight-code either: they aren't supported yet and do nothing. codevideo_validate_course rejects lessons that use any of these.\n- A file you just created with the mouse is ALREADY open in the editor \u2014 type into it immediately with editor-type; do NOT reopen it. A file made with touch is not open yet: open it as below. When a lesson continues from the previous one, the file you last edited is already the active editor.\n- To open a DIFFERENT existing file: mouse-move-file-explorer-file \"<filename>\" -> file-explorer-open-file \"<filename>\". IMPORTANT: file-explorer-open-file's value is the FILENAME, not \"1\" \u2014 using \"1\" opens a stray editor and your typing is lost.\n- Save with editor-save once a file's typing is done (narrating it first): compile checks and file reads see saved contents only.\n- editor-type's value is the literal text to insert (including newlines).\n\nNARRATION:\n- Every lesson opens with an author-speak-before (codevideo_validate_course checks this). The first lesson frames what the course builds. Every lesson after the first opens with one sentence recapping the previous lesson (codevideo_add_lesson returns its name and description), e.g. \"In the last lesson, we built an add function.\" Vary the wording from lesson to lesson. If the lesson starts something new, bridge instead: \"Last time we built X; now we start Y.\"\n- After the recap sentence you may show what it refers to: at most two stops, each an action followed by the sentence about what's now on screen. Bring an open file forward with mouse-move-editor-tab \"<path>\" + mouse-left-click, or open one with mouse-move-file-explorer-file \"<path>\" + file-explorer-open-file \"<path>\"; codevideo_add_lesson lists the previous lesson's files and whether they're open. Pick what this lesson builds on. Don't select code in a recap: the next editor-type would replace the selection.\n- Use the inclusive \"we\" for what happens on screen: \"Now we export the function.\" Switch to \"you\" for options, advice, caveats and things to try: \"You can also write this as an arrow function.\" Never \"I\".\n- End each lesson with a short summary, after its final save.\n- Write file and code names as they appear on screen (math.ts, useState): captions show your exact words.`;\n", "import { actionReference } from \"./actionReference\"\nimport { AUTHORING_RULES } from \"./authoringRules\";\n\n// this prompt describes how to make lesson actions in the CodeVideo framework\nexport const instructionsToCreateLessonActions = (additionalContext: string): string => {\n return `You are an expert senior software engineer and educator, and skilled in describing software concepts in a way that is easy for students to understand. You are going to create lesson actions using the CodeVideo framework, a framework that uses simple JSON to generate tutorials in a variety of formats (video, markdown, pdf, and more). This JSON array is always an array of simple \\`IAction\\`:\n\n \\`\\`\\`typescript\n export interface IAction {\n name: AllActions;\n value: string;\n }\n \\`\\`\\`\n\n\n Where \\`AllActions\\` is:\n\n \\`\\`\\`typescript\n import { AuthorActions } from \"./AuthorActions\";\n import { MouseActions } from \"./MouseActions\";\n import { FileExplorerActions } from \"./FileExplorerActions\";\n import { TerminalActions } from \"./TerminalActions\";\n import { EditorActions } from \"./EditorActions\";\n import { ExternalActions } from \"./ExternalActions\";\n\n // all actions combines the 5 domains of the CodeVideo world\n export type AllActions =\n | AuthorActions\n | FileExplorerActions\n | EditorActions\n | MouseActions\n | TerminalActions\n | AuthorActions\n | ExternalActions;\n \\`\\`\\`\n\n Here is every action you can choose from, with what it does and what its value takes:\n\n \\`\\`\\`\n${actionReference()}\n \\`\\`\\`\n\n Use the MCP tool \\`codevideo_get_action_names\\` to get the most updated list of all the actions available. You can also use the MCP tool \\`codevideo_get_example_actions_array_by_keyword\\` \\`codevideo_get_example_lessons_by_keyword\\` to get verified, accurate, and correct lessons created by humans.\n \n ${AUTHORING_RULES}\n\n WORKFLOW TIP: After creating actions, you can store them using \\`codevideo_set_current_actions\\` and then use other tools like \\`codevideo_validate_actions\\`, \\`codevideo_make_video_from_actions\\`, \\`codevideo_make_markdown_from_actions\\`, etc. without needing to re-specify the actions each time. These tools will automatically retrieve the stored actions if no actions parameter is provided. Videos render as background jobs: the video tools return a jobId at once, and \\`codevideo_get_render_job\\` reports progress and the saved file. For a course of several lessons, use \\`codevideo_instructions_to_build_course_from_scratch\\` and the course tools instead of this single shared slot.\n\n LESSON MANAGEMENT: You can also store complete lesson objects using \\`codevideo_set_current_lesson\\` and retrieve them with \\`codevideo_get_current_lesson\\`. This is useful for the FULL LESSON OBJECT approach where you need to manage complete lessons with snapshots.\n\n ACTIONS vs FULL LESSON OBJECTS:\n You have two approaches for creating lessons:\n \n 1. **ACTIONS-ONLY APPROACH**: Use when creating simple tutorials that start from a clean slate or when you only need to demonstrate specific coding steps. Use \\`codevideo_make_video_from_actions\\` for this approach.\n \n 2. **FULL LESSON OBJECT APPROACH**: Use when you need to:\n - Start from an existing codebase (use \\`codevideo_get_initial_lesson_state_from_local_repo\\` or \\`codevideo_get_initial_lesson_state_from_github_repo\\`)\n - Continue from a previous lesson's ending state (use \\`codevideo_get_final_lesson_snapshot\\` from a previous lesson)\n - Have complete control over the initial and final snapshots\n - Use \\`codevideo_make_video_from_lesson\\` for this approach\n\n RECOMMENDED WORKFLOW:\n 1. **DECIDE YOUR APPROACH**: \n - For simple tutorials starting from scratch: Use ACTIONS-ONLY approach\n - For tutorials starting from existing code or continuing from previous lessons: Use FULL LESSON OBJECT approach\n \n 2. Call the \\`codevideo_get_example_lesson\\` tool to examine example lessons to get a feel for the codevideo actions and composition.\n 3. Use the \\`codevideo_get_action_names\\` tool to get the complete and most up-to-date list of actions.\n \n 4. **IF USING FULL LESSON OBJECT APPROACH**:\n 4a. If starting from an existing codebase, use \\`codevideo_get_initial_lesson_state_from_local_repo\\` or \\`codevideo_get_initial_lesson_state_from_github_repo\\`\n 4b. If continuing from a previous lesson, use \\`codevideo_get_final_lesson_snapshot\\` from the previous lesson's actions\n 4c. Create your actions array and build the complete lesson object with initial/final snapshots\n 4d. Optionally store the lesson using \\`codevideo_set_current_lesson\\` for later retrieval\n 4e. Use \\`codevideo_make_video_from_lesson\\` with the complete lesson object\n \n 5. **IF USING ACTIONS-ONLY APPROACH**:\n 5a. Create the actions array\n 5b. Call \\`codevideo_set_current_actions\\` once with the actions\n 5c. Call \\`codevideo_validate_actions\\` WITHOUT the actions parameter - it will use the stored actions automatically\n 5d. Call \\`codevideo_make_video_from_actions\\` WITHOUT the actions parameter - it will use the stored actions automatically\n \n 6. Use \\`codevideo_get_current_actions\\` or \\`codevideo_get_current_lesson\\` at any later time to inspect what's currently stored if needed\n\n With all this in mind, you are to create a lesson about the following:\n\n ${additionalContext}\n\n Be sure to include a corresponding initial \\`author-speak-before\\` action and a final summary \\`author-speak-before\\` action.\n `\n}", "import { actionReference } from \"./actionReference\"\nimport { AUTHORING_RULES } from \"./authoringRules\";\n\n// this prompt describes how to make lesson actions in the CodeVideo framework\nexport const instructionsToCreateLessonVideo = (additionalContext: string): string => {\n return `You are an expert senior software engineer and educator, and skilled in describing software concepts in a way that is easy for students to understand. You are going to create a video lesson using the CodeVideo framework, a framework that uses simple JSON to generate tutorials in a variety of formats (video, markdown, pdf, and more). The JSON array used to create video is always an array of simple type \\`IAction\\`:\n\n \\`\\`\\`typescript\n export interface IAction {\n name: AllActions;\n value: string;\n }\n \\`\\`\\`\n\n\n Where \\`AllActions\\` is:\n\n \\`\\`\\`typescript\n import { AuthorActions } from \"./AuthorActions\";\n import { MouseActions } from \"./MouseActions\";\n import { FileExplorerActions } from \"./FileExplorerActions\";\n import { TerminalActions } from \"./TerminalActions\";\n import { EditorActions } from \"./EditorActions\";\n import { ExternalActions } from \"./ExternalActions\";\n\n // all actions combines the 5 domains of the CodeVideo world\n export type AllActions =\n | AuthorActions\n | FileExplorerActions\n | EditorActions\n | MouseActions\n | TerminalActions\n | AuthorActions\n | ExternalActions;\n \\`\\`\\`\n\n Here is every action you can choose from, with what it does and what its value takes:\n\n \\`\\`\\`\n${actionReference()}\n \\`\\`\\`\n\n When in doubt with these names, feel free to use the MCP tool \\`codevideo_get_action_names\\` to get the most updated list of all the actions available. You can also use the MCP tool \\`codevideo_get_example_lesson\\` to get verified, accurate, and correct lessons created by humans.\n \n ${AUTHORING_RULES}\n\n LESSON MANAGEMENT: You can store complete lesson objects using \\`codevideo_set_current_lesson\\` and retrieve them with \\`codevideo_get_current_lesson\\`. This is particularly useful for the FULL LESSON OBJECT approach where you need to manage complete lessons with snapshots.\n\n ACTIONS vs FULL LESSON OBJECTS - CHOOSE YOUR APPROACH:\n You have two approaches for creating video lessons:\n \n 1. **ACTIONS-ONLY APPROACH**: Use when creating simple tutorials that start from a clean slate or when you only need to demonstrate specific coding steps. Use \\`codevideo_make_video_from_actions\\` for this approach.\n \n 2. **FULL LESSON OBJECT APPROACH**: Use when you need to:\n - Start from an existing codebase (use \\`codevideo_get_initial_lesson_state_from_local_repo\\` or \\`codevideo_get_initial_lesson_state_from_github_repo\\`)\n - Continue from a previous lesson's ending state (use \\`codevideo_get_final_lesson_snapshot\\` from a previous lesson)\n - Have complete control over the initial and final snapshots\n - Use \\`codevideo_make_video_from_lesson\\` for this approach\n\n RECOMMENDED WORKFLOW:\n 1. **DECIDE YOUR APPROACH**: \n - For simple tutorials starting from scratch: Use ACTIONS-ONLY approach\n - For tutorials starting from existing code or continuing from previous lessons: Use FULL LESSON OBJECT approach\n \n 2. **IF USING ACTIONS-ONLY APPROACH**:\n 2a. Create your actions array\n 2b. Call \\`codevideo_set_current_actions\\` once with the actions\n 2c. Call \\`codevideo_validate_actions\\` WITHOUT the actions parameter - it will use the stored actions automatically - if issues are found, fix them in the actions array and call \\`codevideo_set_current_actions\\` again\n 2d. Call \\`codevideo_make_video_from_actions\\` WITHOUT the actions parameter - it will use the stored actions automatically\n \n 3. **IF USING FULL LESSON OBJECT APPROACH**:\n 3a. If starting from an existing codebase, use \\`codevideo_get_initial_lesson_state_from_local_repo\\` or \\`codevideo_get_initial_lesson_state_from_github_repo\\`\n 3b. If continuing from a previous lesson, use \\`codevideo_get_final_lesson_snapshot\\` from the previous lesson's actions\n 3c. Create your actions array and build the complete lesson object with initial/final snapshots\n 3d. Optionally store the lesson using \\`codevideo_set_current_lesson\\` for later retrieval\n 3e. Use \\`codevideo_make_video_from_lesson\\` with the complete lesson object\n\n With all this in mind, you are to create a lesson about the following:\n\n ${additionalContext}\n\n Be sure to include a corresponding initial \\`author-speak-before\\` action and a final summary \\`author-speak-before\\` action.\n `\n}", "// Database key constants for the CodeVideo MCP key-value store\nexport const CURRENT_ACTIONS_KEY = 'current_actions';\nexport const CURRENT_LESSON_KEY = 'current_lesson';\n", "import fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { createRequire } from \"node:module\";\nimport type { DatabaseSync } from \"node:sqlite\";\n\n/**\n * The MCP's one SQLite database, ~/.codevideo/codevideo.db, on Node's built-in\n * node:sqlite (Node 22.13+), so installs need no native addon. The MCP server\n * and the background render worker share it: WAL lets one read while the\n * other writes, and the busy timeout makes a writer wait instead of failing.\n */\n\nexport type Db = DatabaseSync;\n\n/** ~/.codevideo, created on first use. The database, render jobs, exports and previews live here. */\nexport const codevideoHome = (): string => {\n const dir = path.join(os.homedir(), \".codevideo\");\n fs.mkdirSync(dir, { recursive: true });\n return dir;\n};\n\nexport const databasePath = (): string => path.join(codevideoHome(), \"codevideo.db\");\n\nlet sqlite: typeof import(\"node:sqlite\") | undefined;\n\n// Node 22-24 print \"SQLite is an experimental feature\" when the module first\n// loads. Load it lazily with that one warning muted so stderr stays clean.\nconst loadSqlite = (): typeof import(\"node:sqlite\") => {\n if (!sqlite) {\n const emitWarning = process.emitWarning;\n process.emitWarning = ((warning: string | Error, ...rest: unknown[]) => {\n const message = typeof warning === \"string\" ? warning : warning.message;\n if (/SQLite/.test(message)) return;\n return (emitWarning as (...args: unknown[]) => void).call(process, warning, ...rest);\n }) as typeof process.emitWarning;\n try {\n sqlite = createRequire(import.meta.url)(\"node:sqlite\") as typeof import(\"node:sqlite\");\n } finally {\n process.emitWarning = emitWarning;\n }\n }\n return sqlite;\n};\n\nexport const openDatabase = (): Db => {\n // Newer Node renames the class to Database and keeps DatabaseSync as an alias.\n const db = new (loadSqlite().DatabaseSync)(databasePath());\n // The timeout goes first so the WAL pragma waits too: the MCP server and the\n // render worker open connections all the time, and one may be holding the\n // file for a moment (checkpointing as the last to close).\n db.exec(\"PRAGMA busy_timeout = 5000; PRAGMA journal_mode = WAL;\");\n return db;\n};\n\n/** Runs fn with an open connection and always closes it afterwards. */\nexport const withDatabase = <T>(fn: (db: Db) => T): T => {\n const db = openDatabase();\n try {\n return fn(db);\n } finally {\n db.close();\n }\n};\n\n/**\n * Runs fn in a write transaction: everything commits or nothing does.\n * BEGIN IMMEDIATE takes the write lock up front, so the server and the worker\n * can't both read a row and then race to update it.\n */\nexport const transaction = <T>(db: Db, fn: () => T): T => {\n db.exec(\"BEGIN IMMEDIATE\");\n try {\n const result = fn();\n db.exec(\"COMMIT\");\n return result;\n } catch (error) {\n db.exec(\"ROLLBACK\");\n throw error;\n }\n};\n", "import { databasePath, withDatabase } from \"./db\";\n\n/**\n * The single-slot key-value store behind the legacy tools\n * (codevideo_set_current_actions, codevideo_set_current_lesson and the tools that\n * read them). Every conversation shares one slot per key; the course tools keep\n * courses apart by id.\n */\n\nconst SCHEMA = `\n CREATE TABLE IF NOT EXISTS key_value_store (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )\n`;\n\n/** Stores value under key and returns the database path. */\nexport const setStoredValue = (key: string, value: string): string =>\n withDatabase((db) => {\n db.exec(SCHEMA);\n db.prepare(`INSERT OR REPLACE INTO key_value_store (key, value, updated_at) VALUES (?, ?, CURRENT_TIMESTAMP)`).run(key, value);\n return databasePath();\n });\n\n/** The value stored under key, or null when there is none. */\nexport const getStoredValue = (key: string): string | null =>\n withDatabase((db) => {\n db.exec(SCHEMA);\n const row = db.prepare(`SELECT value FROM key_value_store WHERE key = ?`).get(key) as { value: string } | undefined;\n return row ? row.value : null;\n });\n", "import { IAction } from \"@fullstackcraftllc/codevideo-types\";\nimport { CURRENT_ACTIONS_KEY } from \"../constants/Constants\";\nimport { getStoredValue } from \"../store/keyValueStore\";\n\n// retrieves the current actions from the key value sqlite database\nexport const getCurrentActions = (): IAction[] | null => {\n try {\n const value = getStoredValue(CURRENT_ACTIONS_KEY);\n return value ? (JSON.parse(value) as IAction[]) : null;\n } catch (error: any) {\n console.error('Error retrieving actions:', error);\n return null;\n }\n};\n\n// Helper function that returns actions or throws descriptive error\nexport const getCurrentActionsOrThrow = (): IAction[] => {\n const actions = getCurrentActions();\n if (!actions) {\n throw new Error('No actions found in storage. Please use codevideo_set_current_actions first to store actions, then try again.');\n }\n return actions;\n};\n", "import { IAction } from \"@fullstackcraftllc/codevideo-types\";\nimport { getCurrentActions } from \"../tools/get_current_actions\";\n\nexport const instructionsToTranslateActions = (actionsToTranslation: Array<IAction> | undefined, targetLanguage: string): string => {\n let actionsToUse: IAction[];\n\n if (actionsToTranslation && actionsToTranslation.length > 0) {\n // Use provided actions\n actionsToUse = actionsToTranslation;\n } else {\n // Try to get from storage\n const storedActions = getCurrentActions();\n if (!storedActions || storedActions.length === 0) {\n return \"No actions provided and no actions found in storage. Either pass actionsToTranslation parameter or use codevideo_set_current_actions first to store actions.\";\n }\n actionsToUse = storedActions;\n }\n\n return `## \uD83D\uDCDD Translation Instructions for CodeVideo Action Scripts\n\n### \uD83D\uDCCC Objective\n\nYou are given a JSON array representing an instructional CodeVideo script. Your task is to translate all relevant text **within the \\`\"value\"\\` fields** into the specified target language **while preserving CodeVideo execution logic**.\n\n**WORKFLOW TIP**: Use \\`codevideo_set_current_actions\\` to store actions first, then call this tool without the actionsToTranslation parameter. This allows you to work with the same actions across multiple translation operations or other CodeVideo tools like \\`codevideo_validate_actions\\`, \\`codevideo_make_video_from_actions\\`, etc.\n\n**VALIDATION TIP**: After translation, you can validate the translated actions using \\`codevideo_validate_actions\\` to ensure the CodeVideo structure remains intact.\n\n---\n\n### \u2705 What to Translate\nTranslate the following, only if found within the \\`\"value\"\\` fields:\n\n1. **All narration steps:**\n - \\`\"name\": \"author-speak-before\"\\` or \\`\"author-speak-after\"\\` \n \u2192 Fully translate narration into fluent, natural speech in the target language.\n\n2. **Code comments:**\n - Lines starting with \\`#\\` or \\`//\\` or between \\`/* ... */\\` \n \u2192 Translate the comment portion only, preserving syntax markers.\n\n3. **String literals inside code:**\n - e.g., \\`print(\"Hello, World!\")\\` \n \u2192 Translate the string inside the quotes, preserving quote marks and code structure.\n\n4. **Terminal outputs:**\n - \\`\"name\": \"terminal-set-output\"\\` \n \u2192 Translate printed outputs line by line.\n\n---\n\n### \uD83D\uDEAB What NOT to Translate\nDo **not** translate the following:\n\n- \\`\"name\"\\` field values (e.g., \\`\"author-speak-before\"\\`, \\`\"editor-type\"\\`, etc.) \n \u2192 These are CodeVideo internal commands.\n\n- File names, identifiers, variables, or function names (e.g., \\`main.py\\`, \\`range\\`, \\`print\\`, \\`for i in range(...)\\`) \n \u2192 Keep code functional and consistent.\n\n- Keywords or syntax in programming code \n \u2192 Only translate *comments* and *strings*.\n\n---\n\n### \uD83D\uDCA1 Examples\n\n#### Code Comment\n\n\\`\\`\\`json\n{ \"name\": \"editor-type\", \"value\": \"# Print 'Hello, World!' five times\" }\n\\`\\`\\`\n\n\u27A1 becomes:\n\n\\`\\`\\`json\n{ \"name\": \"editor-type\", \"value\": \"# Imprime '\u00A1Hola, Mundo!' cinco veces\" }\n\\`\\`\\`\n\n#### Narration\n\\`\\`\\`json\n{ \"name\": \"author-speak-before\", \"value\": \"Let's open a terminal and run the script.\" }\n\\`\\`\\`\n\u27A1 becomes:\n\\`\\`\\`json\n{ \"name\": \"author-speak-before\", \"value\": \"Vamos a abrir una terminal y ejecutar el script.\" }\n\\`\\`\\`\n\n#### Terminal Output\n\\`\\`\\`json\n{ \"name\": \"terminal-set-output\", \"value\": \"Hello, World!\\nHello, World!\" }\n\\`\\`\\`\n\u27A1 becomes:\n\\`\\`\\`json\n{ \"name\": \"terminal-set-output\", \"value\": \"\u00A1Hola, Mundo!\\n\u00A1Hola, Mundo!\" }\n\\`\\`\\`\n\n---\n\n### \uD83C\uDF0D Target Language\n\nThe target language will be specified separately. Use fluent, natural phrasing appropriate to that language's tone and style of instruction.\n\n---\n\n### \uD83E\uDDE0 Tips\n\n- Match tone to the original narration (enthusiastic, beginner-friendly).\n- Avoid overly literal translations \u2014 clarity is more important.\n- Maintain line breaks, indentation, and special characters exactly as they appear in code or outputs.\n\n---\n\n### \u2705 Output Format\n\nReturn the translated JSON with only the \\`value\\` fields updated. The JSON structure must remain valid and unchanged otherwise.\n\n\\`\\`\\`json\n{ \"name\": \"editor-type\", \"value\": \"# Print 'Hello, World!' five times\" }\n\\`\\`\\`\n\u27A1 becomes:\n\\`\\`\\`json\n{ \"name\": \"editor-type\", \"value\": \"# Imprime '\u00A1Hola, Mundo!' cinco veces\" }\n\\`\\`\\`\n\n#### Narration\n\\`\\`\\`json\n{ \"name\": \"author-speak-before\", \"value\": \"Let's open a terminal and run the script.\" }\n\\`\\`\\`\n\u27A1 becomes:\n\\`\\`\\`json\n{ \"name\": \"author-speak-before\", \"value\": \"Vamos a abrir una terminal y ejecutar el script.\" }\n\\`\\`\\`\n\n#### Terminal Output\n\\`\\`\\`json\n{ \"name\": \"terminal-set-output\", \"value\": \"Hello, World!\\nHello, World!\" }\n\\`\\`\\`\n\u27A1 becomes:\n\\`\\`\\`json\n{ \"name\": \"terminal-set-output\", \"value\": \"\u00A1Hola, Mundo!\\n\u00A1Hola, Mundo!\" }\n\\`\\`\\`\n\n---\n\n### \uD83C\uDF0D Target Language\n\nThe target language will be specified below. Use fluent, natural phrasing appropriate to that language's tone and style of instruction.\n\n---\n\n### \uD83E\uDDE0 Tips\n\n- Match tone to the original narration (enthusiastic, beginner-friendly).\n- Avoid overly literal translations \u2014 clarity is more important.\n- Maintain line breaks, indentation, and special characters exactly as they appear in code or outputs.\n\n---\n\n### \u2705 Output Format\nReturn the translated JSON with only the \\`\"value\"\\` fields updated. The JSON structure must remain valid and unchanged otherwise.\n\n\nConsidering all these instructions, please translate the following actions into ${targetLanguage}:\n\n\\`\\`\\`json\n${JSON.stringify(actionsToUse, null, 4)}\n\\`\\`\\`\n\n`;\n}", "import { AUTHORING_RULES } from \"./authoringRules\";\n\n// Drives a calling LLM (Claude Code, Codex, etc.) through building a full, multi-lesson\n// \"build from scratch\" CodeVideo course using the server-side course store tools \u2014 so\n// snapshots never round-trip through the model's context and the build stays reliable.\nexport const instructionsToBuildCourseFromScratch = (additionalContext: string): string => {\n return `You are an expert senior software engineer and educator building a COMPLETE, multi-lesson software course with the CodeVideo MCP, in the clear, small-step style of a polished screencast series: one concept per lesson, narrate before and while you type, and make sure the code actually builds at the end of every lesson that is meant to build.\n\nCRITICAL ARCHITECTURE RULE \u2014 keep snapshots OUT of your context:\nThe course lives server-side, keyed by id. You author and read only ACTIONS, compile errors, and small targeted file reads. Do NOT fetch or pass whole snapshots or whole course/lesson objects around \u2014 that is what used to make full courses fail. Use the course store tools below; they chain each lesson's ending state into the next for you, server-side.\n\nTHE LOOP:\n1. Outline the course first: a numbered list of lessons, each teaching ONE concept, ordered so the code grows naturally from nothing to a working result.\n2. codevideo_create_course(name, description, primaryLanguage) -> returns courseId. Hold onto it.\n3. For each lesson, in order:\n a. codevideo_add_lesson(courseId, { name, description, mustCompile, continuesFromPrevious: true }).\n - description is required: one sentence on what the lesson teaches. The next lesson's opening recap is written from it, and the reply gives you the previous lesson's.\n - Set mustCompile: false for pure slideshow/discussion lessons or deliberate half-refactors where the code is NOT expected to compile yet.\n - Set mustCompile: true (the default) for any lesson that ends with working code.\n - The FINAL lesson must be mustCompile: true \u2014 all code must work by the end of the course.\n b. Author the actions array for this lesson. Build on the previous lesson's ending state (it is already the starting state of this lesson \u2014 you don't pass it in). If you need to know what exists, use codevideo_get_lesson_file_tree, and codevideo_get_lesson_file for a specific file's current contents \u2014 never dump everything.\n c. codevideo_set_lesson_actions(courseId, lessonId, actions). The server recomputes the lesson's final state.\n d. If this lesson is mustCompile, codevideo_compile_lesson(courseId, lessonId). If there are errors, fix just the actions involved with codevideo_edit_lesson_actions (find indices with codevideo_get_lesson_actions; don't re-send the whole lesson) and repeat (d) until it compiles clean. (If the compile tool reports \"pending\", continue \u2014 compilation will be enforced once available.)\n4. When all lessons are authored: codevideo_validate_course(courseId). It returns \"done\": true only when every lesson's actions are valid, every lesson follows cleanly from the last, every mustCompile lesson compiles, and the final lesson compiles clean. If \"done\" is false, read \"reasons\" and the per-lesson results, fix, and re-validate.\n5. Use codevideo_get_course_status(courseId) at any time to see what's authored without pulling snapshots.\n\nHARD RULE \u2014 NO VIDEO DURING AUTHORING:\nFor a full course, do NOT generate any video until codevideo_validate_course returns done: true. Rendering is the expensive final step. After validation passes, previews (codevideo_generate_course_previews: an HTML page per lesson) and the final videos (codevideo_render_course_videos: background render jobs, one lesson at a time; follow them with codevideo_list_render_jobs) are a separate, human-approved step \u2014 do not interleave them with authoring.\n\n${AUTHORING_RULES}\n\nNow build the course described here:\n\n${additionalContext}\n`;\n}\n", "import { IAction } from \"@fullstackcraftllc/codevideo-types\";\nimport { VirtualIDE } from \"@fullstackcraftllc/codevideo-virtual-ide\";\nimport { getCurrentActionsOrThrow } from \"./get_current_actions\";\n\nexport const getFinalLessonSnapshot = (actions?: Array<IAction>): string => {\n // Use provided actions or retrieve from storage\n const actionsToUse = actions || getCurrentActionsOrThrow();\n \n // create a new instance of codevideo virtual ide\n const virtualIDE = new VirtualIDE();\n\n // apply all actions to the virtual IDE\n virtualIDE.applyActions(actionsToUse);\n\n // get the final snapshot of the virtual IDE\n const finalSnapshot = virtualIDE.getCourseSnapshot();\n \n // return the final snapshot as a string\n return JSON.stringify(finalSnapshot, null, 2);\n}", "import { ILesson } from \"@fullstackcraftllc/codevideo-types\";\nimport { buildFileStructureFromRootFolder } from \"@fullstackcraftllc/codevideo-adapters\";\n\nexport const getInitialLessonStateFromLocalCodebase = async (repoLocation: string): Promise<string> => {\n // build file structure from files and content from the file system adapter\n const { fileStructure } = await buildFileStructureFromRootFolder(repoLocation);\n\n // build the project using the file structure\n const project: ILesson = {\n id: \"\",\n name: \"\",\n description: \"\",\n actions: [],\n initialSnapshot: {\n isUnsavedChangesDialogOpen: false,\n unsavedFileName: '',\n fileExplorerSnapshot: {\n isFileExplorerContextMenuOpen: false,\n isFileContextMenuOpen: false,\n isFolderContextMenuOpen: false,\n isNewFileInputVisible: false,\n isNewFolderInputVisible: false,\n isRenameFileInputVisible: false,\n isRenameFolderInputVisible: false,\n newFileInputValue: '',\n newFolderInputValue: '',\n renameFileInputValue: '',\n renameFolderInputValue: '',\n originalFileBeingRenamed: '',\n originalFolderBeingRenamed: '',\n newFileParentPath: '',\n newFolderParentPath: '',\n fileStructure,\n },\n editorSnapshot: {\n isEditorContextMenuOpen: false,\n editors: []\n },\n terminalSnapshot: {\n terminals: []\n },\n mouseSnapshot: {\n location: 'editor',\n currentHoveredFileName: '',\n currentHoveredFolderName: '',\n currentHoveredEditorTabFileName: '',\n x: 0,\n y: 0,\n timestamp: 0,\n type: 'move',\n buttonStates: {\n left: false,\n right: false,\n middle: false,\n },\n scrollPosition: {\n x: 0,\n y: 0\n },\n },\n authorSnapshot: {\n authors: [\n { currentSpeechCaption: '' }\n ]\n }\n }\n };\n\n // return the project as a string\n return JSON.stringify(project);\n}", "import { buildFileStructureFromGitHub } from \"@fullstackcraftllc/codevideo-adapters\";\nimport { ILesson } from \"@fullstackcraftllc/codevideo-types\";\n\nexport const getInitialLessonStateFromGitHubRepo = async (repoUrl: string): Promise<string> => {\n // build file structure from files and content from the file system adapter\n const fileStructure = await buildFileStructureFromGitHub(repoUrl);\n\n // build the project using the file structure\n const project: ILesson = {\n id: \"\",\n name: \"\",\n description: \"\",\n actions: [],\n initialSnapshot: {\n isUnsavedChangesDialogOpen: false,\n unsavedFileName: '',\n fileExplorerSnapshot: {\n isFileExplorerContextMenuOpen: false,\n isFileContextMenuOpen: false,\n isFolderContextMenuOpen: false,\n isNewFileInputVisible: false,\n isNewFolderInputVisible: false,\n isRenameFileInputVisible: false,\n isRenameFolderInputVisible: false,\n newFileInputValue: '',\n newFolderInputValue: '',\n renameFileInputValue: '',\n renameFolderInputValue: '',\n originalFileBeingRenamed: '',\n originalFolderBeingRenamed: '',\n newFileParentPath: '',\n newFolderParentPath: '',\n fileStructure,\n },\n editorSnapshot: {\n isEditorContextMenuOpen: false,\n editors: []\n },\n terminalSnapshot: {\n terminals: []\n },\n mouseSnapshot: {\n location: 'editor',\n currentHoveredFileName: '',\n currentHoveredFolderName: '',\n currentHoveredEditorTabFileName: '',\n x: 0,\n y: 0,\n timestamp: 0,\n type: 'move',\n buttonStates: {\n left: false,\n right: false,\n middle: false,\n },\n scrollPosition: {\n x: 0,\n y: 0\n },\n },\n authorSnapshot: {\n authors: [\n { currentSpeechCaption: '' }\n ]\n }\n }\n };\n\n // return the project as a string\n return JSON.stringify(project);\n}", "import { IAction, isRepeatableAction } from \"@fullstackcraftllc/codevideo-types\";\n\n\nexport const isRepeatable = (action: IAction): string => {\n return isRepeatableAction(action) ? \"true\" : \"false\";\n}", "import { ICourse } from \"@fullstackcraftllc/codevideo-types\"\n\nexport const createCourseWithInitialMetadata = (id: string, name: string, description: string, primaryLanguage: string): string => {\n const course: ICourse = {\n id,\n name,\n description,\n primaryLanguage,\n lessons: []\n };\n return JSON.stringify(course, null, 2);\n}", "import { ICourse, ILesson } from \"@fullstackcraftllc/codevideo-types\"\n\nexport const addLessonToCourse = (course: ICourse, id: string, name: string, description: string): string => {\n const lesson: ILesson = {\n id,\n name,\n description,\n actions: [],\n initialSnapshot: {\n isUnsavedChangesDialogOpen: false,\n unsavedFileName: '',\n fileExplorerSnapshot: {\n isFileExplorerContextMenuOpen: false,\n isFileContextMenuOpen: false,\n isFolderContextMenuOpen: false,\n isNewFileInputVisible: false,\n isNewFolderInputVisible: false,\n isRenameFileInputVisible: false,\n isRenameFolderInputVisible: false,\n newFileInputValue: '',\n newFolderInputValue: '',\n renameFileInputValue: '',\n renameFolderInputValue: '',\n originalFileBeingRenamed: '',\n originalFolderBeingRenamed: '',\n newFileParentPath: '',\n newFolderParentPath: '',\n fileStructure: {\n },\n },\n editorSnapshot: {\n isEditorContextMenuOpen: false,\n editors: []\n },\n terminalSnapshot: {\n terminals: []\n },\n mouseSnapshot: {\n location: 'editor',\n currentHoveredFileName: '',\n currentHoveredFolderName: '',\n currentHoveredEditorTabFileName: '',\n x: 0,\n y: 0,\n timestamp: 0,\n type: 'move',\n buttonStates: {\n left: false,\n right: false,\n middle: false,\n },\n scrollPosition: {\n x: 0,\n y: 0\n },\n },\n authorSnapshot: {\n authors: [\n { currentSpeechCaption: '' }\n ] \n }\n }\n };\n\n // Add the lesson to the course\n course.lessons.push(lesson);\n\n // Return the updated course object\n return JSON.stringify(course, null, 2);\n}", "import { ILesson, IAction } from \"@fullstackcraftllc/codevideo-types\";\n\nexport const addActionsToLesson = (lesson: ILesson, actions: IAction[]): string => {\n lesson.actions = actions;\n return JSON.stringify(lesson, null, 2);\n}", "import { ICourseSnapshot } from \"@fullstackcraftllc/codevideo-types\"\n\nexport const getEmptyLessonSnapshot = (): string => {\n const snapshot: ICourseSnapshot = {\n isUnsavedChangesDialogOpen: false,\n unsavedFileName: '',\n fileExplorerSnapshot: {\n isFileExplorerContextMenuOpen: false,\n isFileContextMenuOpen: false,\n isFolderContextMenuOpen: false,\n isNewFileInputVisible: false,\n isNewFolderInputVisible: false,\n isRenameFileInputVisible: false,\n isRenameFolderInputVisible: false,\n newFileInputValue: '',\n newFolderInputValue: '',\n renameFileInputValue: '',\n renameFolderInputValue: '',\n originalFileBeingRenamed: '',\n originalFolderBeingRenamed: '',\n newFileParentPath: '',\n newFolderParentPath: '',\n fileStructure: {\n },\n },\n editorSnapshot: {\n isEditorContextMenuOpen: false,\n editors: []\n },\n terminalSnapshot: {\n terminals: []\n },\n mouseSnapshot: {\n location: 'editor',\n currentHoveredFileName: '',\n currentHoveredFolderName: '',\n currentHoveredEditorTabFileName: '',\n x: 0,\n y: 0,\n timestamp: 0,\n type: 'move',\n buttonStates: {\n left: false,\n right: false,\n middle: false,\n },\n scrollPosition: {\n x: 0,\n y: 0\n },\n },\n authorSnapshot: {\n authors: [\n { currentSpeechCaption: '' }\n ] \n }\n }\n\n // return the snapshot as a string\n return JSON.stringify(snapshot, null, 2);\n}", "import { IAction } from \"@fullstackcraftllc/codevideo-types\"\nimport { GoogleGenAI } from '@google/genai'\nimport fs from 'fs'\nimport { getCurrentActionsOrThrow } from \"./get_current_actions\";\n\nexport const critiqueVideoUsingGemini = async (videoPath: string, actions?: Array<IAction>) => {\n // Use provided actions or retrieve from storage\n const actionsToUse = actions || getCurrentActionsOrThrow();\n \n // Check if GEMINI_API_KEY is set in the environment\n const GEMINI_API_KEY = process.env.GEMINI_API_KEY\n if (!GEMINI_API_KEY) {\n throw new Error('GEMINI_API_KEY environment variable is not set')\n }\n\n // Initialize the Gemini API client\n const ai = new GoogleGenAI({ apiKey: GEMINI_API_KEY })\n\n const prompt = `You are an expert senior software engineer and educator, and skilled in describing software concepts in a way that is easy for students to understand. You are going to critique a procedurally generated software video lesson for clarity, accuracy, and correctness. The video and the lesson actions are attached. Your tasks:\n \n 1. Watch the video and read the lesson actions.\n 2. Critique the video for clarity, accuracy, and correctness. Of particular note is any audio artifacts or strange wordings. Do not suggest any new material, only critique the existing material.\n 3. Provide a summary of your critique, including any errors or inaccuracies you found, and at what action they occur at.`\n\n try {\n // Read the video file as a binary blob for the API\n const videoBuffer = fs.readFileSync(videoPath)\n const videoBase64 = videoBuffer.toString('base64')\n\n // Convert actions array to JSON string\n const actionsJson = JSON.stringify(actionsToUse, null, 2)\n\n // Prepare the request with content parts including text and file data\n const response = await ai.models.generateContent({\n model: 'gemini-2.5-pro-preview-05-06',\n contents: [\n {\n role: 'user',\n parts: [\n { text: prompt },\n {\n fileData: {\n mimeType: 'video/mp4',\n fileUri: `data:video/mp4;base64,${videoBase64}`\n }\n },\n {\n text: `Here are the lesson actions in JSON format:\\n\\n${actionsJson}`\n }\n ]\n }\n ]\n })\n\n // Get the response text\n const result = response.text\n return result || \"No critique was generated\"\n } catch (error: unknown) {\n console.error('Error during Gemini API call:', error)\n const errorMessage = error instanceof Error ? error.message : String(error)\n throw new Error(`Failed to critique video using Gemini: ${errorMessage}`)\n }\n}", "import { IAction } from \"@fullstackcraftllc/codevideo-types\";\nimport { CURRENT_ACTIONS_KEY } from \"../constants/Constants\";\nimport { setStoredValue } from \"../store/keyValueStore\";\n\n// sets the current actions to the key value sqlite database\nexport const setCurrentActions = (actions: IAction[]): string => {\n try {\n const dbPath = setStoredValue(CURRENT_ACTIONS_KEY, JSON.stringify(actions));\n return `Successfully stored ${actions.length} actions in local database at ${dbPath}`;\n } catch (error: any) {\n return `Error storing actions: ${error.message}`;\n }\n};\n", "import { ILesson } from \"@fullstackcraftllc/codevideo-types\";\nimport { CURRENT_LESSON_KEY } from \"../constants/Constants\";\nimport { setStoredValue } from \"../store/keyValueStore\";\n\n// sets the current lesson to the key value sqlite database\nexport const setCurrentLesson = (lesson: ILesson): string => {\n try {\n const dbPath = setStoredValue(CURRENT_LESSON_KEY, JSON.stringify(lesson));\n return `Successfully stored lesson \"${lesson.name}\" (ID: ${lesson.id}) in local database at ${dbPath}`;\n } catch (error: any) {\n return `Error storing lesson: ${error.message}`;\n }\n};\n", "import { ILesson } from \"@fullstackcraftllc/codevideo-types\";\nimport { CURRENT_LESSON_KEY } from \"../constants/Constants\";\nimport { getStoredValue } from \"../store/keyValueStore\";\n\n// retrieves the current lesson from the key value sqlite database\nexport const getCurrentLesson = (): ILesson | null => {\n try {\n const value = getStoredValue(CURRENT_LESSON_KEY);\n return value ? (JSON.parse(value) as ILesson) : null;\n } catch (error: any) {\n console.error('Error retrieving lesson:', error);\n return null;\n }\n};\n\n// Helper function that returns lesson or throws descriptive error\nexport const getCurrentLessonOrThrow = (): ILesson => {\n const lesson = getCurrentLesson();\n if (!lesson) {\n throw new Error('No lesson found in storage. Please use codevideo_set_current_lesson first to store a lesson, then try again.');\n }\n return lesson;\n};\n", "import { IAction } from \"@fullstackcraftllc/codevideo-types\";\nimport { generateMarkdownStringFromActions } from '@fullstackcraftllc/codevideo-exporters';\nimport { getCurrentActionsOrThrow } from \"./get_current_actions\";\n\n// calls the markdown generator from the codevideo-exporters package\nexport const makeMarkdownFromActions = (actions?: Array<IAction>): string => {\n // Use provided actions or retrieve from storage\n const actionsToUse = actions || getCurrentActionsOrThrow();\n return generateMarkdownStringFromActions(actionsToUse);\n}", "import { ILesson } from \"@fullstackcraftllc/codevideo-types\";\nimport { generateMarkdownStringFromActions } from '@fullstackcraftllc/codevideo-exporters';\nimport { getCurrentLesson } from \"./get_current_lesson\";\n\n// calls the markdown generator from the codevideo-exporters package using a lesson's actions\nexport const makeMarkdownFromLesson = (lesson?: ILesson): string => {\n // Use provided lesson or retrieve from storage\n const lessonToUse = lesson || getCurrentLesson();\n \n if (!lessonToUse) {\n throw new Error(\"No lesson provided and no current lesson found in storage\");\n }\n \n return generateMarkdownStringFromActions(lessonToUse.actions);\n}\n", "import os from \"os\";\nimport path from \"path\";\nimport fs from \"fs\";\nimport { spawn } from \"child_process\";\nimport { IAction } from \"@fullstackcraftllc/codevideo-types\";\nimport { generateHtmlStringFromActions } from '@fullstackcraftllc/codevideo-exporters';\nimport { getCurrentActionsOrThrow } from \"./get_current_actions\";\n\n// calls the HTML generator from the codevideo-exporters package\nexport const makeHTMLWebPage = async (actions?: Array<IAction>): Promise<string> => {\n // Use provided actions or retrieve from storage\n const actionsToUse = actions || getCurrentActionsOrThrow();\n \n // Generate the HTML content\n // (exporters >=0.0.10 takes markdown + optional html options; defaults are fine here)\n const htmlContent = await generateHtmlStringFromActions(actionsToUse, {});\n \n // Create output file on desktop with timestamp\n const homeDir = os.homedir();\n const desktopDir = path.join(homeDir, 'Desktop');\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const outputFile = path.join(desktopDir, `codevideo-${timestamp}.html`);\n \n // Write HTML to file\n fs.writeFileSync(outputFile, htmlContent, 'utf8');\n \n // Open the HTML file in the default browser\n const openChild = spawn('open', [outputFile], {\n detached: true,\n stdio: 'ignore'\n });\n openChild.unref();\n \n // Return the HTML content\n return htmlContent;\n}", "import fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { IAction, estimateVideoDurationInSeconds } from \"@fullstackcraftllc/codevideo-types\";\nimport { enqueueJobs, getJob, jobDir, JobSpec, queuePosition, RenderJob } from \"./jobs\";\nimport { ensureWorker } from \"./worker\";\nimport { estimatePauses, RenderOptions } from \"./options\";\n\n/** Where videos are saved: CODEVIDEO_OUTPUT_DIR, or the Desktop. */\nexport const outputDirectory = (): string => process.env.CODEVIDEO_OUTPUT_DIR || path.join(os.homedir(), \"Desktop\");\n\nconst timestamp = (): string => new Date().toISOString().replace(/[:.]/g, \"-\");\n\n/** A new video path in the output directory, optionally named after a lesson. */\nexport const newVideoPath = (name?: string): string => {\n const safe = (name ?? \"\").replace(/[^a-z0-9-_]+/gi, \"_\").slice(0, 40);\n return path.join(outputDirectory(), safe ? `codevideo-${safe}-${timestamp()}.mp4` : `codevideo-${timestamp()}.mp4`);\n};\n\n/** Rough video length in seconds at the render page's pauses (or the options'). */\nexport const estimatedSeconds = (actions: IAction[], options?: RenderOptions): number =>\n Math.round(estimateVideoDurationInSeconds(actions, estimatePauses(options)));\n\n/** Queues jobs in order and makes sure the background worker is running them. */\nexport const queueRenders = (specs: JobSpec[]): RenderJob[] => {\n const jobs = enqueueJobs(specs);\n ensureWorker();\n return jobs;\n};\n\nconst lastLines = (file: string, count: number): string[] => {\n try {\n return fs.readFileSync(file, \"utf8\").split(\"\\n\").map((line) => line.trimEnd()).filter(Boolean).slice(-count);\n } catch {\n return [];\n }\n};\n\n/** A compact view of a job for tool replies; the log tail only when asked for. */\nexport const describeJob = (job: RenderJob, withLog = false): Record<string, unknown> => {\n const position = job.status === \"queued\" ? queuePosition(job.id) : null;\n return {\n jobId: job.id,\n label: job.label,\n status: job.status,\n ...(position !== null ? { jobsAhead: position } : {}),\n outputPath: job.outputPath,\n ...(job.status === \"succeeded\" ? { saved: true } : {}),\n ...(job.progress ? { progress: job.progress } : {}),\n ...(job.error ? { error: job.error } : {}),\n createdAt: job.createdAt,\n ...(job.startedAt ? { startedAt: job.startedAt } : {}),\n ...(job.finishedAt ? { finishedAt: job.finishedAt } : {}),\n ...(withLog ? { logTail: lastLines(path.join(jobDir(job.id), \"stderr.log\"), 8), logs: jobDir(job.id) } : {}),\n };\n};\n\nexport const describeJobById = (jobId: string, withLog = false): Record<string, unknown> | null => {\n const job = getJob(jobId);\n return job ? describeJob(job, withLog) : null;\n};\n", "import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { randomUUID } from \"node:crypto\";\nimport { codevideoHome, Db, transaction, withDatabase } from \"../store/db\";\nimport { ideConfigFor, maxProjectArgumentBytes } from \"./cli\";\nimport { RenderOptions } from \"./options\";\n\n/**\n * Render jobs, kept in the shared database so the background worker and any\n * MCP server process see the same queue. Each job also has a folder,\n * ~/.codevideo/jobs/<id>/, holding its project, config and the CLI's logs.\n */\n\nexport type JobStatus = \"queued\" | \"running\" | \"succeeded\" | \"failed\" | \"cancelled\" | \"interrupted\";\nexport type JobKind = \"actions\" | \"lesson\" | \"course-lesson\";\n\nexport interface RenderJob {\n id: string;\n kind: JobKind;\n courseId: string | null;\n lessonId: string | null;\n label: string;\n outputPath: string;\n options: RenderOptions;\n open: boolean;\n status: JobStatus;\n pid: number | null;\n progress: string | null;\n error: string | null;\n cancelRequested: boolean;\n createdAt: string;\n startedAt: string | null;\n finishedAt: string | null;\n}\n\nexport interface JobSpec {\n kind: JobKind;\n courseId?: string;\n lessonId?: string;\n label: string;\n /** The actions array, lesson or course the CLI renders. */\n project: unknown;\n outputPath: string;\n options: RenderOptions;\n open: boolean;\n /** For the CLI's config file when IDE options are set. */\n defaultLanguage: string;\n}\n\nconst SCHEMA = `\n CREATE TABLE IF NOT EXISTS render_jobs (\n id TEXT PRIMARY KEY,\n kind TEXT NOT NULL,\n course_id TEXT,\n lesson_id TEXT,\n label TEXT NOT NULL DEFAULT '',\n output_path TEXT NOT NULL,\n options TEXT NOT NULL DEFAULT '{}',\n open INTEGER NOT NULL DEFAULT 0,\n status TEXT NOT NULL,\n pid INTEGER,\n progress TEXT,\n error TEXT,\n cancel_requested INTEGER NOT NULL DEFAULT 0,\n created_at TEXT NOT NULL,\n started_at TEXT,\n finished_at TEXT\n );\n CREATE INDEX IF NOT EXISTS idx_render_jobs_status ON render_jobs(status);\n CREATE TABLE IF NOT EXISTS render_worker (\n id INTEGER PRIMARY KEY CHECK (id = 1),\n pid INTEGER NOT NULL,\n heartbeat_at INTEGER NOT NULL\n );\n`;\n\n/** A worker that hasn't written its heartbeat for this long is gone. */\nexport const WORKER_STALE_MS = 10_000;\n\nconst withJobs = <T>(fn: (db: Db) => T): T =>\n withDatabase((db) => {\n db.exec(SCHEMA);\n return fn(db);\n });\n\nexport const jobDir = (id: string): string => path.join(codevideoHome(), \"jobs\", id);\n\ninterface JobRow {\n id: string;\n kind: string;\n course_id: string | null;\n lesson_id: string | null;\n label: string;\n output_path: string;\n options: string;\n open: number;\n status: string;\n pid: number | null;\n progress: string | null;\n error: string | null;\n cancel_requested: number;\n created_at: string;\n started_at: string | null;\n finished_at: string | null;\n}\n\nconst toJob = (row: JobRow): RenderJob => ({\n id: row.id,\n kind: row.kind as JobKind,\n courseId: row.course_id,\n lessonId: row.lesson_id,\n label: row.label,\n outputPath: row.output_path,\n options: JSON.parse(row.options),\n open: row.open === 1,\n status: row.status as JobStatus,\n pid: row.pid,\n progress: row.progress,\n error: row.error,\n cancelRequested: row.cancel_requested === 1,\n createdAt: row.created_at,\n startedAt: row.started_at,\n finishedAt: row.finished_at,\n});\n\nconst now = (): string => new Date().toISOString();\n\n/**\n * Queues jobs in order, writing each one's project (and config) to its folder.\n * Refuses the whole batch if any project is too big for the CLI's -p argument.\n */\nexport const enqueueJobs = (specs: JobSpec[]): RenderJob[] => {\n const prepared = specs.map((spec) => {\n const projectJson = JSON.stringify(spec.project);\n const bytes = Buffer.byteLength(projectJson);\n if (bytes > maxProjectArgumentBytes()) {\n throw new Error(\n `\"${spec.label}\" is ${Math.ceil(bytes / 1024)} KiB as JSON, more than the ${Math.floor(maxProjectArgumentBytes() / 1024)} KiB ` +\n \"the CLI can take on its command line here. Split it into smaller lessons.\"\n );\n }\n return { spec, projectJson, id: randomUUID() };\n });\n for (const { spec, projectJson, id } of prepared) {\n const dir = jobDir(id);\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(path.join(dir, \"project.json\"), projectJson);\n const config = ideConfigFor(spec.options, spec.defaultLanguage);\n if (config) fs.writeFileSync(path.join(dir, \"config.json\"), JSON.stringify(config, null, 2));\n }\n return withJobs((db) =>\n transaction(db, () => {\n const insert = db.prepare(`\n INSERT INTO render_jobs (id, kind, course_id, lesson_id, label, output_path, options, open, status, created_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'queued', ?)\n `);\n return prepared.map(({ spec, id }) => {\n insert.run(id, spec.kind, spec.courseId ?? null, spec.lessonId ?? null, spec.label, spec.outputPath, JSON.stringify(spec.options), spec.open ? 1 : 0, now());\n return toJob(db.prepare(`SELECT * FROM render_jobs WHERE id = ?`).get(id) as unknown as JobRow);\n });\n })\n );\n};\n\nexport const getJob = (id: string): RenderJob | null =>\n withJobs((db) => {\n const row = db.prepare(`SELECT * FROM render_jobs WHERE id = ?`).get(id) as unknown as JobRow | undefined;\n return row ? toJob(row) : null;\n });\n\n/** Jobs newest first, optionally for one course or in one status. */\nexport const listJobs = (filter: { courseId?: string; status?: JobStatus; limit?: number } = {}): RenderJob[] =>\n withJobs((db) => {\n const where: string[] = [];\n const params: string[] = [];\n if (filter.courseId) {\n where.push(\"course_id = ?\");\n params.push(filter.courseId);\n }\n if (filter.status) {\n where.push(\"status = ?\");\n params.push(filter.status);\n }\n const sql = `SELECT * FROM render_jobs ${where.length ? `WHERE ${where.join(\" AND \")}` : \"\"} ORDER BY rowid DESC LIMIT ?`;\n return (db.prepare(sql).all(...params, filter.limit ?? 20) as unknown as JobRow[]).map(toJob);\n });\n\n/** How many queued jobs will run before this one (0 = next), or null when it isn't queued. */\nexport const queuePosition = (id: string): number | null =>\n withJobs((db) => {\n const row = db.prepare(`SELECT rowid AS seq, status FROM render_jobs WHERE id = ?`).get(id) as { seq: number; status: string } | undefined;\n if (!row || row.status !== \"queued\") return null;\n const ahead = db.prepare(`SELECT COUNT(*) AS n FROM render_jobs WHERE status = 'queued' AND rowid < ?`).get(row.seq) as { n: number };\n return ahead.n;\n });\n\n/**\n * Cancels one job, or every unfinished job of a course. Queued jobs are\n * cancelled at once; running ones are flagged, and the worker stops them.\n */\nexport const requestCancel = (target: { jobId?: string; courseId?: string }): { cancelled: number; stopping: number } =>\n withJobs((db) =>\n transaction(db, () => {\n const [column, value] = target.jobId ? [\"id\", target.jobId] : [\"course_id\", target.courseId ?? \"\"];\n const cancelled = db\n .prepare(`UPDATE render_jobs SET status = 'cancelled', finished_at = ? WHERE ${column} = ? AND status = 'queued'`)\n .run(now(), value).changes;\n const stopping = db\n .prepare(`UPDATE render_jobs SET cancel_requested = 1 WHERE ${column} = ? AND status = 'running'`)\n .run(value).changes;\n return { cancelled: Number(cancelled), stopping: Number(stopping) };\n })\n );\n\n// ---------- worker side ----------\n\n/** Takes the oldest queued job and marks it running, or returns null when the queue is empty. */\nexport const claimNextJob = (): RenderJob | null =>\n withJobs((db) =>\n transaction(db, () => {\n const row = db.prepare(`SELECT * FROM render_jobs WHERE status = 'queued' ORDER BY rowid LIMIT 1`).get() as unknown as JobRow | undefined;\n if (!row) return null;\n db.prepare(`UPDATE render_jobs SET status = 'running', started_at = ? WHERE id = ?`).run(now(), row.id);\n return toJob({ ...row, status: \"running\" });\n })\n );\n\nexport const hasQueuedJobs = (): boolean =>\n withJobs((db) => !!db.prepare(`SELECT 1 FROM render_jobs WHERE status = 'queued' LIMIT 1`).get());\n\nexport const setJobPid = (id: string, pid: number): void =>\n withJobs((db) => {\n db.prepare(`UPDATE render_jobs SET pid = ? WHERE id = ?`).run(pid, id);\n });\n\nexport const setJobProgress = (id: string, progress: string): void =>\n withJobs((db) => {\n db.prepare(`UPDATE render_jobs SET progress = ? WHERE id = ?`).run(progress, id);\n });\n\nexport const isCancelRequested = (id: string): boolean =>\n withJobs((db) => !!db.prepare(`SELECT 1 FROM render_jobs WHERE id = ? AND cancel_requested = 1`).get(id));\n\nexport const finishJob = (id: string, status: Exclude<JobStatus, \"queued\" | \"running\">, error: string | null): void =>\n withJobs((db) => {\n db.prepare(`UPDATE render_jobs SET status = ?, error = ?, finished_at = ? WHERE id = ?`).run(status, error, now(), id);\n });\n\nexport const isProcessAlive = (pid: number): boolean => {\n try {\n process.kill(pid, 0);\n return true;\n } catch (error) {\n return (error as NodeJS.ErrnoException).code === \"EPERM\";\n }\n};\n\n/** The worker currently draining the queue, if its process is alive and its heartbeat recent. */\nexport const liveWorker = (): { pid: number } | null =>\n withJobs((db) => {\n const row = db.prepare(`SELECT pid, heartbeat_at FROM render_worker WHERE id = 1`).get() as { pid: number; heartbeat_at: number } | undefined;\n if (!row || Date.now() - row.heartbeat_at > WORKER_STALE_MS || !isProcessAlive(row.pid)) return null;\n return { pid: row.pid };\n });\n\n/** Makes pid the queue's worker unless another live worker already is. */\nexport const acquireWorkerLock = (pid: number): boolean =>\n withJobs((db) =>\n transaction(db, () => {\n const row = db.prepare(`SELECT pid, heartbeat_at FROM render_worker WHERE id = 1`).get() as { pid: number; heartbeat_at: number } | undefined;\n if (row && row.pid !== pid && Date.now() - row.heartbeat_at <= WORKER_STALE_MS && isProcessAlive(row.pid)) return false;\n db.prepare(`INSERT OR REPLACE INTO render_worker (id, pid, heartbeat_at) VALUES (1, ?, ?)`).run(pid, Date.now());\n return true;\n })\n );\n\nexport const workerHeartbeat = (pid: number): void =>\n withJobs((db) => {\n db.prepare(`UPDATE render_worker SET heartbeat_at = ? WHERE id = 1 AND pid = ?`).run(Date.now(), pid);\n });\n\nexport const releaseWorkerLock = (pid: number): void =>\n withJobs((db) => {\n db.prepare(`DELETE FROM render_worker WHERE id = 1 AND pid = ?`).run(pid);\n });\n\n/** Jobs left running by a worker that died; called before starting a new one. */\nexport const markOrphanedJobsInterrupted = (): number =>\n withJobs((db) =>\n Number(\n db.prepare(`UPDATE render_jobs SET status = 'interrupted', error = 'The render worker stopped before this job finished.', finished_at = ? WHERE status = 'running'`)\n .run(now()).changes\n )\n );\n", "import path from \"node:path\";\nimport { IDE_PROP_KEYS, RenderOptions } from \"./options\";\n\n/**\n * How the worker talks to codevideo-cli 0.0.13: arguments in, and three kinds\n * of output lines back. stdout carries only the saved-file lines; stderr has a\n * plain progress line per stage and per 25% (stderr isn't a terminal here),\n * plus \"error: ...\" lines when something fails.\n */\n\n/**\n * The largest project JSON the CLI can take as one -p argument. Linux caps a\n * single argument at 128 KiB; Windows caps the whole command line at 32,767\n * characters. A project file flag in a later CLI removes this limit.\n */\nexport const maxProjectArgumentBytes = (platform: NodeJS.Platform = process.platform): number =>\n platform === \"win32\" ? 30_000 : 120 * 1024;\n\n/**\n * The CLI's -c file: IDE props from the options, with the two the CLI requires\n * filled in. withCaptions is filled in too: CLI 0.0.13 reads a missing one as\n * false, while a render without a config file keeps the page's default, on.\n */\nexport const ideConfigFor = (options: RenderOptions, defaultLanguage: string): Record<string, unknown> | null => {\n const props: Record<string, unknown> = {};\n for (const key of IDE_PROP_KEYS) {\n if (options[key] !== undefined) props[key] = options[key];\n }\n if (Object.keys(props).length === 0) return null;\n return { theme: \"dark\", defaultLanguage, withCaptions: true, ...props };\n};\n\nexport const cliArgs = (input: {\n projectJson: string;\n outputPath: string;\n configPath?: string;\n options: RenderOptions;\n open: boolean;\n}): string[] => {\n const args = [\"-p\", input.projectJson, \"-o\", input.outputPath];\n if (input.configPath) args.push(\"-c\", input.configPath);\n if (input.options.orientation === \"portrait\") args.push(\"-n\", \"portrait\");\n if (input.options.renderMode) args.push(\"--render-mode\", input.options.renderMode);\n if (input.options.renderSeed) args.push(\"--render-seed\", input.options.renderSeed);\n if (input.options.renderWorkers) args.push(\"--render-workers\", String(input.options.renderWorkers));\n if (input.open) args.push(\"--open\");\n return args;\n};\n\n/**\n * The CLI's environment: the npm runtime's, plus a voice or TTS provider from\n * the options. The Node running the MCP goes first on PATH, because the CLI\n * starts its Puppeteer runner as plain `node`, and a desktop client may start\n * the MCP with a PATH that has none (nvm, fnm, Volta).\n */\nexport const cliEnv = (base: NodeJS.ProcessEnv, options: RenderOptions, nodePath = process.execPath): NodeJS.ProcessEnv => {\n const env = { ...base };\n const pathKey = Object.keys(env).find((key) => key.toUpperCase() === \"PATH\") ?? \"PATH\";\n env[pathKey] = [path.dirname(nodePath), env[pathKey]].filter(Boolean).join(path.delimiter);\n if (options.voiceId) env.ELEVEN_LABS_VOICE_ID = options.voiceId;\n if (options.ttsProvider) env.CODEVIDEO_TTS_PROVIDER = options.ttsProvider;\n return env;\n};\n\nexport type CliLine =\n | { kind: \"progress\"; text: string }\n | { kind: \"error\"; text: string }\n | { kind: \"saved\"; path: string }\n | { kind: \"other\" };\n\nconst PROGRESS = /^\\[\\s*\\d+%\\] /;\nconst ERROR = /^error: (.+)$/;\nconst SAVED = /^\u2705 CodeVideo successfully generated and saved to (.+)$/;\n\nexport const parseCliLine = (line: string): CliLine => {\n const text = line.trimEnd();\n if (PROGRESS.test(text)) return { kind: \"progress\", text };\n const error = ERROR.exec(text);\n if (error) return { kind: \"error\", text: error[1] };\n const saved = SAVED.exec(text);\n if (saved) return { kind: \"saved\", path: saved[1] };\n return { kind: \"other\" };\n};\n", "import { z } from \"zod\";\n\n/**\n * Options every render tool accepts. The IDE and layout names match\n * codevideo-api's ICreateVideoOptions, so a request reads the same in both.\n * Resolution is missing on purpose: 4K needs a prop the render page doesn't\n * take yet, so the CLI would record 4K frames of a 1080p layout.\n */\nexport const RenderOptionsSchema = z\n .object({\n orientation: z.enum([\"landscape\", \"portrait\"]).optional()\n .describe(\"Video orientation; portrait renders 1080x1920. For portrait, usually also set isFileExplorerVisible, isEditorTabsVisible, isMinimapVisible and isMouseVisible to false.\"),\n theme: z.enum([\"light\", \"dark\"]).optional(),\n withCaptions: z.boolean().optional(),\n fontSizePx: z.number().positive().optional(),\n fileExplorerWidth: z.number().positive().optional(),\n terminalHeight: z.number().positive().optional(),\n mouseColor: z.string().min(1).optional(),\n isFileExplorerVisible: z.boolean().optional(),\n isTerminalVisible: z.boolean().optional(),\n isEditorTabsVisible: z.boolean().optional(),\n isMinimapVisible: z.boolean().optional(),\n isMouseVisible: z.boolean().optional()\n .describe(\"Draw the mouse cursor. Mouse actions keep their timing and effects when it's hidden.\"),\n wordWrap: z.enum([\"on\", \"off\"]).optional(),\n keyboardTypingPauseMs: z.number().int().positive().optional(),\n standardPauseMs: z.number().int().positive().optional(),\n longPauseMs: z.number().int().positive().optional(),\n defaultLanguage: z.string().min(1).optional(),\n voiceId: z.string().min(1).optional().describe(\"ElevenLabs voice ID for the narration.\"),\n ttsProvider: z.enum([\"elevenlabs\", \"kokoro\"]).optional(),\n renderMode: z.enum([\"realtime\", \"deterministic\"]).optional()\n .describe(\"realtime (default) records the browser as it plays; deterministic renders frame by frame.\"),\n renderSeed: z.string().min(1).optional().describe(\"Deterministic renders only: a seed for reproducible output.\"),\n renderWorkers: z.number().int().min(1).max(32).optional().describe(\"Deterministic renders only: browser workers, 1-32.\"),\n })\n .strict()\n .refine((o) => (o.renderSeed === undefined && o.renderWorkers === undefined) || o.renderMode === \"deterministic\", {\n message: \"renderSeed and renderWorkers need renderMode: \\\"deterministic\\\"\",\n });\n\nexport type RenderOptions = z.infer<typeof RenderOptionsSchema>;\n\n/** The options that become the CLI's -c config file (CodeVideoIDE props). */\nexport const IDE_PROP_KEYS = [\n \"theme\",\n \"withCaptions\",\n \"fontSizePx\",\n \"fileExplorerWidth\",\n \"terminalHeight\",\n \"mouseColor\",\n \"isFileExplorerVisible\",\n \"isTerminalVisible\",\n \"isEditorTabsVisible\",\n \"isMinimapVisible\",\n \"isMouseVisible\",\n \"wordWrap\",\n \"keyboardTypingPauseMs\",\n \"standardPauseMs\",\n \"longPauseMs\",\n \"defaultLanguage\",\n] as const satisfies readonly (keyof RenderOptions)[];\n\n/**\n * The pauses the CLI's render page plays actions with unless the options say\n * otherwise (codevideo-ide-react example/src/pages/v3.tsx). Estimates use them.\n */\nexport const RENDER_PAGE_PAUSES = { keyboardTypingPauseMs: 40, standardPauseMs: 200 } as const;\n\nexport const estimatePauses = (options: RenderOptions = {}) => ({\n keyboardTypingPauseMs: options.keyboardTypingPauseMs ?? RENDER_PAGE_PAUSES.keyboardTypingPauseMs,\n standardPauseMs: options.standardPauseMs ?? RENDER_PAGE_PAUSES.standardPauseMs,\n});\n", "import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { spawn, spawnSync, type ChildProcess } from \"node:child_process\";\nimport { getCodeVideoCliRuntime } from \"../utils/getBinaryPath\";\nimport { codevideoHome } from \"../store/db\";\nimport { cliArgs, cliEnv, parseCliLine } from \"./cli\";\nimport {\n acquireWorkerLock,\n claimNextJob,\n finishJob,\n hasQueuedJobs,\n isCancelRequested,\n jobDir,\n liveWorker,\n markOrphanedJobsInterrupted,\n releaseWorkerLock,\n RenderJob,\n setJobPid,\n setJobProgress,\n workerHeartbeat,\n} from \"./jobs\";\n\n/**\n * The background render worker: a detached process, started on demand by the\n * MCP server, that renders queued jobs one at a time and exits when the queue\n * is empty. It outlives the MCP client, so closing Claude doesn't stop a\n * course halfway. One CLI at a time, because every CLI run binds port 7001\n * for its embedded site; running renders in parallel would need a port\n * override in the CLI.\n */\n\nconst HEARTBEAT_MS = 2_000;\nconst CANCEL_POLL_MS = 500;\nconst KILL_GRACE_MS = 10_000;\n\nconst log = (message: string): void => {\n try {\n fs.appendFileSync(path.join(codevideoHome(), \"jobs\", \"worker.log\"), `${new Date().toISOString()} [${process.pid}] ${message}\\n`);\n } catch {\n // Logging must never take the worker down.\n }\n};\n\n/**\n * Runs a database update from a timer or stream callback, where a throw would\n * crash the worker mid-render; a missed heartbeat or progress line is harmless.\n */\nconst quietly = (what: string, fn: () => void): void => {\n try {\n fn();\n } catch (error) {\n log(`${what} failed: ${(error as Error).message}`);\n }\n};\n\n/**\n * Makes sure a worker is draining the queue: starts one unless a live worker\n * already is. Call it after queueing jobs.\n */\nexport const ensureWorker = (): void => {\n if (liveWorker()) return;\n const orphaned = markOrphanedJobsInterrupted();\n if (orphaned > 0) log(`marked ${orphaned} job(s) left by a stopped worker as interrupted`);\n const entry = process.argv[1];\n if (!entry) throw new Error(\"Can't start the render worker: the MCP's entry script is unknown.\");\n const child = spawn(process.execPath, [...process.execArgv, entry, \"--render-worker\"], {\n detached: true,\n stdio: \"ignore\",\n windowsHide: true,\n env: process.env,\n });\n child.unref();\n};\n\n/** The worker's main loop; resolves when there's nothing left to render. */\nexport const runWorker = async (): Promise<void> => {\n fs.mkdirSync(path.join(codevideoHome(), \"jobs\"), { recursive: true });\n while (acquireWorkerLock(process.pid)) {\n log(\"worker started\");\n const heartbeat = setInterval(() => quietly(\"heartbeat\", () => workerHeartbeat(process.pid)), HEARTBEAT_MS);\n try {\n for (let job = claimNextJob(); job; job = claimNextJob()) {\n await runJob(job);\n }\n } finally {\n clearInterval(heartbeat);\n releaseWorkerLock(process.pid);\n }\n // A job queued while this worker was finishing saw it as alive and didn't\n // start another one, so look again before exiting.\n if (!hasQueuedJobs()) break;\n }\n log(\"worker exiting: queue empty\");\n};\n\nconst sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\n/** Whether any process is left in a process group (Unix). */\nconst groupAlive = (pgid: number): boolean => {\n try {\n process.kill(-pgid, 0);\n return true;\n } catch (error) {\n return (error as NodeJS.ErrnoException).code === \"EPERM\";\n }\n};\n\n/**\n * Stops the CLI and everything it started (node, Chrome, ffmpeg), and resolves\n * once they're gone. On Unix the CLI leads its own process group. It gets\n * SIGINT, not SIGTERM: the recorder's Puppeteer answers SIGTERM by closing\n * Chrome and staying alive, but answers SIGINT by closing Chrome and exiting.\n * Anything still in the group after the grace period gets SIGKILL.\n */\nconst stopTree = async (child: ChildProcess): Promise<void> => {\n if (!child.pid) return;\n if (process.platform === \"win32\") {\n spawnSync(\"taskkill\", [\"/PID\", String(child.pid), \"/T\", \"/F\"], { windowsHide: true });\n return;\n }\n const group = child.pid;\n const signal = (name: NodeJS.Signals) => {\n try {\n process.kill(-group, name);\n } catch {\n // Already gone.\n }\n };\n signal(\"SIGINT\");\n for (const deadline = Date.now() + KILL_GRACE_MS; groupAlive(group) && Date.now() < deadline; ) await sleep(200);\n if (groupAlive(group)) {\n log(`process group ${group} outlived SIGINT by ${KILL_GRACE_MS / 1000}s; killing it`);\n signal(\"SIGKILL\");\n }\n};\n\n/** Feeds a stream's text to onLine one complete line at a time, also copying it to a file. */\nconst followLines = (stream: NodeJS.ReadableStream, file: fs.WriteStream, onLine: (line: string) => void): void => {\n let pending = \"\";\n stream.on(\"data\", (chunk: Buffer) => {\n file.write(chunk);\n pending += chunk.toString(\"utf8\");\n const lines = pending.split(\"\\n\");\n pending = lines.pop() ?? \"\";\n lines.forEach((line) => onLine(line.replace(/\\r$/, \"\")));\n });\n stream.on(\"end\", () => {\n if (pending) onLine(pending);\n file.end();\n });\n};\n\nconst runJob = async (job: RenderJob): Promise<void> => {\n const dir = jobDir(job.id);\n try {\n const runtime = getCodeVideoCliRuntime();\n const configPath = path.join(dir, \"config.json\");\n const args = cliArgs({\n projectJson: fs.readFileSync(path.join(dir, \"project.json\"), \"utf8\"),\n outputPath: job.outputPath,\n configPath: fs.existsSync(configPath) ? configPath : undefined,\n options: job.options,\n open: job.open,\n });\n fs.mkdirSync(path.dirname(job.outputPath), { recursive: true });\n log(`job ${job.id} (${job.label}) starting`);\n\n // PATH_TO_CODEVIDEO_CLI may name a Node script (a wrapper, or the tests'\n // fake CLI); run those with this Node, since Windows can't execute them.\n const [command, commandArgs] = /\\.[cm]?js$/i.test(runtime.binaryPath)\n ? [process.execPath, [runtime.binaryPath, ...args]]\n : [runtime.binaryPath, args];\n const child = spawn(command, commandArgs, {\n cwd: dir,\n env: cliEnv(runtime.env, job.options),\n // Its own process group on Unix, so a cancel can stop Chrome too.\n detached: process.platform !== \"win32\",\n windowsHide: true,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n if (child.pid) setJobPid(job.id, child.pid);\n\n let lastError: string | null = null;\n let lastProgress: string | null = null;\n const onLine = (line: string): void => {\n const parsed = parseCliLine(line);\n if (parsed.kind === \"progress\" && parsed.text !== lastProgress) {\n lastProgress = parsed.text;\n quietly(\"progress update\", () => setJobProgress(job.id, parsed.text));\n } else if (parsed.kind === \"error\") {\n lastError = parsed.text;\n }\n };\n followLines(child.stdout!, fs.createWriteStream(path.join(dir, \"stdout.log\")), onLine);\n followLines(child.stderr!, fs.createWriteStream(path.join(dir, \"stderr.log\")), onLine);\n\n let stopping: Promise<void> | undefined;\n const cancelPoll = setInterval(() => quietly(\"cancel check\", () => {\n if (!stopping && isCancelRequested(job.id)) {\n log(`job ${job.id} cancelling`);\n stopping = stopTree(child);\n }\n }), CANCEL_POLL_MS);\n\n const exit = await new Promise<{ code: number | null; signal: NodeJS.Signals | null; spawnError?: Error }>((resolve) => {\n child.once(\"error\", (spawnError) => resolve({ code: null, signal: null, spawnError }));\n child.once(\"close\", (code, signal) => resolve({ code, signal }));\n });\n clearInterval(cancelPoll);\n\n if (stopping) {\n // Settle only once nothing from this job is left running. Any file at\n // the output path is a partial video: the path is new for each job.\n await stopping;\n fs.rmSync(job.outputPath, { force: true });\n finishJob(job.id, \"cancelled\", null);\n } else if (exit.spawnError) {\n finishJob(job.id, \"failed\", `Couldn't start codevideo-cli: ${exit.spawnError.message}`);\n } else if (exit.code === 0 && fs.existsSync(job.outputPath) && fs.statSync(job.outputPath).size > 0) {\n finishJob(job.id, \"succeeded\", null);\n } else {\n const how = exit.code === 0 ? \"exited without writing the video\" : exit.code !== null ? `exited with code ${exit.code}` : `was stopped by ${exit.signal}`;\n finishJob(job.id, \"failed\", lastError ?? `codevideo-cli ${how}; see ${path.join(dir, \"stderr.log\")}`);\n }\n log(`job ${job.id} finished`);\n } catch (error) {\n finishJob(job.id, \"failed\", (error as Error).message);\n log(`job ${job.id} failed in the worker: ${(error as Error).stack ?? error}`);\n }\n};\n", "import {\n resolveCodeVideoCliRuntime,\n type CodeVideoCliRuntime,\n} from \"@fullstackcraftllc/codevideo-cli\";\n\nexport function getCodeVideoCliRuntime(): CodeVideoCliRuntime {\n return resolveCodeVideoCliRuntime();\n}\n\nexport function getBinaryPath(): string {\n return getCodeVideoCliRuntime().binaryPath;\n}\n", "import { IAction } from \"@fullstackcraftllc/codevideo-types\";\nimport { getCurrentActionsOrThrow } from \"./get_current_actions\";\nimport { RenderOptions } from \"../render/options\";\nimport { RenderJob } from \"../render/jobs\";\nimport { estimatedSeconds, newVideoPath, queueRenders } from \"../render/queue\";\n\nexport interface VideoResult {\n outputPath: string;\n stdout: string;\n stderr: string;\n error?: any;\n jobId?: string;\n}\n\n/** Queues a video of actions (given, or the stored current actions) for the background render worker. */\nexport const queueVideoFromActions = (\n actions?: IAction[],\n options: RenderOptions = {},\n open = true\n): { job: RenderJob; estimatedSeconds: number } => {\n const actionsToUse = actions || getCurrentActionsOrThrow();\n const [job] = queueRenders([{\n kind: \"actions\",\n label: `${actionsToUse.length} actions`,\n project: actionsToUse,\n outputPath: newVideoPath(),\n options,\n open,\n defaultLanguage: options.defaultLanguage ?? \"typescript\",\n }]);\n return { job, estimatedSeconds: estimatedSeconds(actionsToUse, options) };\n};\n\n// Kept for the REST server, which reports results in this shape.\nexport const makeVideoFromActions = async (actions?: IAction[], options: RenderOptions = {}, open = true): Promise<VideoResult> => {\n try {\n const { job } = queueVideoFromActions(actions, options, open);\n return {\n outputPath: job.outputPath,\n jobId: job.id,\n stdout: `Video queued as render job ${job.id}; it renders in the background to ${job.outputPath}.`,\n stderr: \"\",\n };\n } catch (error: any) {\n return { outputPath: \"\", stdout: \"\", stderr: `Failed to queue the video: ${error.message}`, error };\n }\n};\n", "import { ILesson } from \"@fullstackcraftllc/codevideo-types\";\nimport { getCurrentLessonOrThrow } from \"./get_current_lesson\";\nimport { RenderOptions } from \"../render/options\";\nimport { RenderJob } from \"../render/jobs\";\nimport { estimatedSeconds, newVideoPath, queueRenders } from \"../render/queue\";\nimport type { VideoResult } from \"./make_video_from_actions\";\n\n/** Queues a video of a lesson (given, or the stored current lesson) for the background render worker. */\nexport const queueVideoFromLesson = (\n lesson?: ILesson,\n options: RenderOptions = {},\n open = true\n): { job: RenderJob; estimatedSeconds: number } => {\n const lessonToUse = lesson || getCurrentLessonOrThrow();\n const [job] = queueRenders([{\n kind: \"lesson\",\n label: lessonToUse.name || \"lesson\",\n project: lessonToUse,\n outputPath: newVideoPath(lessonToUse.name),\n options,\n open,\n defaultLanguage: options.defaultLanguage ?? \"typescript\",\n }]);\n return { job, estimatedSeconds: estimatedSeconds(lessonToUse.actions, options) };\n};\n\n// Kept for the REST server, which reports results in this shape.\nexport const makeVideoFromLesson = async (lesson?: ILesson, options: RenderOptions = {}, open = true): Promise<VideoResult> => {\n try {\n const { job } = queueVideoFromLesson(lesson, options, open);\n return {\n outputPath: job.outputPath,\n jobId: job.id,\n stdout: `Video queued as render job ${job.id}; it renders in the background to ${job.outputPath}.`,\n stderr: \"\",\n };\n } catch (error: any) {\n return { outputPath: \"\", stdout: \"\", stderr: `Failed to queue the video: ${error.message}`, error };\n }\n};\n", "import { IAction, isValidAction } from \"@fullstackcraftllc/codevideo-types\";\nimport { getCurrentActionsOrThrow } from \"./get_current_actions\";\nimport { describeRejected, rejectedActionsIn, valueHint } from \"../validation/rejectedActions\";\n\n/**\n * Validates the actions so the LLM can correct any mistakes it makes\n *\n */\nexport const validateActions = (actions?: Array<IAction>): string => {\n // Use provided actions or retrieve from storage\n const actionsToUse = actions || getCurrentActionsOrThrow();\n\n // loop over all actions and call the isValidAction function from the types package\n const errorMessages: string[] = [];\n for (var i = 0; i < actionsToUse.length; i++) {\n const action = actionsToUse[i];\n const hint = valueHint(action.name);\n // codevideo-types package should cover all action types - normal and repeatable!\n if (!isValidAction(action)) {\n errorMessages.push(hint\n ? `Invalid action at action index ${i}: ${JSON.stringify(action)}. Its value should be: ${hint}`\n : `Invalid action at action index ${i}: ${JSON.stringify(action)}. Unknown action name; codevideo_get_action_names lists them all.`);\n }\n }\n\n // Valid actions that lessons still can't use, with what to do instead.\n const rejected = rejectedActionsIn(actionsToUse);\n if (rejected.length > 0) {\n errorMessages.push(`Actions lessons can't use: ${describeRejected(rejected)}.`);\n }\n\n if (errorMessages.length > 0) {\n return JSON.stringify({ isError: true, errorMessages });\n }\n\n return JSON.stringify({ isError: false, errorMessages: [] });\n}\n", "import { AllActionDescriptions, IAction } from \"@fullstackcraftllc/codevideo-types\";\n\n/**\n * Actions lessons can't use, each with how to do the same thing instead. The\n * file-explorer ones change files with nothing on screen causing it, so in the\n * video things just appear; editor-highlight-code and the composites aren't\n * implemented yet, so the IDE and the renderer skip them and nothing happens.\n */\nconst REJECTED_ACTIONS = new Map<string, string>([\n [\"file-explorer-create-file\", \"make the file with the mouse (right-click, New File) or touch in the terminal\"],\n [\"file-explorer-create-folder\", \"make the folder with the mouse (right-click, New Folder) or mkdir in the terminal\"],\n [\"file-explorer-set-file-contents\", \"type the contents with editor-type\"],\n [\"editor-highlight-code\", \"not supported yet; to point at code, bring its file forward with mouse-move-editor-tab and talk about it\"],\n [\"composite-create-file-with-mouse\", \"not supported yet; make the file with the mouse steps (right-click, New File)\"],\n [\"composite-create-file-with-terminal\", \"not supported yet; type touch <path> with terminal-type, then terminal-enter\"],\n [\"composite-create-folder-with-mouse\", \"not supported yet; make the folder with the mouse steps (right-click, New Folder)\"],\n [\"composite-create-folder-with-terminal\", \"not supported yet; type mkdir <path> with terminal-type, then terminal-enter\"],\n [\"composite-close-all-editors-with-mouse\", \"not supported yet; close editors one at a time with mouse-move-editor-tab-close and mouse-left-click\"],\n [\"composite-close-all-editors-with-keyboard\", \"not supported yet; close editors one at a time with mouse-move-editor-tab-close and mouse-left-click\"],\n]);\n\nexport interface RejectedAction {\n index: number;\n name: string;\n}\n\n/** The actions in a lesson that lessons can't use, with their positions. */\nexport const rejectedActionsIn = (actions: IAction[]): RejectedAction[] =>\n actions.flatMap((action, index) => (REJECTED_ACTIONS.has(action.name) ? [{ index, name: action.name }] : []));\n\n/** \"action 3 is file-explorer-create-file: make the file with ...\" for each one. */\nexport const describeRejected = (rejected: RejectedAction[]): string =>\n rejected.map((action) => `action ${action.index} is ${action.name}: ${REJECTED_ACTIONS.get(action.name)}`).join(\"; \");\n\n/** What an action's value should be, from codevideo-types, for error messages. */\nexport const valueHint = (name: string): string | undefined =>\n AllActionDescriptions.find((action) => action.name === name)?.valueDescription;\n", "import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { IAction, isValidAction } from \"@fullstackcraftllc/codevideo-types\";\nimport {\n ActionEdit,\n addLesson,\n createCourse,\n editLessonActions,\n exportCourse,\n fileEntriesFromSnapshot,\n getCourse,\n getLesson,\n getLessonActions,\n getLessonFile,\n getLessonFileTree,\n getLessonFiles,\n listLessons,\n setLessonActions,\n} from \"../store/courseStore\";\nimport { codevideoHome } from \"../store/db\";\nimport { estimatedSeconds } from \"../render/queue\";\nimport { compileFiles, ICompileCheckResult } from \"./compileCheck\";\nimport { describeRejected, RejectedAction, rejectedActionsIn, valueHint } from \"../validation/rejectedActions\";\n\n// All tool functions return a JSON string for the MCP `text` content block.\nconst ok = (data: unknown): string => JSON.stringify(data, null, 2);\n\n// ---------- course / lesson lifecycle ----------\n\nexport const createCourseTool = (input: { name: string; description?: string; primaryLanguage?: string; id?: string }): string => {\n const courseId = createCourse(input);\n return ok({ courseId, message: `Created course \"${input.name}\". Add lessons with codevideo_add_lesson(courseId).` });\n};\n\n/** Files a lesson created or changed, by path, and whether each is open at its end. */\nconst filesChangedBy = (lesson: ReturnType<typeof listLessons>[number]) => {\n const before = new Map(fileEntriesFromSnapshot(lesson.initialSnapshot).map((f) => [f.path, f.content]));\n const open = new Set((lesson.finalSnapshot.editorSnapshot?.editors ?? []).map((e) => e.filename));\n return fileEntriesFromSnapshot(lesson.finalSnapshot)\n .filter((f) => before.get(f.path) !== f.content)\n .slice(0, 10)\n .map((f) => {\n const path = f.path.replace(/^\\/+/, \"\");\n return { path, change: before.has(f.path) ? \"changed\" : \"created\", open: open.has(path) };\n });\n};\n\nexport const addLessonTool = (\n courseId: string,\n input: { name: string; description?: string; mustCompile?: boolean; continuesFromPrevious?: boolean }\n): string => {\n const lessonId = addLesson(courseId, input);\n const lessons = listLessons(courseId);\n const index = lessons.findIndex((l) => l.id === lessonId);\n const lesson = lessons[index];\n const previous = index > 0 ? lessons[index - 1] : undefined;\n return ok({\n lessonId,\n continuesFromPrevious: lesson.continuesFromPrevious,\n mustCompile: lesson.mustCompile,\n // What the opening recap is written from, without loading that lesson.\n ...(previous ? { previousLesson: { name: previous.name, description: previous.description, files: filesChangedBy(previous) } } : {}),\n message:\n `Added lesson \"${input.name}\". ` +\n (lesson.continuesFromPrevious\n ? \"Its initial state is the previous lesson's final state (chained server-side).\"\n : \"Its initial state is empty (from scratch).\") +\n \" Author its actions, then call codevideo_set_lesson_actions(courseId, lessonId, actions).\",\n });\n};\n\n/**\n * Flags actions lessons can't use as soon as they're stored, so the model can\n * swap just those with codevideo_edit_lesson_actions instead of finding out\n * at validation and resending the lesson.\n */\nconst problemsWith = (actions: IAction[]): { problems?: string } => {\n const rejected = rejectedActionsIn(actions);\n return rejected.length === 0\n ? {}\n : { problems: `Stored, but codevideo_validate_course rejects this lesson until these are replaced: ${describeRejected(rejected)}. codevideo_edit_lesson_actions can swap just those actions.` };\n};\n\nexport const setLessonActionsTool = (courseId: string, lessonId: string, actions: IAction[]): string => {\n const { actionCount } = setLessonActions(courseId, lessonId, actions);\n return ok({\n ok: true,\n actionCount,\n message: `Stored ${actionCount} actions. Final snapshot recomputed server-side. Use codevideo_compile_lesson to check it builds.`,\n ...problemsWith(actions),\n });\n};\n\nexport const editLessonActionsTool = (courseId: string, lessonId: string, edits: ActionEdit[]): string => {\n const { actionCount } = editLessonActions(courseId, lessonId, edits);\n return ok({\n ok: true,\n actionCount,\n message: `Applied ${edits.length} edit(s); the lesson now has ${actionCount} actions. Snapshots recomputed server-side.`,\n ...problemsWith(getLesson(courseId, lessonId).actions),\n });\n};\n\n// ---------- context-lean reads (never dump the whole snapshot) ----------\n\nexport const getLessonActionsTool = (courseId: string, lessonId: string, start?: number, limit?: number): string =>\n ok({ courseId, lessonId, ...getLessonActions(courseId, lessonId, start, limit) });\n\nexport const getLessonFileTreeTool = (courseId: string, lessonId: string): string =>\n ok({ courseId, lessonId, files: getLessonFileTree(courseId, lessonId) });\n\nexport const getLessonFileTool = (courseId: string, lessonId: string, filePath: string): string => {\n const content = getLessonFile(courseId, lessonId, filePath);\n if (content === null) {\n return ok({ courseId, lessonId, path: filePath, found: false, message: \"No such file at the end of this lesson.\" });\n }\n return ok({ courseId, lessonId, path: filePath, found: true, content });\n};\n\n// ---------- compile + validate ----------\n\nexport const compileLessonTool = async (courseId: string, lessonId: string): Promise<string> => {\n const files = getLessonFiles(courseId, lessonId);\n const result = await compileFiles(files);\n return ok({ courseId, lessonId, fileCount: files.length, ...result });\n};\n\ninterface LessonValidation {\n lessonId: string;\n name: string;\n index: number;\n actionsValid: boolean;\n hasActions: boolean;\n invalidActionIndexes: number[];\n rejectedActions: RejectedAction[];\n opensWithSpeech: boolean;\n snapshotContinuous: boolean;\n mustCompile: boolean;\n compileStatus: ICompileCheckResult[\"status\"] | \"skipped\";\n compileErrors: ICompileCheckResult[\"projects\"];\n}\n\nexport interface CourseValidation {\n courseId: string;\n done: boolean;\n lessonCount: number;\n lessons: LessonValidation[];\n reasons: string[];\n}\n\nexport const validateCourse = async (courseId: string): Promise<CourseValidation> => {\n const lessons = listLessons(courseId);\n const results: LessonValidation[] = [];\n\n for (let i = 0; i < lessons.length; i++) {\n const lesson = lessons[i];\n\n const invalidActionIndexes: number[] = [];\n lesson.actions.forEach((a, idx) => {\n if (!isValidAction(a)) invalidActionIndexes.push(idx);\n });\n const rejectedActions = rejectedActionsIn(lesson.actions);\n // An empty lesson already fails for having no actions.\n const opensWithSpeech = lesson.actions.length === 0 || lesson.actions[0].name === \"author-speak-before\";\n\n // continuity: a continuing lesson's initial state must equal the previous lesson's final state.\n // (The store sets these equal by construction; the check still catches externally-imported lessons.)\n let snapshotContinuous = true;\n if (i > 0 && lesson.continuesFromPrevious) {\n snapshotContinuous = JSON.stringify(lesson.initialSnapshot) === JSON.stringify(lessons[i - 1].finalSnapshot);\n }\n\n let compileStatus: LessonValidation[\"compileStatus\"] = \"skipped\";\n let compileErrors: ICompileCheckResult[\"projects\"] = [];\n if (lesson.mustCompile) {\n const c = await compileFiles(getLessonFiles(courseId, lesson.id));\n compileStatus = c.status;\n compileErrors = c.projects.filter((p) => p.errors.length > 0);\n }\n\n results.push({\n lessonId: lesson.id,\n name: lesson.name,\n index: i,\n actionsValid: invalidActionIndexes.length === 0,\n hasActions: lesson.actions.length > 0,\n invalidActionIndexes,\n rejectedActions,\n opensWithSpeech,\n snapshotContinuous,\n mustCompile: lesson.mustCompile,\n compileStatus,\n compileErrors,\n });\n }\n\n const allActionsValid = results.every((r) => r.actionsValid);\n const allAllowed = results.every((r) => r.rejectedActions.length === 0);\n const allOpenWithSpeech = results.every((r) => r.opensWithSpeech);\n const allHaveActions = results.every((r) => r.hasActions);\n const allContinuous = results.every((r) => r.snapshotContinuous);\n const compileEnforced = results.filter((r) => r.mustCompile);\n const allMustCompileClean = compileEnforced.every((r) => r.compileStatus === \"ok\");\n const finalLesson = results[results.length - 1];\n const finalCompilesClean = !!finalLesson && finalLesson.mustCompile && finalLesson.compileStatus === \"ok\";\n\n const done = lessons.length > 0 && allHaveActions && allActionsValid && allAllowed && allOpenWithSpeech && allContinuous && allMustCompileClean && finalCompilesClean;\n\n const reasons: string[] = [];\n if (lessons.length === 0) reasons.push(\"Course has no lessons.\");\n for (const lesson of results.filter((result) => !result.hasActions)) {\n reasons.push(`Lesson \"${lesson.name}\" (${lesson.lessonId}) has no actions.`);\n }\n results.forEach((result, i) => {\n if (result.invalidActionIndexes.length === 0) return;\n const details = result.invalidActionIndexes.map((index) => {\n const action = lessons[i].actions[index];\n const hint = valueHint(action.name);\n return hint\n ? `action ${index} is ${action.name} ${JSON.stringify(action.value)}, but its value should be: ${hint}`\n : `action ${index} has an unknown name, ${JSON.stringify(action.name)}`;\n });\n reasons.push(`Lesson \"${result.name}\" (${result.lessonId}) has invalid actions: ${details.join(\"; \")}.`);\n });\n for (const result of results.filter((r) => !r.opensWithSpeech)) {\n reasons.push(`Lesson \"${result.name}\" (${result.lessonId}) must open with author-speak-before: frame the lesson, and after the first lesson start by recapping the previous one.`);\n }\n for (const result of results.filter((r) => r.rejectedActions.length > 0)) {\n reasons.push(`Lesson \"${result.name}\" (${result.lessonId}) uses actions lessons can't use; ${describeRejected(result.rejectedActions)}.`);\n }\n if (!allContinuous) reasons.push(\"Some lessons do not follow cleanly from the previous lesson (snapshot discontinuity).\");\n if (!allMustCompileClean) reasons.push(\"Some lessons marked mustCompile do not compile cleanly.\");\n if (finalLesson && !finalLesson.mustCompile) reasons.push(\"The final lesson must be marked mustCompile (all code should work by the end).\");\n else if (!finalCompilesClean) reasons.push(\"The final lesson does not compile cleanly.\");\n\n return { courseId, done, lessonCount: lessons.length, lessons: results, reasons };\n};\n\nexport const validateCourseTool = async (courseId: string): Promise<string> => ok(await validateCourse(courseId));\n\n// ---------- status + export ----------\n\nexport const getCourseStatusTool = (courseId: string): string => {\n const course = getCourse(courseId);\n return ok({\n courseId: course.id,\n name: course.name,\n primaryLanguage: course.primaryLanguage,\n lessonCount: course.lessons.length,\n lessons: course.lessons.map((l, i) => ({\n index: i,\n lessonId: l.id,\n name: l.name,\n actionCount: l.actions.length,\n // At the render page's default pauses, with narration estimated from word counts.\n estimatedSeconds: estimatedSeconds(l.actions),\n mustCompile: l.mustCompile,\n continuesFromPrevious: l.continuesFromPrevious,\n hasActions: l.actions.length > 0,\n })),\n });\n};\n\n/** Writes the whole course (snapshots included) to a file and replies with only its path. */\nexport const exportCourseTool = (courseId: string): string => {\n const course = exportCourse(courseId);\n const dir = path.join(codevideoHome(), \"exports\");\n fs.mkdirSync(dir, { recursive: true });\n const file = path.join(dir, `${courseId.replace(/[^a-z0-9-_]+/gi, \"_\")}.json`);\n const json = JSON.stringify(course, null, 2);\n fs.writeFileSync(file, json);\n return ok({\n courseId,\n path: file,\n lessonCount: course.lessons.length,\n bytes: Buffer.byteLength(json),\n message: \"Exported to a file. Open or pass that path instead of reading the course into context.\",\n });\n};\n", "import { randomUUID } from 'node:crypto';\nimport { IAction, ICourse, ICourseSnapshot, IFileEntry, ILesson } from \"@fullstackcraftllc/codevideo-types\";\nimport { VirtualIDE } from \"@fullstackcraftllc/codevideo-virtual-ide\";\nimport { emptyCourseSnapshot } from \"../utils/emptyCourseSnapshot\";\nimport { Db, transaction, withDatabase } from \"./db\";\n\n/**\n * Server-side course store for the CodeVideo MCP.\n *\n * The whole point of this module: snapshots and full course/lesson objects live HERE,\n * inside the MCP process (a local SQLite file), keyed by id. The driving LLM only ever\n * sees actions, compile errors, and small targeted reads \u2014 never the multi-KB snapshots\n * that used to round-trip through its context and make full-course generation flakey.\n *\n * Snapshots are derived state: a lesson's finalSnapshot is recomputed from its\n * initialSnapshot + actions via the VirtualIDE, and \"continuing\" lessons chain off the\n * previous lesson's finalSnapshot. All of that happens server-side.\n */\n\n// ---------- types ----------\n\nexport interface StoredLesson {\n id: string;\n courseId: string;\n name: string;\n description: string;\n /** Whether this lesson is expected to compile cleanly (false for slideshow/discussion/half-refactor lessons). */\n mustCompile: boolean;\n /** Whether the initialSnapshot is derived from the previous lesson's finalSnapshot. */\n continuesFromPrevious: boolean;\n actions: IAction[];\n initialSnapshot: ICourseSnapshot;\n finalSnapshot: ICourseSnapshot;\n}\n\nexport interface StoredCourse {\n id: string;\n name: string;\n description: string;\n primaryLanguage: string;\n lessons: StoredLesson[];\n}\n\n// ---------- db plumbing ----------\n\nconst COURSE_SCHEMA = `\n CREATE TABLE IF NOT EXISTS courses (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n description TEXT NOT NULL DEFAULT '',\n primary_language TEXT NOT NULL DEFAULT '',\n lesson_order TEXT NOT NULL DEFAULT '[]',\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP\n );\n CREATE TABLE IF NOT EXISTS lessons (\n id TEXT PRIMARY KEY,\n course_id TEXT NOT NULL,\n name TEXT NOT NULL,\n description TEXT NOT NULL DEFAULT '',\n must_compile INTEGER NOT NULL DEFAULT 1,\n continues_from_previous INTEGER NOT NULL DEFAULT 1,\n actions TEXT NOT NULL DEFAULT '[]',\n initial_snapshot TEXT NOT NULL,\n final_snapshot TEXT NOT NULL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP\n );\n CREATE INDEX IF NOT EXISTS idx_lessons_course ON lessons(course_id);\n`;\n\n/** Run a function with an open db connection, always closing it afterwards. */\nconst withDb = <T>(fn: (db: Db) => T): T =>\n withDatabase((db) => {\n db.exec(COURSE_SCHEMA);\n return fn(db);\n });\n\n/** node:sqlite types rows loosely; this names the row shape a query returns. */\nconst asRow = <T>(row: unknown): T | undefined => row as T | undefined;\n\n// ---------- snapshot helpers (derive state via the VirtualIDE) ----------\n\nconst replay = (initial: ICourseSnapshot, actions: IAction[]): ICourseSnapshot => {\n const virtualIDE = new VirtualIDE();\n virtualIDE.applyCourseSnapshot(initial);\n if (actions.length > 0) {\n virtualIDE.applyActions(actions);\n }\n return virtualIDE.getCourseSnapshot();\n};\n\nconst replayBreaks = (initial: ICourseSnapshot, actions: IAction[]): boolean => {\n try {\n replay(initial, actions);\n return false;\n } catch {\n return true;\n }\n};\n\n/**\n * A replay error that names the action responsible. The VirtualIDE can throw\n * long after the bad action (e.g. clicking a tab by name instead of path only\n * fails when the snapshot is taken), so this bisects for the shortest prefix\n * of the actions that breaks.\n */\nconst replayError = (initial: ICourseSnapshot, actions: IAction[], error: Error): Error => {\n if (replayBreaks(initial, [])) return new Error(`The lesson's starting state can't be loaded: ${error.message}`);\n let works = 0;\n let breaks = actions.length;\n while (breaks - works > 1) {\n const mid = (works + breaks) >> 1;\n if (replayBreaks(initial, actions.slice(0, mid))) breaks = mid;\n else works = mid;\n }\n const index = breaks - 1;\n const action = actions[index];\n const pathHint = `names something that exists at that point, by path (e.g. \"src/index.ts\")`;\n const moveIndex = /click/.test(action.name) ? actions.slice(0, index).map((a) => a.name.startsWith(\"mouse-move\")).lastIndexOf(true) : -1;\n if (moveIndex >= 0) {\n const move = actions[moveIndex];\n return new Error(\n `Action ${index} (${action.name}) clicks what action ${moveIndex} (${move.name} ${JSON.stringify(move.value)}) points at, and that breaks the replay: ${error.message}. ` +\n `Check that action ${moveIndex}'s value ${pathHint}.`\n );\n }\n return new Error(`Action ${index} (${action.name} ${JSON.stringify(action.value)}) breaks the replay: ${error.message}. Check that its value ${pathHint}.`);\n};\n\n/** Replay `actions` on top of `initial` and return the resulting course snapshot. */\nexport const snapshotFromActions = (initial: ICourseSnapshot, actions: IAction[]): ICourseSnapshot => {\n try {\n return replay(initial, actions);\n } catch (error) {\n throw replayError(initial, actions, error as Error);\n }\n};\n\n/** Materialize a snapshot into its file paths + contents (used for compile + file reads). */\nexport const fileEntriesFromSnapshot = (snapshot: ICourseSnapshot): IFileEntry[] => {\n const virtualIDE = new VirtualIDE();\n virtualIDE.applyCourseSnapshot(snapshot);\n return virtualIDE.getFullFilePathsAndContents();\n};\n\n// ---------- row <-> object mapping ----------\n\ninterface LessonRow {\n id: string;\n course_id: string;\n name: string;\n description: string;\n must_compile: number;\n continues_from_previous: number;\n actions: string;\n initial_snapshot: string;\n final_snapshot: string;\n}\n\nconst rowToLesson = (row: LessonRow): StoredLesson => ({\n id: row.id,\n courseId: row.course_id,\n name: row.name,\n description: row.description,\n mustCompile: row.must_compile === 1,\n continuesFromPrevious: row.continues_from_previous === 1,\n actions: JSON.parse(row.actions),\n initialSnapshot: JSON.parse(row.initial_snapshot),\n finalSnapshot: JSON.parse(row.final_snapshot),\n});\n\nconst getLessonOrder = (db: Db, courseId: string): string[] => {\n const row = asRow<{ lesson_order: string }>(db.prepare(`SELECT lesson_order FROM courses WHERE id = ?`).get(courseId));\n if (!row) throw new Error(`Course not found: ${courseId}`);\n return JSON.parse(row.lesson_order);\n};\n\nconst getLessonsOrdered = (db: Db, courseId: string): StoredLesson[] => {\n const order = getLessonOrder(db, courseId);\n const get = db.prepare(`SELECT * FROM lessons WHERE id = ?`);\n return order\n .map((id) => asRow<LessonRow>(get.get(id)))\n .filter((r): r is LessonRow => !!r)\n .map(rowToLesson);\n};\n\n/**\n * Recompute the whole snapshot chain for a course. Non-continuing lessons keep their own\n * stored initialSnapshot; continuing lessons take the previous lesson's recomputed\n * finalSnapshot as their initial. Each lesson's finalSnapshot = initial + its actions.\n * Cheap (local VirtualIDE replay) and keeps continuity correct after any edit.\n *\n * Callers run it inside the same transaction as the edit that made it necessary, so a\n * replay that throws rolls the edit back too: actions and snapshots never disagree.\n */\nconst recomputeChain = (db: Db, courseId: string): void => {\n const order = getLessonOrder(db, courseId);\n const getRow = db.prepare(`SELECT * FROM lessons WHERE id = ?`);\n const update = db.prepare(`UPDATE lessons SET initial_snapshot = ?, final_snapshot = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?`);\n let previousFinal: ICourseSnapshot | null = null;\n for (const lessonId of order) {\n const row = asRow<LessonRow>(getRow.get(lessonId));\n if (!row) continue;\n const lesson = rowToLesson(row);\n const initial = lesson.continuesFromPrevious && previousFinal ? previousFinal : lesson.initialSnapshot;\n let final: ICourseSnapshot;\n try {\n final = snapshotFromActions(initial, lesson.actions);\n } catch (error) {\n throw new Error(`Lesson \"${lesson.name}\": ${(error as Error).message}`);\n }\n update.run(JSON.stringify(initial), JSON.stringify(final), lessonId);\n previousFinal = final;\n }\n};\n\n// ---------- public API ----------\n\nexport const courseExists = (courseId: string): boolean =>\n withDb((db) => !!db.prepare(`SELECT 1 FROM courses WHERE id = ?`).get(courseId));\n\nexport const createCourse = (input: { name: string; description?: string; primaryLanguage?: string; id?: string }): string =>\n withDb((db) => {\n const id = input.id && input.id.trim().length > 0 ? input.id.trim() : randomUUID();\n db.prepare(`\n INSERT INTO courses (id, name, description, primary_language, lesson_order)\n VALUES (?, ?, ?, ?, '[]')\n `).run(id, input.name, input.description ?? '', input.primaryLanguage ?? '');\n return id;\n });\n\nexport const addLesson = (\n courseId: string,\n input: { name: string; description?: string; mustCompile?: boolean; continuesFromPrevious?: boolean; initialSnapshot?: ICourseSnapshot }\n): string =>\n withDb((db) => {\n const order = getLessonOrder(db, courseId);\n const continuesFromPrevious = input.continuesFromPrevious ?? order.length > 0;\n\n // determine the starting snapshot\n let initial: ICourseSnapshot;\n if (continuesFromPrevious && order.length > 0) {\n const prev = asRow<{ final_snapshot: string }>(db.prepare(`SELECT final_snapshot FROM lessons WHERE id = ?`).get(order[order.length - 1]));\n initial = prev ? JSON.parse(prev.final_snapshot) : emptyCourseSnapshot();\n } else {\n initial = input.initialSnapshot ?? emptyCourseSnapshot();\n }\n\n const lessonId = randomUUID();\n transaction(db, () => {\n db.prepare(`\n INSERT INTO lessons (id, course_id, name, description, must_compile, continues_from_previous, actions, initial_snapshot, final_snapshot)\n VALUES (?, ?, ?, ?, ?, ?, '[]', ?, ?)\n `).run(\n lessonId,\n courseId,\n input.name,\n input.description ?? '',\n (input.mustCompile ?? true) ? 1 : 0,\n continuesFromPrevious ? 1 : 0,\n JSON.stringify(initial),\n JSON.stringify(initial) // no actions yet => final == initial\n );\n order.push(lessonId);\n db.prepare(`UPDATE courses SET lesson_order = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?`).run(JSON.stringify(order), courseId);\n });\n return lessonId;\n });\n\n/** Replaces a lesson's actions and replays the snapshot chain, all or nothing. */\nexport const setLessonActions = (courseId: string, lessonId: string, actions: IAction[]): { actionCount: number } =>\n withDb((db) =>\n transaction(db, () => {\n const exists = db.prepare(`SELECT 1 FROM lessons WHERE id = ? AND course_id = ?`).get(lessonId, courseId);\n if (!exists) throw new Error(`Lesson not found: ${lessonId} in course ${courseId}`);\n db.prepare(`UPDATE lessons SET actions = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?`).run(JSON.stringify(actions), lessonId);\n recomputeChain(db, courseId);\n return { actionCount: actions.length };\n })\n );\n\n/** One splice on a lesson's actions: remove deleteCount actions at start, insert there. */\nexport interface ActionEdit {\n start: number;\n deleteCount: number;\n insert: IAction[];\n}\n\n/**\n * Applies edits in order, as Array.prototype.splice would, then replays the snapshot\n * chain once. Each edit's start refers to the actions as the previous edits left them.\n * All or nothing: a bad range or a failing replay leaves the lesson untouched.\n */\nexport const editLessonActions = (courseId: string, lessonId: string, edits: ActionEdit[]): { actionCount: number } =>\n withDb((db) =>\n transaction(db, () => {\n const row = asRow<{ actions: string }>(db.prepare(`SELECT actions FROM lessons WHERE id = ? AND course_id = ?`).get(lessonId, courseId));\n if (!row) throw new Error(`Lesson not found: ${lessonId} in course ${courseId}`);\n const actions: IAction[] = JSON.parse(row.actions);\n edits.forEach((edit, i) => {\n if (!Number.isInteger(edit.start) || edit.start < 0 || edit.start > actions.length) {\n throw new Error(`Edit ${i}: start ${edit.start} is outside 0..${actions.length}`);\n }\n if (!Number.isInteger(edit.deleteCount) || edit.deleteCount < 0 || edit.start + edit.deleteCount > actions.length) {\n throw new Error(`Edit ${i}: deleting ${edit.deleteCount} from ${edit.start} runs past the last action (${actions.length})`);\n }\n actions.splice(edit.start, edit.deleteCount, ...edit.insert);\n });\n db.prepare(`UPDATE lessons SET actions = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?`).run(JSON.stringify(actions), lessonId);\n recomputeChain(db, courseId);\n return { actionCount: actions.length };\n })\n );\n\n/** A slice of a lesson's actions with their indices, so a model can re-read part of a lesson. */\nexport const getLessonActions = (\n courseId: string,\n lessonId: string,\n start = 0,\n limit = 50\n): { total: number; start: number; actions: { index: number; name: string; value: string }[] } => {\n const lesson = getLesson(courseId, lessonId);\n const from = Math.max(0, Math.min(start, lesson.actions.length));\n return {\n total: lesson.actions.length,\n start: from,\n actions: lesson.actions.slice(from, from + Math.max(0, limit)).map((a, i) => ({ index: from + i, name: a.name, value: a.value })),\n };\n};\n\nexport const getLesson = (courseId: string, lessonId: string): StoredLesson =>\n withDb((db) => {\n const row = asRow<LessonRow>(db.prepare(`SELECT * FROM lessons WHERE id = ? AND course_id = ?`).get(lessonId, courseId));\n if (!row) throw new Error(`Lesson not found: ${lessonId} in course ${courseId}`);\n return rowToLesson(row);\n });\n\nexport const listLessons = (courseId: string): StoredLesson[] => withDb((db) => getLessonsOrdered(db, courseId));\n\nexport const getCourse = (courseId: string): StoredCourse =>\n withDb((db) => {\n const c = asRow<{ id: string; name: string; description: string; primary_language: string }>(\n db.prepare(`SELECT id, name, description, primary_language FROM courses WHERE id = ?`).get(courseId)\n );\n if (!c) throw new Error(`Course not found: ${courseId}`);\n return {\n id: c.id,\n name: c.name,\n description: c.description,\n primaryLanguage: c.primary_language,\n lessons: getLessonsOrdered(db, courseId),\n };\n });\n\n/** Export as a plain ICourse (the shape the CLI / exporters consume). */\nexport const exportCourse = (courseId: string): ICourse => {\n const course = getCourse(courseId);\n const lessons: ILesson[] = course.lessons.map((l) => ({\n id: l.id,\n name: l.name,\n description: l.description,\n actions: l.actions,\n initialSnapshot: l.initialSnapshot,\n finalSnapshot: l.finalSnapshot,\n }));\n return {\n id: course.id,\n name: course.name,\n description: course.description,\n primaryLanguage: course.primaryLanguage,\n lessons,\n } as ICourse;\n};\n\n/** Cheap, context-lean read: just the file paths present at the end of a lesson. */\nexport const getLessonFileTree = (courseId: string, lessonId: string): string[] => {\n const lesson = getLesson(courseId, lessonId);\n return fileEntriesFromSnapshot(lesson.finalSnapshot).map((f) => f.path).sort();\n};\n\n/** Cheap, context-lean read: the contents of a single file at the end of a lesson. */\n/** \"src/a.ts\", \"/src/a.ts\" and \"./src/a.ts\" all name the same file. */\nconst samePath = (a: string, b: string): boolean => a.replace(/^\\.?\\/+/, \"\") === b.replace(/^\\.?\\/+/, \"\");\n\nexport const getLessonFile = (courseId: string, lessonId: string, filePath: string): string | null => {\n const lesson = getLesson(courseId, lessonId);\n const entry = fileEntriesFromSnapshot(lesson.finalSnapshot).find((f) => samePath(f.path, filePath));\n return entry ? entry.content : null;\n};\n\n/** All files at the end of a lesson (used by the compile-check tool). */\nexport const getLessonFiles = (courseId: string, lessonId: string): IFileEntry[] => {\n const lesson = getLesson(courseId, lessonId);\n return fileEntriesFromSnapshot(lesson.finalSnapshot);\n};\n", "import { ICourseSnapshot } from \"@fullstackcraftllc/codevideo-types\";\n\n/**\n * Factory for a fresh, empty CodeVideo course snapshot \u2014 the starting state for a\n * lesson that does NOT continue from a previous lesson (i.e. a from-scratch lesson 1).\n *\n * Single source of truth: both `get_empty_lesson_snapshot` and the course store use this\n * so the empty-state shape can never drift between them.\n */\nexport const emptyCourseSnapshot = (): ICourseSnapshot => ({\n isUnsavedChangesDialogOpen: false,\n unsavedFileName: '',\n fileExplorerSnapshot: {\n isFileExplorerContextMenuOpen: false,\n isFileContextMenuOpen: false,\n isFolderContextMenuOpen: false,\n isNewFileInputVisible: false,\n isNewFolderInputVisible: false,\n isRenameFileInputVisible: false,\n isRenameFolderInputVisible: false,\n newFileInputValue: '',\n newFolderInputValue: '',\n renameFileInputValue: '',\n renameFolderInputValue: '',\n originalFileBeingRenamed: '',\n originalFolderBeingRenamed: '',\n newFileParentPath: '',\n newFolderParentPath: '',\n fileStructure: {},\n },\n editorSnapshot: {\n isEditorContextMenuOpen: false,\n editors: []\n },\n terminalSnapshot: {\n terminals: []\n },\n mouseSnapshot: {\n location: 'editor',\n currentHoveredFileName: '',\n currentHoveredFolderName: '',\n currentHoveredEditorTabFileName: '',\n x: 0,\n y: 0,\n timestamp: 0,\n type: 'move',\n buttonStates: {\n left: false,\n right: false,\n middle: false,\n },\n scrollPosition: {\n x: 0,\n y: 0\n },\n },\n authorSnapshot: {\n authors: [\n { currentSpeechCaption: '' }\n ]\n }\n});\n", "import { createRequire } from \"node:module\";\nimport { IFileSource, IProjectError } from \"@fullstackcraftllc/codevideo-types\";\nimport type { ICompileResult } from \"@fullstackcraftllc/codevideo-dynamic-ast\";\n\n/**\n * Single integration point for the \"does the code actually compile?\" check.\n *\n * Backed by codevideo-dynamic-ast `compileProject`, which detects each project in the file\n * set and compiles it with the native toolchain (ts-morph/tsc for TypeScript, plus go /\n * python3 / dotnet / rustc for those languages when installed). TypeScript needs no external\n * toolchain; the others require their toolchain to be present on the host.\n *\n * Loading note: dynamic-ast's ESM build bundles `typescript`, which references `__filename`\n * and therefore crashes in ES module scope (\"__filename is not defined\"). We load its CJS\n * build (the package's `require` export -> dist/index.cjs) via createRequire, where\n * `__filename` is defined. Loaded lazily so the ~12MB TS bundle only loads on first compile.\n */\n\nexport interface ICompileLanguageResult {\n language: string;\n errors: IProjectError[];\n}\n\nexport interface ICompileCheckResult {\n status: \"ok\" | \"errors\";\n projects: ICompileLanguageResult[];\n message?: string;\n}\n\ntype CompileProjectFn = (files: IFileSource[]) => Promise<ICompileResult>;\n\nlet cachedCompileProject: CompileProjectFn | null = null;\nconst getCompileProject = (): CompileProjectFn => {\n if (!cachedCompileProject) {\n const require = createRequire(import.meta.url);\n cachedCompileProject = require(\"@fullstackcraftllc/codevideo-dynamic-ast\").compileProject as CompileProjectFn;\n }\n return cachedCompileProject;\n};\n\nexport const compileFiles = async (files: IFileSource[]): Promise<ICompileCheckResult> => {\n if (files.length === 0) {\n return { status: \"ok\", projects: [], message: \"No files to compile.\" };\n }\n try {\n const compileProject = getCompileProject();\n const result = await compileProject(files);\n const projects: ICompileLanguageResult[] = result.projects.map((p) => ({\n language: p.language,\n errors: p.errors,\n }));\n const hasErrors = projects.some((p) => p.errors.length > 0);\n return { status: hasErrors ? \"errors\" : \"ok\", projects };\n } catch (error: unknown) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n status: \"errors\",\n projects: [{ language: \"unknown\", errors: [{ file: \"unknown\", line: 0, message: `Compile check failed to run: ${message}` }] }],\n };\n }\n};\n", "import os from \"os\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport { ILesson } from \"@fullstackcraftllc/codevideo-types\";\nimport { generateHtmlStringFromLesson } from \"@fullstackcraftllc/codevideo-exporters\";\nimport { getCourse, getLesson, listLessons, StoredLesson } from \"../store/courseStore\";\nimport { validateCourse } from \"./courseTools\";\nimport { JobSpec } from \"../render/jobs\";\nimport { RenderOptions } from \"../render/options\";\nimport { describeJob, estimatedSeconds, newVideoPath, queueRenders } from \"../render/queue\";\n\n// Phase 2: previews + video render, gated behind validation.\n// - Previews are server-side HTML (cheap, no browser needed in the MCP) for the\n// human-in-the-loop \"does this look right?\" check before any expensive rendering.\n// - Video renders are queued, one job per lesson, ONLY once the course passes\n// validate_course; the background render worker runs them one at a time. (Real PNG/snapshot previews need a headless render step \u2014 best added\n// as a `--screenshot` mode on the CLI, which already drives a browser. See README/notes.)\n\nconst ok = (data: unknown): string => JSON.stringify(data, null, 2);\n\nconst toILesson = (l: StoredLesson): ILesson => ({\n id: l.id,\n name: l.name,\n description: l.description,\n actions: l.actions,\n initialSnapshot: l.initialSnapshot,\n finalSnapshot: l.finalSnapshot,\n});\n\nconst writePreviewHtml = async (courseId: string, l: StoredLesson): Promise<string> => {\n const html = await generateHtmlStringFromLesson(toILesson(l), {});\n const outDir = path.join(os.homedir(), \".codevideo\", \"previews\", courseId);\n fs.mkdirSync(outDir, { recursive: true });\n const file = path.join(outDir, `${l.id}.html`);\n fs.writeFileSync(file, html, \"utf8\");\n return file;\n};\n\n// ---------- previews (server-side HTML) ----------\n\nexport const generateLessonPreviewTool = async (courseId: string, lessonId: string): Promise<string> => {\n const file = await writePreviewHtml(courseId, getLesson(courseId, lessonId));\n return ok({ courseId, lessonId, previewHtml: file, message: \"Open this HTML file in a browser to preview the lesson.\" });\n};\n\nexport const generateCoursePreviewsTool = async (courseId: string): Promise<string> => {\n const validation = await validateCourse(courseId);\n if (!validation.done) {\n return ok({\n ok: false,\n gated: true,\n message: \"Course has not passed validation yet \u2014 fix it first, then generate previews.\",\n reasons: validation.reasons,\n });\n }\n const previews: { lessonId: string; name: string; previewHtml: string }[] = [];\n for (const l of listLessons(courseId)) {\n previews.push({ lessonId: l.id, name: l.name, previewHtml: await writePreviewHtml(courseId, l) });\n }\n return ok({\n ok: true,\n courseId,\n previews,\n message: \"Previews generated. Review them; once a human approves, call codevideo_render_course_videos to spawn per-lesson video rendering.\",\n });\n};\n\n// ---------- video render (gated; queued for the background render worker) ----------\n\nconst lessonSpec = (course: { id: string; primaryLanguage: string }, l: StoredLesson, options: RenderOptions, open: boolean): JobSpec => ({\n kind: \"course-lesson\",\n courseId: course.id,\n lessonId: l.id,\n label: l.name || \"lesson\",\n project: toILesson(l),\n outputPath: newVideoPath(l.name || \"lesson\"),\n options,\n open,\n defaultLanguage: options.defaultLanguage ?? (course.primaryLanguage || \"typescript\"),\n});\n\nexport const renderLessonVideoTool = async (courseId: string, lessonId: string, options: RenderOptions = {}, open = true): Promise<string> => {\n const validation = await validateCourse(courseId);\n if (!validation.done) {\n return ok({ ok: false, gated: true, message: \"No video until the course passes validation. Fix the course first.\", reasons: validation.reasons });\n }\n const course = getCourse(courseId);\n const lesson = getLesson(courseId, lessonId);\n const [job] = queueRenders([lessonSpec(course, lesson, options, open)]);\n return ok({\n ok: true,\n ...describeJob(job),\n estimatedSeconds: estimatedSeconds(lesson.actions, options),\n message: \"Queued. It renders in the background; check it with codevideo_get_render_job.\",\n });\n};\n\nexport const renderCourseVideosTool = async (courseId: string, options: RenderOptions = {}, open = false): Promise<string> => {\n const validation = await validateCourse(courseId);\n if (!validation.done) {\n return ok({ ok: false, gated: true, message: \"No video until the course passes validation. Fix the course first.\", reasons: validation.reasons });\n }\n const course = getCourse(courseId);\n const jobs = queueRenders(course.lessons.map((l) => lessonSpec(course, l, options, open)));\n return ok({\n ok: true,\n courseId,\n jobs: jobs.map((job, i) => ({\n jobId: job.id,\n lessonId: job.lessonId,\n label: job.label,\n outputPath: job.outputPath,\n estimatedSeconds: estimatedSeconds(course.lessons[i].actions, options),\n })),\n message: `Queued ${jobs.length} lesson video(s). They render one at a time in the background; check them with codevideo_list_render_jobs({ courseId }).`,\n });\n};\n", "import { actionReference } from '../prompts/actionReference';\n\n/**\n * Returns every action from the codevideo-types package with what it does and\n * what its value takes, one per line.\n */\nexport const getActionNames = (): string => {\n return actionReference();\n}\n", "import { IAction } from \"@fullstackcraftllc/codevideo-types\";\n\nconst keywordsToExampleActionsMap: Array<{ keywords: string[], actions: Array<IAction> }> = [\n {\n keywords: [\"console log\", \"javascript\", \"js\", \"log\"],\n actions: [\n {\n name: \"author-speak-before\",\n value: \"Today we're going to learn about console.log in JavaScript. Console.log is a function that allows you to print messages to the console, which is useful for debugging and understanding your code better.\",\n },\n {\n name: \"author-speak-before\",\n value: \"Let's first create an index.js file where we will write our code.\",\n },\n {\n name: \"mouse-move-file-explorer\",\n value: \"1\",\n },\n {\n name: \"mouse-right-click\",\n value: \"1\",\n },\n {\n name: \"mouse-move-file-explorer-context-menu-new-file\",\n value: \"1\",\n },\n {\n name: \"mouse-left-click\",\n value: \"1\",\n }\n ]\n },\n {\n keywords: [\"golang\", \"go\", \"golang console log\", \"golang log\"],\n actions: [\n {\n \"name\": \"author-speak-before\",\n \"value\": \"Let's learn how to use the fmt.Println function in Go!\"\n },\n {\n \"name\": \"author-speak-before\",\n \"value\": \"Let's first make a main.go file.\"\n },\n {\n \"name\": \"mouse-move-file-explorer\",\n \"value\": \"1\"\n },\n {\n \"name\": \"mouse-right-click\",\n \"value\": \"1\"\n },\n {\n \"name\": \"mouse-move-file-explorer-context-menu-new-file\",\n \"value\": \"1\"\n },\n {\n \"name\": \"mouse-left-click\",\n \"value\": \"1\"\n },\n {\n \"name\": \"file-explorer-type-new-file-input\",\n \"value\": \"main.go\"\n },\n {\n \"name\": \"file-explorer-enter-new-file-input\",\n \"value\": \"1\"\n },\n {\n \"name\": \"author-speak-before\",\n \"value\": \"First, we'll need to declare the main package, and then the main function.\"\n },\n {\n \"name\": \"editor-type\",\n \"value\": \"package main\"\n },\n {\n \"name\": \"editor-enter\",\n \"value\": \"2\"\n },\n {\n \"name\": \"author-speak-before\",\n \"value\": \"And we'll need the fmt package to use the Println function.\"\n },\n {\n \"name\": \"editor-type\",\n \"value\": 'import \"fmt\"'\n },\n {\n \"name\": \"editor-enter\",\n \"value\": \"1\"\n },\n {\n \"name\": \"editor-type\",\n \"value\": \"func main() {\"\n },\n {\n \"name\": \"editor-enter\",\n \"value\": \"1\"\n },\n {\n \"name\": \"author-speak-before\",\n \"value\": \"and let's just print 'Hello world!' to the console.\"\n },\n {\n \"name\": \"editor-type\",\n \"value\": ' fmt.Println(\"Hello, world!\")'\n },\n {\n \"name\": \"editor-enter\",\n \"value\": \"1\"\n },\n {\n \"name\": \"editor-type\",\n \"value\": \"}\"\n },\n {\n \"name\": \"editor-arrow-up\",\n \"value\": \"1\"\n },\n {\n \"name\": \"editor-command-right\",\n \"value\": \"1\"\n },\n {\n \"name\": \"author-speak-before\",\n \"value\": \"and if I wanted to write the value of some variable to the console, I could do that like so:\"\n },\n {\n \"name\": \"editor-backspace\",\n \"value\": \"32\"\n },\n {\n \"name\": \"editor-type\",\n \"value\": \" myVariable := 5\"\n },\n {\n \"name\": \"editor-enter\",\n \"value\": \"1\"\n },\n {\n \"name\": \"editor-type\",\n \"value\": \" fmt.Println(myVariable)\"\n },\n {\n \"name\": \"author-speak-before\",\n \"value\": \"Now, when I run this code, I would expect the value of 'myVariable' to be printed to the console. Something like:\"\n },\n {\n \"name\": \"editor-enter\",\n \"value\": \"1\"\n },\n {\n \"name\": \"editor-type\",\n \"value\": \" // 5\"\n },\n {\n \"name\": \"author-speak-before\",\n \"value\": \"Console logging is simple, yet powerful and very useful!\"\n }\n ]\n },\n {\n keywords: [\"python\", \"python console log\", \"python log\"],\n actions: [\n {\n \"name\": \"author-speak-before\",\n \"value\": \"Let's learn how to use the print function in Python!\"\n },\n {\n \"name\": \"author-speak-before\",\n \"value\": \"First, let's make a main.py Python file.\"\n },\n {\n \"name\": \"mouse-move-file-explorer\",\n \"value\": \"1\"\n },\n {\n \"name\": \"mouse-right-click\",\n \"value\": \"1\"\n },\n {\n \"name\": \"mouse-move-file-explorer-context-menu-new-file\",\n \"value\": \"1\"\n },\n {\n \"name\": \"mouse-left-click\",\n \"value\": \"1\"\n },\n {\n \"name\": \"file-explorer-type-new-file-input\",\n \"value\": \"main.py\"\n },\n {\n \"name\": \"file-explorer-enter-new-file-input\",\n \"value\": \"1\"\n },\n {\n \"name\": \"author-speak-before\",\n \"value\": \"and let's print 'Hello world!' to the console.\"\n },\n {\n \"name\": \"editor-type\",\n \"value\": \"print('Hello, world!')\"\n },\n {\n \"name\": \"author-speak-before\",\n \"value\": \"and if I wanted to write the value of some variable to the console, I could do that like so:\"\n },\n {\n \"name\": \"editor-backspace\",\n \"value\": \"22\"\n },\n {\n \"name\": \"editor-type\",\n \"value\": \"my_variable = 5\"\n },\n {\n \"name\": \"editor-enter\",\n \"value\": \"1\"\n },\n {\n \"name\": \"editor-type\",\n \"value\": \"print(my_variable)\"\n },\n {\n \"name\": \"author-speak-before\",\n \"value\": \"Now, when I run this code, I would expect the value of 'my_variable' to be printed to the console. Something like:\"\n },\n {\n \"name\": \"editor-enter\",\n \"value\": \"1\"\n },\n {\n \"name\": \"editor-type\",\n \"value\": \"# 5\"\n },\n {\n \"name\": \"author-speak-before\",\n \"value\": \"Console logging is simple, yet powerful and very useful!\"\n },\n {\n \"name\": \"author-speak-before\",\n \"value\": \"Well done, lets save our file.\"\n },\n {\n \"name\": \"editor-save\",\n \"value\": \"main.py\"\n },\n {\n \"name\": \"author-speak-before\",\n \"value\": \"Saved!\"\n },\n {\n \"name\": \"author-speak-before\",\n \"value\": \"Oh... and I've just realized... this file looks a bit strange... let's add the python shebang to the top of the file.\"\n },\n {\n \"name\": \"editor-arrow-up\",\n \"value\": \"4\"\n },\n {\n \"name\": \"editor-command-left\",\n \"value\": \"1\"\n },\n {\n \"name\": \"editor-enter\",\n \"value\": \"1\"\n },\n {\n \"name\": \"editor-arrow-up\",\n \"value\": \"1\"\n },\n {\n \"name\": \"editor-type\",\n \"value\": \"#!/usr/bin/env python\"\n },\n {\n \"name\": \"editor-enter\",\n \"value\": \"1\"\n },\n {\n \"name\": \"author-speak-before\",\n \"value\": \"Ah...much better!\"\n },\n {\n \"name\": \"author-speak-before\",\n \"value\": \"Let's save the file again.\"\n },\n {\n \"name\": \"editor-save\",\n \"value\": \"main.py\"\n },\n {\n \"name\": \"author-speak-before\",\n \"value\": \"Saved!\"\n },\n {\n \"name\": \"author-speak-before\",\n \"value\": \"Now, let's open a terminal and run this file...\"\n },\n {\n \"name\": \"terminal-open\",\n \"value\": \"1\"\n },\n {\n \"name\": \"terminal-type\",\n \"value\": \"python main.py\"\n },\n {\n \"name\": \"terminal-set-output\",\n \"value\": \"5\"\n },\n {\n \"name\": \"terminal-enter\",\n \"value\": \"1\"\n },\n {\n \"name\": \"author-speak-before\",\n \"value\": \"Perfect, our code is working as expected! I hope you enjoyed this lesson!\"\n }\n ]\n },\n]\n\n// returns a JSON representation of an example lesson based on keywords\n// the LLM can use this to learn real example lessons created by humans\nexport const getExampleActionsArrayByKeyword = (keyword: string | string[]): string => {\n const exampleJSONs: string[] = [];\n\n if (Array.isArray(keyword)) {\n // if the keyword is an array, loop over all keywords and get the example lesson for each one\n for (var i = 0; i < keyword.length; i++) {\n const exampleJSON = getExampleActionsArrayByKeyword(keyword[i]);\n exampleJSONs.push(exampleJSON);\n }\n return exampleJSONs.join(\", \");\n } else {\n // if the keyword is a string, get the example lesson for that keyword\n const exampleJSON = getSingleExampleActionsArray(keyword);\n if (exampleJSON) {\n return exampleJSON;\n }\n }\n // if no keywords match, return an empty string\n return \"\";\n}\n\nconst getSingleExampleActionsArray = (keyword: string): string => {\n // loop over all keywords and find the first one that matches\n for (var i = 0; i < keywordsToExampleActionsMap.length; i++) {\n const keywordMap = keywordsToExampleActionsMap[i];\n if (keywordMap.keywords.includes(keyword)) {\n return JSON.stringify(keywordMap.actions);\n }\n }\n\n // if no keywords match, return an empty string\n return \"\";\n}", "import { ILesson } from \"@fullstackcraftllc/codevideo-types\";\n\nconst keywordsToExampleLessonsMap: Array<{ keywords: string[], lesson: ILesson }> = [\n {\n keywords: [\"go\", \"golang\", \"explore codebase\", \"existing codebase\", \"go cli tool\"],\n lesson: {\n \"id\": \"\",\n \"name\": \"\",\n \"description\": \"\",\n \"actions\": [\n {\n \"name\": \"author-speak-before\",\n \"value\": \"Hi there! Let's explore this interesting Golang repository that helps track GitLab contributions. This tool analyzes user activity across different time periods and produces contribution statistics.\"\n },\n {\n \"name\": \"mouse-move-file-explorer\",\n \"value\": \"1\"\n },\n {\n \"name\": \"mouse-move-file-explorer-file\",\n \"value\": \"main.go\"\n },\n {\n \"name\": \"mouse-left-click\",\n \"value\": \"1\"\n },\n {\n \"name\": \"author-speak-before\",\n \"value\": \"The main.go file is the entry point for the application. It defines the core functionality for tracking GitLab contributions.\"\n },\n {\n \"name\": \"mouse-move-to-coordinates-percent\",\n \"value\": \"50,20\"\n },\n {\n \"name\": \"author-speak-before\",\n \"value\": \"This struct stores contribution statistics for a user - tracking both commits and merge requests across different time periods.\"\n },\n {\n \"name\": \"author-speak-before\",\n \"value\": \"The main function initializes the application by loading environment variables, getting the GitLab token, and reading usernames from a file.\"\n },\n {\n \"name\": \"mouse-move-file-explorer-file\",\n \"value\": \"usernames.txt\"\n },\n {\n \"name\": \"mouse-left-click\",\n \"value\": \"1\"\n },\n {\n \"name\": \"author-speak-before\",\n \"value\": \"The usernames.txt file contains the list of GitLab users whose contributions we want to track.\"\n },\n {\n \"name\": \"mouse-move-file-explorer-file\",\n \"value\": \"main.go\"\n },\n {\n \"name\": \"mouse-left-click\",\n \"value\": \"1\"\n },\n {\n \"name\": \"file-explorer-open-file\",\n \"value\": \"main.go\"\n },\n {\n \"name\": \"author-speak-before\",\n \"value\": \"This function calculates contribution statistics for a single user. It defines time windows for different periods - daily, weekly, monthly, and yearly.\"\n },\n {\n \"name\": \"author-speak-before\",\n \"value\": \"The GetUserActivity function connects to the GitLab API to fetch user events within a specified time period.\"\n },\n {\n \"name\": \"author-speak-before\",\n \"value\": \"The printResults function formats and displays the contribution statistics, including calculating metrics like average commits per merge request.\"\n },\n {\n \"name\": \"mouse-move-file-explorer\",\n \"value\": \"1\"\n },\n {\n \"name\": \"mouse-move-file-explorer-folder\",\n \"value\": \"http_helper\"\n },\n {\n \"name\": \"mouse-left-click\",\n \"value\": \"1\"\n },\n {\n \"name\": \"file-explorer-expand-folder\",\n \"value\": \"http_helper\"\n },\n {\n \"name\": \"mouse-move-file-explorer-file\",\n \"value\": \"http_helper/http_helper.go\"\n },\n {\n \"name\": \"mouse-left-click\",\n \"value\": \"1\"\n },\n {\n \"name\": \"file-explorer-open-file\",\n \"value\": \"http_helper/http_helper.go\"\n },\n {\n \"name\": \"author-speak-before\",\n \"value\": \"The project has a dedicated HTTP helper module that handles API requests to GitLab.\"\n },\n {\n \"name\": \"editor-highlight-code\",\n \"value\": \"func MakeHTTPRequest(fullUrl string, httpMethod string, headers map[string]string, queryParameters url.Values, body io.Reader) ([]byte, error) {\"\n },\n {\n \"name\": \"author-speak-before\",\n \"value\": \"This function handles standard HTTP requests, while the function below uses generics for typed responses.\"\n },\n {\n \"name\": \"editor-highlight-code\",\n \"value\": \"func MakeHTTPRequestGeneric[T any](fullUrl string, httpMethod string, headers map[string]string, queryParameters url.Values, body io.Reader, responseType T) (T, error) {\"\n },\n {\n \"name\": \"author-speak-before\",\n \"value\": \"MakeHTTPRequestGeneric is a modern Go function using generics to handle typed API responses, making it easier to work with the GitLab API.\"\n },\n {\n \"name\": \"mouse-move-file-explorer-file\",\n \"value\": \"main_test.go\"\n },\n {\n \"name\": \"mouse-left-click\",\n \"value\": \"1\"\n },\n {\n \"name\": \"file-explorer-open-file\",\n \"value\": \"main_test.go\"\n },\n {\n \"name\": \"author-speak-before\",\n \"value\": \"The project also includes tests to verify the functionality of the GitLab API interactions.\"\n },\n {\n \"name\": \"mouse-move-file-explorer-file\",\n \"value\": \".env\"\n },\n {\n \"name\": \"mouse-left-click\",\n \"value\": \"1\"\n },\n {\n \"name\": \"file-explorer-open-file\",\n \"value\": \".env\"\n },\n {\n \"name\": \"author-speak-before\",\n \"value\": \"The .env file stores the GitLab token needed to authenticate with the API.\"\n },\n {\n \"name\": \"mouse-move-file-explorer-file\",\n \"value\": \"go.mod\"\n },\n {\n \"name\": \"mouse-left-click\",\n \"value\": \"1\"\n },\n {\n \"name\": \"file-explorer-open-file\",\n \"value\": \"go.mod\"\n },\n {\n \"name\": \"author-speak-before\",\n \"value\": \"The go.mod file lists the project dependencies, including the go-gitlab library for API interaction and godotenv for environment variable loading.\"\n },\n {\n \"name\": \"mouse-move-file-explorer-file\",\n \"value\": \"main.go\"\n },\n {\n \"name\": \"mouse-left-click\",\n \"value\": \"1\"\n },\n {\n \"name\": \"file-explorer-open-file\",\n \"value\": \"main.go\"\n },\n {\n \"name\": \"author-speak-after\",\n \"value\": \"To summarize, this Go repository is a GitLab contribution analyzer that fetches user activity data from GitLab's API. It tracks commits and merge requests across multiple time periods, calculates stats, and outputs nicely formatted results with progress bars. It's designed to help track team productivity and identify top contributors. The code is well-structured with separate modules for HTTP requests and includes proper error handling and testing.\"\n }\n ],\n \"initialSnapshot\": {\n \"isUnsavedChangesDialogOpen\": false,\n \"unsavedFileName\": \"\",\n \"fileExplorerSnapshot\": {\n \"isFileExplorerContextMenuOpen\": false,\n \"isFileContextMenuOpen\": false,\n \"isFolderContextMenuOpen\": false,\n \"isNewFileInputVisible\": false,\n \"isNewFolderInputVisible\": false,\n \"isRenameFileInputVisible\": false,\n \"isRenameFolderInputVisible\": false,\n \"newFileInputValue\": \"\",\n \"newFolderInputValue\": \"\",\n \"renameFileInputValue\": \"\",\n \"renameFolderInputValue\": \"\",\n \"originalFileBeingRenamed\": \"\",\n \"originalFolderBeingRenamed\": \"\",\n \"newFileParentPath\": \"\",\n \"newFolderParentPath\": \"\",\n \"fileStructure\": {\n \".env\": {\n \"type\": \"file\",\n \"content\": \"GITLAB_TOKEN=glpat-uShSYUHwpUcDxfkGcoNL\",\n \"language\": \"plaintext\",\n \"caretPosition\": {\n \"row\": 0,\n \"col\": 0\n }\n },\n \".gitignore\": {\n \"type\": \"file\",\n \"content\": \".env\",\n \"language\": \"plaintext\",\n \"caretPosition\": {\n \"row\": 0,\n \"col\": 0\n }\n },\n \"go.mod\": {\n \"type\": \"file\",\n \"content\": \"module solve.ch/solvers\\n\\ngo 1.23.0\\n\\nrequire github.com/xanzy/go-gitlab v0.115.0\\n\\nrequire (\\n\\tgithub.com/golang/protobuf v1.5.3 // indirect\\n\\tgithub.com/google/go-querystring v1.1.0 // indirect\\n\\tgithub.com/hashicorp/go-cleanhttp v0.5.2 // indirect\\n\\tgithub.com/hashicorp/go-retryablehttp v0.7.7 // indirect\\n\\tgithub.com/joho/godotenv v1.5.1\\n\\tgitlab.com/gitlab-org/api/client-go v0.120.0\\n\\tgolang.org/x/net v0.8.0 // indirect\\n\\tgolang.org/x/oauth2 v0.24.0 // indirect\\n\\tgolang.org/x/time v0.8.0 // indirect\\n\\tgoogle.golang.org/appengine v1.6.7 // indirect\\n\\tgoogle.golang.org/protobuf v1.29.1 // indirect\\n)\\n\",\n \"language\": \"plaintext\",\n \"caretPosition\": {\n \"row\": 0,\n \"col\": 0\n }\n },\n \"go.sum\": {\n \"type\": \"file\",\n \"content\": \"github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\\ngithub.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=\\ngithub.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=\\ngithub.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=\\ngithub.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\\ngithub.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=\\ngithub.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=\\ngithub.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=\\ngithub.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=\\ngithub.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=\\ngithub.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=\\ngithub.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=\\ngithub.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=\\ngithub.com/xanzy/go-gitlab v0.115.0 h1:6DmtItNcVe+At/liXSgfE/DZNZrGfalQmBRmOcJjOn8=\\ngithub.com/xanzy/go-gitlab v0.115.0/go.mod h1:5XCDtM7AM6WMKmfDdOiEpyRWUqui2iS9ILfvCZ2gJ5M=\\ngitlab.com/gitlab-org/api/client-go v0.120.0 h1:geCJjojDXxWVmUcTxPcOUCenAWElWB5dVfX3HJGeAMc=\\ngitlab.com/gitlab-org/api/client-go v0.120.0/go.mod h1:ygHmS3AU3TpvK+AC6DYO1QuAxLlv6yxYK+/Votr/WFQ=\\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\\ngolang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=\\ngolang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=\\ngolang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw=\\ngolang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=\\ngolang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=\\ngolang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=\\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\\ngolang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=\\ngolang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\\ngolang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=\\ngolang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=\\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\\ngoogle.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=\\ngoogle.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\\ngoogle.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=\\ngoogle.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\\ngoogle.golang.org/protobuf v1.29.1 h1:7QBf+IK2gx70Ap/hDsOmam3GE0v9HicjfEdAxE62UoM=\\ngoogle.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=\\n\",\n \"language\": \"plaintext\",\n \"caretPosition\": {\n \"row\": 0,\n \"col\": 0\n }\n },\n \"http_helper\": {\n \"type\": \"directory\",\n \"content\": \"\",\n \"collapsed\": false,\n \"children\": {\n \"http_helper.go\": {\n \"type\": \"file\",\n \"content\": \"package http_helper\\n\\nimport (\\n\\t\\\"encoding/json\\\"\\n\\t\\\"fmt\\\"\\n\\t\\\"io\\\"\\n\\t\\\"log\\\"\\n\\t\\\"net/http\\\"\\n\\t\\\"net/url\\\"\\n\\t\\\"strings\\\"\\n)\\n\\n// in the case of GET, the parameter queryParameters is transferred to the URL as query parameters\\n// in the case of POST, the parameter body, an io.Reader, is used\\nfunc MakeHTTPRequest(fullUrl string, httpMethod string, headers map[string]string, queryParameters url.Values, body io.Reader) ([]byte, error) {\\n\\tclient := http.Client{}\\n\\tu, err := url.Parse(fullUrl)\\n\\tif err != nil {\\n\\t\\treturn nil, err\\n\\t}\\n\\n\\t// if it's a GET, we need to append the query parameters.\\n\\tif httpMethod == \\\"GET\\\" {\\n\\t\\tq := u.Query()\\n\\n\\t\\tfor k, v := range queryParameters {\\n\\t\\t\\t// this depends on the type of api, you may need to do it for each of v\\n\\t\\t\\tq.Set(k, strings.Join(v, \\\",\\\"))\\n\\t\\t}\\n\\t\\t// set the query to the encoded parameters\\n\\t\\tu.RawQuery = q.Encode()\\n\\t}\\n\\n\\t// regardless of GET or POST, we can safely add the body\\n\\treq, err := http.NewRequest(httpMethod, u.String(), body)\\n\\tif err != nil {\\n\\t\\treturn nil, err\\n\\t}\\n\\n\\t// for each header passed, add the header value to the request\\n\\tfor k, v := range headers {\\n\\t\\treq.Header.Set(k, v)\\n\\t}\\n\\n\\t// optional: log the request for easier stack tracing\\n\\tlog.Printf(\\\"%s %s\\\\n\\\", httpMethod, req.URL.String())\\n\\n\\t// finally, do the request\\n\\tres, err := client.Do(req)\\n\\tif err != nil {\\n\\t\\treturn nil, err\\n\\t}\\n\\n\\tif res == nil {\\n\\t\\treturn nil, fmt.Errorf(\\\"error: calling %s returned empty response\\\", u.String())\\n\\t}\\n\\n\\tresponseData, err := io.ReadAll(res.Body)\\n\\tif err != nil {\\n\\t\\treturn nil, err\\n\\t}\\n\\n\\tdefer res.Body.Close()\\n\\n\\tif res.StatusCode != http.StatusOK {\\n\\t\\treturn nil, fmt.Errorf(\\\"error calling %s:\\\\nstatus: %s\\\\nresponseData: %s\\\", u.String(), res.Status, responseData)\\n\\t}\\n\\n\\treturn responseData, nil\\n}\\n\\nfunc MakeHTTPRequestGeneric[T any](fullUrl string, httpMethod string, headers map[string]string, queryParameters url.Values, body io.Reader, responseType T) (T, error) {\\n\\tclient := http.Client{}\\n\\tu, err := url.Parse(fullUrl)\\n\\tif err != nil {\\n\\t\\treturn responseType, err\\n\\t}\\n\\n\\t// if it's a GET, we need to append the query parameters.\\n\\tif httpMethod == \\\"GET\\\" {\\n\\t\\tq := u.Query()\\n\\n\\t\\tfor k, v := range queryParameters {\\n\\t\\t\\t// this depends on the type of api, you may need to do it for each of v\\n\\t\\t\\tq.Set(k, strings.Join(v, \\\",\\\"))\\n\\t\\t}\\n\\t\\t// set the query to the encoded parameters\\n\\t\\tu.RawQuery = q.Encode()\\n\\t}\\n\\n\\t// regardless of GET or POST, we can safely add the body\\n\\treq, err := http.NewRequest(httpMethod, u.String(), body)\\n\\tif err != nil {\\n\\t\\treturn responseType, err\\n\\t}\\n\\n\\t// for each header passed, add the header value to the request\\n\\tfor k, v := range headers {\\n\\t\\treq.Header.Set(k, v)\\n\\t}\\n\\n\\t// optional: log the request for easier stack tracing\\n\\tlog.Printf(\\\"%s %s\\\\n\\\", httpMethod, req.URL.String())\\n\\n\\t// finally, do the request\\n\\tres, err := client.Do(req)\\n\\tif err != nil {\\n\\t\\treturn responseType, err\\n\\t}\\n\\n\\tif res == nil {\\n\\t\\treturn responseType, fmt.Errorf(\\\"error: calling %s returned empty response\\\", u.String())\\n\\t}\\n\\n\\tresponseData, err := io.ReadAll(res.Body)\\n\\tif err != nil {\\n\\t\\treturn responseType, err\\n\\t}\\n\\n\\tdefer res.Body.Close()\\n\\n\\tif res.StatusCode != http.StatusOK {\\n\\t\\treturn responseType, fmt.Errorf(\\\"error calling %s:\\\\nstatus: %s\\\\nresponseData: %s\\\", u.String(), res.Status, responseData)\\n\\t}\\n\\n\\tvar responseObject T\\n\\terr = json.Unmarshal(responseData, &responseObject)\\n\\n\\tif err != nil {\\n\\t\\tlog.Printf(\\\"error unmarshaling response: %+v\\\", err)\\n\\t\\treturn responseType, err\\n\\t}\\n\\n\\treturn responseObject, nil\\n}\\n\",\n \"language\": \"Go\",\n \"caretPosition\": {\n \"row\": 0,\n \"col\": 0\n }\n }\n }\n },\n \"main.go\": {\n \"type\": \"file\",\n \"content\": \"package main\\n\\nimport (\\n\\t\\\"bufio\\\"\\n\\t\\\"fmt\\\"\\n\\t\\\"log\\\"\\n\\t\\\"os\\\"\\n\\t\\\"sort\\\"\\n\\t\\\"strings\\\"\\n\\t\\\"time\\\"\\n\\n\\t\\\"github.com/joho/godotenv\\\"\\n\\t\\\"solve.ch/solvers/http_helper\\\"\\n)\\n\\ntype ContributionStats struct {\\n\\tUsername string\\n\\tDailyCommits int\\n\\tWeeklyCommits int\\n\\tMonthlyCommits int\\n\\tYearlyCommits int\\n\\tDailyMRs int\\n\\tWeeklyMRs int\\n\\tMonthlyMRs int\\n\\tYearlyMRs int\\n\\t// New field to store total pages fetched from GitLab\\n\\tPagesFetched int\\n}\\n\\ntype TimePeriod struct {\\n\\tStart time.Time\\n\\tEnd time.Time\\n}\\n\\ntype GitLabAuthor struct {\\n\\tID int `json:\\\"id\\\"`\\n\\tUsername string `json:\\\"username\\\"`\\n\\tName string `json:\\\"name\\\"`\\n\\tState string `json:\\\"state\\\"`\\n\\tAvatarURL string `json:\\\"avatar_url\\\"`\\n\\tWebURL string `json:\\\"web_url\\\"`\\n}\\n\\ntype GitLabPushData struct {\\n\\tCommitCount int `json:\\\"commit_count\\\"`\\n\\tAction string `json:\\\"action\\\"`\\n\\tRefType string `json:\\\"ref_type\\\"`\\n\\tCommitFrom string `json:\\\"commit_from\\\"`\\n\\tCommitTo string `json:\\\"commit_to\\\"`\\n\\tRef string `json:\\\"ref\\\"`\\n\\tCommitTitle string `json:\\\"commit_title\\\"`\\n\\tRefCount *int `json:\\\"ref_count\\\"`\\n}\\n\\ntype GitLabEvent struct {\\n\\tID int `json:\\\"id\\\"`\\n\\tProjectID int `json:\\\"project_id\\\"`\\n\\tActionName string `json:\\\"action_name\\\"`\\n\\tTargetID *int `json:\\\"target_id\\\"`\\n\\tTargetIID *int `json:\\\"target_iid\\\"`\\n\\tTargetType *string `json:\\\"target_type\\\"`\\n\\tAuthorID int `json:\\\"author_id\\\"`\\n\\tTargetTitle *string `json:\\\"target_title\\\"`\\n\\tCreatedAt time.Time `json:\\\"created_at\\\"`\\n\\tAuthor GitLabAuthor `json:\\\"author\\\"`\\n\\tPushData *GitLabPushData `json:\\\"push_data\\\"`\\n}\\n\\nfunc main() {\\n\\t// Load .env file\\n\\terr := godotenv.Load()\\n\\tif err != nil {\\n\\t\\tlog.Println(\\\"Warning: Error loading .env file:\\\", err)\\n\\t}\\n\\n\\t// Replace with your GitLab token\\n\\ttoken := os.Getenv(\\\"GITLAB_TOKEN\\\")\\n\\tif token == \\\"\\\" {\\n\\t\\tlog.Fatal(\\\"GITLAB_TOKEN environment variable not set\\\")\\n\\t}\\n\\n\\t// Read usernames from file\\n\\tusernames, err := readUsernames(\\\"usernames.txt\\\")\\n\\tif err != nil {\\n\\t\\tlog.Fatalf(\\\"Failed to read usernames: %v\\\", err)\\n\\t}\\n\\n\\t// Get contribution stats for each user\\n\\tstats := make([]ContributionStats, 0)\\n\\tfor _, username := range usernames {\\n\\t\\tstat, err := getContributionStats(token, username)\\n\\t\\tif err != nil {\\n\\t\\t\\tlog.Printf(\\\"Warning: Failed to get stats for %s: %v\\\", username, err)\\n\\t\\t\\tcontinue\\n\\t\\t}\\n\\t\\tstats = append(stats, stat)\\n\\t}\\n\\n\\t// Sort by yearly commits (could be modified to use different metrics)\\n\\tsort.Slice(stats, func(i, j int) bool {\\n\\t\\treturn stats[i].YearlyCommits > stats[j].YearlyCommits\\n\\t})\\n\\n\\t// Print results with normalized bars\\n\\tprintResults(stats)\\n}\\n\\nfunc readUsernames(filename string) ([]string, error) {\\n\\tfile, err := os.Open(filename)\\n\\tif err != nil {\\n\\t\\treturn nil, err\\n\\t}\\n\\tdefer file.Close()\\n\\n\\tvar usernames []string\\n\\tscanner := bufio.NewScanner(file)\\n\\tfor scanner.Scan() {\\n\\t\\tusername := strings.TrimSpace(scanner.Text())\\n\\t\\tif username != \\\"\\\" {\\n\\t\\t\\tusernames = append(usernames, username)\\n\\t\\t}\\n\\t}\\n\\treturn usernames, scanner.Err()\\n}\\n\\nfunc getContributionStats(token, username string) (ContributionStats, error) {\\n\\tstats := ContributionStats{Username: username}\\n\\n\\t// Define time windows for daily, weekly, monthly, yearly\\n\\ttodayStart := time.Now().Truncate(24 * time.Hour)\\n\\ttodayEnd := todayStart.Add(24 * time.Hour)\\n\\n\\t// last 7 days (including today)\\n\\tweekStart := todayStart.AddDate(0, 0, -6)\\n\\tweekEnd := todayEnd\\n\\n\\t// last 1 month\\n\\tmonthStart := todayStart.AddDate(0, -1, 0)\\n\\tmonthEnd := todayEnd\\n\\n\\t// last 1 year\\n\\tyearStart := todayStart.AddDate(-1, 0, 0)\\n\\tyearEnd := todayEnd\\n\\n\\tperiods := map[string]TimePeriod{\\n\\t\\t\\\"daily\\\": {\\n\\t\\t\\tStart: todayStart,\\n\\t\\t\\tEnd: todayEnd,\\n\\t\\t},\\n\\t\\t\\\"weekly\\\": {\\n\\t\\t\\tStart: weekStart,\\n\\t\\t\\tEnd: weekEnd,\\n\\t\\t},\\n\\t\\t\\\"monthly\\\": {\\n\\t\\t\\tStart: monthStart,\\n\\t\\t\\tEnd: monthEnd,\\n\\t\\t},\\n\\t\\t\\\"yearly\\\": {\\n\\t\\t\\tStart: yearStart,\\n\\t\\t\\tEnd: yearEnd,\\n\\t\\t},\\n\\t}\\n\\n\\t// We'll track how many pages we fetched total for each user\\n\\ttotalPages := 0\\n\\n\\t// Get user's events for each time period\\n\\tfor period, timeRange := range periods {\\n\\t\\tcommits, mrs, pages, err := GetUserActivity(token, username, timeRange.Start, timeRange.End)\\n\\t\\tif err != nil {\\n\\t\\t\\treturn stats, err\\n\\t\\t}\\n\\t\\t// Accumulate total page count\\n\\t\\ttotalPages += pages\\n\\n\\t\\tswitch period {\\n\\t\\tcase \\\"daily\\\":\\n\\t\\t\\tstats.DailyCommits = commits\\n\\t\\t\\tstats.DailyMRs = mrs\\n\\t\\tcase \\\"weekly\\\":\\n\\t\\t\\tstats.WeeklyCommits = commits\\n\\t\\t\\tstats.WeeklyMRs = mrs\\n\\t\\tcase \\\"monthly\\\":\\n\\t\\t\\tstats.MonthlyCommits = commits\\n\\t\\t\\tstats.MonthlyMRs = mrs\\n\\t\\tcase \\\"yearly\\\":\\n\\t\\t\\tstats.YearlyCommits = commits\\n\\t\\t\\tstats.YearlyMRs = mrs\\n\\t\\t}\\n\\t}\\n\\n\\t// Store the total pages fetched in stats\\n\\tstats.PagesFetched = totalPages\\n\\n\\treturn stats, nil\\n}\\n\\n// GetUserActivity returns the total commits, MRs, and the number of pages fetched\\nfunc GetUserActivity(token, username string, start, end time.Time) (commits int, mrs int, totalPages int, err error) {\\n\\tbaseURL := \\\"https://git.solve.ch/api/v4/users\\\"\\n\\t// We will iterate over pages, so exclude &page from the base URL:\\n\\tfullURL := fmt.Sprintf(\\n\\t\\t\\\"%s/%s/events?after=%s&before=%s&per_page=100\\\",\\n\\t\\tbaseURL,\\n\\t\\tusername,\\n\\t\\tstart.Format(\\\"2006-01-02\\\"),\\n\\t\\tend.Format(\\\"2006-01-02\\\"),\\n\\t)\\n\\n\\theaders := map[string]string{\\n\\t\\t\\\"PRIVATE-TOKEN\\\": token,\\n\\t}\\n\\n\\tvar allEvents []GitLabEvent\\n\\tpage := 1\\n\\n\\tfor {\\n\\t\\t// Construct URL for the current page\\n\\t\\tpagedURL := fmt.Sprintf(\\\"%s&page=%d\\\", fullURL, page)\\n\\n\\t\\tvar tempEvents []GitLabEvent\\n\\t\\ttempEvents, err = http_helper.MakeHTTPRequestGeneric(\\n\\t\\t\\tpagedURL,\\n\\t\\t\\t\\\"GET\\\",\\n\\t\\t\\theaders,\\n\\t\\t\\tnil,\\n\\t\\t\\tnil,\\n\\t\\t\\ttempEvents,\\n\\t\\t)\\n\\t\\tif err != nil {\\n\\t\\t\\treturn 0, 0, 0, fmt.Errorf(\\\"failed to get user events (page %d): %v\\\", page, err)\\n\\t\\t}\\n\\n\\t\\t// If no events, we've reached the end\\n\\t\\tif len(tempEvents) == 0 {\\n\\t\\t\\tbreak\\n\\t\\t}\\n\\n\\t\\t// Accumulate events\\n\\t\\tallEvents = append(allEvents, tempEvents...)\\n\\t\\tpage++\\n\\t}\\n\\n\\t// The page - 1 is how many pages we actually fetched\\n\\ttotalPages = page - 1\\n\\n\\t// Count commits and MRs\\n\\tfor _, event := range allEvents {\\n\\t\\tswitch {\\n\\t\\tcase event.ActionName == \\\"pushed to\\\" || event.ActionName == \\\"pushed new\\\":\\n\\t\\t\\tif event.PushData != nil {\\n\\t\\t\\t\\tcommits += event.PushData.CommitCount\\n\\t\\t\\t}\\n\\t\\tcase event.ActionName == \\\"accepted\\\" && event.TargetType != nil && *event.TargetType == \\\"MergeRequest\\\":\\n\\t\\t\\tmrs++\\n\\t\\tcase event.ActionName == \\\"opened\\\" && event.TargetType != nil && *event.TargetType == \\\"MergeRequest\\\":\\n\\t\\t\\tmrs++\\n\\t\\tdefault:\\n\\t\\t\\t// TODO: define a 'miscellaneous' category for other types of events\\n\\t\\t\\tcommits++\\n\\t\\t}\\n\\t}\\n\\n\\treturn commits, mrs, totalPages, nil\\n}\\n\\nfunc printResults(stats []ContributionStats) {\\n\\n\\t// loop over stats to get the average number of commits per merge request\\n\\tvar totalMRs, totalCommits int\\n\\tfor _, stat := range stats {\\n\\t\\ttotalMRs += stat.YearlyMRs\\n\\t\\ttotalCommits += stat.YearlyCommits\\n\\t}\\n\\n\\taverageCommitsPerMR := int(float64(totalCommits) / float64(totalMRs))\\n\\n\\t// Find maximum values for normalization\\n\\tvar maxDaily, maxWeekly, maxMonthly, maxYearly int\\n\\tfor _, stat := range stats {\\n\\t\\tmaxDaily = max(maxDaily, stat.DailyCommits+(stat.DailyMRs*averageCommitsPerMR))\\n\\t\\tmaxWeekly = max(maxWeekly, stat.WeeklyCommits+(stat.WeeklyMRs*averageCommitsPerMR))\\n\\t\\tmaxMonthly = max(maxMonthly, stat.MonthlyCommits+(stat.MonthlyMRs*averageCommitsPerMR))\\n\\t\\tmaxYearly = max(maxYearly, stat.YearlyCommits+(stat.YearlyMRs*averageCommitsPerMR))\\n\\t}\\n\\n\\tvar maxDailyCommits, maxWeeklyCommits, maxMonthlyCommits, maxYearlyCommits int\\n\\tfor _, stat := range stats {\\n\\t\\tmaxDailyCommits = max(maxDailyCommits, stat.DailyCommits)\\n\\t\\tmaxWeeklyCommits = max(maxWeeklyCommits, stat.WeeklyCommits)\\n\\t\\tmaxMonthlyCommits = max(maxMonthlyCommits, stat.MonthlyCommits)\\n\\t\\tmaxYearlyCommits = max(maxYearlyCommits, stat.YearlyCommits)\\n\\t}\\n\\n\\tvar maxDailyMRs, maxWeeklyMRs, maxMonthlyMRs, maxYearlyMRs int\\n\\tfor _, stat := range stats {\\n\\t\\tmaxDailyMRs = max(maxDailyMRs, stat.DailyMRs)\\n\\t\\tmaxWeeklyMRs = max(maxWeeklyMRs, stat.WeeklyMRs)\\n\\t\\tmaxMonthlyMRs = max(maxMonthlyMRs, stat.MonthlyMRs)\\n\\t\\tmaxYearlyMRs = max(maxYearlyMRs, stat.YearlyMRs)\\n\\t}\\n\\n\\t// sort stats by scaled contributions (commts + scaled MRs)\\n\\tsort.Slice(stats, func(i, j int) bool {\\n\\t\\treturn (stats[i].YearlyCommits + stats[i].YearlyMRs*averageCommitsPerMR) > (stats[j].YearlyCommits + stats[j].YearlyMRs*averageCommitsPerMR)\\n\\t})\\n\\n\\tfmt.Println(\\\"\\\\n\uD83C\uDFC6 Top Contributors Overall\\\\n\\\")\\n\\tfmt.Printf(\\\"(MRs scaled by %dx, the global average commits per MR across Solve)\\\\n\\\", averageCommitsPerMR)\\n\\tfmt.Println(\\\"========================\\\")\\n\\n\\temojisByRank := []string{\\\"\uD83E\uDD47\\\", \\\"\uD83E\uDD48\\\", \\\"\uD83E\uDD49\\\"}\\n\\n\\tfor index, stat := range stats {\\n\\t\\t// Use the index to get the corresponding emoji - if we are less than three, use the list\\n\\t\\temoji := \\\"\uD83D\uDC68\u200D\uD83D\uDCBB\\\"\\n\\t\\tif index < len(emojisByRank) {\\n\\t\\t\\temoji = emojisByRank[index]\\n\\t\\t}\\n\\t\\tfmt.Printf(\\\"\\\\n%s %s\\\\n\\\", emoji, stat.Username)\\n\\t\\tfmt.Printf(\\\" (Fetched %d pages from GitLab)\\\\n\\\", stat.PagesFetched)\\n\\n\\t\\t// Print daily, weekly, monthly, yearly bars\\n\\t\\tprintBar(\\\"Today \\\", stat.DailyCommits+(stat.DailyMRs*averageCommitsPerMR), maxDaily)\\n\\t\\tprintBar(\\\"Week \\\", stat.WeeklyCommits+(stat.WeeklyMRs*averageCommitsPerMR), maxWeekly)\\n\\t\\tprintBar(\\\"Month \\\", stat.MonthlyCommits+(stat.MonthlyMRs*averageCommitsPerMR), maxMonthly)\\n\\t\\tprintBar(\\\"Year \\\", stat.YearlyCommits+(stat.YearlyMRs*averageCommitsPerMR), maxYearly)\\n\\n\\t\\tfmt.Printf(\\\"Details: %d commits, %d MRs in the past year. Total Score: %d\\\\n\\\",\\n\\t\\t\\tstat.YearlyCommits, stat.YearlyMRs, stat.YearlyCommits+(stat.YearlyMRs*averageCommitsPerMR))\\n\\t}\\n\\n\\t// sort stats by commits now\\n\\tsort.Slice(stats, func(i, j int) bool {\\n\\t\\treturn stats[i].YearlyCommits > stats[j].YearlyCommits\\n\\t})\\n\\n\\tfmt.Println(\\\"\\\\n\uD83C\uDFC6 Top Committers\\\")\\n\\tfmt.Println(\\\"========================\\\")\\n\\n\\tfor index, stat := range stats {\\n\\t\\t// Use the index to get the corresponding emoji - if we are less than three, use the list\\n\\t\\temoji := \\\"\uD83D\uDC68\u200D\uD83D\uDCBB\\\"\\n\\t\\tif index < len(emojisByRank) {\\n\\t\\t\\temoji = emojisByRank[index]\\n\\t\\t}\\n\\t\\tfmt.Printf(\\\"\\\\n%s %s\\\\n\\\", emoji, stat.Username)\\n\\t\\tfmt.Printf(\\\" (Fetched %d pages from GitLab)\\\\n\\\", stat.PagesFetched)\\n\\n\\t\\t// Print daily, weekly, monthly, yearly bars\\n\\t\\tprintBar(\\\"Today \\\", stat.DailyCommits, maxDailyCommits)\\n\\t\\tprintBar(\\\"Week \\\", stat.WeeklyCommits, maxWeeklyCommits)\\n\\t\\tprintBar(\\\"Month \\\", stat.MonthlyCommits, maxMonthlyCommits)\\n\\t\\tprintBar(\\\"Year \\\", stat.YearlyCommits, maxYearlyCommits)\\n\\n\\t\\tfmt.Printf(\\\"Details: %d commits in the past year\\\\n\\\",\\n\\t\\t\\tstat.YearlyCommits)\\n\\t}\\n\\n\\t// sort stats by MRs now\\n\\tsort.Slice(stats, func(i, j int) bool {\\n\\t\\treturn stats[i].YearlyMRs > stats[j].YearlyMRs\\n\\t})\\n\\n\\tfmt.Println(\\\"\\\\n\uD83C\uDFC6 Top Merge Requesters\\\")\\n\\tfmt.Println(\\\"========================\\\")\\n\\n\\tfor index, stat := range stats {\\n\\t\\t// Use the index to get the corresponding emoji - if we are less than three, use the list\\n\\t\\temoji := \\\"\uD83D\uDC68\u200D\uD83D\uDCBB\\\"\\n\\t\\tif index < len(emojisByRank) {\\n\\t\\t\\temoji = emojisByRank[index]\\n\\t\\t}\\n\\t\\tfmt.Printf(\\\"\\\\n%s %s\\\\n\\\", emoji, stat.Username)\\n\\t\\tfmt.Printf(\\\" (Fetched %d pages from GitLab)\\\\n\\\", stat.PagesFetched)\\n\\n\\t\\t// Print daily, weekly, monthly, yearly bars\\n\\t\\tprintBar(\\\"Today \\\", stat.DailyMRs, maxDailyMRs)\\n\\t\\tprintBar(\\\"Week \\\", stat.WeeklyMRs, maxWeeklyMRs)\\n\\t\\tprintBar(\\\"Month \\\", stat.MonthlyMRs, maxMonthlyMRs)\\n\\t\\tprintBar(\\\"Year \\\", stat.YearlyMRs, maxYearlyMRs)\\n\\n\\t\\tfmt.Printf(\\\"Details: %d MRs in the past year\\\\n\\\",\\n\\t\\t\\tstat.YearlyMRs)\\n\\t}\\n}\\n\\nfunc printBar(label string, value, maxValue int) {\\n\\tconst barWidth = 30\\n\\tvar percentage float64\\n\\tif maxValue > 0 {\\n\\t\\tpercentage = float64(value) / float64(maxValue) * 100\\n\\t}\\n\\n\\tfilledWidth := int(float64(barWidth) * percentage / 100)\\n\\tbar := strings.Repeat(\\\"\u2588\\\", filledWidth) + strings.Repeat(\\\"\u2591\\\", barWidth-filledWidth)\\n\\n\\tfmt.Printf(\\\"%s [%s] %.1f%% (%d)\\\\n\\\", label, bar, percentage, value)\\n}\\n\\nfunc max(a, b int) int {\\n\\tif a > b {\\n\\t\\treturn a\\n\\t}\\n\\treturn b\\n}\\n\",\n \"language\": \"Go\",\n \"caretPosition\": {\n \"row\": 0,\n \"col\": 0\n }\n },\n \"main_test.go\": {\n \"type\": \"file\",\n \"content\": \"package main\\n\\nimport (\\n\\t\\\"log\\\"\\n\\t\\\"os\\\"\\n\\t\\\"testing\\\"\\n\\t\\\"time\\\"\\n\\n\\t\\\"github.com/joho/godotenv\\\"\\n\\t\\\"solve.ch/solvers/http_helper\\\"\\n)\\n\\nfunc TestGetUserActivity(t *testing.T) {\\n\\t// Load .env file\\n\\terr := godotenv.Load()\\n\\tif err != nil {\\n\\t\\tlog.Println(\\\"Warning: Error loading .env file:\\\", err)\\n\\t}\\n\\n\\t// Get GitLab token from environment\\n\\ttoken := os.Getenv(\\\"GITLAB_TOKEN\\\")\\n\\tif token == \\\"\\\" {\\n\\t\\tt.Fatal(\\\"GITLAB_TOKEN environment variable not set\\\")\\n\\t}\\n\\n\\t// Set up time range for last 7 days\\n\\tnow := time.Now()\\n\\tsevenDaysAgo := now.AddDate(0, 0, -7)\\n\\n\\t// Test GetUserActivity function\\n\\tcommits, mrs, err := GetUserActivity(token, \\\"cfrewin\\\", sevenDaysAgo, now)\\n\\tif err != nil {\\n\\t\\tt.Fatalf(\\\"GetUserActivity failed: %v\\\", err)\\n\\t}\\n\\n\\t// Log the results\\n\\tt.Logf(\\\"Activity for user 'cfrewin' in the last 7 days:\\\")\\n\\tt.Logf(\\\"Commits: %d\\\", commits)\\n\\tt.Logf(\\\"Merge Requests: %d\\\", mrs)\\n\\n\\t// From the sample JSON, we can see several commits and MRs\\n\\t// Let's add some basic assertions\\n\\tif commits == 0 {\\n\\t\\tt.Log(\\\"Warning: No commits found in the last 7 days\\\")\\n\\t}\\n\\tif mrs < 0 {\\n\\t\\tt.Error(\\\"Number of merge requests should not be negative\\\")\\n\\t}\\n\\n\\t// Add detailed logging of commits by looking at the raw response\\n\\tresponseData, err := http_helper.MakeHTTPRequest(\\n\\t\\t\\\"https://git.solve.ch/api/v4/users/cfrewin/events\\\",\\n\\t\\t\\\"GET\\\",\\n\\t\\tmap[string]string{\\\"PRIVATE-TOKEN\\\": token},\\n\\t\\tnil,\\n\\t\\tnil,\\n\\t)\\n\\tif err != nil {\\n\\t\\tt.Logf(\\\"Failed to get raw events: %v\\\", err)\\n\\t} else {\\n\\t\\tt.Logf(\\\"Raw response: %s\\\", string(responseData))\\n\\t}\\n}\\n\",\n \"language\": \"Go\",\n \"caretPosition\": {\n \"row\": 0,\n \"col\": 0\n }\n },\n \"usernames.txt\": {\n \"type\": \"file\",\n \"content\": \"cfrewin\\nsgacond\\nthuber\\nsgulas\\nvkamberi\\nurust\\nsreber\\nsrupp\\nmsobotka\\ndavid.ritter\\nrlauber\\nmkohler\\nakati\\nmhemmi\\njheron\",\n \"language\": \"Plain Text\",\n \"caretPosition\": {\n \"row\": 0,\n \"col\": 0\n }\n }\n }\n },\n \"editorSnapshot\": {\n \"isEditorContextMenuOpen\": false,\n \"editors\": []\n },\n \"terminalSnapshot\": {\n \"terminals\": []\n },\n \"mouseSnapshot\": {\n \"location\": \"editor\",\n \"currentHoveredFileName\": \"\",\n \"currentHoveredFolderName\": \"\",\n \"currentHoveredEditorTabFileName\": \"\",\n \"x\": 0,\n \"y\": 0,\n \"timestamp\": 0,\n \"type\": \"move\",\n \"buttonStates\": {\n \"left\": false,\n \"right\": false,\n \"middle\": false\n },\n \"scrollPosition\": {\n \"x\": 0,\n \"y\": 0\n }\n },\n \"authorSnapshot\": {\n \"authors\": [\n {\n \"currentSpeechCaption\": \"\"\n }\n ]\n }\n }\n }\n }\n]\n\n// returns a JSON representation of an example lesson based on keywords\n// the LLM can use this to learn real example lessons created by humans\nexport const getExampleLessonByKeyword = (keyword: string | string[]): string => {\n const exampleJSONs: string[] = [];\n\n if (Array.isArray(keyword)) {\n // if the keyword is an array, loop over all keywords and get the example lesson for each one\n for (var i = 0; i < keyword.length; i++) {\n const exampleJSON = getExampleLessonByKeyword(keyword[i]);\n exampleJSONs.push(exampleJSON);\n }\n return exampleJSONs.join(\", \");\n } else {\n // if the keyword is a string, get the example lesson for that keyword\n const exampleJSON = getSingleExampleLesson(keyword);\n if (exampleJSON) {\n return exampleJSON;\n }\n }\n // if no keywords match, return an empty string\n return \"\";\n}\n\nconst getSingleExampleLesson = (keyword: string): string => {\n // loop over all keywords and find the first one that matches\n for (var i = 0; i < keywordsToExampleLessonsMap.length; i++) {\n const keywordMap = keywordsToExampleLessonsMap[i];\n if (keywordMap.keywords.includes(keyword)) {\n return JSON.stringify(keywordMap.lesson, null, 2);\n }\n }\n\n // if no keywords match, return an empty string\n return \"\";\n}"],
5
+ "mappings": "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SAAS;AAAA,MACb;AAAA,MACA;AAAA,QACI,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,MACA;AAAA,QACI,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,MACA;AAAA,QACI,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,MACA;AAAA,QACI,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,MACA;AAAA,QACI,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,MACA;AAAA,QACI,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,MACA;AAAA,QACI,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,MACA;AAAA,QACI,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,MACA;AAAA,QACI,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,MACA;AAAA,QACI,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,MACA;AAAA,QACI,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,MACA;AAAA,QACI,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,MACA;AAAA,QACI,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,MACA;AAAA,QACI,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,MACA;AAAA,QACI,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,MACA;AAAA,QACI,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,MACA;AAAA,QACI,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,MACA;AAAA,QACI,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,MACA;AAAA,QACI,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,MACA;AAAA,QACI,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,MACA;AAAA,QACI,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,MACA;AAAA,QACI,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,MACA;AAAA,QACI,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,MACA;AAAA,QACI,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,MACA;AAAA,QACI,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,MACA;AAAA,QACI,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,MACA;AAAA,QACI,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,MACA;AAAA,QACI,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,MACA;AAAA,QACI,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,MACA;AAAA,QACI,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,MACA;AAAA,QACI,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,IACJ;AAAA,EACJ;AACJ;AAIO,IAAM,kCAAkC,CAAC,YAAuC;AACnF,QAAM,eAAyB,CAAC;AAEhC,MAAI,MAAM,QAAQ,OAAO,GAAG;AAExB,aAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACrC,YAAM,cAAc,gCAAgC,QAAQ,CAAC,CAAC;AAC9D,mBAAa,KAAK,WAAW;AAAA,IACjC;AACA,WAAO,aAAa,KAAK,IAAI;AAAA,EACjC,OAAO;AAEH,UAAM,cAAc,6BAA6B,OAAO;AACxD,QAAI,aAAa;AACb,aAAO;AAAA,IACX;AAAA,EACJ;AAEA,SAAO;AACX;AAEA,IAAM,+BAA+B,CAAC,YAA4B;AAE9D,WAAS,IAAI,GAAG,IAAI,4BAA4B,QAAQ,KAAK;AACzD,UAAM,aAAa,4BAA4B,CAAC;AAChD,QAAI,WAAW,SAAS,SAAS,OAAO,GAAG;AACvC,aAAO,KAAK,UAAU,WAAW,OAAO;AAAA,IAC5C;AAAA,EACJ;AAGA,SAAO;AACX;;;ACpWA,IAAM,8BAA8E;AAAA,EAChF;AAAA,IACI,UAAU,CAAC,MAAM,UAAU,oBAAoB,qBAAqB,aAAa;AAAA,IACjF,QAAQ;AAAA,MACJ,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,eAAe;AAAA,MACf,WAAW;AAAA,QACP;AAAA,UACI,QAAQ;AAAA,UACR,SAAS;AAAA,QACb;AAAA,QACA;AAAA,UACI,QAAQ;AAAA,UACR,SAAS;AAAA,QACb;AAAA,QACA;AAAA,UACI,QAAQ;AAAA,UACR,SAAS;AAAA,QACb;AAAA,QACA;AAAA,UACI,QAAQ;AAAA,UACR,SAAS;AAAA,QACb;AAAA,QACA;AAAA,UACI,QAAQ;AAAA,UACR,SAAS;AAAA,QACb;AAAA,QACA;AAAA,UACI,QAAQ;AAAA,UACR,SAAS;AAAA,QACb;AAAA,QACA;AAAA,UACI,QAAQ;AAAA,UACR,SAAS;AAAA,QACb;AAAA,QACA;AAAA,UACI,QAAQ;AAAA,UACR,SAAS;AAAA,QACb;AAAA,QACA;AAAA,UACI,QAAQ;AAAA,UACR,SAAS;AAAA,QACb;AAAA,QACA;AAAA,UACI,QAAQ;AAAA,UACR,SAAS;AAAA,QACb;AAAA,QACA;AAAA,UACI,QAAQ;AAAA,UACR,SAAS;AAAA,QACb;AAAA,QACA;AAAA,UACI,QAAQ;AAAA,UACR,SAAS;AAAA,QACb;AAAA,QACA;AAAA,UACI,QAAQ;AAAA,UACR,SAAS;AAAA,QACb;AAAA,QACA;AAAA,UACI,QAAQ;AAAA,UACR,SAAS;AAAA,QACb;AAAA,QACA;AAAA,UACI,QAAQ;AAAA,UACR,SAAS;AAAA,QACb;AAAA,QACA;AAAA,UACI,QAAQ;AAAA,UACR,SAAS;AAAA,QACb;AAAA,QACA;AAAA,UACI,QAAQ;AAAA,UACR,SAAS;AAAA,QACb;AAAA,QACA;AAAA,UACI,QAAQ;AAAA,UACR,SAAS;AAAA,QACb;AAAA,QACA;AAAA,UACI,QAAQ;AAAA,UACR,SAAS;AAAA,QACb;AAAA,QACA;AAAA,UACI,QAAQ;AAAA,UACR,SAAS;AAAA,QACb;AAAA,QACA;AAAA,UACI,QAAQ;AAAA,UACR,SAAS;AAAA,QACb;AAAA,QACA;AAAA,UACI,QAAQ;AAAA,UACR,SAAS;AAAA,QACb;AAAA,QACA;AAAA,UACI,QAAQ;AAAA,UACR,SAAS;AAAA,QACb;AAAA,QACA;AAAA,UACI,QAAQ;AAAA,UACR,SAAS;AAAA,QACb;AAAA,QACA;AAAA,UACI,QAAQ;AAAA,UACR,SAAS;AAAA,QACb;AAAA,QACA;AAAA,UACI,QAAQ;AAAA,UACR,SAAS;AAAA,QACb;AAAA,QACA;AAAA,UACI,QAAQ;AAAA,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",
6
+ "names": ["fs", "z", "isRepeatableAction", "fs", "fs", "generateMarkdownStringFromActions", "generateMarkdownStringFromActions", "os", "path", "fs", "os", "path", "timestamp", "fs", "fs", "os", "path", "fs", "path", "path", "path", "SCHEMA", "path", "fs", "fs", "path", "spawn", "fs", "path", "spawn", "path", "os", "fs", "AllActionDescriptions", "fs", "path", "isValidAction", "randomUUID", "VirtualIDE", "VirtualIDE", "randomUUID", "createRequire", "require", "path", "isValidAction", "fs", "os", "fs", "path", "ok", "path", "os", "fs", "z", "isRepeatableAction", "path", "fs"]
7
+ }