- The path you followed doesn't exist — but a learning path does. Try one of these instead.
+ The path you followed doesn't exist - but a learning path does. Try one of these instead.
r.slug === slug);
if (!role) return {};
- const title = `${role.tagline} — build real skills in 30 days`;
+ const title = `${role.tagline} - build real skills in 30 days`;
const description = `${role.blurb} Project-based, reviewed by the AI Guide, with role-mapped curricula for ${role.tools.slice(0, 3).join(', ')} and more.`;
return {
diff --git a/apps/web/src/app/roles/page.tsx b/apps/web/src/app/roles/page.tsx
index 3e97a38..baa5391 100644
--- a/apps/web/src/app/roles/page.tsx
+++ b/apps/web/src/app/roles/page.tsx
@@ -6,14 +6,14 @@ import { Eyebrow } from '@/components/ui/primitives';
import { ROLES, SITE_URL } from '@/lib/seo-data';
export const metadata: Metadata = {
- title: 'AI training by role — PMs, engineers, designers and more',
+ title: 'AI training by role - PMs, engineers, designers and more',
description:
'Role-mapped AI curricula for Product Managers, Software Engineers, Designers, Data Analysts and more. 30 days. Project-based. Reviewed by the AI Guide.',
alternates: { canonical: '/roles' },
openGraph: {
type: 'website',
url: `${SITE_URL}/roles`,
- title: 'AI training by role — LearnKit AI',
+ title: 'AI training by role - LearnKit AI',
description:
'Role-mapped AI curricula for every team. PMs, engineers, designers, analysts, marketers, founders, ops, researchers.',
},
diff --git a/apps/web/src/app/teams/page.tsx b/apps/web/src/app/teams/page.tsx
index 74e1722..9a9dd2c 100644
--- a/apps/web/src/app/teams/page.tsx
+++ b/apps/web/src/app/teams/page.tsx
@@ -6,14 +6,14 @@ import { Eyebrow, AmbientArc } from '@/components/ui/primitives';
import { SITE_URL } from '@/lib/seo-data';
export const metadata: Metadata = {
- title: 'LearnKit AI for Teams — Embed AI learning across your org',
+ title: 'LearnKit AI for Teams - Embed AI learning across your org',
description:
'Role-mapped learning paths for every engineer, PM, designer, and data scientist on your team. Self-hosted, open source, no SSO contracts.',
alternates: { canonical: '/teams' },
openGraph: {
type: 'website',
url: `${SITE_URL}/teams`,
- title: 'LearnKit AI for Teams — Embed AI learning across your org',
+ title: 'LearnKit AI for Teams - Embed AI learning across your org',
description:
'Role-mapped learning paths for every engineer, PM, designer, and data scientist on your team. Open source, Apache-2.0.',
},
@@ -34,7 +34,7 @@ const FEATURES = [
},
{
t: 'Pure TypeScript, no lock-in',
- d: 'generateLearningPath() is a pure function — no network, no LLM, no API keys. Clone it, fork it, change the lesson templates. Apache-2.0.',
+ d: 'generateLearningPath() is a pure function - no network, no LLM, no API keys. Clone it, fork it, change the lesson templates. Apache-2.0.',
},
{
t: 'Every role covered out of the box',
@@ -42,7 +42,7 @@ const FEATURES = [
},
{
t: 'Works where your team works',
- d: 'Next.js, Vite, Remix, plain React. The packages ship as source TypeScript — no pre-build step required, bundle it with whatever you use.',
+ d: 'Next.js, Vite, Remix, plain React. The packages ship as source TypeScript - no pre-build step required, bundle it with whatever you use.',
},
];
@@ -126,7 +126,7 @@ export default function TeamsPage() {
}}
>
Make every team
-
good at AI — in your own stack.
+
good at AI - in your own stack.
Role-mapped curricula embedded directly in the tools your team already uses.
- Self-hosted, Apache-2.0, no per-seat contract — ever.
+ Self-hosted, Apache-2.0, no per-seat contract - ever.
t.slug === slug);
if (!tool) return {};
- const title = `Learn ${tool.name} in 30 days — ${tool.vendor} training that ships`;
+ const title = `Learn ${tool.name} in 30 days - ${tool.vendor} training that ships`;
const description = `${tool.tagline}. ${tool.modules} project-based modules, ~${tool.hours} hours, reviewed by the AI Guide. From beginner prompts to production agents.`;
return {
@@ -41,10 +41,10 @@ export async function generateMetadata(
}
const CURRICULUM_PHASES = [
- { phase: 'Week 1 — Fundamentals', count: 3 },
- { phase: 'Week 2 — Workflows', count: 3 },
- { phase: 'Week 3 — Production', count: 3 },
- { phase: 'Week 4 — Practicum', count: 3 },
+ { phase: 'Week 1 - Fundamentals', count: 3 },
+ { phase: 'Week 2 - Workflows', count: 3 },
+ { phase: 'Week 3 - Production', count: 3 },
+ { phase: 'Week 4 - Practicum', count: 3 },
];
export default async function ToolPage({ params }: { params: Promise<{ slug: string }> }) {
@@ -245,7 +245,7 @@ export default async function ToolPage({ params }: { params: Promise<{ slug: str
},
{
h: 'AI Guide reviews your work',
- b: 'Your prompts get read, your agents get critiqued, and your evals get scored — by an LLM that knows what bad output looks like.',
+ b: 'Your prompts get read, your agents get critiqued, and your evals get scored - by an LLM that knows what bad output looks like.',
},
{
h: `${tool.vendor}'s latest, kept current`,
diff --git a/apps/web/src/app/tools/page.tsx b/apps/web/src/app/tools/page.tsx
index d2d53e9..1fe394d 100644
--- a/apps/web/src/app/tools/page.tsx
+++ b/apps/web/src/app/tools/page.tsx
@@ -6,14 +6,14 @@ import { Eyebrow, ToolIcon } from '@/components/ui/primitives';
import { SITE_URL, TOOLS } from '@/lib/seo-data';
export const metadata: Metadata = {
- title: 'Learn 40+ AI tools — Claude, Cursor, ChatGPT & more',
+ title: 'Learn 40+ AI tools - Claude, Cursor, ChatGPT & more',
description:
'Project-based learning paths for every major AI tool. Pick your tool, get a 30-day path, ship something Friday.',
alternates: { canonical: '/tools' },
openGraph: {
type: 'website',
url: `${SITE_URL}/tools`,
- title: 'Learn 40+ AI tools — LearnKit AI',
+ title: 'Learn 40+ AI tools - LearnKit AI',
description:
'Project-based learning paths for Claude, Cursor, ChatGPT, Copilot, Midjourney, Notion AI, Perplexity, Gemini and 32 more.',
},
diff --git a/apps/web/src/components/demo/DemoFlow.tsx b/apps/web/src/components/demo/DemoFlow.tsx
index 0e60ed1..b9d8fa1 100644
--- a/apps/web/src/components/demo/DemoFlow.tsx
+++ b/apps/web/src/components/demo/DemoFlow.tsx
@@ -263,7 +263,7 @@ export function DemoFlow() {
Which tools are
already in your stack?
>
}
- sub="Pick what you use today — even if you barely know how. The AI Guide will fill the gaps."
+ sub="Pick what you use today - even if you barely know how. The AI Guide will fill the gaps."
>
-
Got something else? The AI Guide covers 40+ tools — you can add the rest after signup.
+
Got something else? The AI Guide covers 40+ tools - you can add the rest after signup.
setStep(0)} onNext={advance} canNext={tools.length > 0} />
)}
@@ -547,7 +547,7 @@ export function DemoFlow() {
Here's your 30 days .
>
}
- sub={`Tuned for a ${role} working with ${tools.join(' + ')}. Reorder, swap, or skip — it's yours.`}
+ sub={`Tuned for a ${role} working with ${tools.join(' + ')}. Reorder, swap, or skip - it's yours.`}
>
- Week 1 — {path.weeks[0]!.title}
+ Week 1 - {path.weeks[0]!.title}
{path.weeks[0]!.lessons.map((l, i) => (
{role}. Your primary tool is{' '}
{firstLesson.tool} . The exercise
- below is a real task — not a tutorial. Do it before reading ahead.
+ below is a real task - not a tutorial. Do it before reading ahead.
Embed the AI Guide in three lines. Run evals on your users' work. Fork our
- rubrics — the SDKs are open. Build adaptive learning into your product without
+ rubrics - the SDKs are open. Build adaptive learning into your product without
writing the pedagogy yourself.
diff --git a/apps/web/src/components/developers/EndpointGrid.tsx b/apps/web/src/components/developers/EndpointGrid.tsx
index c54bbeb..7db5bf7 100644
--- a/apps/web/src/components/developers/EndpointGrid.tsx
+++ b/apps/web/src/components/developers/EndpointGrid.tsx
@@ -87,7 +87,7 @@ export function EndpointGrid() {
maxWidth: 680,
}}
>
- Small, stable, and inferred from Zod. The shape of
LearningPathInput is a contract — it will not change without a major version bump.
+ Small, stable, and inferred from Zod. The shape of
LearningPathInput is a contract - it will not change without a major version bump.
A React component, a Web Component, or a vanilla JS bundle. White-label the avatar,
- the voice, and the lesson library — keep your brand, get the pedagogy.
+ the voice, and the lesson library - keep your brand, get the pedagogy.
{FEATURES.map((f) => (
diff --git a/apps/web/src/components/layout/Nav.tsx b/apps/web/src/components/layout/Nav.tsx
index bf77cac..2db1f3d 100644
--- a/apps/web/src/components/layout/Nav.tsx
+++ b/apps/web/src/components/layout/Nav.tsx
@@ -100,7 +100,7 @@ export function Nav() {
- {/* Mobile hamburger button — visible only when lk-nav-links is hidden */}
+ {/* Mobile hamburger button - visible only when lk-nav-links is hidden */}
setOpen((o) => !o)}
diff --git a/apps/web/src/components/sections/FinalCTA.tsx b/apps/web/src/components/sections/FinalCTA.tsx
index 3d5ef79..412473d 100644
--- a/apps/web/src/components/sections/FinalCTA.tsx
+++ b/apps/web/src/components/sections/FinalCTA.tsx
@@ -42,7 +42,7 @@ export function FinalCTA() {
maxWidth: 480,
}}
>
- 90 seconds, no signup. Tell the AI Guide what you do — see your curriculum.
+ 90 seconds, no signup. Tell the AI Guide what you do - see your curriculum.
- Solid prompt — but no refusal clause. When the PDF lacks a claim, your agent will
+ Solid prompt - but no refusal clause. When the PDF lacks a claim, your agent will
invent one. Add:{' '}
LearnKit AI turns Claude, Cursor, ChatGPT and 40 other tools into a curriculum
- your people learn by building real things at work — reviewed by the AI
+ your people learn by building real things at work - reviewed by the AI
Guide.
diff --git a/apps/web/src/components/sections/HowItWorks.tsx b/apps/web/src/components/sections/HowItWorks.tsx
index 4160724..c267959 100644
--- a/apps/web/src/components/sections/HowItWorks.tsx
+++ b/apps/web/src/components/sections/HowItWorks.tsx
@@ -9,7 +9,7 @@ const STEPS = [
{
n: '02',
title: 'Get a 30-day path',
- body: 'Lessons, projects, and a final practicum — sequenced for your job, not a generic curriculum.',
+ body: 'Lessons, projects, and a final practicum - sequenced for your job, not a generic curriculum.',
},
{
n: '03',
@@ -50,7 +50,7 @@ export function HowItWorks() {
Most AI training is a video library. LearnKit AI gives you a 30-day path, a
- workbench, and a tutor that reads your prompts — built for the work, not the
+ workbench, and a tutor that reads your prompts - built for the work, not the
certificate.
diff --git a/apps/web/src/components/sections/PathsForEveryone.tsx b/apps/web/src/components/sections/PathsForEveryone.tsx
index 68cda42..3f1e6db 100644
--- a/apps/web/src/components/sections/PathsForEveryone.tsx
+++ b/apps/web/src/components/sections/PathsForEveryone.tsx
@@ -24,7 +24,7 @@ const AUDIENCES: Audience[] = [
{
tag: 'For teams',
title: 'Self-host the whole platform on your own infrastructure.',
- copy: 'Clone the repo, deploy to your own infra, swap the lesson library for your stack. Apache-2.0 — no vendor lock-in.',
+ copy: 'Clone the repo, deploy to your own infra, swap the lesson library for your stack. Apache-2.0 - no vendor lock-in.',
bullets: ['Role-mapped curricula', 'No tracking · no phone-home', 'Fork the eval rubrics'],
cta: 'Self-host on GitHub',
href: 'https://github.com/learnkit-ai/learnkit',
diff --git a/apps/web/src/components/sections/Proof.tsx b/apps/web/src/components/sections/Proof.tsx
index 943fc16..a39d2e5 100644
--- a/apps/web/src/components/sections/Proof.tsx
+++ b/apps/web/src/components/sections/Proof.tsx
@@ -59,7 +59,7 @@ export function Proof() {
>
LearnKit AI is built in the open under Apache-2.0. The packages on npm, the
marketing site you are reading right now, and the demo flow are all the same
- source — clone it, fork it, run it locally, deploy it. No license keys, no SaaS
+ source - clone it, fork it, run it locally, deploy it. No license keys, no SaaS
tier, no telemetry to disable.
diff --git a/apps/web/src/components/sections/UseItYourWay.tsx b/apps/web/src/components/sections/UseItYourWay.tsx
index 0e63558..72d4b54 100644
--- a/apps/web/src/components/sections/UseItYourWay.tsx
+++ b/apps/web/src/components/sections/UseItYourWay.tsx
@@ -18,7 +18,7 @@ const DISTRIBUTIONS: Distribution[] = [
desc: 'Use the components in any React app.',
command: 'pnpm add @learnkit-ai/react',
bullets: [
- 'Apache-2.0 — commercial use allowed',
+ 'Apache-2.0 - commercial use allowed',
' , , useLearnKit()',
'No Tailwind dependency · CSS variables only',
'Three built-in themes · drop into any host',
@@ -83,7 +83,7 @@ export function UseItYourWay() {
margin: '12px auto 0',
}}
>
- No paid tier. No usage fee. Apache-2.0 from end to end — install it, fork it, embed
+ No paid tier. No usage fee. Apache-2.0 from end to end - install it, fork it, embed
it, deploy it. We do not gate features behind a credit card.
diff --git a/apps/web/src/components/sections/WorkbenchShowcase.tsx b/apps/web/src/components/sections/WorkbenchShowcase.tsx
index e1503be..a066c45 100644
--- a/apps/web/src/components/sections/WorkbenchShowcase.tsx
+++ b/apps/web/src/components/sections/WorkbenchShowcase.tsx
@@ -147,7 +147,7 @@ function WorkbenchMock() {
overflow: 'hidden',
}}
>
-
# System prompt — research analyst
+
# System prompt - research analyst
You are a{' '}
senior research analyst at a
@@ -208,7 +208,7 @@ function WorkbenchMock() {
AI Guide
- Add a refusal clause — last week your agent invented a citation when it
+ Add a refusal clause - last week your agent invented a citation when it
couldn't find one.
diff --git a/apps/web/src/components/seo/JsonLd.tsx b/apps/web/src/components/seo/JsonLd.tsx
index 86d8e23..85ba592 100644
--- a/apps/web/src/components/seo/JsonLd.tsx
+++ b/apps/web/src/components/seo/JsonLd.tsx
@@ -77,7 +77,7 @@ export const FAQ_SCHEMA = {
name: 'How much does LearnKit AI cost?',
acceptedAnswer: {
'@type': 'Answer',
- text: 'LearnKit AI is free and open source under Apache-2.0. There is no paid tier, no usage fee, and no commercial restriction. Install the packages from npm, self-host the marketing site, or embed the React components in your own product — all without paying anyone.',
+ text: 'LearnKit AI is free and open source under Apache-2.0. There is no paid tier, no usage fee, and no commercial restriction. Install the packages from npm, self-host the marketing site, or embed the React components in your own product - all without paying anyone.',
},
},
{
@@ -93,7 +93,7 @@ export const FAQ_SCHEMA = {
name: 'How do I self-host LearnKit AI?',
acceptedAnswer: {
'@type': 'Answer',
- text: 'Clone the repo, run pnpm install and pnpm dev. The marketing site is a Next.js 15 App Router app — deploy it to Vercel, Netlify, Cloudflare Pages, or any Node host. The packages can be consumed from npm or installed directly from the workspace.',
+ text: 'Clone the repo, run pnpm install and pnpm dev. The marketing site is a Next.js 15 App Router app - deploy it to Vercel, Netlify, Cloudflare Pages, or any Node host. The packages can be consumed from npm or installed directly from the workspace.',
},
},
{
diff --git a/apps/web/src/lib/blog-posts.ts b/apps/web/src/lib/blog-posts.ts
index 4361cd5..1e87f7d 100644
--- a/apps/web/src/lib/blog-posts.ts
+++ b/apps/web/src/lib/blog-posts.ts
@@ -14,7 +14,7 @@ export const BLOG_POSTS: BlogPost[] = [
slug: 'why-ai-training-fails-at-most-companies',
title: 'Why AI training fails at most companies (and what to do instead)',
description:
- 'Most enterprise AI training is a video library nobody finishes. The fix is not better videos — it is project-based learning with real review.',
+ 'Most enterprise AI training is a video library nobody finishes. The fix is not better videos - it is project-based learning with real review.',
date: '2026-05-12',
readMin: 6,
category: 'Pedagogy',
@@ -23,7 +23,7 @@ export const BLOG_POSTS: BlogPost[] = [
body: [
'A new wave of AI training programs has launched in the last 18 months. Almost all of them have the same structure: pre-recorded videos, a quiz, a certificate. They report 70-80% completion rates. They also report almost no measurable change in what their learners can actually build.',
'Why? Because completion is the wrong metric. Watching a 12-minute video on prompt engineering does not teach you to write a prompt that survives contact with your job. It teaches you to recognize the right answer on a multiple-choice quiz.',
- 'The fix is project-based learning with real review. Every lesson should end with something you build — a prompt, an agent, a workflow — that gets read and critiqued by an evaluator who knows what bad output looks like. That evaluator can be an LLM (it scales) or a human (it does not), but it has to give you the kind of feedback that changes the next version of your work.',
+ 'The fix is project-based learning with real review. Every lesson should end with something you build - a prompt, an agent, a workflow - that gets read and critiqued by an evaluator who knows what bad output looks like. That evaluator can be an LLM (it scales) or a human (it does not), but it has to give you the kind of feedback that changes the next version of your work.',
'This is what LearnKit AI does. Lessons end in the workbench, not the quiz. The AI Guide reads your prompts, flags missing refusal clauses, catches under-specified personas, and tells you where your agent will invent a citation. You graduate with a portfolio, not a certificate.',
],
},
@@ -31,12 +31,12 @@ export const BLOG_POSTS: BlogPost[] = [
slug: 'embedding-an-ai-tutor-in-three-lines',
title: 'Embedding an AI tutor in three lines of JavaScript',
description:
- 'A walkthrough of dropping the LearnKit AI Guide into any React app — with custom system prompts, route-aware context, and white-label theming.',
+ 'A walkthrough of dropping the LearnKit AI Guide into any React app - with custom system prompts, route-aware context, and white-label theming.',
date: '2026-05-08',
readMin: 5,
category: 'Engineering',
excerpt:
- 'A walkthrough of dropping the LearnKit AI Guide into any React app — three lines, plus custom system prompts and white-label theming.',
+ 'A walkthrough of dropping the LearnKit AI Guide into any React app - three lines, plus custom system prompts and white-label theming.',
body: [
'The pitch for embedded AI tutors is simple: when a user is about to do something they have not done before, a small floating widget appears and offers a 2-minute refresher. Most teams build this from scratch and burn months on the pedagogy.',
'LearnKit AI Guide is a drop-in React component. Install @learnkit-ai/react, pass an API key and a user ID, and you have a route-aware AI tutor in production. The system prompts are versioned, the lesson library is white-labeled, and the evaluation rubrics are open source.',
@@ -48,15 +48,15 @@ export const BLOG_POSTS: BlogPost[] = [
slug: 'launching-learnkit-ai',
title: 'Launching LearnKit AI',
description:
- 'After two years of building internal AI training at three companies, we are launching LearnKit AI — an open-source workbench and tutor for teams that ship.',
+ 'After two years of building internal AI training at three companies, we are launching LearnKit AI - an open-source workbench and tutor for teams that ship.',
date: '2026-05-01',
readMin: 4,
category: 'Launches',
excerpt:
- 'After two years of building internal AI training at three companies, we are launching LearnKit AI — an open-source workbench and tutor for teams that ship.',
+ 'After two years of building internal AI training at three companies, we are launching LearnKit AI - an open-source workbench and tutor for teams that ship.',
body: [
'Today we are launching LearnKit AI: an open-source TypeScript engine and React component for embedding personalized, role-aware AI learning paths inside SaaS products.',
- 'The team behind LearnKit spent the last two years building internal AI training programs at three companies. The pattern was always the same: a few hundred engineers and PMs, a few weeks of video, a quiz, and almost no measurable change. Meanwhile the actual builders on the team — the ones shipping AI features — learned by doing, with feedback, over months.',
+ 'The team behind LearnKit spent the last two years building internal AI training programs at three companies. The pattern was always the same: a few hundred engineers and PMs, a few weeks of video, a quiz, and almost no measurable change. Meanwhile the actual builders on the team - the ones shipping AI features - learned by doing, with feedback, over months.',
'So we built the thing the builders had. A workbench where the lesson is the work. An AI tutor that reads your prompts. A 30-day path tuned to your role and your stack. And we open-sourced it under Apache-2.0 so any team can fork it.',
'You can try the demo at /demo, read the docs at /developers, or star the repo at github.com/learnkit-ai/learnkit.',
],
@@ -70,10 +70,10 @@ export const BLOG_POSTS: BlogPost[] = [
readMin: 5,
category: 'Engineering',
excerpt:
- 'A deterministic pure function is more trustworthy than a stochastic AI call for generating a learning curriculum — same input, same path, every time.',
+ 'A deterministic pure function is more trustworthy than a stochastic AI call for generating a learning curriculum - same input, same path, every time.',
body: [
'The obvious design for a "AI-powered learning path generator" is to call an LLM. Pass the user\'s role, tools, and goal to Claude or GPT-4, prompt it to generate a 4-week curriculum, and stream the response. We seriously considered this. We did not build it.',
- 'The core problem is trust. A stochastic function cannot be unit-tested, cannot be audited, cannot be reproduced. Every time a PM at a company configures a learning path for their team, they need to know what they are getting. Not "roughly this". Exactly this. If the same input produces different output on Tuesday than it did on Monday, something went wrong — even if both outputs are technically good.',
+ 'The core problem is trust. A stochastic function cannot be unit-tested, cannot be audited, cannot be reproduced. Every time a PM at a company configures a learning path for their team, they need to know what they are getting. Not "roughly this". Exactly this. If the same input produces different output on Tuesday than it did on Monday, something went wrong - even if both outputs are technically good.',
'So generateLearningPath() is a pure function. A djb2 hash of the input produces stable lesson IDs across runs. The week structure, lesson count, and durations are computed deterministically from the role, level, and tools. You can test it with Vitest. You can snapshot it. You can pin a version and know that every engineer who joins your team will get the same week one as the engineer who joined six months ago.',
'The tradeoff is expressiveness. A pure function cannot write prose as varied as GPT-4, cannot adapt to a user\'s previous session history, cannot interpolate from a corpus of real practitioners\' notes. We accepted that tradeoff. Version 0 of LearnKit AI ships zero LLM calls and zero API keys. The AI Guide component is a UI primitive, not a live model. If you want LLM personalization on top, the hook-based API makes it easy to swap in your own inference layer.',
'This is the right first trade. Build the substrate deterministic and testable, then add stochasticity where the variance is a feature, not a bug.',
@@ -92,17 +92,17 @@ export const BLOG_POSTS: BlogPost[] = [
body: [
'LearnKit AI ships as a monorepo under Apache-2.0. Everything is editable. The lesson templates are plain TypeScript objects in packages/core/src/generate.ts. Forking the whole stack and adding your company\'s specific tools, terminology, and focus areas takes about an afternoon.',
'Start by cloning and installing: git clone https://github.com/learnkit-ai/learnkit && cd learnkit && pnpm install. The dev server starts with pnpm dev. You will see the full landing page at localhost:3000 and the interactive demo at /demo.',
- 'The lesson generator is in packages/core/src/generate.ts. The function buildLessonsForWeek() accepts a week number and returns three Lesson objects. The lesson titles, descriptions, and tool assignments are plain strings — search for "week === 1" to find week one, edit the title and description fields to match your stack. Run pnpm test after every change to catch regressions in the schema.',
- 'To add a new role, append a string to the SUPPORTED_ROLES array in packages/core/src/data.ts. To add a new tool, append to SUPPORTED_TOOLS. Both are just string arrays — no type changes required, because the schemas are Zod-validated at runtime.',
- 'For white-labeling the React components, the easiest path is to override CSS custom properties. Drop :root { --accent: #7C3AED; --paper: #FAFAF8; } into your global stylesheet and the warm theme will inherit your brand colors. For deeper changes, the component source is in packages/react/src/ — no Tailwind dependency, just inline styles and CSS custom properties.',
- 'The whole stack — schemas, core, react, and the Next.js demo app — is under Apache-2.0. Fork it, ship it commercially, embed it in a closed-source product. The only requirement is preserving the license notice. PRs back to main welcome.',
+ 'The lesson generator is in packages/core/src/generate.ts. The function buildLessonsForWeek() accepts a week number and returns three Lesson objects. The lesson titles, descriptions, and tool assignments are plain strings - search for "week === 1" to find week one, edit the title and description fields to match your stack. Run pnpm test after every change to catch regressions in the schema.',
+ 'To add a new role, append a string to the SUPPORTED_ROLES array in packages/core/src/data.ts. To add a new tool, append to SUPPORTED_TOOLS. Both are just string arrays - no type changes required, because the schemas are Zod-validated at runtime.',
+ 'For white-labeling the React components, the easiest path is to override CSS custom properties. Drop :root { --accent: #7C3AED; --paper: #FAFAF8; } into your global stylesheet and the warm theme will inherit your brand colors. For deeper changes, the component source is in packages/react/src/ - no Tailwind dependency, just inline styles and CSS custom properties.',
+ 'The whole stack - schemas, core, react, and the Next.js demo app - is under Apache-2.0. Fork it, ship it commercially, embed it in a closed-source product. The only requirement is preserving the license notice. PRs back to main welcome.',
],
},
{
slug: 'embedding-learnkit-in-a-saas-product',
title: 'Embedding AI learning paths in your SaaS onboarding flow',
description:
- 'How to use @learnkit-ai/react to drop a personalized 30-day learning path into a SaaS onboarding modal — with role detection, theme matching, and lesson-click callbacks.',
+ 'How to use @learnkit-ai/react to drop a personalized 30-day learning path into a SaaS onboarding modal - with role detection, theme matching, and lesson-click callbacks.',
date: '2026-05-21',
readMin: 6,
category: 'Engineering',
@@ -111,26 +111,26 @@ export const BLOG_POSTS: BlogPost[] = [
body: [
'SaaS products that sell AI features have a new cold-start problem: users sign up, land in the product, and have no idea how to build a prompt that actually works. The standard fix is a tooltip tour. That is not enough.',
'LearnKit AI is designed to sit inside your product and give each user a personalized 30-day curriculum tuned to their role and the specific AI tools your product exposes. The React component takes a role, a set of tools, and a goal, and renders a full path with no backend required.',
- 'The implementation is three steps. First, detect or ask for the user\'s role at signup — store it in your user record. Second, install @learnkit-ai/react and drop
into your onboarding modal or help sidebar. Third, wire the onLessonClick callback to open whatever lesson viewer you want — the component just fires the Lesson object.',
+ 'The implementation is three steps. First, detect or ask for the user\'s role at signup - store it in your user record. Second, install @learnkit-ai/react and drop
into your onboarding modal or help sidebar. Third, wire the onLessonClick callback to open whatever lesson viewer you want - the component just fires the Lesson object.',
'The useLearnKit() hook gives you a headless alternative if your design system does not match the built-in themes. It returns { path, error } and memoizes the result, so re-renders are safe.',
- 'One detail worth knowing: the path is fully generated client-side. There is no network call in generateLearningPath(). That means it works in SSR, in edge functions, in offline mode. It also means you can pregenerate paths for your most common role/level combinations and cache them at build time — a pattern we use on /demo to make the initial render instant.',
+ 'One detail worth knowing: the path is fully generated client-side. There is no network call in generateLearningPath(). That means it works in SSR, in edge functions, in offline mode. It also means you can pregenerate paths for your most common role/level combinations and cache them at build time - a pattern we use on /demo to make the initial render instant.',
],
},
{
slug: 'theming-learnkit-css-custom-properties',
title: 'Theming LearnKit AI: CSS custom properties as a first-class API',
description:
- 'How @learnkit-ai/react ships three themes with zero Tailwind dependency — and how to drop in your own brand colors in four lines of CSS.',
+ 'How @learnkit-ai/react ships three themes with zero Tailwind dependency - and how to drop in your own brand colors in four lines of CSS.',
date: '2026-05-22',
readMin: 5,
category: 'Engineering',
excerpt:
- 'No Tailwind in @learnkit-ai/react — just CSS custom properties. Override four variables and the entire component tree picks up your brand colors.',
+ 'No Tailwind in @learnkit-ai/react - just CSS custom properties. Override four variables and the entire component tree picks up your brand colors.',
body: [
'Most component libraries that ship theming require you to configure a design token system, install a CSS preprocessor, or wrap your app in a theme provider. We wanted something simpler: drop in four lines of CSS and your product\'s colors work everywhere.',
- '@learnkit-ai/react has no Tailwind dependency. Every style is expressed as an inline CSS value or a CSS custom property reference — things like var(--accent), var(--paper), var(--ink). The three built-in themes (warm, midnight, technical) are just different values for those same twelve variables, applied as inline styles on the root component element.',
- 'To match your brand, you do not need to fork the package. Pass theme="warm" and add a stylesheet that overrides the root variables: :root { --accent: #7C3AED; --paper: #FAFAF9; --ink: #1A1523; --ink-soft: #5B5070; }. The full component tree — cards, progress bars, the AI Guide widget — inherits your values immediately.',
- 'The decision to avoid Tailwind was deliberate. Tailwind requires a build step tuned to the host project\'s content paths. A library that ships Tailwind classes leaks its build config into every project that installs it. CSS custom properties are a browser primitive — they compose without configuration.',
+ '@learnkit-ai/react has no Tailwind dependency. Every style is expressed as an inline CSS value or a CSS custom property reference - things like var(--accent), var(--paper), var(--ink). The three built-in themes (warm, midnight, technical) are just different values for those same twelve variables, applied as inline styles on the root component element.',
+ 'To match your brand, you do not need to fork the package. Pass theme="warm" and add a stylesheet that overrides the root variables: :root { --accent: #7C3AED; --paper: #FAFAF9; --ink: #1A1523; --ink-soft: #5B5070; }. The full component tree - cards, progress bars, the AI Guide widget - inherits your values immediately.',
+ 'The decision to avoid Tailwind was deliberate. Tailwind requires a build step tuned to the host project\'s content paths. A library that ships Tailwind classes leaks its build config into every project that installs it. CSS custom properties are a browser primitive - they compose without configuration.',
'If you want to ship your own named theme, the pattern is simple: export a theme object that maps the twelve variable names to your values, pass it as the theme prop, and the component applies them as a style attribute. We will document the full theme object shape in the v1 docs.',
],
},
@@ -145,10 +145,10 @@ export const BLOG_POSTS: BlogPost[] = [
excerpt:
'Eight roles, eight 4-week curricula, zero shared lessons between them. Here is why role specificity matters and what we got wrong in the first draft.',
body: [
- 'The initial design for LearnKit AI was a single shared curriculum: four weeks of foundational AI skills that any professional could follow. We scrapped it after the second pilot. The problem was not quality — the content was fine. The problem was relevance. A PM and a software engineer sitting through the same "building a research agent" lesson are in completely different contexts. The PM needs to scope the agent to a workflow they own. The engineer needs to write the eval harness. A single lesson cannot do both well.',
- 'So we built eight curricula — one for each supported role — with no shared lessons. Product Managers start with discovery briefs and PRDs before ever writing an agent. Software Engineers start with prompt architecture and eval patterns on day one. Designers start with generating and critiquing copy, not writing code.',
- 'The lesson structure is consistent: three lessons per week, with the third always a project. Projects are the unit of completion that actually matters. They are not exercises — they are deliverables your team could use. The week-three project for a PM is a go-to-market brief. For an Operations specialist, it is a weekly reporting prompt they can run every Monday. For a Researcher, it is a literature synthesis they would file in their research archive.',
- 'The hardest role to design for was Founder. Founders span every function — they are their own PM, their own engineer, their own marketer in the early days. We resisted the temptation to build a generalist curriculum. Instead, the Founder track focuses on the three AI use cases that are most asymmetric for a solo operator: strategic stress-testing (using AI to argue against your own plan), voice-consistent writing at scale, and rapid market validation from public signals.',
+ 'The initial design for LearnKit AI was a single shared curriculum: four weeks of foundational AI skills that any professional could follow. We scrapped it after the second pilot. The problem was not quality - the content was fine. The problem was relevance. A PM and a software engineer sitting through the same "building a research agent" lesson are in completely different contexts. The PM needs to scope the agent to a workflow they own. The engineer needs to write the eval harness. A single lesson cannot do both well.',
+ 'So we built eight curricula - one for each supported role - with no shared lessons. Product Managers start with discovery briefs and PRDs before ever writing an agent. Software Engineers start with prompt architecture and eval patterns on day one. Designers start with generating and critiquing copy, not writing code.',
+ 'The lesson structure is consistent: three lessons per week, with the third always a project. Projects are the unit of completion that actually matters. They are not exercises - they are deliverables your team could use. The week-three project for a PM is a go-to-market brief. For an Operations specialist, it is a weekly reporting prompt they can run every Monday. For a Researcher, it is a literature synthesis they would file in their research archive.',
+ 'The hardest role to design for was Founder. Founders span every function - they are their own PM, their own engineer, their own marketer in the early days. We resisted the temptation to build a generalist curriculum. Instead, the Founder track focuses on the three AI use cases that are most asymmetric for a solo operator: strategic stress-testing (using AI to argue against your own plan), voice-consistent writing at scale, and rapid market validation from public signals.',
'None of the eight curricula are finished. They will not be in v1 either. The right approach is to ship opinionated first drafts, collect signal from what people actually build in week-three projects, and iterate. If you fork the repo and find that one role\'s curriculum is weak, a PR is exactly the right response.',
],
},
@@ -163,11 +163,11 @@ export const BLOG_POSTS: BlogPost[] = [
excerpt:
'companyContext is in the public API schema today but does not yet change the output. Here is what it will do in v1 and why we shipped the field first.',
body: [
- 'LearningPathInput has an optional companyContext field: a string of up to 500 characters describing the learner\'s team, stack, and context. You can pass it today — the schema accepts it and validates it. It does not yet change the generated path. That is intentional, and this post explains why.',
- 'We shipped the field before the feature for two reasons. First, it locks in the API shape before any consuming code depends on a version without it. If we added it in v1, every product that had already shipped LearnKit AI would need to handle a schema change. By shipping it in v0, all valid v0 inputs are valid v1 inputs — the only change is that the engine starts doing something with a field it was already accepting.',
+ 'LearningPathInput has an optional companyContext field: a string of up to 500 characters describing the learner\'s team, stack, and context. You can pass it today - the schema accepts it and validates it. It does not yet change the generated path. That is intentional, and this post explains why.',
+ 'We shipped the field before the feature for two reasons. First, it locks in the API shape before any consuming code depends on a version without it. If we added it in v1, every product that had already shipped LearnKit AI would need to handle a schema change. By shipping it in v0, all valid v0 inputs are valid v1 inputs - the only change is that the engine starts doing something with a field it was already accepting.',
'Second, we needed to decide what "using companyContext" actually means before we built it. The obvious design is string interpolation: embed the context into lesson summary templates wherever there is a gap. The better design is semantic: parse the context for signals (team size, stack, industry, pace) and use those signals to weight lesson selection, adjust project scope, and tune the language of summaries.',
'In v1, companyContext will be parsed for four signals: tech stack keywords (to prefer lessons that use tools the team already has), pace signals ("ships weekly" versus "quarterly releases"), industry signals (regulated industries get different compliance framing), and team size signals ("40-person team" versus "solo founder"). The lesson templates will expose hooks for each signal so the interpolation is structured, not arbitrary.',
- 'If you are building on LearnKit AI today, pass companyContext. It will be silently ignored for now. In v1, it will start doing something useful — and your integration will not need to change.',
+ 'If you are building on LearnKit AI today, pass companyContext. It will be silently ignored for now. In v1, it will start doing something useful - and your integration will not need to change.',
],
},
];
diff --git a/apps/web/src/lib/changelog-data.ts b/apps/web/src/lib/changelog-data.ts
index 32e5daa..fd19bfd 100644
--- a/apps/web/src/lib/changelog-data.ts
+++ b/apps/web/src/lib/changelog-data.ts
@@ -16,39 +16,39 @@ export const CHANGELOG: ChangelogVersion[] = [
version: 'Unreleased',
date: null,
entries: [
- { kind: 'Added', text: 'Level picker (Beginner / Intermediate / Advanced) in the /demo flow — wired to generateLearningPath() to adjust lesson pacing' },
- { kind: 'Added', text: 'Optional company context textarea in /demo — passes companyContext to the engine to personalise project lesson summaries' },
- { kind: 'Added', text: 'Role-specific 4-week curricula for Marketer, Founder, Operations, and Researcher — all 8 supported roles now have custom content' },
- { kind: 'Added', text: 'ROADMAP.md — v0 done, v1 planned, v2 exploratory, explicit no-go list' },
+ { kind: 'Added', text: 'Level picker (Beginner / Intermediate / Advanced) in the /demo flow - wired to generateLearningPath() to adjust lesson pacing' },
+ { kind: 'Added', text: 'Optional company context textarea in /demo - passes companyContext to the engine to personalise project lesson summaries' },
+ { kind: 'Added', text: 'Role-specific 4-week curricula for Marketer, Founder, Operations, and Researcher - all 8 supported roles now have custom content' },
+ { kind: 'Added', text: 'ROADMAP.md - v0 done, v1 planned, v2 exploratory, explicit no-go list' },
{ kind: 'Added', text: '4 new blog posts: CSS theming deep-dive, role curriculum design decisions, companyContext field explainer, and SaaS embedding guide' },
- { kind: 'Added', text: 'Mobile hamburger nav — slide-down menu with all nav links, accessible and keyboard-friendly' },
- { kind: 'Added', text: '/changelog page — this page, derived from CHANGELOG.md' },
- { kind: 'Added', text: '@learnkit-ai/cli — npx @learnkit-ai/cli generate outputs a JSON or pretty-printed learning path with zero setup' },
- { kind: 'Changed', text: 'companyContext field now used in project lesson summaries — parsed for stack, pace, and team-size signals' },
+ { kind: 'Added', text: 'Mobile hamburger nav - slide-down menu with all nav links, accessible and keyboard-friendly' },
+ { kind: 'Added', text: '/changelog page - this page, derived from CHANGELOG.md' },
+ { kind: 'Added', text: '@learnkit-ai/cli - npx @learnkit-ai/cli generate outputs a JSON or pretty-printed learning path with zero setup' },
+ { kind: 'Changed', text: 'companyContext field now used in project lesson summaries - parsed for stack, pace, and team-size signals' },
],
},
{
version: '0.1.0',
date: '2026-05-12',
entries: [
- { kind: 'Added', text: 'Custom 404 page — branded design with Nav, serif headline, and three CTA links' },
- { kind: 'Added', text: 'OG image — updated headline "Personalized AI paths for every role" and OSS-aligned description' },
+ { kind: 'Added', text: 'Custom 404 page - branded design with Nav, serif headline, and three CTA links' },
+ { kind: 'Added', text: 'OG image - updated headline "Personalized AI paths for every role" and OSS-aligned description' },
{ kind: 'Added', text: 'Example page metadata via layout.tsx (page itself stays \'use client\')' },
- { kind: 'Fixed', text: 'Footer dead links — all href="#" replaced with real GitHub URLs (SECURITY.md, LICENSE, CONTRIBUTING.md)' },
+ { kind: 'Fixed', text: 'Footer dead links - all href="#" replaced with real GitHub URLs (SECURITY.md, LICENSE, CONTRIBUTING.md)' },
{ kind: 'Fixed', text: '"Docs" footer link corrected to /docs (was pointing to /developers)' },
{ kind: 'Changed', text: 'Footer brand tagline updated to OSS messaging: "Open-source TypeScript engine for embedding personalized AI learning paths"' },
- { kind: 'Added', text: 'CodeBlock copy button — wired with navigator.clipboard.writeText; shows "copied!" with green colour for 1.8s' },
- { kind: 'Added', text: '/teams page — OSS-framed guide for embedding LearnKit AI across an org' },
- { kind: 'Added', text: '/docs page — full API reference with sticky sidebar and IntersectionObserver-based active-section highlighting' },
+ { kind: 'Added', text: 'CodeBlock copy button - wired with navigator.clipboard.writeText; shows "copied!" with green colour for 1.8s' },
+ { kind: 'Added', text: '/teams page - OSS-framed guide for embedding LearnKit AI across an org' },
+ { kind: 'Added', text: '/docs page - full API reference with sticky sidebar and IntersectionObserver-based active-section highlighting' },
{ kind: 'Added', text: 'DocsSidebar client component with IntersectionObserver-based active section tracking' },
{ kind: 'Added', text: '6 blog posts across Pedagogy, Engineering, and Launches categories' },
- { kind: 'Added', text: 'tsup build for all packages — ESM + CJS + .d.ts output with "source" custom export condition' },
- { kind: 'Added', text: '21 React component tests — LearningPath, LessonCard, AIGuide, useLearnKit() using @testing-library/react + happy-dom' },
+ { kind: 'Added', text: 'tsup build for all packages - ESM + CJS + .d.ts output with "source" custom export condition' },
+ { kind: 'Added', text: '21 React component tests - LearningPath, LessonCard, AIGuide, useLearnKit() using @testing-library/react + happy-dom' },
{ kind: 'Added', text: 'Per-role lesson curricula for Software Engineer, Product Manager, Designer, and Data Analyst' },
- { kind: 'Added', text: 'vitest.config.ts with resolve.alias in core and react packages — resolves workspace deps to TypeScript source without pre-build' },
- { kind: 'Changed', text: '/developers page — rewrote to show the real package API surface instead of fabricated REST endpoints' },
- { kind: 'Changed', text: 'UseItYourWay section — replaced pricing cards with OSS install/embed/self-host options' },
- { kind: 'Changed', text: 'Proof section — replaced fake testimonials with real OSS facts' },
+ { kind: 'Added', text: 'vitest.config.ts with resolve.alias in core and react packages - resolves workspace deps to TypeScript source without pre-build' },
+ { kind: 'Changed', text: '/developers page - rewrote to show the real package API surface instead of fabricated REST endpoints' },
+ { kind: 'Changed', text: 'UseItYourWay section - replaced pricing cards with OSS install/embed/self-host options' },
+ { kind: 'Changed', text: 'Proof section - replaced fake testimonials with real OSS facts' },
{ kind: 'Fixed', text: 'getSupportedRoles() comment in docs now shows the actual return value' },
{ kind: 'Fixed', text: 'next-env.d.ts added to .gitignore' },
],
@@ -57,13 +57,13 @@ export const CHANGELOG: ChangelogVersion[] = [
version: '0.0.1',
date: '2026-05-01',
entries: [
- { kind: 'Added', text: 'packages/schemas — @learnkit-ai/schemas: Zod schemas and inferred TypeScript types' },
- { kind: 'Added', text: 'packages/core — @learnkit-ai/core: generateLearningPath(), getSupportedRoles(), getSupportedTools(), isRoleSupported()' },
- { kind: 'Added', text: 'packages/react — @learnkit-ai/react:
,
,
, useLearnKit()' },
- { kind: 'Added', text: 'apps/web — Next.js 15 App Router landing page, /demo, /docs, /roles, /tools, /blog, /example, /developers' },
- { kind: 'Added', text: 'examples/nextjs-basic — standalone Next.js integration example' },
- { kind: 'Added', text: 'GitHub Actions CI — install, lint, typecheck, test, build pipeline' },
- { kind: 'Added', text: '.agent/ — Agent Anatomy universal config with rules, commands, agents, tasks' },
+ { kind: 'Added', text: 'packages/schemas - @learnkit-ai/schemas: Zod schemas and inferred TypeScript types' },
+ { kind: 'Added', text: 'packages/core - @learnkit-ai/core: generateLearningPath(), getSupportedRoles(), getSupportedTools(), isRoleSupported()' },
+ { kind: 'Added', text: 'packages/react - @learnkit-ai/react:
,
,
, useLearnKit()' },
+ { kind: 'Added', text: 'apps/web - Next.js 15 App Router landing page, /demo, /docs, /roles, /tools, /blog, /example, /developers' },
+ { kind: 'Added', text: 'examples/nextjs-basic - standalone Next.js integration example' },
+ { kind: 'Added', text: 'GitHub Actions CI - install, lint, typecheck, test, build pipeline' },
+ { kind: 'Added', text: '.agent/ - Agent Anatomy universal config with rules, commands, agents, tasks' },
{ kind: 'Added', text: 'Apache-2.0 license, CONTRIBUTING.md, SECURITY.md, CODE_OF_CONDUCT.md' },
],
},
diff --git a/apps/web/src/lib/compare-data.ts b/apps/web/src/lib/compare-data.ts
index 83b99b6..ee66d15 100644
--- a/apps/web/src/lib/compare-data.ts
+++ b/apps/web/src/lib/compare-data.ts
@@ -26,7 +26,7 @@ export const COMPARISONS: ComparisonPage[] = [
role: 'Software Engineer',
tagline: 'Claude vs ChatGPT for Software Engineers',
intro:
- 'Both Claude and ChatGPT can write, review, and explain code — but they make different trade-offs that matter when you are shipping production software. This comparison covers the dimensions engineers actually care about.',
+ 'Both Claude and ChatGPT can write, review, and explain code - but they make different trade-offs that matter when you are shipping production software. This comparison covers the dimensions engineers actually care about.',
verdict:
'Claude tends to be stronger for large-codebase reasoning, structured output, and following constrained system prompts without drift. ChatGPT wins on ecosystem breadth, Code Interpreter for exploratory data work, and the GPT Store for pre-built task-specific models. Most senior engineers use both with purpose-built prompts rather than picking one.',
points: [
@@ -70,7 +70,7 @@ export const COMPARISONS: ComparisonPage[] = [
role: 'Software Engineer',
tagline: 'Cursor vs GitHub Copilot for Software Engineers',
intro:
- 'Cursor and GitHub Copilot both put AI inside your editor — but they are built on different premises about what AI-assisted development looks like. Copilot optimizes for autocomplete flow; Cursor optimizes for multi-file reasoning and agent-driven edits.',
+ 'Cursor and GitHub Copilot both put AI inside your editor - but they are built on different premises about what AI-assisted development looks like. Copilot optimizes for autocomplete flow; Cursor optimizes for multi-file reasoning and agent-driven edits.',
verdict:
'Cursor is the stronger choice for engineers who want to hand off multi-file tasks, run agents, and iterate on the codebase as a whole. Copilot is better embedded in GitHub workflows, has lower friction for teams that standardize on VSCode, and its Chat and PR Review features integrate directly with your GitHub Actions and pull request cycle.',
points: [
@@ -116,7 +116,7 @@ export const COMPARISONS: ComparisonPage[] = [
intro:
'Data analysts need AI that handles long documents, reasons carefully about numbers, and integrates with the tools you already use. Claude and Gemini take different approaches to each of these.',
verdict:
- 'Claude is stronger for careful, citation-honest analysis of long documents and complex datasets pasted into the context. Gemini wins on Google Workspace integration — if your org runs on Sheets, Docs, and BigQuery, Gemini in Workspace is already embedded in your workflow. For standalone analysis work, Claude\'s document reasoning is more reliable.',
+ 'Claude is stronger for careful, citation-honest analysis of long documents and complex datasets pasted into the context. Gemini wins on Google Workspace integration - if your org runs on Sheets, Docs, and BigQuery, Gemini in Workspace is already embedded in your workflow. For standalone analysis work, Claude\'s document reasoning is more reliable.',
points: [
{
dimension: 'Long-document analysis',
@@ -202,9 +202,9 @@ export const COMPARISONS: ComparisonPage[] = [
role: 'Software Engineer',
tagline: 'Windsurf vs Cursor for Software Engineers',
intro:
- 'Both Windsurf and Cursor are AI-first IDEs built on VS Code\'s foundation — but they have different philosophies. Windsurf\'s Cascade model is trained for multi-step autonomous reasoning; Cursor\'s strength is in the explicitness of Composer and deep codebase indexing.',
+ 'Both Windsurf and Cursor are AI-first IDEs built on VS Code\'s foundation - but they have different philosophies. Windsurf\'s Cascade model is trained for multi-step autonomous reasoning; Cursor\'s strength is in the explicitness of Composer and deep codebase indexing.',
verdict:
- 'Windsurf\'s Cascade is more autonomous and better at planning multi-step changes without hand-holding. Cursor gives you more control over every step — better for engineers who want to review before applying. The best choice comes down to your trust level: high-trust autonomous agent or explicit pair-programmer. Most teams that switch from Copilot land on Cursor for familiarity, then graduate some engineers to Windsurf for larger refactors.',
+ 'Windsurf\'s Cascade is more autonomous and better at planning multi-step changes without hand-holding. Cursor gives you more control over every step - better for engineers who want to review before applying. The best choice comes down to your trust level: high-trust autonomous agent or explicit pair-programmer. Most teams that switch from Copilot land on Cursor for familiarity, then graduate some engineers to Windsurf for larger refactors.',
points: [
{
dimension: 'Autonomous task completion',
diff --git a/apps/web/src/lib/guides-data.ts b/apps/web/src/lib/guides-data.ts
index 0c6559e..d7eea2c 100644
--- a/apps/web/src/lib/guides-data.ts
+++ b/apps/web/src/lib/guides-data.ts
@@ -21,7 +21,7 @@ export const GUIDES: GuidePage[] = [
title: 'Embed LearnKit AI in a Next.js app',
tagline: 'A step-by-step guide to adding role-aware learning paths to your Next.js product',
intro:
- 'LearnKit AI is designed to drop into any SaaS product. This guide shows you the minimal integration: install the packages, render a learning path, and wire up the interactive demo — in under 30 minutes.',
+ 'LearnKit AI is designed to drop into any SaaS product. This guide shows you the minimal integration: install the packages, render a learning path, and wire up the interactive demo - in under 30 minutes.',
readingMinutes: 8,
sections: [
{
@@ -54,7 +54,7 @@ export function MyOnboarding() {
},
{
heading: 'Use the hook for custom rendering',
- body: 'If you want to build your own lesson UI, use the useLearnKit hook directly. It returns the full LearningPath object — all four weeks, every lesson, total minutes — which you can render however you like.',
+ body: 'If you want to build your own lesson UI, use the useLearnKit hook directly. It returns the full LearningPath object - all four weeks, every lesson, total minutes - which you can render however you like.',
code: `import { useLearnKit } from '@learnkit-ai/react';
export function CustomPath({ role }: { role: string }) {
@@ -71,7 +71,7 @@ export function CustomPath({ role }: { role: string }) {
return (
{path.weeks.flatMap((w) => w.lessons).map((l) => (
- {l.title} — {l.minutes}m
+ {l.title} - {l.minutes}m
))}
);
@@ -100,7 +100,7 @@ export function OnboardingWithProgress() {
},
{
heading: 'Pass a companyContext for personalised paths',
- body: 'The optional companyContext field (max 500 chars) lets you inject stack and team information. The engine uses it to personalise project lesson summaries — no AI call required.',
+ body: 'The optional companyContext field (max 500 chars) lets you inject stack and team information. The engine uses it to personalise project lesson summaries - no AI call required.',
code: `
` from `@learnkit-ai/react`
- The `
` tip card
-- Zero backend, zero API key, zero LLM call — the path is computed deterministically from the input
+- Zero backend, zero API key, zero LLM call - the path is computed deterministically from the input
Edit `src/app/page.tsx` to change the role, tools, or goal and watch the path update.
diff --git a/examples/nextjs-basic/src/app/page.tsx b/examples/nextjs-basic/src/app/page.tsx
index 048af75..53be0c7 100644
--- a/examples/nextjs-basic/src/app/page.tsx
+++ b/examples/nextjs-basic/src/app/page.tsx
@@ -12,7 +12,7 @@ export default function Home() {
diff --git a/packages/cli/package.json b/packages/cli/package.json
index 6446c5a..199a066 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -1,7 +1,7 @@
{
"name": "@learnkit-ai/cli",
"version": "0.1.0",
- "description": "CLI for @learnkit-ai/core — generate AI learning paths from the terminal",
+ "description": "CLI for @learnkit-ai/core - generate AI learning paths from the terminal",
"license": "Apache-2.0",
"repository": {
"type": "git",
diff --git a/packages/cli/src/__tests__/generate.test.ts b/packages/cli/src/__tests__/generate.test.ts
index b4a9052..fe0673f 100644
--- a/packages/cli/src/__tests__/generate.test.ts
+++ b/packages/cli/src/__tests__/generate.test.ts
@@ -1,7 +1,7 @@
import { describe, expect, it } from 'vitest';
import { generateLearningPath, getSupportedRoles, getSupportedTools } from '@learnkit-ai/core';
-describe('CLI integration — generateLearningPath', () => {
+describe('CLI integration - generateLearningPath', () => {
it('generates a valid path for all supported roles', () => {
const roles = getSupportedRoles();
const tools = getSupportedTools();
diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts
index f1503c8..7f57fdd 100644
--- a/packages/cli/src/index.ts
+++ b/packages/cli/src/index.ts
@@ -2,7 +2,7 @@ import { parseArgs } from 'node:util';
import { generateLearningPath, getSupportedRoles, getSupportedTools } from '@learnkit-ai/core';
const HELP = `
-@learnkit-ai/cli — generate AI learning paths from the terminal
+@learnkit-ai/cli - generate AI learning paths from the terminal
Usage:
npx @learnkit-ai/cli generate [options]
@@ -99,7 +99,7 @@ function cmdGenerate(argv: string[]): void {
// Pretty-print
const hr = '─'.repeat(60);
- process.stdout.write(`\n LearnKit AI — 30-day learning path\n`);
+ process.stdout.write(`\n LearnKit AI - 30-day learning path\n`);
process.stdout.write(` Role: ${path.input.role} · Level: ${path.input.level} · ${path.totalMinutes} min total\n`);
process.stdout.write(` Goal: ${path.input.goal}\n`);
if (path.input.companyContext) {
@@ -109,7 +109,7 @@ function cmdGenerate(argv: string[]): void {
for (const week of path.weeks) {
process.stdout.write(` ${hr}\n`);
- process.stdout.write(` Week ${week.index} — ${week.title}\n`);
+ process.stdout.write(` Week ${week.index} - ${week.title}\n`);
process.stdout.write(` ${hr}\n`);
for (const lesson of week.lessons) {
const kindTag = lesson.kind === 'project' ? '[PROJECT]' : lesson.kind === 'practicum' ? '[PRACTICUM]' : '[LESSON]';
diff --git a/packages/core/src/__tests__/generate.test.ts b/packages/core/src/__tests__/generate.test.ts
index 549b15e..a6de09e 100644
--- a/packages/core/src/__tests__/generate.test.ts
+++ b/packages/core/src/__tests__/generate.test.ts
@@ -18,7 +18,7 @@ describe('generateLearningPath', () => {
}
});
- it('is deterministic — same input always produces the same id and total minutes', () => {
+ it('is deterministic - same input always produces the same id and total minutes', () => {
const a = generateLearningPath(SAMPLE);
const b = generateLearningPath(SAMPLE);
expect(a.id).toBe(b.id);
diff --git a/packages/core/src/__tests__/lesson-content.test.ts b/packages/core/src/__tests__/lesson-content.test.ts
index cc56222..03feb6b 100644
--- a/packages/core/src/__tests__/lesson-content.test.ts
+++ b/packages/core/src/__tests__/lesson-content.test.ts
@@ -39,7 +39,7 @@ describe('generateLessonContent', () => {
expect(content.rubric.length).toBeGreaterThanOrEqual(1);
});
- it('is deterministic — same lesson always returns the same content', () => {
+ it('is deterministic - same lesson always returns the same content', () => {
const lesson = getFirstLesson();
const a = generateLessonContent(lesson);
const b = generateLessonContent(lesson);
diff --git a/packages/core/src/__tests__/progress.test.ts b/packages/core/src/__tests__/progress.test.ts
index 50208d8..a6569ee 100644
--- a/packages/core/src/__tests__/progress.test.ts
+++ b/packages/core/src/__tests__/progress.test.ts
@@ -49,7 +49,7 @@ describe('computeProgress', () => {
const path = generateLearningPath(INPUT);
const firstId = path.weeks[0]!.lessons[0]!.id;
const progress = computeProgress(path, [firstId, firstId]);
- // Set filtering: only first occurrence is valid, second is duplicate — both valid but same ID
+ // Set filtering: only first occurrence is valid, second is duplicate - both valid but same ID
expect(progress.completedLessonIds.filter((id) => id === firstId).length).toBeGreaterThanOrEqual(1);
});
diff --git a/packages/core/src/generate.ts b/packages/core/src/generate.ts
index 9211489..b6715bc 100644
--- a/packages/core/src/generate.ts
+++ b/packages/core/src/generate.ts
@@ -30,7 +30,7 @@ const GENERIC_WEEKS: WeekDef[] = [
{
title: () => 'Your first system prompt',
summary: ({ tool, role }) =>
- `Write a system prompt for ${tool} that gives it a persona, a process, and constraints — tuned for the day-to-day work of a ${role}.`,
+ `Write a system prompt for ${tool} that gives it a persona, a process, and constraints - tuned for the day-to-day work of a ${role}.`,
minutes: 12,
kind: 'lesson',
},
@@ -44,7 +44,7 @@ const GENERIC_WEEKS: WeekDef[] = [
{
title: ({ role }) => `Project: redesign a ${role.toLowerCase()} workflow`,
summary: ({ tool, goal }) =>
- `Pick one task you do every day. Turn it into a repeatable ${tool} prompt or workflow. Ship it before Friday — aimed at: ${goal}.`,
+ `Pick one task you do every day. Turn it into a repeatable ${tool} prompt or workflow. Ship it before Friday - aimed at: ${goal}.`,
minutes: 22,
kind: 'project',
},
@@ -70,7 +70,7 @@ const GENERIC_WEEKS: WeekDef[] = [
{
title: () => 'Project: a research agent for your team',
summary: ({ role, goal }) =>
- `Ship a small agent your team can actually use — built to support: ${goal}. The kind of thing a senior ${role.toLowerCase()} would pick up and run with.`,
+ `Ship a small agent your team can actually use - built to support: ${goal}. The kind of thing a senior ${role.toLowerCase()} would pick up and run with.`,
minutes: 45,
kind: 'project',
},
@@ -142,7 +142,7 @@ const ROLE_WEEKS: Partial
> = {
{
title: () => 'System prompts that survive code review',
summary: ({ tool }) =>
- `Write a ${tool} system prompt that is version-controlled, tested, and documented — the same bar you hold your code to.`,
+ `Write a ${tool} system prompt that is version-controlled, tested, and documented - the same bar you hold your code to.`,
minutes: 14,
kind: 'lesson',
},
@@ -208,7 +208,7 @@ const ROLE_WEEKS: Partial> = {
{
title: () => 'Project: add AI to an existing endpoint',
summary: ({ goal }) =>
- `Take a real endpoint in your codebase and add an AI layer — with evals, error handling, and a fallback. Goal: ${goal}.`,
+ `Take a real endpoint in your codebase and add an AI layer - with evals, error handling, and a fallback. Goal: ${goal}.`,
minutes: 60,
kind: 'project',
},
@@ -224,7 +224,7 @@ const ROLE_WEEKS: Partial> = {
{
title: () => 'Structuring a discovery brief with AI',
summary: ({ tool }) =>
- `Use ${tool} to write a discovery brief that surfaces assumptions, maps stakeholders, and frames the problem — before a single engineer is in the room.`,
+ `Use ${tool} to write a discovery brief that surfaces assumptions, maps stakeholders, and frames the problem - before a single engineer is in the room.`,
minutes: 14,
kind: 'lesson',
},
@@ -306,7 +306,7 @@ const ROLE_WEEKS: Partial> = {
{
title: () => 'Writing prompts that describe design intent',
summary: ({ tool }) =>
- `Learn to write ${tool} prompts that describe UI intent, brand constraints, and accessibility requirements — not just visual descriptions.`,
+ `Learn to write ${tool} prompts that describe UI intent, brand constraints, and accessibility requirements - not just visual descriptions.`,
minutes: 14,
kind: 'lesson',
},
@@ -332,7 +332,7 @@ const ROLE_WEEKS: Partial> = {
{
title: () => 'From brief to concept in an hour',
summary: ({ tool }) =>
- `Use ${tool} to generate 10 concept directions from a client brief. Present the three strongest with rationale — in the time it used to take to sketch one.`,
+ `Use ${tool} to generate 10 concept directions from a client brief. Present the three strongest with rationale - in the time it used to take to sketch one.`,
minutes: 20,
kind: 'lesson',
},
@@ -363,7 +363,7 @@ const ROLE_WEEKS: Partial> = {
{
title: () => 'Writing SQL with AI that you understand',
summary: ({ tool }) =>
- `Use ${tool} to generate SQL queries — then review every line before running. Build the habit of AI-assisted, human-verified analysis.`,
+ `Use ${tool} to generate SQL queries - then review every line before running. Build the habit of AI-assisted, human-verified analysis.`,
minutes: 16,
kind: 'lesson',
},
@@ -427,7 +427,7 @@ const ROLE_WEEKS: Partial> = {
{
title: () => 'Repurposing one asset into ten',
summary: ({ tool }) =>
- `Take a blog post or webinar transcript and use ${tool} to spin it into a Twitter thread, three LinkedIn posts, an email nurture, and a one-pager — in under an hour.`,
+ `Take a blog post or webinar transcript and use ${tool} to spin it into a Twitter thread, three LinkedIn posts, an email nurture, and a one-pager - in under an hour.`,
minutes: 16,
kind: 'lesson',
},
@@ -460,7 +460,7 @@ const ROLE_WEEKS: Partial> = {
{
title: () => 'Project: messaging matrix',
summary: ({ goal }) =>
- `Build a full messaging matrix: audience segments, jobs-to-be-done, proof points, and objection handles — researched and drafted with AI. Goal: ${goal}.`,
+ `Build a full messaging matrix: audience segments, jobs-to-be-done, proof points, and objection handles - researched and drafted with AI. Goal: ${goal}.`,
minutes: 45,
kind: 'project',
},
@@ -472,7 +472,7 @@ const ROLE_WEEKS: Partial> = {
{
title: () => 'Building an AI-assisted email sequence',
summary: ({ tool }) =>
- `Use ${tool} to draft a 5-email nurture sequence. Define personas and triggers up front so every email lands in context — not in the bin.`,
+ `Use ${tool} to draft a 5-email nurture sequence. Define personas and triggers up front so every email lands in context - not in the bin.`,
minutes: 20,
kind: 'lesson',
},
@@ -486,7 +486,7 @@ const ROLE_WEEKS: Partial> = {
{
title: () => 'Project: automated campaign reporting',
summary: ({ goal }) =>
- `Build a prompt that turns raw campaign metrics into a one-page performance narrative for your stakeholders — every week, in minutes. Goal: ${goal}.`,
+ `Build a prompt that turns raw campaign metrics into a one-page performance narrative for your stakeholders - every week, in minutes. Goal: ${goal}.`,
minutes: 40,
kind: 'project',
},
@@ -502,7 +502,7 @@ const ROLE_WEEKS: Partial> = {
{
title: () => 'Using AI as a thinking partner',
summary: ({ tool }) =>
- `Learn to use ${tool} as a sounding board that pushes back. Write prompts that force the model to steelman your plan, then attack it — before the market does.`,
+ `Learn to use ${tool} as a sounding board that pushes back. Write prompts that force the model to steelman your plan, then attack it - before the market does.`,
minutes: 14,
kind: 'lesson',
},
@@ -528,21 +528,21 @@ const ROLE_WEEKS: Partial> = {
{
title: () => 'Delegating writing to AI without losing your voice',
summary: ({ tool }) =>
- `Build a ${tool} persona that writes in your voice — for investor updates, hiring emails, and team memos. Train it with 5 examples before you trust it.`,
+ `Build a ${tool} persona that writes in your voice - for investor updates, hiring emails, and team memos. Train it with 5 examples before you trust it.`,
minutes: 16,
kind: 'lesson',
},
{
title: () => 'Automating founder-mode research',
summary: ({ tool }) =>
- `Use ${tool} to produce weekly competitive intelligence, funding round summaries, and hiring market signals — in the time it used to take to read one newsletter.`,
+ `Use ${tool} to produce weekly competitive intelligence, funding round summaries, and hiring market signals - in the time it used to take to read one newsletter.`,
minutes: 20,
kind: 'lesson',
},
{
title: () => 'Project: AI-powered investor memo',
summary: ({ goal }) =>
- `Draft a one-page investor memo with AI: traction, market, team, and ask — structured and pressure-tested for the objections you hear most. Goal: ${goal}.`,
+ `Draft a one-page investor memo with AI: traction, market, team, and ask - structured and pressure-tested for the objections you hear most. Goal: ${goal}.`,
minutes: 45,
kind: 'project',
},
@@ -561,14 +561,14 @@ const ROLE_WEEKS: Partial> = {
{
title: () => 'Hiring and culture at AI speed',
summary: ({ tool }) =>
- `Use ${tool} to write job descriptions, structure interviews, and draft offer letters that reflect your actual culture — not the generic startup template.`,
+ `Use ${tool} to write job descriptions, structure interviews, and draft offer letters that reflect your actual culture - not the generic startup template.`,
minutes: 16,
kind: 'lesson',
},
{
title: () => 'Project: 90-day operating plan',
summary: ({ goal }) =>
- `Write a 90-day operating plan: goals, experiments, metrics, and the kill criteria for each bet — drafted with AI, owned by you. Goal: ${goal}.`,
+ `Write a 90-day operating plan: goals, experiments, metrics, and the kill criteria for each bet - drafted with AI, owned by you. Goal: ${goal}.`,
minutes: 50,
kind: 'project',
},
@@ -591,7 +591,7 @@ const ROLE_WEEKS: Partial> = {
{
title: () => 'Turning meetings into structured outputs',
summary: ({ tool }) =>
- `Feed raw meeting notes into ${tool}. Get back a structured decision log, action items with owners, and a one-paragraph summary — in 90 seconds.`,
+ `Feed raw meeting notes into ${tool}. Get back a structured decision log, action items with owners, and a one-paragraph summary - in 90 seconds.`,
minutes: 12,
kind: 'lesson',
},
@@ -624,7 +624,7 @@ const ROLE_WEEKS: Partial> = {
{
title: () => 'Project: AI-assisted onboarding programme',
summary: ({ goal }) =>
- `Build a week-one onboarding programme: schedule, readings, checkpoints, and a 30-60-90 plan — structured and drafted with AI. Goal: ${goal}.`,
+ `Build a week-one onboarding programme: schedule, readings, checkpoints, and a 30-60-90 plan - structured and drafted with AI. Goal: ${goal}.`,
minutes: 45,
kind: 'project',
},
@@ -650,7 +650,7 @@ const ROLE_WEEKS: Partial> = {
{
title: () => 'Project: ops dashboard narrative',
summary: ({ goal }) =>
- `Write a monthly ops narrative that translates your key metrics into leadership language — built with AI, signed off by you. Goal: ${goal}.`,
+ `Write a monthly ops narrative that translates your key metrics into leadership language - built with AI, signed off by you. Goal: ${goal}.`,
minutes: 35,
kind: 'project',
},
@@ -718,7 +718,7 @@ const ROLE_WEEKS: Partial> = {
{
title: () => 'Writing for multiple audiences from one dataset',
summary: ({ tool }) =>
- `Use ${tool} to adapt the same research findings for a technical paper, an executive summary, and a public-facing blog post — each with the right framing.`,
+ `Use ${tool} to adapt the same research findings for a technical paper, an executive summary, and a public-facing blog post - each with the right framing.`,
minutes: 20,
kind: 'lesson',
},
@@ -732,7 +732,7 @@ const ROLE_WEEKS: Partial> = {
{
title: () => 'Project: research communication package',
summary: ({ goal }) =>
- `Build a communication package for a real project: abstract, one-pager, and 5-slide deck — each audience-appropriate and drafted with AI. Goal: ${goal}.`,
+ `Build a communication package for a real project: abstract, one-pager, and 5-slide deck - each audience-appropriate and drafted with AI. Goal: ${goal}.`,
minutes: 45,
kind: 'project',
},
@@ -755,7 +755,7 @@ const ROLE_WEEKS: Partial> = {
{
title: () => 'Writing cold outreach that gets replies',
summary: ({ tool }) =>
- `Build a ${tool} prompt that generates personalised first-touch emails — not templates with [NAME] placeholders, but messages that reference what the buyer actually cares about.`,
+ `Build a ${tool} prompt that generates personalised first-touch emails - not templates with [NAME] placeholders, but messages that reference what the buyer actually cares about.`,
minutes: 14,
kind: 'lesson',
},
@@ -781,7 +781,7 @@ const ROLE_WEEKS: Partial> = {
{
title: () => 'Turning call notes into structured CRM updates',
summary: ({ tool }) =>
- `Paste raw call notes into ${tool} and get back a structured MEDDIC or BANT summary, next steps, and a draft follow-up email — in under 90 seconds.`,
+ `Paste raw call notes into ${tool} and get back a structured MEDDIC or BANT summary, next steps, and a draft follow-up email - in under 90 seconds.`,
minutes: 16,
kind: 'lesson',
},
@@ -814,7 +814,7 @@ const ROLE_WEEKS: Partial> = {
{
title: () => 'Project: end-to-end deal support package',
summary: ({ goal }) =>
- `Build a full deal support package for one active opportunity: account brief, discovery questions, objection bank, and a draft proposal intro — all AI-assisted. Goal: ${goal}.`,
+ `Build a full deal support package for one active opportunity: account brief, discovery questions, objection bank, and a draft proposal intro - all AI-assisted. Goal: ${goal}.`,
minutes: 50,
kind: 'project',
},
@@ -882,14 +882,14 @@ const ROLE_WEEKS: Partial> = {
{
title: () => 'Preparing QBR materials in half the time',
summary: ({ tool }) =>
- `Use ${tool} to draft the QBR narrative: what happened, what it means for the customer's goals, and what you recommend next — structured and editable before the meeting.`,
+ `Use ${tool} to draft the QBR narrative: what happened, what it means for the customer's goals, and what you recommend next - structured and editable before the meeting.`,
minutes: 20,
kind: 'lesson',
},
{
title: () => 'Identifying expansion opportunities in account data',
summary: ({ tool }) =>
- `Use ${tool} to scan your account notes and identify gaps between what the customer is using and what they could be using — surfacing natural expansion conversations.`,
+ `Use ${tool} to scan your account notes and identify gaps between what the customer is using and what they could be using - surfacing natural expansion conversations.`,
minutes: 16,
kind: 'lesson',
},
@@ -912,14 +912,14 @@ const ROLE_WEEKS: Partial> = {
{
title: () => 'Using AI to build and stress-test financial models',
summary: ({ tool }) =>
- `Use ${tool} to draft model structures, check formula logic, and document assumptions — so the model survives the next person who opens it, not just the person who built it.`,
+ `Use ${tool} to draft model structures, check formula logic, and document assumptions - so the model survives the next person who opens it, not just the person who built it.`,
minutes: 18,
kind: 'lesson',
},
{
title: () => 'Turning raw data into executive-ready analysis',
summary: ({ tool }) =>
- `Give ${tool} a table of numbers and a business question. Get back the three-sentence story, the table that supports it, and the caveat the CFO will ask about — drafted in minutes.`,
+ `Give ${tool} a table of numbers and a business question. Get back the three-sentence story, the table that supports it, and the caveat the CFO will ask about - drafted in minutes.`,
minutes: 16,
kind: 'lesson',
},
@@ -971,14 +971,14 @@ const ROLE_WEEKS: Partial> = {
{
title: () => 'FP&A scenario modelling with AI',
summary: ({ tool }) =>
- `Use ${tool} to generate and document multiple forecast scenarios: base, upside, and downside — each with the assumptions and the metric impacts written in language leadership can read.`,
+ `Use ${tool} to generate and document multiple forecast scenarios: base, upside, and downside - each with the assumptions and the metric impacts written in language leadership can read.`,
minutes: 20,
kind: 'lesson',
},
{
title: () => 'Project: scenario analysis package',
summary: ({ goal }) =>
- `Build a scenario analysis package for a live business decision: three scenarios, key assumptions, financial impact, and a recommendation memo — drafted with AI, validated by you. Goal: ${goal}.`,
+ `Build a scenario analysis package for a live business decision: three scenarios, key assumptions, financial impact, and a recommendation memo - drafted with AI, validated by you. Goal: ${goal}.`,
minutes: 50,
kind: 'project',
},
diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts
index d5c5a0c..3b8641e 100644
--- a/packages/core/src/index.ts
+++ b/packages/core/src/index.ts
@@ -1,5 +1,5 @@
/**
- * @learnkit-ai/core — Deterministic learning-path engine.
+ * @learnkit-ai/core - Deterministic learning-path engine.
* Apache-2.0. https://learnkit-ai.com
*/
diff --git a/packages/core/src/lesson-content.ts b/packages/core/src/lesson-content.ts
index 14be93e..e75b4d7 100644
--- a/packages/core/src/lesson-content.ts
+++ b/packages/core/src/lesson-content.ts
@@ -14,7 +14,7 @@ function buildBody(lesson: Lesson): string {
return (
`${summary}\n\n` +
`This practicum is evaluated on the depth and honesty of your portfolio. ` +
- `Submit three artifacts that show a complete workflow — from initial prompt to final output — along with your own commentary on what worked, what you edited, and why. ` +
+ `Submit three artifacts that show a complete workflow - from initial prompt to final output - along with your own commentary on what worked, what you edited, and why. ` +
`The goal is not a polished showcase but an accurate record of how you think with ${tool}.`
);
}
@@ -22,7 +22,7 @@ function buildBody(lesson: Lesson): string {
if (kind === 'project') {
return (
`${summary}\n\n` +
- `Deliver a working output by the end of the week. Scope it so it is shareable — something a colleague could pick up and use without your explanation. ` +
+ `Deliver a working output by the end of the week. Scope it so it is shareable - something a colleague could pick up and use without your explanation. ` +
`Document the prompt that produced it, the edits you made, and the one thing you would change with more time. ` +
`Projects in this curriculum are designed to take approximately ${minutes} minutes including iteration.`
);
@@ -45,10 +45,10 @@ function buildExercises(lesson: Lesson): Exercise[] {
{
prompt: `Draft an outline of the three portfolio artifacts you will submit. For each, describe the workflow it demonstrates, the tool used, and how you will evaluate its quality.`,
expectedOutput: `A structured list of three artifacts, each with: title, tool, workflow description, and at least one measurable success criterion.`,
- rubricHint: `Each artifact should target a distinct skill from the course — overlapping coverage is a sign the scope is too narrow.`,
+ rubricHint: `Each artifact should target a distinct skill from the course - overlapping coverage is a sign the scope is too narrow.`,
},
{
- prompt: `Write the commentary for your strongest artifact. Explain what you prompted, what the model returned, and what edits you made — and why.`,
+ prompt: `Write the commentary for your strongest artifact. Explain what you prompted, what the model returned, and what edits you made - and why.`,
expectedOutput: `A 200–400 word annotation covering: initial prompt, model output, edits made, and a reflection on the gap between the first draft and the final output.`,
rubricHint: `Strong commentary explains decisions, not just actions. "I changed X because Y" beats "I edited the output."`,
},
@@ -60,11 +60,11 @@ function buildExercises(lesson: Lesson): Exercise[] {
{
prompt: `Before starting the project, write a one-paragraph brief: what you are building, who it is for, and how you will know it is done.`,
expectedOutput: `A project brief with: goal statement, intended audience, at least two success criteria, and a rough delivery timeline.`,
- rubricHint: `Success criteria should be specific enough to evaluate objectively — "it works" does not count.`,
+ rubricHint: `Success criteria should be specific enough to evaluate objectively - "it works" does not count.`,
},
{
prompt: `After completing the project, write a 3-bullet retrospective: what worked, what you would do differently, and what you learned about using ${tool} for this type of task.`,
- expectedOutput: `Three concise bullet points covering retrospective observations. Each should reference a specific prompt decision or output — avoid vague statements like "it was useful."`,
+ expectedOutput: `Three concise bullet points covering retrospective observations. Each should reference a specific prompt decision or output - avoid vague statements like "it was useful."`,
rubricHint: `Retrospectives that reference specific prompt decisions are stronger than general reflections.`,
},
];
@@ -74,12 +74,12 @@ function buildExercises(lesson: Lesson): Exercise[] {
{
prompt: `Apply the technique from "${title}" to a real task from your own work. Document the prompt you used and the output you got.`,
expectedOutput: `A before/after record: the task description, the ${tool} prompt you wrote, the output you received, and a one-sentence verdict on whether it met your standard.`,
- rubricHint: `Use a real task, not a fabricated one — specificity makes the exercise useful to you and reviewable by others.`,
+ rubricHint: `Use a real task, not a fabricated one - specificity makes the exercise useful to you and reviewable by others.`,
},
{
prompt: `Identify one way the technique from this lesson could fail in your context. Write a prompt that deliberately tries to trigger that failure, then write a revised prompt that prevents it.`,
expectedOutput: `A failure-mode description, a prompt that demonstrates it, the problematic output, a revised prompt, and the improved output.`,
- rubricHint: `Understanding failure modes is more transferable than demonstrating success — a reviewer can tell whether you actually tried to break it.`,
+ rubricHint: `Understanding failure modes is more transferable than demonstrating success - a reviewer can tell whether you actually tried to break it.`,
},
];
}
@@ -114,7 +114,7 @@ function buildRubric(lesson: Lesson): RubricItem[] {
},
{
criterion: 'Prompt design',
- excellent: `The prompt is structured, documented, and repeatable — someone else could run it and get consistent results.`,
+ excellent: `The prompt is structured, documented, and repeatable - someone else could run it and get consistent results.`,
acceptable: `The prompt works but is not documented or is difficult to reproduce.`,
needsWork: `Output was edited heavily to compensate for a weak prompt, without revisiting the prompt itself.`,
},
diff --git a/packages/react/src/AIGuide.tsx b/packages/react/src/AIGuide.tsx
index 500cf96..d6aa323 100644
--- a/packages/react/src/AIGuide.tsx
+++ b/packages/react/src/AIGuide.tsx
@@ -13,7 +13,7 @@ export interface AIGuideProps {
const SIZE_MAP = { sm: 24, md: 32, lg: 44 };
/**
- * AIGuide — a small avatar + a one-line tip.
+ * AIGuide - a small avatar + a one-line tip.
* Drop-in for surfacing AI Guide reviews and hints inside any host UI.
*/
export function AIGuide({
diff --git a/packages/react/src/ProgressTracker.tsx b/packages/react/src/ProgressTracker.tsx
index 2f54e30..2df88b4 100644
--- a/packages/react/src/ProgressTracker.tsx
+++ b/packages/react/src/ProgressTracker.tsx
@@ -33,7 +33,7 @@ function writeStorage(key: string, ids: Set): void {
try {
window.localStorage.setItem(key, JSON.stringify([...ids]));
} catch {
- // localStorage unavailable — progress not persisted
+ // localStorage unavailable - progress not persisted
}
}
diff --git a/packages/react/src/__tests__/ProgressTracker.test.tsx b/packages/react/src/__tests__/ProgressTracker.test.tsx
index 4c19b3f..f9d68ca 100644
--- a/packages/react/src/__tests__/ProgressTracker.test.tsx
+++ b/packages/react/src/__tests__/ProgressTracker.test.tsx
@@ -68,7 +68,7 @@ describe('ProgressTracker', () => {
it('second lesson is locked until the first is complete', () => {
render( );
const buttons = screen.getAllByRole('button');
- // Button at index 1 should be disabled (locked — prerequisites not met)
+ // Button at index 1 should be disabled (locked - prerequisites not met)
expect(buttons[1]).toBeDisabled();
});
diff --git a/packages/react/src/__tests__/useLearnKit.test.tsx b/packages/react/src/__tests__/useLearnKit.test.tsx
index 367b164..6297402 100644
--- a/packages/react/src/__tests__/useLearnKit.test.tsx
+++ b/packages/react/src/__tests__/useLearnKit.test.tsx
@@ -36,7 +36,7 @@ describe('useLearnKit', () => {
expect(result.current.path).toBeNull();
});
- it('is deterministic — same input returns identical path id', () => {
+ it('is deterministic - same input returns identical path id', () => {
const { result: a } = renderHook(() => useLearnKit(INPUT));
const { result: b } = renderHook(() => useLearnKit({ ...INPUT }));
expect(a.current.path?.id).toBe(b.current.path?.id);
diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts
index 498b10d..139f086 100644
--- a/packages/react/src/index.ts
+++ b/packages/react/src/index.ts
@@ -1,5 +1,5 @@
/**
- * @learnkit-ai/react — React components for embedding LearnKit AI.
+ * @learnkit-ai/react - React components for embedding LearnKit AI.
* Apache-2.0. https://learnkit-ai.com
*
* No Tailwind dependency. CSS custom properties only. Drop into any host theme.
diff --git a/packages/react/src/useLearnKit.ts b/packages/react/src/useLearnKit.ts
index d79afe7..2b27131 100644
--- a/packages/react/src/useLearnKit.ts
+++ b/packages/react/src/useLearnKit.ts
@@ -11,7 +11,7 @@ export interface UseLearnKitResult {
}
/**
- * useLearnKit — synchronous, deterministic. The path is computed from input on render.
+ * useLearnKit - synchronous, deterministic. The path is computed from input on render.
* Loading state is always false because `generateLearningPath` is pure.
*/
export function useLearnKit(input: LearningPathInput | null | undefined): UseLearnKitResult {
diff --git a/packages/schemas/src/index.ts b/packages/schemas/src/index.ts
index 3491651..3ad009f 100644
--- a/packages/schemas/src/index.ts
+++ b/packages/schemas/src/index.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
/**
- * LearnKit AI — Zod schemas and inferred TypeScript types.
+ * LearnKit AI - Zod schemas and inferred TypeScript types.
* Apache-2.0. https://learnkit-ai.com
*/