@gonzih/debate-coach 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Maksim Soltan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,250 @@
1
+ # debate-coach
2
+
3
+ > An MCP server that trains critical thinking by making students argue both sides of any topic, catching logical fallacies in real time, scoring argument quality, and teaching the difference between an opinion and a reasoned position.
4
+
5
+ [![npm](https://img.shields.io/npm/v/@gonzih/debate-coach)](https://www.npmjs.com/package/@gonzih/debate-coach)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
7
+
8
+ ---
9
+
10
+ ## The Dream
11
+
12
+ A 16-year-old says *"I think social media should be banned for under 18s."*
13
+
14
+ Instead of agreeing or disagreeing, the AI says: *"Interesting. Make your case — give me your 3 strongest arguments."*
15
+
16
+ The student argues. The AI rates each argument:
17
+ - *"Argument 1: solid (evidence-based)."*
18
+ - *"Argument 2: weak (anecdotal, logical fallacy: hasty generalization)."*
19
+ - *"Argument 3: strong."*
20
+
21
+ Then: *"Now flip sides. Argue against your own position as convincingly as you can."*
22
+
23
+ This is how lawyers are trained. This is how scientists think. This is how good citizens reason.
24
+
25
+ ---
26
+
27
+ ## What It Does
28
+
29
+ ### 1. Argument Quality Scoring (0–10 per argument)
30
+ Each argument is scored on four dimensions:
31
+ - **Evidence (0–3):** Factual backing — studies, data, expert consensus
32
+ - **Logic (0–3):** Soundness — no fallacies, valid reasoning
33
+ - **Relevance (0–2):** Does it actually support the proposition?
34
+ - **Originality (0–2):** Nuanced insight vs. generic platitude
35
+
36
+ ### 2. Real-Time Logical Fallacy Detection
37
+ Detects 20 logical fallacies including:
38
+ - Ad hominem, straw man, false dichotomy
39
+ - Slippery slope, hasty generalization, appeal to authority
40
+ - Post hoc (correlation ≠ causation), cherry picking, and 12 more
41
+
42
+ ### 3. Steel-Manning
43
+ After you argue your side, the AI presents the *strongest possible opposing argument* — not a strawman. This prepares you for the flip challenge and teaches genuine intellectual honesty.
44
+
45
+ ### 4. Mandatory Flip-Sides Exercise
46
+ You must argue the *opposite* of your original position. This is the hardest and most valuable part. It teaches:
47
+ - Epistemic humility
48
+ - Understanding of opposing views
49
+ - How to construct genuinely compelling arguments you disagree with
50
+
51
+ ### 5. 50+ Curated Debate Topics
52
+ Categories: Social/Ethical, Science/Environment, Politics/Policy, Philosophy/Values, Technology, Historical, Economics, Health
53
+
54
+ ### 6. Progress Tracking
55
+ - Fallacy rate over time: *"You used hasty generalization 8 times 3 weeks ago, 2 times this week"*
56
+ - Average argument score trend
57
+ - Topics breadth
58
+ - Flip success rate
59
+
60
+ ---
61
+
62
+ ## Installation
63
+
64
+ ```bash
65
+ npm install -g @gonzih/debate-coach
66
+ ```
67
+
68
+ Or use with `npx`:
69
+ ```bash
70
+ npx @gonzih/debate-coach
71
+ ```
72
+
73
+ ---
74
+
75
+ ## MCP Configuration
76
+
77
+ Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json`):
78
+
79
+ ```json
80
+ {
81
+ "mcpServers": {
82
+ "debate-coach": {
83
+ "command": "npx",
84
+ "args": ["@gonzih/debate-coach"]
85
+ }
86
+ }
87
+ }
88
+ ```
89
+
90
+ Or if installed globally:
91
+ ```json
92
+ {
93
+ "mcpServers": {
94
+ "debate-coach": {
95
+ "command": "debate-coach"
96
+ }
97
+ }
98
+ }
99
+ ```
100
+
101
+ ---
102
+
103
+ ## MCP Tools
104
+
105
+ ### `start_debate`
106
+ ```
107
+ start_debate({ profileId, topic?, position? })
108
+ → { proposition, sessionId, instructions }
109
+ ```
110
+ Start a new debate session. The AI frames your topic as a proper debate proposition.
111
+
112
+ ### `submit_argument`
113
+ ```
114
+ submit_argument({ sessionId, argument })
115
+ → { score, fallacies[], feedback, strengtheningSuggestion }
116
+ ```
117
+ Submit an argument. Get scored on evidence/logic/relevance/originality and fallacy-checked.
118
+
119
+ ### `get_steelman`
120
+ ```
121
+ get_steelman({ sessionId })
122
+ → { steelman, keyCounterArguments[] }
123
+ ```
124
+ After 3+ PRO arguments: see the strongest possible case against your position.
125
+
126
+ ### `flip_sides`
127
+ ```
128
+ flip_sides({ sessionId })
129
+ → { prompt, newPosition }
130
+ ```
131
+ Switch to arguing the opposite side.
132
+
133
+ ### `complete_debate`
134
+ ```
135
+ complete_debate({ sessionId })
136
+ → { synthesis, scores, fallaciesDetected }
137
+ ```
138
+ Finalize the debate and receive a synthesis of both sides.
139
+
140
+ ### `build_argument`
141
+ ```
142
+ build_argument({ sessionId, thought })
143
+ → { questions[], hint }
144
+ ```
145
+ Argument Builder Mode: Socratic scaffolding for students who don't know where to start.
146
+
147
+ ### `get_topic_list`
148
+ ```
149
+ get_topic_list({ category?, ageGroup?, difficulty? })
150
+ → Topic[]
151
+ ```
152
+ Browse 50+ curated debate topics.
153
+
154
+ ### `get_progress`
155
+ ```
156
+ get_progress({ profileId })
157
+ → { debatesCompleted, avgScore, fallacyTrend, improvementAreas, ... }
158
+ ```
159
+ View progress over time.
160
+
161
+ ### `get_fallacy_guide`
162
+ ```
163
+ get_fallacy_guide({ fallacyType? })
164
+ → Fallacy guide or specific fallacy detail
165
+ ```
166
+ Learn about the 20 logical fallacies the system detects.
167
+
168
+ ---
169
+
170
+ ## Debate Flow
171
+
172
+ ```
173
+ start_debate
174
+
175
+ submit_argument ×3-5 (PRO phase)
176
+
177
+ get_steelman (see opposing case)
178
+
179
+ flip_sides (switch positions)
180
+
181
+ submit_argument ×3-5 (CON phase)
182
+
183
+ complete_debate (synthesis + scoring)
184
+
185
+ get_progress (track improvement)
186
+ ```
187
+
188
+ ---
189
+
190
+ ## The Socratic Method Connection
191
+
192
+ Socrates never stated opinions — he asked questions until his interlocutors discovered contradictions in their own positions. The *elenchus* (cross-examination) was designed not to teach facts but to develop the capacity for rigorous thinking.
193
+
194
+ debate-coach uses the same approach: it doesn't tell you what to think, it forces you to *defend* what you think — and then defend the opposite.
195
+
196
+ ---
197
+
198
+ ## Debate Formats Supported Conceptually
199
+
200
+ ### Lincoln-Douglas (LD) Debate
201
+ One-on-one. Values-focused. Emphasizes philosophical and ethical clash. Perfect for the philosophical and policy topics in the topic list.
202
+
203
+ **Structure adapted here:** PRO arguments → Steel-man → CON arguments → Synthesis
204
+
205
+ ### Oxford-Style Debate
206
+ Motion-based. Audience votes before and after. The Oxford format's key insight: you must be able to articulate *why* the other side is reasonable before you can effectively argue against it. This is exactly what the steel-man step provides.
207
+
208
+ ### Karl Popper Format
209
+ Teams of three argue competing positions. Focus on cross-examination. The Karl Popper format explicitly teaches students to attack *the strongest version* of the opposition — which is the steel-manning principle.
210
+
211
+ ---
212
+
213
+ ## Classroom Integration Guide
214
+
215
+ ### Individual Practice
216
+ Students use debate-coach independently to prepare for class discussions. The progress tracking helps teachers see who is improving.
217
+
218
+ ### Paired Debate Prep
219
+ Two students start debates on the same topic but opposite sides. They compare their steel-mans — seeing how well each predicted the other's actual arguments.
220
+
221
+ ### Flipped Classroom
222
+ Assign debate-coach sessions as homework. Class time is spent discussing: *"What surprised you when you had to argue the other side?"*
223
+
224
+ ### Formative Assessment
225
+ Use `get_progress` to track:
226
+ - Which fallacies students commit most
227
+ - Score improvement over the term
228
+ - Whether students attempt the flip challenge
229
+
230
+ ### Discussion Starter
231
+ After completing a debate session, share the synthesis in class. Use it as a jumping-off point: *"The AI says the core tension here is X vs. Y — do you agree?"*
232
+
233
+ ---
234
+
235
+ ## Progress Data Storage
236
+
237
+ Debate history is stored locally in `~/.debate-coach/debates.db` (SQLite). No data is sent to any server. API calls are made only to Anthropic's Claude API for AI-powered scoring and analysis.
238
+
239
+ ---
240
+
241
+ ## Requirements
242
+
243
+ - Node.js 18+
244
+ - `ANTHROPIC_API_KEY` environment variable
245
+
246
+ ---
247
+
248
+ ## License
249
+
250
+ MIT © Gonzih
package/SKILL.md ADDED
@@ -0,0 +1,91 @@
1
+ # debate-coach Skill Guide
2
+
3
+ ## What Is This?
4
+ debate-coach is an MCP server that trains critical thinking through structured debate. It implements the Socratic method digitally: instead of telling you what to think, it forces you to defend what you think — and then defend the opposite.
5
+
6
+ ## Quick Start
7
+
8
+ ### 1. Start a Debate
9
+ ```
10
+ Use the start_debate tool with:
11
+ - profileId: "student_alice"
12
+ - topic: "Social media should be banned for users under 18"
13
+ ```
14
+
15
+ ### 2. Submit Arguments (PRO side first)
16
+ ```
17
+ Use submit_argument with your sessionId and each argument.
18
+ Example: "Multiple peer-reviewed studies, including the Haidt/Twenge research,
19
+ show strong correlations between heavy social media use and increased rates of
20
+ anxiety and depression in adolescent girls."
21
+ ```
22
+
23
+ ### 3. Get the Steel-Man
24
+ ```
25
+ After 3+ arguments, call get_steelman to see the strongest opposing case.
26
+ This is essential preparation for the flip challenge.
27
+ ```
28
+
29
+ ### 4. Flip Sides
30
+ ```
31
+ Call flip_sides to switch to arguing AGAINST your original position.
32
+ This is the hardest and most valuable part.
33
+ ```
34
+
35
+ ### 5. Submit CON Arguments
36
+ ```
37
+ Submit 3-5 arguments against the proposition using submit_argument.
38
+ ```
39
+
40
+ ### 6. Complete & Synthesize
41
+ ```
42
+ Call complete_debate to receive your final synthesis and save progress.
43
+ ```
44
+
45
+ ## Argument Builder Mode (For Beginners)
46
+ If you don't know where to start:
47
+ ```
48
+ Call build_argument with:
49
+ - sessionId: your session ID
50
+ - thought: "I think social media is bad but I don't know why exactly"
51
+ ```
52
+ You'll receive Socratic questions to develop your rough thought into a strong argument.
53
+
54
+ ## Browse Topics
55
+ ```
56
+ Call get_topic_list to browse 50+ curated topics.
57
+ Filter by category: social, science, policy, philosophy, technology, historical, economics, health
58
+ Filter by difficulty: easy, medium, hard
59
+ Filter by ageGroup: teens, general
60
+ ```
61
+
62
+ ## Track Progress
63
+ ```
64
+ Call get_progress with your profileId to see:
65
+ - Score improvement over time
66
+ - Which logical fallacies you commit most
67
+ - Flip success rate
68
+ - Topics debated
69
+ ```
70
+
71
+ ## What Makes a Good Argument?
72
+ A strong argument (7+/10) has:
73
+ - **Specific evidence**: Not just "studies show" but "the 2019 Twenge et al. meta-analysis of..."
74
+ - **Logical structure**: Premise → Reasoning → Conclusion with no gaps
75
+ - **Relevance**: Directly addresses the proposition, not a tangent
76
+ - **Nuance**: Goes beyond the obvious — acknowledge complexity
77
+
78
+ ## Common Mistakes to Avoid
79
+ 1. **Anecdotal evidence**: "My cousin tried X and it worked" — use systematic data
80
+ 2. **Hasty generalization**: "Three examples prove all cases" — be careful with scale
81
+ 3. **False dichotomy**: "Either we do X or catastrophe" — more options usually exist
82
+ 4. **Post hoc**: "A happened, then B happened, so A caused B" — correlation ≠ causation
83
+ 5. **Appeal to emotion**: Emotional language without supporting logic
84
+
85
+ ## Learning Outcomes
86
+ After regular use of debate-coach, students develop:
87
+ - Ability to construct evidence-based arguments
88
+ - Recognition of logical fallacies in real-world discourse (news, politics, advertising)
89
+ - Intellectual empathy — understanding why reasonable people disagree
90
+ - Comfort arguing positions they personally disagree with
91
+ - Awareness of their own cognitive biases
@@ -0,0 +1,26 @@
1
+ import type { ScoredArgument, DebateSession } from "./types.js";
2
+ export declare function refineProposition(rawTopic: string): Promise<{
3
+ proposition: string;
4
+ explanation: string;
5
+ }>;
6
+ export declare function scoreArgument(params: {
7
+ proposition: string;
8
+ position: string;
9
+ argument: string;
10
+ previousArguments: string[];
11
+ }): Promise<ScoredArgument>;
12
+ export declare function generateSteelman(session: DebateSession): Promise<{
13
+ steelman: string;
14
+ keyCounterArguments: string[];
15
+ }>;
16
+ export declare function generateSynthesis(session: DebateSession): Promise<string>;
17
+ export declare function scaffoldArgument(params: {
18
+ proposition: string;
19
+ position: string;
20
+ userThought: string;
21
+ }): Promise<{
22
+ questions: string[];
23
+ hint: string;
24
+ }>;
25
+ export declare function formatScoredArgument(arg: ScoredArgument): string;
26
+ //# sourceMappingURL=claude.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../src/claude.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAGV,cAAc,EACd,aAAa,EACd,MAAM,YAAY,CAAC;AAOpB,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IACjE,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC,CA+BD;AAID,wBAAsB,aAAa,CAAC,MAAM,EAAE;IAC1C,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,EAAE,CAAC;CAC7B,GAAG,OAAO,CAAC,cAAc,CAAC,CAyF1B;AAID,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC;IACtE,QAAQ,EAAE,MAAM,CAAC;IACjB,mBAAmB,EAAE,MAAM,EAAE,CAAC;CAC/B,CAAC,CAoDD;AAID,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CA8D/E;AAID,wBAAsB,gBAAgB,CAAC,MAAM,EAAE;IAC7C,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC;IACV,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC,CAkCD;AAID,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,cAAc,GAAG,MAAM,CAkChE"}
package/dist/claude.js ADDED
@@ -0,0 +1,285 @@
1
+ import Anthropic from "@anthropic-ai/sdk";
2
+ import { FALLACY_NAMES_FOR_PROMPT } from "./fallacies.js";
3
+ const client = new Anthropic();
4
+ const MODEL = "claude-opus-4-6";
5
+ // ─── Proposition Refinement ────────────────────────────────────────────────
6
+ export async function refineProposition(rawTopic) {
7
+ const response = await client.messages.create({
8
+ model: MODEL,
9
+ max_tokens: 1024,
10
+ messages: [
11
+ {
12
+ role: "user",
13
+ content: `You are a debate coach helping a student frame a debate topic as a proper proposition.
14
+
15
+ The student's topic: "${rawTopic}"
16
+
17
+ A good debate proposition:
18
+ - Is a clear, specific statement (not a question)
19
+ - Can be argued FOR or AGAINST
20
+ - Is falsifiable and not purely subjective
21
+ - Is stated in present tense: "X should/is/does..."
22
+
23
+ Respond with a JSON object only:
24
+ {
25
+ "proposition": "The refined, properly-stated proposition",
26
+ "explanation": "Brief explanation of why this framing works well for debate (1-2 sentences)"
27
+ }`,
28
+ },
29
+ ],
30
+ });
31
+ const text = response.content.find(b => b.type === "text")?.text ?? "{}";
32
+ const jsonMatch = text.match(/\{[\s\S]*\}/);
33
+ if (!jsonMatch)
34
+ throw new Error("Failed to parse proposition from Claude");
35
+ return JSON.parse(jsonMatch[0]);
36
+ }
37
+ // ─── Argument Scoring ──────────────────────────────────────────────────────
38
+ export async function scoreArgument(params) {
39
+ const previousContext = params.previousArguments.length > 0
40
+ ? `\nPrevious arguments made:\n${params.previousArguments.map((a, i) => `${i + 1}. ${a}`).join("\n")}`
41
+ : "";
42
+ const response = await client.messages.create({
43
+ model: MODEL,
44
+ max_tokens: 2048,
45
+ messages: [
46
+ {
47
+ role: "user",
48
+ content: `You are an expert debate coach and logician. Analyze this argument rigorously.
49
+
50
+ PROPOSITION: "${params.proposition}"
51
+ POSITION BEING ARGUED: ${params.position}
52
+ ${previousContext}
53
+
54
+ NEW ARGUMENT TO ANALYZE:
55
+ "${params.argument}"
56
+
57
+ Score the argument on these criteria:
58
+ - Evidence (0-3): Does it include factual backing? Studies, data, specific examples, expert consensus?
59
+ 0 = pure opinion, 1 = vague reference, 2 = reasonable claim with some support, 3 = strong empirical backing
60
+ - Logic (0-3): Is the reasoning sound? No fallacies? Does the conclusion follow from premises?
61
+ 0 = deeply flawed, 1 = some logical problems, 2 = mostly sound, 3 = rigorous and valid
62
+ - Relevance (0-2): Does it actually address and support the specific proposition?
63
+ 0 = tangential, 1 = partially relevant, 2 = directly on-point
64
+ - Originality (0-2): Is this a nuanced/specific point or a generic/obvious one?
65
+ 0 = very common/generic, 1 = somewhat specific, 2 = genuinely nuanced insight
66
+
67
+ Detect ALL logical fallacies present (from this list):
68
+ ${FALLACY_NAMES_FOR_PROMPT}
69
+
70
+ Respond with a JSON object ONLY (no other text):
71
+ {
72
+ "scores": {
73
+ "evidence": <0-3>,
74
+ "logic": <0-3>,
75
+ "relevance": <0-2>,
76
+ "originality": <0-2>
77
+ },
78
+ "fallacies": [
79
+ {
80
+ "type": "<fallacy_type_key>",
81
+ "name": "<Human readable name>",
82
+ "description": "<what this fallacy is>",
83
+ "explanation": "<exactly how it appears in this specific argument>",
84
+ "quote": "<the specific phrase in the argument that contains the fallacy>"
85
+ }
86
+ ],
87
+ "feedback": "<2-3 sentence evaluation: what's strong, what's weak, how it relates to the proposition>",
88
+ "strengtheningSuggestion": "<specific, actionable suggestion to make this argument stronger>"
89
+ }`,
90
+ },
91
+ ],
92
+ });
93
+ const text = response.content.find(b => b.type === "text")?.text ?? "{}";
94
+ const jsonMatch = text.match(/\{[\s\S]*\}/);
95
+ if (!jsonMatch)
96
+ throw new Error("Failed to parse argument score from Claude");
97
+ const parsed = JSON.parse(jsonMatch[0]);
98
+ const score = {
99
+ evidence: Math.min(3, Math.max(0, parsed.scores.evidence)),
100
+ logic: Math.min(3, Math.max(0, parsed.scores.logic)),
101
+ relevance: Math.min(2, Math.max(0, parsed.scores.relevance)),
102
+ originality: Math.min(2, Math.max(0, parsed.scores.originality)),
103
+ total: Math.min(3, Math.max(0, parsed.scores.evidence)) +
104
+ Math.min(3, Math.max(0, parsed.scores.logic)) +
105
+ Math.min(2, Math.max(0, parsed.scores.relevance)) +
106
+ Math.min(2, Math.max(0, parsed.scores.originality)),
107
+ };
108
+ return {
109
+ text: params.argument,
110
+ score,
111
+ fallacies: parsed.fallacies || [],
112
+ feedback: parsed.feedback || "",
113
+ strengtheningSuggestion: parsed.strengtheningSuggestion || "",
114
+ timestamp: new Date().toISOString(),
115
+ };
116
+ }
117
+ // ─── Steel-Man Generation ──────────────────────────────────────────────────
118
+ export async function generateSteelman(session) {
119
+ const proArgsSummary = session.proArguments
120
+ .map((a, i) => `${i + 1}. "${a.text}" (Score: ${a.score.total}/10)`)
121
+ .join("\n");
122
+ const response = await client.messages.create({
123
+ model: MODEL,
124
+ max_tokens: 3000,
125
+ messages: [
126
+ {
127
+ role: "user",
128
+ content: `You are a master debater and philosopher. A student has just argued FOR the following proposition.
129
+
130
+ PROPOSITION: "${session.proposition}"
131
+
132
+ THE STUDENT'S ARGUMENTS (arguing FOR):
133
+ ${proArgsSummary}
134
+
135
+ Your task: Present the STRONGEST POSSIBLE CASE AGAINST this proposition.
136
+ This is steel-manning — you must construct the best version of the opposing argument,
137
+ not a straw man. Think like the best lawyer for the opposing side.
138
+
139
+ Rules:
140
+ - Engage with the actual strongest version of the opposing position
141
+ - Use real evidence, studies, and expert perspectives where possible
142
+ - Acknowledge any valid points in the student's arguments while showing their limits
143
+ - Be intellectually honest — this should be a genuinely challenging counter-case
144
+ - This will help the student understand what they're really up against
145
+
146
+ Respond with a JSON object ONLY:
147
+ {
148
+ "steelman": "<A compelling, well-reasoned paragraph (150-250 words) presenting the strongest case AGAINST the proposition. Write in first person as if you are arguing this side.>",
149
+ "keyCounterArguments": [
150
+ "<Counter-argument 1: specific, evidence-backed, most powerful>",
151
+ "<Counter-argument 2: addresses a weakness in the student's arguments>",
152
+ "<Counter-argument 3: presents a genuine trade-off or unintended consequence>",
153
+ "<Counter-argument 4: philosophical or values-based counter>",
154
+ "<Counter-argument 5: empirical/practical counter>"
155
+ ]
156
+ }`,
157
+ },
158
+ ],
159
+ });
160
+ const text = response.content.find(b => b.type === "text")?.text ?? "{}";
161
+ const jsonMatch = text.match(/\{[\s\S]*\}/);
162
+ if (!jsonMatch)
163
+ throw new Error("Failed to parse steelman from Claude");
164
+ return JSON.parse(jsonMatch[0]);
165
+ }
166
+ // ─── Debate Synthesis ──────────────────────────────────────────────────────
167
+ export async function generateSynthesis(session) {
168
+ const proArgsSummary = session.proArguments
169
+ .map((a, i) => ` ${i + 1}. "${a.text.substring(0, 100)}..." — Score: ${a.score.total}/10, Fallacies: ${a.fallacies.length}`)
170
+ .join("\n");
171
+ const conArgsSummary = session.conArguments
172
+ .map((a, i) => ` ${i + 1}. "${a.text.substring(0, 100)}..." — Score: ${a.score.total}/10, Fallacies: ${a.fallacies.length}`)
173
+ .join("\n");
174
+ const proAvg = session.proArguments.length > 0
175
+ ? (session.proArguments.reduce((s, a) => s + a.score.total, 0) /
176
+ session.proArguments.length).toFixed(1)
177
+ : "N/A";
178
+ const conAvg = session.conArguments.length > 0
179
+ ? (session.conArguments.reduce((s, a) => s + a.score.total, 0) /
180
+ session.conArguments.length).toFixed(1)
181
+ : "N/A";
182
+ const response = await client.messages.create({
183
+ model: MODEL,
184
+ max_tokens: 2000,
185
+ messages: [
186
+ {
187
+ role: "user",
188
+ content: `You are a master debate coach providing a final synthesis after a student has argued BOTH sides of a proposition.
189
+
190
+ PROPOSITION: "${session.proposition}"
191
+
192
+ ARGUMENTS FOR (avg score: ${proAvg}/10):
193
+ ${proArgsSummary}
194
+
195
+ ARGUMENTS AGAINST (avg score: ${conAvg}/10):
196
+ ${conArgsSummary}
197
+
198
+ Write a final synthesis (200-300 words) that:
199
+ 1. Acknowledges what the student did well on BOTH sides
200
+ 2. Identifies the strongest arguments that emerged from the full debate
201
+ 3. Explains what "the strongest version of this debate" looks like — what would a professional debater argue on each side?
202
+ 4. Names the key tension or core value clash at the heart of this debate
203
+ 5. Leaves the student with a philosophical insight about why these debates matter
204
+
205
+ Be encouraging but honest. This synthesis should make the student feel they've grown intellectually.
206
+
207
+ Write as flowing prose, no bullet points, no JSON — just a thoughtful paragraph.`,
208
+ },
209
+ ],
210
+ });
211
+ return response.content.find(b => b.type === "text")?.text ?? "Debate completed.";
212
+ }
213
+ // ─── Argument Builder Scaffold ─────────────────────────────────────────────
214
+ export async function scaffoldArgument(params) {
215
+ const response = await client.messages.create({
216
+ model: MODEL,
217
+ max_tokens: 1024,
218
+ messages: [
219
+ {
220
+ role: "user",
221
+ content: `You are a Socratic debate coach. A student wants to argue ${params.position} on this proposition:
222
+
223
+ "${params.proposition}"
224
+
225
+ The student says: "${params.userThought}"
226
+
227
+ Guide them to build a stronger argument using the Socratic method.
228
+
229
+ Respond with JSON ONLY:
230
+ {
231
+ "questions": [
232
+ "<Question 1: ask them to clarify or deepen their belief>",
233
+ "<Question 2: ask for evidence or examples>",
234
+ "<Question 3: ask how they'd respond to the strongest counter-argument>",
235
+ "<Question 4: ask about trade-offs or unintended consequences>"
236
+ ],
237
+ "hint": "<1-sentence suggestion for how to turn their thought into a strong argument>"
238
+ }`,
239
+ },
240
+ ],
241
+ });
242
+ const text = response.content.find(b => b.type === "text")?.text ?? "{}";
243
+ const jsonMatch = text.match(/\{[\s\S]*\}/);
244
+ if (!jsonMatch)
245
+ throw new Error("Failed to parse scaffold from Claude");
246
+ return JSON.parse(jsonMatch[0]);
247
+ }
248
+ // ─── Format Scoring Output ─────────────────────────────────────────────────
249
+ export function formatScoredArgument(arg) {
250
+ const scoreEmoji = (score, max) => {
251
+ const pct = score / max;
252
+ if (pct >= 0.8)
253
+ return "✅";
254
+ if (pct >= 0.5)
255
+ return "⚠️";
256
+ return "❌";
257
+ };
258
+ const lines = [
259
+ `**Score: ${arg.score.total}/10**`,
260
+ "",
261
+ `${scoreEmoji(arg.score.evidence, 3)} **Evidence (${arg.score.evidence}/3):** ${arg.score.evidence === 3 ? "Strong factual backing" : arg.score.evidence === 2 ? "Reasonable support" : arg.score.evidence === 1 ? "Vague, needs specifics" : "No evidence — pure opinion"}`,
262
+ `${scoreEmoji(arg.score.logic, 3)} **Logic (${arg.score.logic}/3):** ${arg.score.logic === 3 ? "Sound and valid reasoning" : arg.score.logic === 2 ? "Mostly sound reasoning" : arg.score.logic === 1 ? "Some logical problems" : "Flawed reasoning"}`,
263
+ `${scoreEmoji(arg.score.relevance, 2)} **Relevance (${arg.score.relevance}/2):** ${arg.score.relevance === 2 ? "Directly supports the proposition" : arg.score.relevance === 1 ? "Partially relevant" : "Off-topic"}`,
264
+ `${scoreEmoji(arg.score.originality, 2)} **Originality (${arg.score.originality}/2):** ${arg.score.originality === 2 ? "Nuanced insight" : arg.score.originality === 1 ? "Somewhat specific" : "Common/generic point"}`,
265
+ "",
266
+ `**Feedback:** ${arg.feedback}`,
267
+ "",
268
+ `💡 **Could strengthen:** ${arg.strengtheningSuggestion}`,
269
+ ];
270
+ if (arg.fallacies.length > 0) {
271
+ lines.push("");
272
+ lines.push(`**⚠️ Logical fallacies detected (${arg.fallacies.length}):**`);
273
+ for (const fallacy of arg.fallacies) {
274
+ lines.push(`- **${fallacy.name}**: ${fallacy.explanation}`);
275
+ if (fallacy.quote)
276
+ lines.push(` *"${fallacy.quote}"*`);
277
+ }
278
+ }
279
+ else {
280
+ lines.push("");
281
+ lines.push("**✅ No logical fallacies detected**");
282
+ }
283
+ return lines.join("\n");
284
+ }
285
+ //# sourceMappingURL=claude.js.map