epam-ai-conductor 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
- # react-workshop-cli
1
+ # epam-ai-conductor
2
2
 
3
- Socratic mentor CLI for the React Fundamentals course, powered by CodeMie + Claude.
3
+ **EPAM AI Conductor** — a Socratic AI mentor CLI for the **React Fundamentals** course, powered by CodeMie + Claude.
4
4
 
5
- The agent knows the full course content and test expectations but guides students with questions, not answers.
5
+ The mentor knows the full course content and hidden test expectations, but guides students with questions instead of answers — so they learn by doing, never by copy-pasting.
6
6
 
7
7
  ## Student setup
8
8
 
@@ -11,23 +11,38 @@ The agent knows the full course content and test expectations but guides student
11
11
  git clone <template-repo-url> my-react-app
12
12
  cd my-react-app
13
13
 
14
- # 2. Install the workshop CLI
15
- npm install -g react-workshop-cli # once codemie installs claude
14
+ # 2. Install EPAM AI Conductor
15
+ npm install -g epam-ai-conductor
16
16
 
17
17
  # 3. Start a module session
18
- react-workshop start --module 1
18
+ ai-conductor start -m 1
19
19
  ```
20
20
 
21
- On first run it will walk you through CodeMie setup (EPAM SSO login).
21
+ On first run it walks you through CodeMie setup (EPAM SSO login).
22
22
 
23
- ### Slash commands during the session
23
+ ### Commands
24
+
25
+ ```bash
26
+ ai-conductor help # show usage
27
+ ai-conductor start # pick a module interactively
28
+ ai-conductor start -m <1-5> # start a specific module
29
+ ai-conductor start -m <1-5> -d <path> # start in a specific directory
30
+ ```
31
+
32
+ ### Slash commands during a session
24
33
 
25
34
  | Command | What it does |
26
35
  |----------|-------------|
27
36
  | `/hint` | One targeted hint — a question, not code |
28
- | `/check` | Reviews your src/ against task criteria |
37
+ | `/check` | Reviews your `src/` against the task criteria |
29
38
  | `/step` | Suggests the single most logical next step |
30
39
 
40
+ ## Requirements
41
+
42
+ - Node.js >= 20
43
+ - A CodeMie account (EPAM SSO) — set up automatically on first run
44
+ - `codemie-claude` installed (`codemie install claude`)
45
+
31
46
  ---
32
47
 
33
48
  ## Course coordinator — updating content
@@ -43,4 +58,48 @@ npm run sync-content # copies from sibling repos into src/content/
43
58
  npm run build # compiles TS + copies content into dist/
44
59
  ```
45
60
 
46
- Then publish the new version of the package.
61
+ ### Publishing a new version
62
+
63
+ ```bash
64
+ # 1. Bump the version
65
+ npm version patch # 0.1.2 → 0.1.3
66
+ # or
67
+ npm version minor # 0.1.2 → 0.2.0
68
+
69
+ # 2. Push the tag — GitLab CI will publish to npm automatically
70
+ git push origin --tags
71
+ ```
72
+
73
+ > Requires `NPM_TOKEN` set in GitLab: **Settings → CI/CD → Variables**
74
+
75
+ ### How the mentor evaluates (and stays grounded)
76
+
77
+ The MCP server exposes these tools to the mentor:
78
+
79
+ | Tool | Purpose |
80
+ |------|---------|
81
+ | `get_criteria` | Graded checklist with stable ids + point weights (parsed from the `## Criteria` section of each module's task). The source of truth for "is it done". |
82
+ | `get_test_expectations` | The objective contract a component's hidden tests assert (expected export/path, props, required output text, queried roles/testids, interactions) — never the raw test code. |
83
+ | `get_module_overview` | The module map linking each component to its criteria and test contract. |
84
+ | `get_theory` | Course theory, filterable by topic. |
85
+ | `scan_project` | A fresh structural snapshot of the student's current code. `/check` and `/hint` call this first so assessments are never stale. |
86
+
87
+ The persona enforces an intake triage (conceptual / debugging / answer-request / progress-check) and a pre-send self-check that strips any task-solving code or quoted test source.
88
+
89
+ ### Curated criteria overrides
90
+
91
+ Auto-extraction follows the coursebook. To hand-tune a module's checklist, add
92
+ `src/content/module-N/criteria.override.json` — an array of
93
+ `{ id, component, points, requirement, details, optional }` — and it fully
94
+ replaces the parsed list for that module.
95
+
96
+ > Note: a few coursebook modules state a "max points" total that doesn't equal
97
+ > the sum of their listed items (e.g. Module 2 lists 56 pts under a "35 max"
98
+ > heading). `get_criteria` reports the actual itemized total; fix the source or
99
+ > add an override to reconcile.
100
+
101
+ ### Session logs
102
+
103
+ Each MCP tool call is appended (locally, anonymous) to
104
+ `~/.ai-conductor/logs/tools-YYYY-MM-DD.jsonl` — useful for spotting where the
105
+ mentor mis-assessed or leaked. Delete the dir to clear.
package/bin/workshop.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import('../dist/cli.js').catch((error) => {
4
- console.error('Error starting workshop:', error.message);
4
+ console.error('Error starting EPAM AI Conductor:', error.message);
5
5
  process.exit(1);
6
6
  });
@@ -10,10 +10,10 @@ export async function ensureAuth() {
10
10
  return;
11
11
  console.log();
12
12
  console.log(chalk.bold.cyan('╔══════════════════════════════════════════════════════╗'));
13
- console.log(chalk.bold.cyan('║ React Workshop — First Time Setup ║'));
13
+ console.log(chalk.bold.cyan('║ EPAM AI Conductor — First Time Setup ║'));
14
14
  console.log(chalk.bold.cyan('╚══════════════════════════════════════════════════════╝'));
15
15
  console.log();
16
- console.log(chalk.white('To use the workshop mentor you need a CodeMie account.'));
16
+ console.log(chalk.white('To use the AI Conductor mentor you need a CodeMie account.'));
17
17
  console.log();
18
18
  console.log(chalk.bold('Steps:'));
19
19
  console.log(chalk.white(' 1. Go to ') + chalk.cyan('https://codemie.ai'));
@@ -27,7 +27,7 @@ export async function ensureAuth() {
27
27
  default: true,
28
28
  }]);
29
29
  if (!ready) {
30
- console.log(chalk.yellow('\nSetup cancelled. Run `react-workshop start` again when ready.'));
30
+ console.log(chalk.yellow('\nSetup cancelled. Run `ai-conductor start` again when ready.'));
31
31
  process.exit(0);
32
32
  }
33
33
  console.log();
@@ -1 +1 @@
1
- {"version":3,"file":"auth-check.js","sourceRoot":"","sources":["../src/auth-check.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"auth-check.js","sourceRoot":"","sources":["../src/auth-check.ts"],"names":[],"mappings":"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"}
package/dist/cli.js CHANGED
@@ -11,6 +11,7 @@ import * as pty from 'node-pty';
11
11
  import { ensureAuth } from './auth-check.js';
12
12
  import { buildContext, MODULE_TITLES } from './context-builder.js';
13
13
  import { TOTAL_MODULES } from './content-loader.js';
14
+ import { startMcpServer } from './mcp-server.js';
14
15
  const __dirname = dirname(fileURLToPath(import.meta.url));
15
16
  function readVersion() {
16
17
  try {
@@ -45,7 +46,7 @@ function launchMentor(moduleNum, targetDir, execPath) {
45
46
  const titleLine = ` Module ${moduleNum}: ${rawTitle}`;
46
47
  const banner = '\r\n' +
47
48
  chalk.bold.cyan(` ╔${'═'.repeat(BOX)}╗`) + '\r\n' +
48
- chalk.bold.cyan(' ║') + chalk.bold.white(pad(' ⚛ React Course Workshop')) + chalk.bold.cyan('║') + '\r\n' +
49
+ chalk.bold.cyan(' ║') + chalk.bold.white(pad(' ⚛ EPAM AI Conductor')) + chalk.bold.cyan('║') + '\r\n' +
49
50
  chalk.bold.cyan(' ║') + chalk.cyan(pad(titleLine)) + chalk.bold.cyan('║') + '\r\n' +
50
51
  chalk.bold.cyan(' ║') + chalk.gray(pad(' /hint · /check · /step')) + chalk.bold.cyan('║') + '\r\n' +
51
52
  chalk.bold.cyan(` ╚${'═'.repeat(BOX)}╝`) + '\r\n';
@@ -81,8 +82,8 @@ function launchMentor(moduleNum, targetDir, execPath) {
81
82
  }
82
83
  const program = new Command();
83
84
  program
84
- .name('react-workshop')
85
- .description('React course workshop — Socratic mentor powered by CodeMie')
85
+ .name('ai-conductor')
86
+ .description('EPAM AI Conductor — Socratic mentor for the React Fundamentals course, powered by CodeMie')
86
87
  .version(readVersion());
87
88
  program
88
89
  .command('start')
@@ -91,7 +92,7 @@ program
91
92
  .option('-d, --dir <path>', 'Path to your React project (default: current directory)', process.cwd())
92
93
  .action(async (opts) => {
93
94
  console.log();
94
- console.log(chalk.bold.cyan(' React Course Workshop'));
95
+ console.log(chalk.bold.cyan(' EPAM AI Conductor'));
95
96
  console.log(chalk.gray(' Powered by CodeMie + Claude\n'));
96
97
  // 1. Auth check
97
98
  await ensureAuth();
@@ -152,5 +153,43 @@ program
152
153
  // 6. Launch codemie-claude — blocks until student exits
153
154
  launchMentor(moduleNum, opts.dir, codemieClaudePath);
154
155
  });
156
+ program
157
+ .command('help')
158
+ .description('Show usage information')
159
+ .action(() => {
160
+ const BOX = 56;
161
+ const pad = (s) => s.length >= BOX ? s.slice(0, BOX - 1) + ' ' : s.padEnd(BOX);
162
+ console.log();
163
+ console.log(chalk.bold.cyan(` ╔${'═'.repeat(BOX)}╗`));
164
+ console.log(chalk.bold.cyan(' ║') + chalk.bold.white(pad(' ⚛ EPAM AI Conductor')) + chalk.bold.cyan('║'));
165
+ console.log(chalk.bold.cyan(' ║') + chalk.gray(pad(' React Fundamentals Course Mentor')) + chalk.bold.cyan('║'));
166
+ console.log(chalk.bold.cyan(` ╚${'═'.repeat(BOX)}╝`));
167
+ console.log();
168
+ console.log(chalk.bold(' Commands:'));
169
+ console.log();
170
+ console.log(' ' + chalk.cyan('ai-conductor start') + chalk.gray(' — pick a module interactively'));
171
+ console.log(' ' + chalk.cyan('ai-conductor start -m <1-5>') + chalk.gray(' — start a specific module'));
172
+ console.log(' ' + chalk.cyan('ai-conductor start -m <1-5> -d <path>') + chalk.gray(' — start in a specific directory'));
173
+ console.log();
174
+ console.log(chalk.bold(' Examples:'));
175
+ console.log();
176
+ console.log(' ' + chalk.white('ai-conductor start -m 1'));
177
+ console.log(' ' + chalk.white('ai-conductor start -m 3 -d ~/projects/my-react-app'));
178
+ console.log();
179
+ console.log(chalk.bold(' During a session:'));
180
+ console.log();
181
+ console.log(' ' + chalk.cyan('/hint') + chalk.gray(' — get a hint without spoiling the solution'));
182
+ console.log(' ' + chalk.cyan('/check') + chalk.gray(' — check your current progress'));
183
+ console.log(' ' + chalk.cyan('/step') + chalk.gray(' — move to the next step'));
184
+ console.log();
185
+ });
186
+ program
187
+ .command('mcp')
188
+ .description('Start MCP server for a module (used internally by Claude Code)')
189
+ .requiredOption('-m, --module <number>', 'Module number')
190
+ .action(async (opts) => {
191
+ const moduleNum = parseInt(opts.module, 10);
192
+ await startMcpServer(moduleNum);
193
+ });
155
194
  program.parse(process.argv);
156
195
  //# sourceMappingURL=cli.js.map
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { writeFileSync, mkdirSync } from 'fs';
2
2
  import { join } from 'path';
3
- import { loadModule } from './content-loader.js';
3
+ import { hintCommand, checkCommand, stepCommand } from './prompts.js';
4
4
  export const MODULE_TITLES = {
5
5
  1: 'React Components & Project Structure',
6
6
  2: 'Routing, Forms & Custom Hooks',
@@ -9,106 +9,30 @@ export const MODULE_TITLES = {
9
9
  5: 'Testing with React Testing Library',
10
10
  };
11
11
  export function buildContext(moduleNum, targetDir) {
12
- const { theory, task, tests } = loadModule(moduleNum);
13
- const title = MODULE_TITLES[moduleNum] ?? `Module ${moduleNum}`;
14
- // CLAUDE.md: persona + rules only (~2k chars), heavy content in @-imported files
15
- const claudeMd = `# React Course — Module ${moduleNum}: ${title}
16
-
17
- You are a Socratic mentor for a React fundamentals course.
18
- The student is working on Module ${moduleNum} in their local project.
19
-
20
- ## Working Directory
21
-
22
- You are running inside the student's React project at: \`${targetDir}\`
23
- The student's source code lives in the \`src/\` subdirectory of this folder.
24
- The project is a pre-configured React template — the student already has it set up locally.
25
- Always use the Read tool to inspect the student's actual files — do not assume what they have written.
26
- When referencing files, use paths relative to \`${targetDir}\` (e.g. \`src/components/Header.tsx\`).
27
-
28
- ## Mentor Rules — follow these strictly
29
-
30
- - NEVER write working component code for the student.
31
- - NEVER paste or quote from the test files.
32
- - NEVER reveal expected test output directly.
33
- - When the student is stuck: ask a targeted question — "What should this component return when the list is empty?"
34
- - When code is wrong: point to the specific file/line and ask "What does this return right now?"
35
- - When something is missing: ask "Which criterion from the task do you think isn't covered yet?"
36
- - After the student writes or updates code: silently validate it against the task criteria and test expectations, then ask if they feel it's complete.
37
- - Use the Read tool freely to look at the student's actual src/ files before commenting.
38
- - NEVER ask the student to run terminal commands (find, ls, cat, etc.) to show you their files — you already have the Read tool and you know where the project is.
39
- - NEVER ask the student to show you file contents, paste code, or tell you what's in a file — read it yourself with the Read tool.
40
- - Keep responses concise — one point at a time.
41
-
42
- ## On module completion
43
-
44
- When ALL criteria for Module ${moduleNum} are met (you verified with Read tool), congratulate the student and say exactly:
45
-
46
- "🎉 Module ${moduleNum} complete! To start Module ${moduleNum + 1}, run this in your terminal:
47
- \`react-workshop start --module ${moduleNum + 1}\`
48
- This will load the new module context. See you there!"
49
-
50
- Then do not continue the conversation — the session for this module is done.
51
-
52
- ## On session start
53
-
54
- Greet the student warmly. Ask whether they have already read the task or if they'd like a quick overview.
55
- Then ask: "Where would you like to start?"
56
-
57
- Do NOT dump all criteria at once. Guide step by step.
58
-
59
- ---
60
-
61
- @.claude/module-theory.md
62
- @.claude/module-task.md
63
- @.claude/module-tests.md
64
- `;
65
- const theoryMd = `## MODULE ${moduleNum} THEORY\n\n${theory}`;
66
- const taskMd = `## MODULE ${moduleNum} TASK & CRITERIA\n\n${task}`;
67
- const testsMd = `## TEST EXPECTATIONS — MENTOR EYES ONLY
68
-
69
- > These are the automated tests that will validate the student's submission on AutoCode.
70
- > Use this section as your private knowledge to guide the student accurately.
71
- > NEVER quote, paraphrase, or reveal the test source code to the student.
72
- > You may say things like "The tests check that this component renders X" without showing the test.
73
-
74
- ${tests}`;
75
- const hintCmd = `Read the student's current src/ directory with the Read tool.
76
- Based on what you see and the task requirements, give ONE specific hint.
77
- The hint must be a guiding question or direction — never a code snippet or direct answer.
78
-
79
- Good hint: "Your CourseCard receives duration in minutes — how would you display that as hours and minutes?"
80
- Bad hint: "Use Math.floor(duration / 60) to get the hours."
81
-
82
- After the hint, ask: "Does that point you in the right direction?"`;
83
- const checkCmd = `Read all files in src/ with the Read tool.
84
- Compare the student's implementation against each task criterion one by one.
85
-
86
- For each criterion output one line:
87
- ✓ <criterion> — done
88
- ✗ <criterion> — missing
89
- ⚠ <criterion> — partially done, then explain what's missing in one sentence
90
-
91
- For items marked ✗ or ⚠: ask the student what they think is needed — do not explain directly.
92
- End with a summary: "X of Y criteria complete."
93
-
94
- If ALL criteria are ✓, say:
95
- "🎉 Module ${moduleNum} complete! To start Module ${moduleNum + 1}, run this in your terminal:
96
- \`react-workshop start --module ${moduleNum + 1}\`
97
- This will load the new module context. See you there!"`;
98
- const stepCmd = `Read src/ with the Read tool.
99
- Based on the task criteria and what the student has already implemented, identify the single most logical next step.
100
-
101
- Present it as a question:
102
- "It looks like Header is done. What component would you work on next according to the project architecture?"
103
-
104
- Do not list multiple steps — one at a time only.`;
105
12
  const claudeDir = join(targetDir, '.claude');
106
13
  const commandsDir = join(claudeDir, 'commands');
107
14
  mkdirSync(commandsDir, { recursive: true });
15
+ // Minimal CLAUDE.md — slash commands only, no persona (persona lives in MCP server instructions)
16
+ const claudeMd = `## Slash Commands
17
+
18
+ - \`/hint\` — get a targeted hint without spoiling the solution
19
+ - \`/check\` — check your current progress against all criteria
20
+ - \`/step\` — find out the single most logical next step
21
+ `;
22
+ const hintCmd = hintCommand(moduleNum);
23
+ const checkCmd = checkCommand(moduleNum);
24
+ const stepCmd = stepCommand(moduleNum);
25
+ // settings.json — wires up the MCP server
26
+ const settings = {
27
+ mcpServers: {
28
+ 'ai-conductor': {
29
+ command: 'ai-conductor',
30
+ args: ['mcp', '--module', String(moduleNum)],
31
+ },
32
+ },
33
+ };
108
34
  writeFileSync(join(claudeDir, 'CLAUDE.md'), claudeMd, 'utf-8');
109
- writeFileSync(join(claudeDir, 'module-theory.md'), theoryMd, 'utf-8');
110
- writeFileSync(join(claudeDir, 'module-task.md'), taskMd, 'utf-8');
111
- writeFileSync(join(claudeDir, 'module-tests.md'), testsMd, 'utf-8');
35
+ writeFileSync(join(claudeDir, 'settings.json'), JSON.stringify(settings, null, 2), 'utf-8');
112
36
  writeFileSync(join(commandsDir, 'hint.md'), hintCmd, 'utf-8');
113
37
  writeFileSync(join(commandsDir, 'check.md'), checkCmd, 'utf-8');
114
38
  writeFileSync(join(commandsDir, 'step.md'), stepCmd, 'utf-8');
@@ -1 +1 @@
1
- {"version":3,"file":"context-builder.js","sourceRoot":"","sources":["../src/context-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD,MAAM,CAAC,MAAM,aAAa,GAA2B;IACnD,CAAC,EAAE,sCAAsC;IACzC,CAAC,EAAE,+BAA+B;IAClC,CAAC,EAAE,wBAAwB;IAC3B,CAAC,EAAE,8BAA8B;IACjC,CAAC,EAAE,oCAAoC;CACxC,CAAC;AAEF,MAAM,UAAU,YAAY,CAAC,SAAiB,EAAE,SAAiB;IAC/D,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC;IACtD,MAAM,KAAK,GAAG,aAAa,CAAC,SAAS,CAAC,IAAI,UAAU,SAAS,EAAE,CAAC;IAEhE,iFAAiF;IACjF,MAAM,QAAQ,GAAG,2BAA2B,SAAS,KAAK,KAAK;;;mCAG9B,SAAS;;;;2DAIe,SAAS;;;;kDAIlB,SAAS;;;;;;;;;;;;;;;;;;+BAkB5B,SAAS;;aAE3B,SAAS,8BAA8B,SAAS,GAAG,CAAC;kCAC/B,SAAS,GAAG,CAAC;;;;;;;;;;;;;;;;;CAiB9C,CAAC;IAEA,MAAM,QAAQ,GAAG,aAAa,SAAS,cAAc,MAAM,EAAE,CAAC;IAE9D,MAAM,MAAM,GAAG,aAAa,SAAS,uBAAuB,IAAI,EAAE,CAAC;IAEnE,MAAM,OAAO,GAAG;;;;;;;EAOhB,KAAK,EAAE,CAAC;IAER,MAAM,OAAO,GAAG;;;;;;;mEAOiD,CAAC;IAElE,MAAM,QAAQ,GAAG;;;;;;;;;;;;aAYN,SAAS,8BAA8B,SAAS,GAAG,CAAC;kCAC/B,SAAS,GAAG,CAAC;uDACQ,CAAC;IAEtD,MAAM,OAAO,GAAG;;;;;;iDAM+B,CAAC;IAEhD,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IAC7C,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IAChD,SAAS,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE5C,aAAa,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC/D,aAAa,CAAC,IAAI,CAAC,SAAS,EAAE,kBAAkB,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IACtE,aAAa,CAAC,IAAI,CAAC,SAAS,EAAE,gBAAgB,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IAClE,aAAa,CAAC,IAAI,CAAC,SAAS,EAAE,iBAAiB,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IACpE,aAAa,CAAC,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAC9D,aAAa,CAAC,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAChE,aAAa,CAAC,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AAChE,CAAC"}
1
+ {"version":3,"file":"context-builder.js","sourceRoot":"","sources":["../src/context-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEtE,MAAM,CAAC,MAAM,aAAa,GAA2B;IACnD,CAAC,EAAE,sCAAsC;IACzC,CAAC,EAAE,+BAA+B;IAClC,CAAC,EAAE,wBAAwB;IAC3B,CAAC,EAAE,8BAA8B;IACjC,CAAC,EAAE,oCAAoC;CACxC,CAAC;AAEF,MAAM,UAAU,YAAY,CAAC,SAAiB,EAAE,SAAiB;IAC/D,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IAC7C,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IAChD,SAAS,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE5C,iGAAiG;IACjG,MAAM,QAAQ,GAAG;;;;;CAKlB,CAAC;IAEA,MAAM,OAAO,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC;IACvC,MAAM,QAAQ,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;IACzC,MAAM,OAAO,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC;IAEvC,0CAA0C;IAC1C,MAAM,QAAQ,GAAG;QACf,UAAU,EAAE;YACV,cAAc,EAAE;gBACd,OAAO,EAAE,cAAc;gBACvB,IAAI,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC;aAC7C;SACF;KACF,CAAC;IAEF,aAAa,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC/D,aAAa,CAAC,IAAI,CAAC,SAAS,EAAE,eAAe,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IAC5F,aAAa,CAAC,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAC9D,aAAa,CAAC,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAChE,aAAa,CAAC,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AAChE,CAAC"}
@@ -0,0 +1,35 @@
1
+ export interface Criterion {
2
+ /** Stable id, e.g. "m1-coursecard-2" */
3
+ id: string;
4
+ /** Component / section this criterion belongs to */
5
+ component: string;
6
+ /** Point value (0 for ungraded extra-task items) */
7
+ points: number;
8
+ /** The requirement text */
9
+ requirement: string;
10
+ /** Indented sub-bullets that detail the requirement */
11
+ details: string[];
12
+ /** True for "Extra Task" items that are not part of the graded total */
13
+ optional: boolean;
14
+ }
15
+ /**
16
+ * Extract structured, graded criteria from a module's task markdown.
17
+ *
18
+ * Anchors to the "## Criteria" section so setup steps, project-structure
19
+ * trees and code samples earlier in the file are never mistaken for
20
+ * criteria. Captures point weights and indented sub-requirements, and
21
+ * marks everything under an "Extra Task" heading as optional.
22
+ */
23
+ export declare function extractCriteria(taskMd: string, moduleNum: number): Criterion[];
24
+ /**
25
+ * Load criteria for a module. If a curated `criteria.override.json` exists in
26
+ * the module's content dir, it fully replaces the auto-extracted list — this
27
+ * lets a coordinator hand-tune criteria without changing the parser.
28
+ */
29
+ export declare function loadCriteria(moduleNum: number, taskMd: string): Criterion[];
30
+ /** Group criteria by component for display. */
31
+ export declare function groupByComponent(criteria: Criterion[]): Map<string, Criterion[]>;
32
+ /** Total graded points (excludes optional extra-task items). */
33
+ export declare function totalPoints(criteria: Criterion[]): number;
34
+ /** Render a single criterion as one human-readable line. */
35
+ export declare function formatCriterion(c: Criterion): string;
@@ -0,0 +1,151 @@
1
+ import { readFileSync, existsSync } from 'fs';
2
+ import { join, dirname } from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ const __dirname = dirname(fileURLToPath(import.meta.url));
5
+ const CONTENT_DIR = join(__dirname, 'content');
6
+ function slugify(s) {
7
+ return s
8
+ .toLowerCase()
9
+ .replace(/[^a-z0-9]+/g, '-')
10
+ .replace(/^-+|-+$/g, '') || 'general';
11
+ }
12
+ /** Clean a "### ..." heading into a component/section label. */
13
+ function cleanComponentHeading(raw) {
14
+ let s = raw.trim();
15
+ // Strip markdown link: [Header component](url) -> Header component
16
+ s = s.replace(/\[([^\]]+)\]\([^)]*\)/g, '$1');
17
+ // Prefer a bold token if present (modules 3 & 5 bold the component name)
18
+ const bold = s.match(/\*\*([^*]+)\*\*/);
19
+ if (bold)
20
+ return stripComponentWord(bold[1]);
21
+ // Otherwise strip formatting and trailing punctuation
22
+ s = s.replace(/\*\*/g, '').replace(/[:.\s]+$/g, '').trim();
23
+ return stripComponentWord(s) || 'General';
24
+ }
25
+ /** Drop a trailing "Component"/"components" word so headings match test names. */
26
+ function stripComponentWord(s) {
27
+ return s.replace(/\s+components?\s*$/i, '').trim() || s.trim();
28
+ }
29
+ const CRITERIA_HEADING = /^##\s+Criteria\b/i;
30
+ const EXTRA_HEADING = /^#{2,4}\s+.*\bExtra\s+Task\b/i;
31
+ const SUBHEADING = /^###\s+(.+)/;
32
+ const POINT_LINE = /^[*-]\s*\[\s*(\d+)\s*points?\s*\]\s*-?\s*(.+)$/i;
33
+ const TOP_BULLET = /^[*-]\s+(.+)$/;
34
+ const SUB_BULLET = /^\s+[*-]\s+(.+)$/;
35
+ /**
36
+ * Extract structured, graded criteria from a module's task markdown.
37
+ *
38
+ * Anchors to the "## Criteria" section so setup steps, project-structure
39
+ * trees and code samples earlier in the file are never mistaken for
40
+ * criteria. Captures point weights and indented sub-requirements, and
41
+ * marks everything under an "Extra Task" heading as optional.
42
+ */
43
+ export function extractCriteria(taskMd, moduleNum) {
44
+ const lines = taskMd.split('\n');
45
+ // Find the start of the graded criteria section.
46
+ const start = lines.findIndex(l => CRITERIA_HEADING.test(l));
47
+ const scan = start === -1 ? lines : lines.slice(start + 1);
48
+ const criteria = [];
49
+ let component = 'Common';
50
+ let optional = false;
51
+ const seq = new Map();
52
+ const pushCriterion = (points, requirement) => {
53
+ const slug = slugify(component);
54
+ const n = (seq.get(slug) ?? 0) + 1;
55
+ seq.set(slug, n);
56
+ const c = {
57
+ id: `m${moduleNum}-${slug}-${n}`,
58
+ component,
59
+ points,
60
+ requirement: requirement.trim(),
61
+ details: [],
62
+ optional,
63
+ };
64
+ criteria.push(c);
65
+ return c;
66
+ };
67
+ let current = null;
68
+ for (const line of scan) {
69
+ if (EXTRA_HEADING.test(line)) {
70
+ optional = true;
71
+ const m = line.match(SUBHEADING);
72
+ if (m)
73
+ component = cleanComponentHeading(m[1]);
74
+ current = null;
75
+ continue;
76
+ }
77
+ const heading = line.match(SUBHEADING);
78
+ if (heading) {
79
+ component = cleanComponentHeading(heading[1]);
80
+ current = null;
81
+ continue;
82
+ }
83
+ const sub = line.match(SUB_BULLET);
84
+ if (sub && current) {
85
+ current.details.push(sub[1].trim());
86
+ continue;
87
+ }
88
+ const pts = line.match(POINT_LINE);
89
+ if (pts) {
90
+ current = pushCriterion(parseInt(pts[1], 10), pts[2]);
91
+ continue;
92
+ }
93
+ // Top-level bullet without a point value (e.g. optional extra-task items)
94
+ const bullet = line.match(TOP_BULLET);
95
+ if (bullet && !/^\s*\[/.test(bullet[1])) {
96
+ current = pushCriterion(0, bullet[1]);
97
+ continue;
98
+ }
99
+ }
100
+ return criteria;
101
+ }
102
+ /**
103
+ * Load criteria for a module. If a curated `criteria.override.json` exists in
104
+ * the module's content dir, it fully replaces the auto-extracted list — this
105
+ * lets a coordinator hand-tune criteria without changing the parser.
106
+ */
107
+ export function loadCriteria(moduleNum, taskMd) {
108
+ const overridePath = join(CONTENT_DIR, `module-${moduleNum}`, 'criteria.override.json');
109
+ if (existsSync(overridePath)) {
110
+ try {
111
+ const parsed = JSON.parse(readFileSync(overridePath, 'utf-8'));
112
+ if (Array.isArray(parsed) && parsed.length > 0) {
113
+ return parsed.map((c, i) => ({
114
+ id: c.id ?? `m${moduleNum}-override-${i + 1}`,
115
+ component: c.component ?? 'General',
116
+ points: c.points ?? 0,
117
+ requirement: c.requirement ?? '',
118
+ details: c.details ?? [],
119
+ optional: c.optional ?? false,
120
+ }));
121
+ }
122
+ }
123
+ catch {
124
+ // Fall through to auto-extraction on malformed override.
125
+ }
126
+ }
127
+ return extractCriteria(taskMd, moduleNum);
128
+ }
129
+ /** Group criteria by component for display. */
130
+ export function groupByComponent(criteria) {
131
+ const map = new Map();
132
+ for (const c of criteria) {
133
+ const list = map.get(c.component) ?? [];
134
+ list.push(c);
135
+ map.set(c.component, list);
136
+ }
137
+ return map;
138
+ }
139
+ /** Total graded points (excludes optional extra-task items). */
140
+ export function totalPoints(criteria) {
141
+ return criteria.filter(c => !c.optional).reduce((sum, c) => sum + c.points, 0);
142
+ }
143
+ /** Render a single criterion as one human-readable line. */
144
+ export function formatCriterion(c) {
145
+ const pts = c.optional ? 'extra' : `${c.points} pt${c.points === 1 ? '' : 's'}`;
146
+ const head = `[${c.id}] (${pts}) ${c.requirement}`;
147
+ if (c.details.length === 0)
148
+ return head;
149
+ return head + '\n' + c.details.map(d => ` • ${d}`).join('\n');
150
+ }
151
+ //# sourceMappingURL=criteria.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"criteria.js","sourceRoot":"","sources":["../src/criteria.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function getLogDir(): string;
2
+ export declare function logToolCall(moduleNum: number, tool: string, args: Record<string, unknown>, resultSummary?: string): void;
package/dist/logger.js ADDED
@@ -0,0 +1,32 @@
1
+ import { appendFileSync, mkdirSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { homedir } from 'os';
4
+ /**
5
+ * Lightweight, local-only logging of MCP tool calls so the course coordinator
6
+ * can see how students use the mentor and spot where assessments went wrong.
7
+ * Never throws — logging must not break a mentor session.
8
+ */
9
+ const LOG_DIR = join(homedir(), '.ai-conductor', 'logs');
10
+ export function getLogDir() {
11
+ return LOG_DIR;
12
+ }
13
+ function today() {
14
+ return new Date().toISOString().slice(0, 10);
15
+ }
16
+ export function logToolCall(moduleNum, tool, args, resultSummary) {
17
+ try {
18
+ mkdirSync(LOG_DIR, { recursive: true });
19
+ const entry = {
20
+ ts: new Date().toISOString(),
21
+ module: moduleNum,
22
+ tool,
23
+ args,
24
+ result: resultSummary,
25
+ };
26
+ appendFileSync(join(LOG_DIR, `tools-${today()}.jsonl`), JSON.stringify(entry) + '\n', 'utf-8');
27
+ }
28
+ catch {
29
+ // Intentionally silent.
30
+ }
31
+ }
32
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAE7B;;;;GAIG;AAEH,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,eAAe,EAAE,MAAM,CAAC,CAAC;AAEzD,MAAM,UAAU,SAAS;IACvB,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,KAAK;IACZ,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,WAAW,CACzB,SAAiB,EACjB,IAAY,EACZ,IAA6B,EAC7B,aAAsB;IAEtB,IAAI,CAAC;QACH,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACxC,MAAM,KAAK,GAAG;YACZ,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YAC5B,MAAM,EAAE,SAAS;YACjB,IAAI;YACJ,IAAI;YACJ,MAAM,EAAE,aAAa;SACtB,CAAC;QACF,cAAc,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,KAAK,EAAE,QAAQ,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;IACjG,CAAC;IAAC,MAAM,CAAC;QACP,wBAAwB;IAC1B,CAAC;AACH,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function startMcpServer(moduleNum: number): Promise<void>;
@@ -0,0 +1,97 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { z } from 'zod';
4
+ import { loadModule } from './content-loader.js';
5
+ import { MODULE_TITLES } from './context-builder.js';
6
+ import { loadCriteria, groupByComponent, totalPoints, formatCriterion } from './criteria.js';
7
+ import { parseTestExpectation, formatExpectation } from './test-expectations.js';
8
+ import { scanProject, formatSnapshot } from './project-scan.js';
9
+ import { buildPersona } from './prompts.js';
10
+ import { logToolCall } from './logger.js';
11
+ /** Split the bundled tests blob (one "### file" section each) into raw code by component. */
12
+ function parseTestsByComponent(rawTests) {
13
+ const result = {};
14
+ const sections = rawTests.split(/\n---\n/);
15
+ for (const section of sections) {
16
+ const match = section.match(/^###\s+(.+?\.test\.[tj]sx?)/m);
17
+ if (!match)
18
+ continue;
19
+ const component = match[1].replace(/\.test\.[tj]sx?$/, '');
20
+ result[component] = section.trim();
21
+ }
22
+ return result;
23
+ }
24
+ export async function startMcpServer(moduleNum) {
25
+ const { theory, task, tests } = loadModule(moduleNum);
26
+ const title = MODULE_TITLES[moduleNum] ?? `Module ${moduleNum}`;
27
+ const testsByComponent = parseTestsByComponent(tests);
28
+ const criteria = loadCriteria(moduleNum, task);
29
+ const graded = totalPoints(criteria);
30
+ const server = new McpServer({ name: 'ai-conductor', version: '1.0.0' }, { instructions: buildPersona(moduleNum) });
31
+ server.tool('get_criteria', 'Get the graded checklist for this module (stable ids + point weights). Pass a component name to filter. This is the source of truth for "is it done".', { component: z.string().optional().describe('Filter by component/section, e.g. "Header"') }, async ({ component }) => {
32
+ const filtered = component
33
+ ? criteria.filter(c => c.component.toLowerCase().includes(component.toLowerCase()))
34
+ : criteria;
35
+ logToolCall(moduleNum, 'get_criteria', { component }, `${filtered.length} criteria`);
36
+ if (filtered.length === 0) {
37
+ return { content: [{ type: 'text', text: `No criteria found${component ? ` for "${component}"` : ''}.` }] };
38
+ }
39
+ const groups = groupByComponent(filtered);
40
+ const blocks = [`Module ${moduleNum}: ${title} — ${graded} graded points total\n`];
41
+ for (const [comp, items] of groups) {
42
+ blocks.push(`### ${comp}`);
43
+ for (const c of items)
44
+ blocks.push(' ' + formatCriterion(c).replace(/\n/g, '\n '));
45
+ }
46
+ return { content: [{ type: 'text', text: blocks.join('\n') }] };
47
+ });
48
+ server.tool('get_test_expectations', 'Get the objective test contract a component must satisfy (expected export/path, props, required output text, queried roles/testids, interactions). Never reveals raw test code.', { component: z.string().describe('Component name, e.g. "Header", "CourseCard"') }, async ({ component }) => {
49
+ const key = Object.keys(testsByComponent).find(k => k.toLowerCase() === component.toLowerCase());
50
+ logToolCall(moduleNum, 'get_test_expectations', { component }, key ? 'found' : 'not found');
51
+ if (!key) {
52
+ const available = Object.keys(testsByComponent).join(', ') || '(none)';
53
+ return { content: [{ type: 'text', text: `No tests found for "${component}". Available: ${available}` }] };
54
+ }
55
+ const exp = parseTestExpectation(key, testsByComponent[key]);
56
+ return { content: [{ type: 'text', text: formatExpectation(exp) }] };
57
+ });
58
+ server.tool('get_theory', 'Get the theory content for this module. Pass a topic keyword to filter relevant sections.', { topic: z.string().optional().describe('Keyword to filter theory sections, e.g. "useState"') }, async ({ topic }) => {
59
+ logToolCall(moduleNum, 'get_theory', { topic });
60
+ if (!topic)
61
+ return { content: [{ type: 'text', text: theory }] };
62
+ const lower = topic.toLowerCase();
63
+ const sections = theory.split(/\n---\n/);
64
+ const relevant = sections.filter(s => s.toLowerCase().includes(lower));
65
+ const text = relevant.length > 0
66
+ ? relevant.join('\n\n---\n\n')
67
+ : `No theory sections found for topic "${topic}".`;
68
+ return { content: [{ type: 'text', text }] };
69
+ });
70
+ server.tool('get_module_overview', 'Get the module map: each component linked to its graded criteria (ids + points) and whether a hidden test contract exists for it. Use this to reason about the module as a whole.', {}, async () => {
71
+ logToolCall(moduleNum, 'get_module_overview', {});
72
+ const groups = groupByComponent(criteria);
73
+ const testedComponents = new Set(Object.keys(testsByComponent).map(k => k.toLowerCase()));
74
+ const blocks = [
75
+ `Module ${moduleNum}: ${title}`,
76
+ `Graded total: ${graded} points across ${criteria.filter(c => !c.optional).length} criteria.`,
77
+ `Components with hidden tests: ${Object.keys(testsByComponent).join(', ') || '(none)'}\n`,
78
+ ];
79
+ for (const [comp, items] of groups) {
80
+ const pts = items.filter(c => !c.optional).reduce((s, c) => s + c.points, 0);
81
+ const tested = [...testedComponents].some(t => comp.toLowerCase().includes(t) || t.includes(comp.toLowerCase()));
82
+ blocks.push(`### ${comp} — ${pts} pts${tested ? ' · has test contract' : ''}`);
83
+ for (const c of items)
84
+ blocks.push(` - ${c.id}: ${c.requirement}`);
85
+ }
86
+ return { content: [{ type: 'text', text: blocks.join('\n') }] };
87
+ });
88
+ server.tool('scan_project', "Get a fresh structural snapshot of the student's current code (file tree, detected components and their export style, helpers). Objective signals only — call this before assessing progress so judgments are never stale.", { dir: z.string().optional().describe('Project root (defaults to the current working directory)') }, async ({ dir }) => {
89
+ const root = dir || process.cwd();
90
+ const snapshot = scanProject(root);
91
+ logToolCall(moduleNum, 'scan_project', { dir: root }, `${snapshot.files.length} files, ${snapshot.components.length} components`);
92
+ return { content: [{ type: 'text', text: formatSnapshot(snapshot) }] };
93
+ });
94
+ const transport = new StdioServerTransport();
95
+ await server.connect(transport);
96
+ }
97
+ //# sourceMappingURL=mcp-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-server.js","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC7F,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AACjF,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,6FAA6F;AAC7F,SAAS,qBAAqB,CAAC,QAAgB;IAC7C,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IAC3C,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAC;QAC5D,IAAI,CAAC,KAAK;YAAE,SAAS;QACrB,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;QAC3D,MAAM,CAAC,SAAS,CAAC,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IACrC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,SAAiB;IACpD,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC;IACtD,MAAM,KAAK,GAAG,aAAa,CAAC,SAAS,CAAC,IAAI,UAAU,SAAS,EAAE,CAAC;IAChE,MAAM,gBAAgB,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC;IACtD,MAAM,QAAQ,GAAG,YAAY,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC/C,MAAM,MAAM,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;IAErC,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,OAAO,EAAE,EAC1C,EAAE,YAAY,EAAE,YAAY,CAAC,SAAS,CAAC,EAAE,CAC1C,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,cAAc,EACd,uJAAuJ,EACvJ,EAAE,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,4CAA4C,CAAC,EAAE,EAC3F,KAAK,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE;QACtB,MAAM,QAAQ,GAAG,SAAS;YACxB,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC;YACnF,CAAC,CAAC,QAAQ,CAAC;QACb,WAAW,CAAC,SAAS,EAAE,cAAc,EAAE,EAAE,SAAS,EAAE,EAAE,GAAG,QAAQ,CAAC,MAAM,WAAW,CAAC,CAAC;QACrF,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,oBAAoB,SAAS,CAAC,CAAC,CAAC,SAAS,SAAS,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;QAC9G,CAAC;QACD,MAAM,MAAM,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAC1C,MAAM,MAAM,GAAa,CAAC,UAAU,SAAS,KAAK,KAAK,MAAM,MAAM,wBAAwB,CAAC,CAAC;QAC7F,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,EAAE,CAAC;YACnC,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;YAC3B,KAAK,MAAM,CAAC,IAAI,KAAK;gBAAE,MAAM,CAAC,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;QACvF,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;IAClE,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,uBAAuB,EACvB,iLAAiL,EACjL,EAAE,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,6CAA6C,CAAC,EAAE,EACjF,KAAK,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE;QACtB,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,KAAK,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC;QACjG,WAAW,CAAC,SAAS,EAAE,uBAAuB,EAAE,EAAE,SAAS,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;QAC5F,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC;YACvE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,uBAAuB,SAAS,iBAAiB,SAAS,EAAE,EAAE,CAAC,EAAE,CAAC;QAC7G,CAAC;QACD,MAAM,GAAG,GAAG,oBAAoB,CAAC,GAAG,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC;QAC7D,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;IACvE,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,YAAY,EACZ,2FAA2F,EAC3F,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,oDAAoD,CAAC,EAAE,EAC/F,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;QAClB,WAAW,CAAC,SAAS,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QAChD,IAAI,CAAC,KAAK;YAAE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;QACjE,MAAM,KAAK,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;QAClC,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACzC,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;QACvE,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC;YAC9B,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC;YAC9B,CAAC,CAAC,uCAAuC,KAAK,IAAI,CAAC;QACrD,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IAC/C,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,qBAAqB,EACrB,mLAAmL,EACnL,EAAE,EACF,KAAK,IAAI,EAAE;QACT,WAAW,CAAC,SAAS,EAAE,qBAAqB,EAAE,EAAE,CAAC,CAAC;QAClD,MAAM,MAAM,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAC1C,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;QAC1F,MAAM,MAAM,GAAa;YACvB,UAAU,SAAS,KAAK,KAAK,EAAE;YAC/B,iBAAiB,MAAM,kBAAkB,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,MAAM,YAAY;YAC7F,iCAAiC,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,QAAQ,IAAI;SAC1F,CAAC;QACF,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,EAAE,CAAC;YACnC,MAAM,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;YAC7E,MAAM,MAAM,GAAG,CAAC,GAAG,gBAAgB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;YACjH,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,MAAM,GAAG,OAAO,MAAM,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC/E,KAAK,MAAM,CAAC,IAAI,KAAK;gBAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;QACtE,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;IAClE,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,cAAc,EACd,4NAA4N,EAC5N,EAAE,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,0DAA0D,CAAC,EAAE,EACnG,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;QAChB,MAAM,IAAI,GAAG,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QAClC,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;QACnC,WAAW,CAAC,SAAS,EAAE,cAAc,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,MAAM,WAAW,QAAQ,CAAC,UAAU,CAAC,MAAM,aAAa,CAAC,CAAC;QAClI,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;IACzE,CAAC,CACF,CAAC;IAEF,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * A deterministic, structural snapshot of the student's project. It reports
3
+ * objective signals only — which files/components exist and how they export —
4
+ * and never judges whether the logic is correct. The mentor uses it to ground
5
+ * every assessment in the current state of the code rather than memory.
6
+ */
7
+ export interface ComponentInfo {
8
+ name: string;
9
+ path: string;
10
+ hasDefaultExport: boolean;
11
+ hasNamedExport: boolean;
12
+ isFunctionComponent: boolean;
13
+ importsReact: boolean;
14
+ }
15
+ export interface ProjectSnapshot {
16
+ root: string;
17
+ hasSrc: boolean;
18
+ tree: string;
19
+ files: string[];
20
+ components: ComponentInfo[];
21
+ helpers: string[];
22
+ }
23
+ export declare function scanProject(root: string): ProjectSnapshot;
24
+ /** Render a snapshot as a concise, mentor-facing summary. */
25
+ export declare function formatSnapshot(s: ProjectSnapshot): string;
@@ -0,0 +1,129 @@
1
+ import { readdirSync, readFileSync, statSync, existsSync } from 'fs';
2
+ import { join, relative, basename, extname } from 'path';
3
+ const IGNORE = new Set(['node_modules', '.git', 'build', 'dist', 'coverage', '.next']);
4
+ const CODE_EXT = new Set(['.js', '.jsx', '.ts', '.tsx']);
5
+ function walk(dir, root, acc, depth = 0) {
6
+ if (depth > 8)
7
+ return;
8
+ let entries;
9
+ try {
10
+ entries = readdirSync(dir).sort();
11
+ }
12
+ catch {
13
+ return;
14
+ }
15
+ for (const name of entries) {
16
+ if (IGNORE.has(name))
17
+ continue;
18
+ const full = join(dir, name);
19
+ let st;
20
+ try {
21
+ st = statSync(full);
22
+ }
23
+ catch {
24
+ continue;
25
+ }
26
+ if (st.isDirectory()) {
27
+ walk(full, root, acc, depth + 1);
28
+ }
29
+ else {
30
+ acc.push(relative(root, full));
31
+ }
32
+ }
33
+ }
34
+ function isComponentFile(rel) {
35
+ const ext = extname(rel);
36
+ if (!CODE_EXT.has(ext))
37
+ return false;
38
+ if (/\.(test|spec)\./.test(rel))
39
+ return false;
40
+ const base = basename(rel, ext);
41
+ // Component files are PascalCase by convention in this course.
42
+ return /^[A-Z][A-Za-z0-9]*$/.test(base);
43
+ }
44
+ function analyzeComponent(root, rel) {
45
+ const name = basename(rel, extname(rel));
46
+ let code = '';
47
+ try {
48
+ code = readFileSync(join(root, rel), 'utf-8');
49
+ }
50
+ catch {
51
+ /* ignore */
52
+ }
53
+ const hasDefaultExport = /export\s+default\b/.test(code);
54
+ const hasNamedExport = new RegExp(`export\\s+(?:const|function|class)\\s+${name}\\b`).test(code)
55
+ || new RegExp(`export\\s*\\{[^}]*\\b${name}\\b`).test(code);
56
+ const isFunctionComponent = new RegExp(`function\\s+${name}\\b`).test(code)
57
+ || new RegExp(`const\\s+${name}\\s*[:=]`).test(code);
58
+ const importsReact = /from\s+['"]react['"]/.test(code);
59
+ return { name, path: rel, hasDefaultExport, hasNamedExport, isFunctionComponent, importsReact };
60
+ }
61
+ /** Build an indented tree string from a list of relative file paths. */
62
+ function renderTree(files) {
63
+ const lines = [];
64
+ const seenDirs = new Set();
65
+ for (const f of files) {
66
+ const parts = f.split('/');
67
+ for (let i = 0; i < parts.length; i++) {
68
+ const isFile = i === parts.length - 1;
69
+ const dirKey = parts.slice(0, i + 1).join('/');
70
+ if (!isFile && seenDirs.has(dirKey))
71
+ continue;
72
+ if (!isFile)
73
+ seenDirs.add(dirKey);
74
+ lines.push(' '.repeat(i) + (isFile ? parts[i] : parts[i] + '/'));
75
+ }
76
+ }
77
+ return lines.join('\n');
78
+ }
79
+ export function scanProject(root) {
80
+ const srcDir = join(root, 'src');
81
+ const base = existsSync(srcDir) ? srcDir : root;
82
+ const hasSrc = existsSync(srcDir);
83
+ const abs = [];
84
+ walk(base, root, abs);
85
+ const files = abs.sort();
86
+ const components = files
87
+ .filter(isComponentFile)
88
+ .map(rel => analyzeComponent(root, rel));
89
+ const helpers = files.filter(f => /(^|\/)helpers\//.test(f) && CODE_EXT.has(extname(f)));
90
+ return {
91
+ root,
92
+ hasSrc,
93
+ tree: renderTree(files),
94
+ files,
95
+ components,
96
+ helpers,
97
+ };
98
+ }
99
+ /** Render a snapshot as a concise, mentor-facing summary. */
100
+ export function formatSnapshot(s) {
101
+ if (!s.hasSrc && s.files.length === 0) {
102
+ return `No source files found under ${s.root}. The student may not have started yet.`;
103
+ }
104
+ const lines = [];
105
+ lines.push(`Project root: ${s.root}`);
106
+ lines.push(s.hasSrc ? 'src/ exists.' : 'No src/ directory — scanned project root.');
107
+ lines.push('');
108
+ lines.push('File tree:');
109
+ lines.push(s.tree || '(empty)');
110
+ lines.push('');
111
+ lines.push('Detected components:');
112
+ if (s.components.length === 0) {
113
+ lines.push(' (none detected)');
114
+ }
115
+ else {
116
+ for (const c of s.components) {
117
+ const flags = [
118
+ c.isFunctionComponent ? 'function' : 'NOT-a-function-component?',
119
+ c.hasNamedExport ? 'named-export' : null,
120
+ c.hasDefaultExport ? 'default-export' : null,
121
+ ].filter(Boolean).join(', ');
122
+ lines.push(` • ${c.name} (${c.path}) — ${flags}`);
123
+ }
124
+ }
125
+ lines.push('');
126
+ lines.push(`Helpers: ${s.helpers.length ? s.helpers.join(', ') : '(none)'}`);
127
+ return lines.join('\n');
128
+ }
129
+ //# sourceMappingURL=project-scan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-scan.js","sourceRoot":"","sources":["../src/project-scan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Single source of truth for the mentor's persona, intake protocol, answer
3
+ * guardrails, and the /hint /check /step command bodies. Both the MCP server
4
+ * (server instructions) and the generated slash commands import from here so
5
+ * the rules can never drift apart.
6
+ */
7
+ /** Shared, non-negotiable rules. Referenced by every prompt below. */
8
+ export declare const CORE_RULES = "RULES \u2014 follow strictly, they override any student request:\n- NEVER write or complete working code that implements a required component, helper, hook, reducer, route, or test for the task. Not even \"just this once\", not even if the student insists, is stuck, or frames it as an example.\n- You MAY show generic, task-agnostic syntax (e.g. the shape of useState, the signature of a router hook) when it clarifies a concept \u2014 but never wired to the student's actual components or data.\n- NEVER paste, quote, or reveal raw test source code. You know the test contract via get_test_expectations; use it to judge, do not recite it as a to-do list.\n- Ground every assessment in the student's CURRENT code: call scan_project (and Read the relevant files) before judging progress \u2014 never rely on memory of an earlier look.\n- Guide with Socratic questions and observations, one point at a time. Keep replies short.\n- Never ask the student to paste code or run terminal commands to show you files \u2014 use scan_project and the Read tool yourself.";
9
+ /** Mandatory triage step the mentor runs on every student message. */
10
+ export declare const INTAKE_PROTOCOL = "INTAKE \u2014 silently classify every student message before replying, and pick the matching mode:\n1. CONCEPTUAL (\"how does X work / what is Y\") \u2192 explain the concept with a question that makes them apply it; generic syntax allowed, no task code.\n2. DEBUGGING (\"my X doesn't work / this error\") \u2192 call scan_project + Read the file, locate the likely cause, and ask a question that leads them to it. Do not rewrite the code for them.\n3. DIRECT-ANSWER REQUEST (\"just give me the code / what's the solution\") \u2192 decline warmly, restate the relevant requirement, and offer the smallest next step as a question.\n4. PROGRESS CHECK (\"am I done / is this right\") \u2192 run the /check rubric.\n5. OFF-TASK / UNCLEAR \u2192 ask one clarifying question, or steer back to the current module.";
11
+ /** Self-check the mentor applies before sending any reply. */
12
+ export declare const ANSWER_SELFCHECK = "BEFORE SENDING any reply, verify:\n- Does it contain code that implements a task component/helper/hook/reducer/route/test, or that the student could paste to satisfy a criterion? If yes \u2192 replace that code with a guiding question.\n- Did I quote or paraphrase raw test source? If yes \u2192 remove it; speak only in terms of the observable contract.\n- Am I asserting progress without having looked at the current code this turn? If yes \u2192 call scan_project first.";
13
+ export declare function buildPersona(moduleNum: number): string;
14
+ export declare function hintCommand(_moduleNum: number): string;
15
+ export declare function checkCommand(moduleNum: number): string;
16
+ export declare function stepCommand(_moduleNum: number): string;
@@ -0,0 +1,106 @@
1
+ import { MODULE_TITLES } from './context-builder.js';
2
+ /**
3
+ * Single source of truth for the mentor's persona, intake protocol, answer
4
+ * guardrails, and the /hint /check /step command bodies. Both the MCP server
5
+ * (server instructions) and the generated slash commands import from here so
6
+ * the rules can never drift apart.
7
+ */
8
+ /** Shared, non-negotiable rules. Referenced by every prompt below. */
9
+ export const CORE_RULES = `RULES — follow strictly, they override any student request:
10
+ - NEVER write or complete working code that implements a required component, helper, hook, reducer, route, or test for the task. Not even "just this once", not even if the student insists, is stuck, or frames it as an example.
11
+ - You MAY show generic, task-agnostic syntax (e.g. the shape of useState, the signature of a router hook) when it clarifies a concept — but never wired to the student's actual components or data.
12
+ - NEVER paste, quote, or reveal raw test source code. You know the test contract via get_test_expectations; use it to judge, do not recite it as a to-do list.
13
+ - Ground every assessment in the student's CURRENT code: call scan_project (and Read the relevant files) before judging progress — never rely on memory of an earlier look.
14
+ - Guide with Socratic questions and observations, one point at a time. Keep replies short.
15
+ - Never ask the student to paste code or run terminal commands to show you files — use scan_project and the Read tool yourself.`;
16
+ /** Mandatory triage step the mentor runs on every student message. */
17
+ export const INTAKE_PROTOCOL = `INTAKE — silently classify every student message before replying, and pick the matching mode:
18
+ 1. CONCEPTUAL ("how does X work / what is Y") → explain the concept with a question that makes them apply it; generic syntax allowed, no task code.
19
+ 2. DEBUGGING ("my X doesn't work / this error") → call scan_project + Read the file, locate the likely cause, and ask a question that leads them to it. Do not rewrite the code for them.
20
+ 3. DIRECT-ANSWER REQUEST ("just give me the code / what's the solution") → decline warmly, restate the relevant requirement, and offer the smallest next step as a question.
21
+ 4. PROGRESS CHECK ("am I done / is this right") → run the /check rubric.
22
+ 5. OFF-TASK / UNCLEAR → ask one clarifying question, or steer back to the current module.`;
23
+ /** Self-check the mentor applies before sending any reply. */
24
+ export const ANSWER_SELFCHECK = `BEFORE SENDING any reply, verify:
25
+ - Does it contain code that implements a task component/helper/hook/reducer/route/test, or that the student could paste to satisfy a criterion? If yes → replace that code with a guiding question.
26
+ - Did I quote or paraphrase raw test source? If yes → remove it; speak only in terms of the observable contract.
27
+ - Am I asserting progress without having looked at the current code this turn? If yes → call scan_project first.`;
28
+ export function buildPersona(moduleNum) {
29
+ const title = MODULE_TITLES[moduleNum] ?? `Module ${moduleNum}`;
30
+ return `
31
+ You are a Socratic mentor for a React fundamentals course.
32
+ The student is working on Module ${moduleNum}: "${title}" in their local React project.
33
+
34
+ Your job is to help them learn by doing — never by handing them the answer.
35
+
36
+ Tools available to you:
37
+ - get_module_overview — the module map: components linked to their criteria and test contracts.
38
+ - get_criteria — the graded checklist (stable ids, point weights). The source of truth for "is it done".
39
+ - get_test_expectations — the objective contract each component's hidden tests assert.
40
+ - get_theory — course theory; filter by topic keyword.
41
+ - scan_project — a fresh structural snapshot of the student's current code.
42
+
43
+ ${CORE_RULES}
44
+
45
+ ${INTAKE_PROTOCOL}
46
+
47
+ ${ANSWER_SELFCHECK}
48
+
49
+ On session start: greet the student warmly, ask whether they've read the task, then ask "Where would you like to start?"
50
+
51
+ When ALL graded criteria are met (verified via get_criteria + scan_project + Read):
52
+ Congratulate the student and say exactly:
53
+ "🎉 Module ${moduleNum} complete! To start Module ${moduleNum + 1}, run:
54
+ \`ai-conductor start --module ${moduleNum + 1}\`"
55
+ Then stop — do not continue the conversation.
56
+ `.trim();
57
+ }
58
+ export function hintCommand(_moduleNum) {
59
+ return `${INTAKE_PROTOCOL}
60
+
61
+ You are giving a HINT.
62
+ 1. Call scan_project and Read the most relevant src/ file so the hint fits the student's actual code.
63
+ 2. Call get_criteria (and get_test_expectations for the component in question) to know the target.
64
+ 3. Give exactly ONE hint, phrased as a guiding question or direction — never a code snippet, never the answer.
65
+
66
+ Good: "Your CourseCard receives duration in minutes — how would you turn that into an 'hh:mm' string?"
67
+ Bad: "Use Math.floor(duration / 60) + ':' + (duration % 60)."
68
+
69
+ ${ANSWER_SELFCHECK}
70
+
71
+ End with: "Does that point you in the right direction?"`;
72
+ }
73
+ export function checkCommand(moduleNum) {
74
+ return `You are running a PROGRESS CHECK. Be objective and grounded.
75
+
76
+ 1. Call scan_project to get the current file/component state.
77
+ 2. Call get_criteria to get the full graded checklist (each item has a stable id and point value).
78
+ 3. Read the relevant src/ files with the Read tool. For UI/behaviour criteria, cross-reference get_test_expectations.
79
+
80
+ For EACH criterion output one line, citing where you observed it:
81
+ ✓ <id> <requirement> — done (file:line)
82
+ ✗ <id> <requirement> — missing
83
+ ⚠ <id> <requirement> — partial: <what's missing in one sentence> (file:line)
84
+
85
+ Rules:
86
+ - Base every verdict on what you actually saw this turn — never on memory.
87
+ - For ✗ or ⚠ items: ask the student what they think is needed; do NOT explain the fix or write code.
88
+ - Do not reveal test source; refer only to the observable contract.
89
+
90
+ End with: "X of Y graded criteria complete (Z/<total> points)."
91
+
92
+ If ALL graded criteria are ✓, say exactly:
93
+ "🎉 Module ${moduleNum} complete! To start Module ${moduleNum + 1}, run:
94
+ \`ai-conductor start --module ${moduleNum + 1}\`"`;
95
+ }
96
+ export function stepCommand(_moduleNum) {
97
+ return `You are suggesting the NEXT STEP.
98
+ 1. Call scan_project and Read src/ to see what already exists.
99
+ 2. Call get_criteria to see what remains.
100
+ 3. Identify the SINGLE most logical next step given what's done and what's not.
101
+
102
+ Present it as one question — one step only, never a list, never code.
103
+
104
+ ${ANSWER_SELFCHECK}`;
105
+ }
106
+ //# sourceMappingURL=prompts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD;;;;;GAKG;AAEH,sEAAsE;AACtE,MAAM,CAAC,MAAM,UAAU,GAAG;;;;;;gIAMsG,CAAC;AAEjI,sEAAsE;AACtE,MAAM,CAAC,MAAM,eAAe,GAAG;;;;;0FAK2D,CAAC;AAE3F,8DAA8D;AAC9D,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;iHAGiF,CAAC;AAElH,MAAM,UAAU,YAAY,CAAC,SAAiB;IAC5C,MAAM,KAAK,GAAG,aAAa,CAAC,SAAS,CAAC,IAAI,UAAU,SAAS,EAAE,CAAC;IAChE,OAAO;;mCAE0B,SAAS,MAAM,KAAK;;;;;;;;;;;EAWrD,UAAU;;EAEV,eAAe;;EAEf,gBAAgB;;;;;;aAML,SAAS,8BAA8B,SAAS,GAAG,CAAC;gCACjC,SAAS,GAAG,CAAC;;CAE5C,CAAC,IAAI,EAAE,CAAC;AACT,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,UAAkB;IAC5C,OAAO,GAAG,eAAe;;;;;;;;;;EAUzB,gBAAgB;;wDAEsC,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,SAAiB;IAC5C,OAAO;;;;;;;;;;;;;;;;;;;aAmBI,SAAS,8BAA8B,SAAS,GAAG,CAAC;gCACjC,SAAS,GAAG,CAAC,KAAK,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,UAAkB;IAC5C,OAAO;;;;;;;EAOP,gBAAgB,EAAE,CAAC;AACrB,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Derives a structured "contract" from a component's test file: what the test
3
+ * imports, the props it renders the component with, the queries it runs
4
+ * (text / testid / role / label / placeholder) and the interactions it fires.
5
+ *
6
+ * This is the objective ground truth the mentor evaluates against. It never
7
+ * exposes raw test source — only the externally observable contract a correct
8
+ * implementation must satisfy.
9
+ */
10
+ export interface TestExpectation {
11
+ component: string;
12
+ /** Import path the test expects the component at, e.g. "../components/.../CourseCard" */
13
+ importPath?: string;
14
+ /** Whether the test imports a named export ({ X }) or a default export */
15
+ exportStyle?: 'named' | 'default';
16
+ /** Prop names the component is rendered with */
17
+ props: string[];
18
+ /** Text / regex the rendered output must contain */
19
+ requiredText: string[];
20
+ testIds: string[];
21
+ roles: string[];
22
+ labels: string[];
23
+ placeholders: string[];
24
+ /** True if the test fires user interactions (click/type/etc.) */
25
+ hasInteractions: boolean;
26
+ /** describe/it/test labels, for a quick human overview */
27
+ labelsOutline: string[];
28
+ }
29
+ export declare function parseTestExpectation(component: string, code: string): TestExpectation;
30
+ /** Render an expectation as a concise, mentor-facing summary. */
31
+ export declare function formatExpectation(e: TestExpectation): string;
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Derives a structured "contract" from a component's test file: what the test
3
+ * imports, the props it renders the component with, the queries it runs
4
+ * (text / testid / role / label / placeholder) and the interactions it fires.
5
+ *
6
+ * This is the objective ground truth the mentor evaluates against. It never
7
+ * exposes raw test source — only the externally observable contract a correct
8
+ * implementation must satisfy.
9
+ */
10
+ function uniq(arr) {
11
+ return [...new Set(arr.filter(Boolean))];
12
+ }
13
+ function collect(re, code) {
14
+ const out = [];
15
+ let m;
16
+ while ((m = re.exec(code)) !== null)
17
+ out.push(m[1]);
18
+ return out;
19
+ }
20
+ export function parseTestExpectation(component, code) {
21
+ // Scan ALL imports and pick the one that references the component under test
22
+ // (named: import { X } from "path" | default: import X from "path").
23
+ let importPath;
24
+ let exportStyle;
25
+ const importRe = /import\s+(?:\{\s*([A-Za-z0-9_,\s]+?)\s*\}|([A-Za-z0-9_]+))\s+from\s*['"]([^'"]+)['"]/g;
26
+ let im;
27
+ while ((im = importRe.exec(code)) !== null) {
28
+ const namedBindings = im[1];
29
+ const defaultBinding = im[2];
30
+ const path = im[3];
31
+ const refsComponent = (namedBindings && namedBindings.split(',').some(b => b.trim() === component))
32
+ || defaultBinding === component
33
+ || path.includes(component);
34
+ if (refsComponent) {
35
+ importPath = path;
36
+ exportStyle = namedBindings ? 'named' : 'default';
37
+ break;
38
+ }
39
+ }
40
+ // Props from JSX: <Component foo={...} bar="..." />
41
+ const props = [];
42
+ const jsxRe = new RegExp(`<${component}\\b([^>]*)`, 'g');
43
+ let jsx;
44
+ while ((jsx = jsxRe.exec(code)) !== null) {
45
+ for (const p of collect(/([A-Za-z_][A-Za-z0-9_]*)=/g, jsx[1]))
46
+ props.push(p);
47
+ }
48
+ const requiredText = collect(/getBy(?:Text|DisplayValue)\(\s*['"/]([^'")/]+)/g, code)
49
+ .concat(collect(/findBy(?:Text|DisplayValue)\(\s*['"/]([^'")/]+)/g, code))
50
+ .concat(collect(/queryBy(?:Text|DisplayValue)\(\s*['"/]([^'")/]+)/g, code));
51
+ const testIds = collect(/getBy(?:TestId)\(\s*['"]([^'"]+)['"]/g, code)
52
+ .concat(collect(/(?:find|query)By(?:TestId)\(\s*['"]([^'"]+)['"]/g, code));
53
+ const roles = collect(/getByRole\(\s*['"]([^'"]+)['"]/g, code)
54
+ .concat(collect(/(?:find|query)ByRole\(\s*['"]([^'"]+)['"]/g, code));
55
+ const labels = collect(/getByLabelText\(\s*['"/]([^'")/]+)/g, code);
56
+ const placeholders = collect(/getByPlaceholderText\(\s*['"/]([^'")/]+)/g, code);
57
+ const labelsOutline = collect(/(?:describe|it|test)\s*\(\s*['"`]([^'"`]+)['"`]/g, code);
58
+ const hasInteractions = /fireEvent|userEvent|\.click\(|\.type\(/.test(code);
59
+ return {
60
+ component,
61
+ importPath,
62
+ exportStyle,
63
+ props: uniq(props),
64
+ requiredText: uniq(requiredText),
65
+ testIds: uniq(testIds),
66
+ roles: uniq(roles),
67
+ labels: uniq(labels),
68
+ placeholders: uniq(placeholders),
69
+ hasInteractions,
70
+ labelsOutline: uniq(labelsOutline),
71
+ };
72
+ }
73
+ /** Render an expectation as a concise, mentor-facing summary. */
74
+ export function formatExpectation(e) {
75
+ const lines = [`Test contract for ${e.component}:`];
76
+ if (e.importPath) {
77
+ lines.push(` • Expected as a ${e.exportStyle ?? 'named'} export at: ${e.importPath}`);
78
+ }
79
+ if (e.props.length)
80
+ lines.push(` • Rendered with props: ${e.props.join(', ')}`);
81
+ if (e.requiredText.length)
82
+ lines.push(` • Output must contain text: ${e.requiredText.map(t => `"${t}"`).join(', ')}`);
83
+ if (e.roles.length)
84
+ lines.push(` • Queries roles: ${e.roles.join(', ')}`);
85
+ if (e.testIds.length)
86
+ lines.push(` • Queries testids: ${e.testIds.join(', ')}`);
87
+ if (e.labels.length)
88
+ lines.push(` • Queries by label: ${e.labels.join(', ')}`);
89
+ if (e.placeholders.length)
90
+ lines.push(` • Queries by placeholder: ${e.placeholders.join(', ')}`);
91
+ if (e.hasInteractions)
92
+ lines.push(' • Fires user interactions (clicks/typing) and asserts the result');
93
+ if (e.labelsOutline.length)
94
+ lines.push(` • Cases: ${e.labelsOutline.join(' · ')}`);
95
+ return lines.join('\n');
96
+ }
97
+ //# sourceMappingURL=test-expectations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-expectations.js","sourceRoot":"","sources":["../src/test-expectations.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "epam-ai-conductor",
3
- "version": "0.1.0",
4
- "description": "React course workshop mentor guided learning via codemie-claude",
3
+ "version": "0.2.0",
4
+ "description": "EPAM AI Conductor — Socratic AI mentor for the React Fundamentals course, powered by CodeMie + Claude",
5
5
  "type": "module",
6
6
  "main": "./dist/cli.js",
7
7
  "bin": {
@@ -23,6 +23,7 @@
23
23
  "postinstall": "chmod +x node_modules/node-pty/prebuilds/darwin-arm64/spawn-helper 2>/dev/null || true"
24
24
  },
25
25
  "dependencies": {
26
+ "@modelcontextprotocol/sdk": "^1.29.0",
26
27
  "chalk": "^5.3.0",
27
28
  "commander": "^11.1.0",
28
29
  "inquirer": "^9.2.12",