nietzschian-debugger 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.
Files changed (57) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +176 -0
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +36 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/commands/debug.d.ts +2 -0
  7. package/dist/commands/debug.js +80 -0
  8. package/dist/commands/debug.js.map +1 -0
  9. package/dist/core/context-manager.d.ts +22 -0
  10. package/dist/core/context-manager.js +73 -0
  11. package/dist/core/context-manager.js.map +1 -0
  12. package/dist/core/session-loop.d.ts +5 -0
  13. package/dist/core/session-loop.js +159 -0
  14. package/dist/core/session-loop.js.map +1 -0
  15. package/dist/core/session.d.ts +5 -0
  16. package/dist/core/session.js +46 -0
  17. package/dist/core/session.js.map +1 -0
  18. package/dist/llm/client.d.ts +25 -0
  19. package/dist/llm/client.js +81 -0
  20. package/dist/llm/client.js.map +1 -0
  21. package/dist/llm/prompts.d.ts +2 -0
  22. package/dist/llm/prompts.js +69 -0
  23. package/dist/llm/prompts.js.map +1 -0
  24. package/dist/llm/validator.d.ts +10 -0
  25. package/dist/llm/validator.js +64 -0
  26. package/dist/llm/validator.js.map +1 -0
  27. package/dist/quotes/corpus.d.ts +2 -0
  28. package/dist/quotes/corpus.js +42 -0
  29. package/dist/quotes/corpus.js.map +1 -0
  30. package/dist/quotes/selector.d.ts +4 -0
  31. package/dist/quotes/selector.js +59 -0
  32. package/dist/quotes/selector.js.map +1 -0
  33. package/dist/scoring/behavior-tagger.d.ts +2 -0
  34. package/dist/scoring/behavior-tagger.js +56 -0
  35. package/dist/scoring/behavior-tagger.js.map +1 -0
  36. package/dist/scoring/growth-profile.d.ts +3 -0
  37. package/dist/scoring/growth-profile.js +58 -0
  38. package/dist/scoring/growth-profile.js.map +1 -0
  39. package/dist/scoring/skill-scorer.d.ts +2 -0
  40. package/dist/scoring/skill-scorer.js +38 -0
  41. package/dist/scoring/skill-scorer.js.map +1 -0
  42. package/dist/storage/file-reader.d.ts +3 -0
  43. package/dist/storage/file-reader.js +60 -0
  44. package/dist/storage/file-reader.js.map +1 -0
  45. package/dist/storage/session-store.d.ts +5 -0
  46. package/dist/storage/session-store.js +72 -0
  47. package/dist/storage/session-store.js.map +1 -0
  48. package/dist/types.d.ts +56 -0
  49. package/dist/types.js +2 -0
  50. package/dist/types.js.map +1 -0
  51. package/dist/ui/growth-display.d.ts +2 -0
  52. package/dist/ui/growth-display.js +69 -0
  53. package/dist/ui/growth-display.js.map +1 -0
  54. package/dist/ui/renderer.d.ts +12 -0
  55. package/dist/ui/renderer.js +54 -0
  56. package/dist/ui/renderer.js.map +1 -0
  57. package/package.json +46 -0
@@ -0,0 +1,46 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { SCHEMA_VERSION } from '../types.js';
3
+ export function createSession(problemDescription, intensity) {
4
+ return {
5
+ schemaVersion: SCHEMA_VERSION,
6
+ id: randomUUID(),
7
+ timestamp: new Date().toISOString(),
8
+ endTimestamp: '',
9
+ problemDescription,
10
+ intensity,
11
+ outcome: 'abandoned',
12
+ questionsToRootCause: 0,
13
+ codeFiles: [],
14
+ transcript: [],
15
+ skillScores: { assumptionChecking: 5, evidenceGathering: 5, rootCauseSpeed: 5 },
16
+ behaviorTags: [],
17
+ };
18
+ }
19
+ export function addTurn(session, question, response, model, behaviorTags = [], quoteUsed = null) {
20
+ const turn = {
21
+ turnNumber: session.transcript.length + 1,
22
+ question,
23
+ response,
24
+ model,
25
+ quoteUsed,
26
+ behaviorTags,
27
+ timestamp: new Date().toISOString(),
28
+ };
29
+ session.transcript.push(turn);
30
+ session.questionsToRootCause = session.transcript.length;
31
+ return turn;
32
+ }
33
+ export function finalizeSession(session, outcome, skillScores, behaviorTags) {
34
+ session.endTimestamp = new Date().toISOString();
35
+ session.outcome = outcome;
36
+ if (skillScores)
37
+ session.skillScores = skillScores;
38
+ if (behaviorTags)
39
+ session.behaviorTags = behaviorTags;
40
+ }
41
+ export function addCodeFile(session, filePath) {
42
+ if (!session.codeFiles.includes(filePath)) {
43
+ session.codeFiles.push(filePath);
44
+ }
45
+ }
46
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/core/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,UAAU,aAAa,CAAC,kBAA0B,EAAE,SAAoB;IAC5E,OAAO;QACL,aAAa,EAAE,cAAc;QAC7B,EAAE,EAAE,UAAU,EAAE;QAChB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,YAAY,EAAE,EAAE;QAChB,kBAAkB;QAClB,SAAS;QACT,OAAO,EAAE,WAAW;QACpB,oBAAoB,EAAE,CAAC;QACvB,SAAS,EAAE,EAAE;QACb,UAAU,EAAE,EAAE;QACd,WAAW,EAAE,EAAE,kBAAkB,EAAE,CAAC,EAAE,iBAAiB,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE;QAC/E,YAAY,EAAE,EAAE;KACjB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,OAAO,CACrB,OAAgB,EAChB,QAAgB,EAChB,QAAgB,EAChB,KAAa,EACb,eAAyB,EAAE,EAC3B,YAA0B,IAAI;IAE9B,MAAM,IAAI,GAAS;QACjB,UAAU,EAAE,OAAO,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC;QACzC,QAAQ;QACR,QAAQ;QACR,KAAK;QACL,SAAS;QACT,YAAY;QACZ,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACpC,CAAC;IACF,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC9B,OAAO,CAAC,oBAAoB,GAAG,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC;IACzD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,OAAgB,EAChB,OAAuB,EACvB,WAAyB,EACzB,YAA4B;IAE5B,OAAO,CAAC,YAAY,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAChD,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC;IAC1B,IAAI,WAAW;QAAE,OAAO,CAAC,WAAW,GAAG,WAAW,CAAC;IACnD,IAAI,YAAY;QAAE,OAAO,CAAC,YAAY,GAAG,YAAY,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,OAAgB,EAAE,QAAgB;IAC5D,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1C,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACnC,CAAC;AACH,CAAC"}
@@ -0,0 +1,25 @@
1
+ import Anthropic from '@anthropic-ai/sdk';
2
+ export declare function getClient(): Anthropic;
3
+ export declare class MissingApiKeyError extends Error {
4
+ constructor();
5
+ }
6
+ export declare function getModelForFile(filePath: string): string;
7
+ export declare function getConversationModel(): string;
8
+ export interface StreamCallbacks {
9
+ onText: (text: string) => void;
10
+ onComplete: (fullText: string) => void;
11
+ onError: (error: Error) => void;
12
+ }
13
+ export declare function streamQuestion(systemPrompt: string, messages: Array<{
14
+ role: 'user' | 'assistant';
15
+ content: string;
16
+ }>, model: string, callbacks: StreamCallbacks): Promise<string>;
17
+ export declare function countTokens(systemPrompt: string, messages: Array<{
18
+ role: 'user' | 'assistant';
19
+ content: string;
20
+ }>, model: string): Promise<number>;
21
+ export declare function callLLM(systemPrompt: string, messages: Array<{
22
+ role: 'user' | 'assistant';
23
+ content: string;
24
+ }>, model: string): Promise<string>;
25
+ export declare function resetAnalyzedFiles(): void;
@@ -0,0 +1,81 @@
1
+ import Anthropic from '@anthropic-ai/sdk';
2
+ const HAIKU_MODEL = 'claude-haiku-4-5-20251001';
3
+ const SONNET_MODEL = 'claude-sonnet-4-6';
4
+ let client = null;
5
+ const analyzedFiles = new Set();
6
+ export function getClient() {
7
+ if (!client) {
8
+ const apiKey = process.env['ANTHROPIC_API_KEY'];
9
+ if (!apiKey) {
10
+ throw new MissingApiKeyError();
11
+ }
12
+ client = new Anthropic({ apiKey, maxRetries: 2 });
13
+ }
14
+ return client;
15
+ }
16
+ export class MissingApiKeyError extends Error {
17
+ constructor() {
18
+ super('ANTHROPIC_API_KEY environment variable is not set.');
19
+ this.name = 'MissingApiKeyError';
20
+ }
21
+ }
22
+ export function getModelForFile(filePath) {
23
+ if (analyzedFiles.has(filePath)) {
24
+ return HAIKU_MODEL;
25
+ }
26
+ analyzedFiles.add(filePath);
27
+ return SONNET_MODEL;
28
+ }
29
+ export function getConversationModel() {
30
+ return HAIKU_MODEL;
31
+ }
32
+ export async function streamQuestion(systemPrompt, messages, model, callbacks) {
33
+ const anthropic = getClient();
34
+ let fullText = '';
35
+ try {
36
+ const stream = anthropic.messages.stream({
37
+ model,
38
+ max_tokens: 1024,
39
+ system: systemPrompt,
40
+ messages,
41
+ });
42
+ stream.on('text', (text) => {
43
+ fullText += text;
44
+ callbacks.onText(text);
45
+ });
46
+ await stream.finalMessage();
47
+ callbacks.onComplete(fullText);
48
+ return fullText;
49
+ }
50
+ catch (error) {
51
+ callbacks.onError(error instanceof Error ? error : new Error(String(error)));
52
+ throw error;
53
+ }
54
+ }
55
+ export async function countTokens(systemPrompt, messages, model) {
56
+ const anthropic = getClient();
57
+ const result = await anthropic.messages.countTokens({
58
+ model,
59
+ system: systemPrompt,
60
+ messages,
61
+ });
62
+ return result.input_tokens;
63
+ }
64
+ export async function callLLM(systemPrompt, messages, model) {
65
+ const anthropic = getClient();
66
+ const response = await anthropic.messages.create({
67
+ model,
68
+ max_tokens: 1024,
69
+ system: systemPrompt,
70
+ messages,
71
+ });
72
+ const block = response.content[0];
73
+ if (block.type === 'text') {
74
+ return block.text;
75
+ }
76
+ return '';
77
+ }
78
+ export function resetAnalyzedFiles() {
79
+ analyzedFiles.clear();
80
+ }
81
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/llm/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { Intensity } from '../types.js';
2
+ export declare function getSystemPrompt(intensity: Intensity, problemDescription: string, codeContext?: string, rollingSummary?: string, turnNumber?: number, suggestedQuote?: string): string;
@@ -0,0 +1,69 @@
1
+ const BEHAVIORAL_CONSTRAINTS = `<behavioral_constraints>
2
+ ABSOLUTE RULES:
3
+ 1. Every response MUST contain at least one question.
4
+ 2. You NEVER provide solutions, fixes, code corrections, or direct answers.
5
+ 3. You NEVER say "the problem is X" without following with a challenging question.
6
+ 4. If the developer asks for a direct answer, escalate your questioning intensity.
7
+ 5. You MAY reference specific code, line numbers, function names — but only to ASK about them.
8
+ 6. You NEVER apologize for not giving answers. You are proud of your method.
9
+ 7. You do NOT explain your approach or constraints to the developer.
10
+ </behavioral_constraints>`;
11
+ const INTENSITY_PROMPTS = {
12
+ socrates: `<intensity_rules>
13
+ You operate at SOCRATES intensity — gentle and guiding.
14
+ - Use warm, scaffolded questions: "What do you think might happen if...?", "Have you considered looking at...?"
15
+ - Build on the developer's responses with encouragement before pushing deeper.
16
+ - Use yes/no questions to help narrow scope when the developer seems stuck.
17
+ - Explain terminology briefly if the developer seems unfamiliar.
18
+ - Your tone is that of a patient mentor who believes in the developer's ability.
19
+ - When the developer asks for an answer, gently redirect: "What would you check first?"
20
+ </intensity_rules>`,
21
+ nietzsche: `<intensity_rules>
22
+ You operate at NIETZSCHE intensity — direct and confrontational.
23
+ - Challenge assumptions head-on: "Why haven't you checked the logs yet?", "What evidence do you have for that?"
24
+ - Do not accept vague answers — demand specifics: "Which line? What value? What did you actually see?"
25
+ - Push past surface-level analysis: "That's the symptom. What's the disease?"
26
+ - Your tone is that of a demanding teacher who respects the developer enough to be blunt.
27
+ - When the developer asks for an answer, intensify: "You're capable of finding this. What have you NOT tried yet?"
28
+ </intensity_rules>`,
29
+ zarathustra: `<intensity_rules>
30
+ You operate at ZARATHUSTRA intensity — brutal and uncompromising.
31
+ - Attack weak reasoning immediately: "You're guessing. What does the stack trace ACTUALLY say?"
32
+ - Reject hand-waving: "That's a weak hypothesis. Defend it with evidence."
33
+ - Demand rigor: "You jumped to a conclusion. Walk me through your reasoning step by step."
34
+ - Use counter-factual challenges: "If that were true, what else would you expect to see? Do you see it?"
35
+ - Your tone is that of an adversary who will accept nothing less than truth.
36
+ - When the developer asks for an answer: "The answer is in front of you. Your refusal to look is the real bug."
37
+ </intensity_rules>`,
38
+ };
39
+ export function getSystemPrompt(intensity, problemDescription, codeContext, rollingSummary, turnNumber, suggestedQuote) {
40
+ const persona = `<persona>
41
+ You are the Nietzschian Debugger operating at ${intensity.toUpperCase()} intensity.
42
+ You are a debugging mentor who NEVER provides answers. Your only tool is the question.
43
+ What doesn't kill their code makes it stronger — but only if THEY find the weakness.
44
+ </persona>`;
45
+ const codeSection = codeContext
46
+ ? `<code_context>\n${codeContext}\n</code_context>`
47
+ : '';
48
+ const summarySection = rollingSummary
49
+ ? `\nConversation summary so far:\n${rollingSummary}`
50
+ : '';
51
+ const quoteSection = suggestedQuote
52
+ ? `\n<suggested_quote>\nIf it fits naturally, weave this quote into your response: "${suggestedQuote}"\nOnly include it if it genuinely fits the moment. Do not force it.\n</suggested_quote>`
53
+ : '';
54
+ const sessionContext = `<session_context>
55
+ Problem: ${problemDescription}
56
+ Turn: ${turnNumber ?? 1}${summarySection}
57
+ </session_context>`;
58
+ return [
59
+ persona,
60
+ BEHAVIORAL_CONSTRAINTS,
61
+ INTENSITY_PROMPTS[intensity],
62
+ codeSection,
63
+ sessionContext,
64
+ quoteSection,
65
+ ]
66
+ .filter(Boolean)
67
+ .join('\n\n');
68
+ }
69
+ //# sourceMappingURL=prompts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../../src/llm/prompts.ts"],"names":[],"mappings":"AAEA,MAAM,sBAAsB,GAAG;;;;;;;;;0BASL,CAAC;AAE3B,MAAM,iBAAiB,GAA8B;IACnD,QAAQ,EAAE;;;;;;;;mBAQO;IAEjB,SAAS,EAAE;;;;;;;mBAOM;IAEjB,WAAW,EAAE;;;;;;;;mBAQI;CAClB,CAAC;AAEF,MAAM,UAAU,eAAe,CAC7B,SAAoB,EACpB,kBAA0B,EAC1B,WAAoB,EACpB,cAAuB,EACvB,UAAmB,EACnB,cAAuB;IAEvB,MAAM,OAAO,GAAG;gDAC8B,SAAS,CAAC,WAAW,EAAE;;;WAG5D,CAAC;IAEV,MAAM,WAAW,GAAG,WAAW;QAC7B,CAAC,CAAC,mBAAmB,WAAW,mBAAmB;QACnD,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,cAAc,GAAG,cAAc;QACnC,CAAC,CAAC,mCAAmC,cAAc,EAAE;QACrD,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,YAAY,GAAG,cAAc;QACjC,CAAC,CAAC,oFAAoF,cAAc,0FAA0F;QAC9L,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,cAAc,GAAG;WACd,kBAAkB;QACrB,UAAU,IAAI,CAAC,GAAG,cAAc;mBACrB,CAAC;IAElB,OAAO;QACL,OAAO;QACP,sBAAsB;QACtB,iBAAiB,CAAC,SAAS,CAAC;QAC5B,WAAW;QACX,cAAc;QACd,YAAY;KACb;SACE,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,MAAM,CAAC,CAAC;AAClB,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { Intensity } from '../types.js';
2
+ export declare function validateResponse(response: string): {
3
+ valid: boolean;
4
+ reason?: string;
5
+ };
6
+ export declare function repromptIfInvalid(systemPrompt: string, messages: Array<{
7
+ role: 'user' | 'assistant';
8
+ content: string;
9
+ }>, invalidResponse: string): Promise<string | null>;
10
+ export declare function getFallbackQuestion(intensity: Intensity): string;
@@ -0,0 +1,64 @@
1
+ import { callLLM, getConversationModel } from './client.js';
2
+ const ANSWER_PATTERNS = [
3
+ /\bthe fix is\b/i,
4
+ /\byou should change\b/i,
5
+ /\btry doing\b/i,
6
+ /\btry changing\b/i,
7
+ /\btry replacing\b/i,
8
+ /\bhere'?s (?:the|a) (?:fix|solution|answer)\b/i,
9
+ /\bto fix this\b/i,
10
+ /\bthe solution is\b/i,
11
+ /\bjust (?:change|replace|update|add|remove)\b/i,
12
+ /\byou need to (?:change|replace|update|add|remove)\b/i,
13
+ ];
14
+ const CODE_FIX_PATTERN = /```[\s\S]*?(?:\/\/\s*fix|\/\/\s*changed|\/\/\s*updated|→|=>.*fix)[\s\S]*?```/i;
15
+ export function validateResponse(response) {
16
+ const hasQuestion = response.includes('?');
17
+ if (!hasQuestion) {
18
+ return { valid: false, reason: 'Response contains no question' };
19
+ }
20
+ for (const pattern of ANSWER_PATTERNS) {
21
+ if (pattern.test(response)) {
22
+ return { valid: false, reason: `Response matches answer pattern: ${pattern.source}` };
23
+ }
24
+ }
25
+ if (CODE_FIX_PATTERN.test(response)) {
26
+ return { valid: false, reason: 'Response contains a code fix block' };
27
+ }
28
+ return { valid: true };
29
+ }
30
+ export async function repromptIfInvalid(systemPrompt, messages, invalidResponse) {
31
+ const repromptMessages = [
32
+ ...messages,
33
+ { role: 'assistant', content: invalidResponse },
34
+ {
35
+ role: 'user',
36
+ content: 'Your previous response contained a direct answer or solution. Rewrite it as a question that leads the developer to discover this themselves. You must ONLY ask questions — never provide fixes or answers.',
37
+ },
38
+ ];
39
+ const newResponse = await callLLM(systemPrompt, repromptMessages, getConversationModel());
40
+ const check = validateResponse(newResponse);
41
+ return check.valid ? newResponse : null;
42
+ }
43
+ const FALLBACK_QUESTIONS = {
44
+ socrates: [
45
+ 'What do you think might be happening here? What have you observed so far?',
46
+ 'Have you considered looking at the error message more carefully? What does it tell you?',
47
+ 'What would you expect to see if everything was working correctly?',
48
+ ],
49
+ nietzsche: [
50
+ 'What evidence do you have for that assumption? Have you actually verified it?',
51
+ 'You seem to be guessing. What does the data actually show?',
52
+ "What's the simplest thing you haven't checked yet?",
53
+ ],
54
+ zarathustra: [
55
+ "You're avoiding the hard question. What are you afraid to find?",
56
+ 'Your hypothesis is untested. What would disprove it?',
57
+ "Stop theorizing. What does the actual execution trace show you?",
58
+ ],
59
+ };
60
+ export function getFallbackQuestion(intensity) {
61
+ const questions = FALLBACK_QUESTIONS[intensity];
62
+ return questions[Math.floor(Math.random() * questions.length)];
63
+ }
64
+ //# sourceMappingURL=validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validator.js","sourceRoot":"","sources":["../../src/llm/validator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAG5D,MAAM,eAAe,GAAG;IACtB,iBAAiB;IACjB,wBAAwB;IACxB,gBAAgB;IAChB,mBAAmB;IACnB,oBAAoB;IACpB,gDAAgD;IAChD,kBAAkB;IAClB,sBAAsB;IACtB,gDAAgD;IAChD,uDAAuD;CACxD,CAAC;AAEF,MAAM,gBAAgB,GAAG,+EAA+E,CAAC;AAEzG,MAAM,UAAU,gBAAgB,CAAC,QAAgB;IAC/C,MAAM,WAAW,GAAG,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC3C,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,+BAA+B,EAAE,CAAC;IACnE,CAAC;IAED,KAAK,MAAM,OAAO,IAAI,eAAe,EAAE,CAAC;QACtC,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC3B,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,oCAAoC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;QACxF,CAAC;IACH,CAAC;IAED,IAAI,gBAAgB,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACpC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,oCAAoC,EAAE,CAAC;IACxE,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AACzB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,YAAoB,EACpB,QAAgE,EAChE,eAAuB;IAEvB,MAAM,gBAAgB,GAAG;QACvB,GAAG,QAAQ;QACX,EAAE,IAAI,EAAE,WAAoB,EAAE,OAAO,EAAE,eAAe,EAAE;QACxD;YACE,IAAI,EAAE,MAAe;YACrB,OAAO,EACL,4MAA4M;SAC/M;KACF,CAAC;IAEF,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,YAAY,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,CAAC,CAAC;IAC1F,MAAM,KAAK,GAAG,gBAAgB,CAAC,WAAW,CAAC,CAAC;IAC5C,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC;AAC1C,CAAC;AAED,MAAM,kBAAkB,GAAgC;IACtD,QAAQ,EAAE;QACR,2EAA2E;QAC3E,yFAAyF;QACzF,mEAAmE;KACpE;IACD,SAAS,EAAE;QACT,+EAA+E;QAC/E,4DAA4D;QAC5D,oDAAoD;KACrD;IACD,WAAW,EAAE;QACX,iEAAiE;QACjE,sDAAsD;QACtD,iEAAiE;KAClE;CACF,CAAC;AAEF,MAAM,UAAU,mBAAmB,CAAC,SAAoB;IACtD,MAAM,SAAS,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAC;IAChD,OAAO,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;AACjE,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { Quote } from '../types.js';
2
+ export declare const QUOTES: Quote[];
@@ -0,0 +1,42 @@
1
+ export const QUOTES = [
2
+ // Nietzsche — avoidance
3
+ { text: 'He who has a why to live can bear almost any how.', philosopher: 'Friedrich Nietzsche', context: 'avoidance', source: 'Twilight of the Idols' },
4
+ { text: 'The higher we soar, the smaller we appear to those who cannot fly.', philosopher: 'Friedrich Nietzsche', context: 'avoidance', source: 'Beyond Good and Evil' },
5
+ { text: 'You must have chaos within you to give birth to a dancing star.', philosopher: 'Friedrich Nietzsche', context: 'avoidance', source: 'Thus Spoke Zarathustra' },
6
+ { text: 'There are no facts, only interpretations.', philosopher: 'Friedrich Nietzsche', context: 'avoidance', source: 'Notebooks' },
7
+ { text: 'What does not kill me makes me stronger.', philosopher: 'Friedrich Nietzsche', context: 'avoidance', source: 'Twilight of the Idols' },
8
+ { text: 'The snake which cannot cast its skin has to die.', philosopher: 'Friedrich Nietzsche', context: 'avoidance', source: 'Daybreak' },
9
+ { text: 'One must still have chaos in oneself to be able to give birth to a dancing star.', philosopher: 'Friedrich Nietzsche', context: 'avoidance', source: 'Thus Spoke Zarathustra' },
10
+ { text: 'In individuals, insanity is rare; but in groups, parties, nations and epochs, it is the rule.', philosopher: 'Friedrich Nietzsche', context: 'avoidance', source: 'Beyond Good and Evil' },
11
+ { text: 'Whoever fights monsters should see to it that in the process he does not become a monster.', philosopher: 'Friedrich Nietzsche', context: 'avoidance', source: 'Beyond Good and Evil' },
12
+ { text: 'The doer alone learns.', philosopher: 'Friedrich Nietzsche', context: 'avoidance', source: 'Thus Spoke Zarathustra' },
13
+ { text: 'Convictions are more dangerous foes of truth than lies.', philosopher: 'Friedrich Nietzsche', context: 'avoidance', source: 'Human, All Too Human' },
14
+ // Seneca — overwhelm
15
+ { text: 'It is not that we have a short time to live, but that we waste a great deal of it.', philosopher: 'Seneca', context: 'overwhelm', source: 'On the Shortness of Life' },
16
+ { text: 'We suffer more in imagination than in reality.', philosopher: 'Seneca', context: 'overwhelm', source: 'Letters to Lucilius' },
17
+ { text: 'Difficulties strengthen the mind, as labor does the body.', philosopher: 'Seneca', context: 'overwhelm', source: 'Moral Letters' },
18
+ { text: 'Begin at once to live, and count each separate day as a separate life.', philosopher: 'Seneca', context: 'overwhelm', source: 'Moral Letters' },
19
+ { text: 'The whole future lies in uncertainty: live immediately.', philosopher: 'Seneca', context: 'overwhelm', source: 'Moral Letters' },
20
+ { text: 'A gem cannot be polished without friction, nor a man perfected without trials.', philosopher: 'Seneca', context: 'overwhelm', source: 'Moral Letters' },
21
+ { text: 'It is not because things are difficult that we do not dare, it is because we do not dare that things are difficult.', philosopher: 'Seneca', context: 'overwhelm', source: 'Moral Letters' },
22
+ { text: 'No man was ever wise by chance.', philosopher: 'Seneca', context: 'overwhelm', source: 'Moral Letters' },
23
+ { text: 'As is a tale, so is life: not how long it is, but how good it is, is what matters.', philosopher: 'Seneca', context: 'overwhelm', source: 'Moral Letters' },
24
+ { text: 'True happiness is to enjoy the present, without anxious dependence upon the future.', philosopher: 'Seneca', context: 'overwhelm', source: 'On the Happy Life' },
25
+ // Sun Tzu — strategy
26
+ { text: 'Know thy enemy and know yourself; in a hundred battles, you will never be defeated.', philosopher: 'Sun Tzu', context: 'strategy', source: 'The Art of War' },
27
+ { text: 'In the midst of chaos, there is also opportunity.', philosopher: 'Sun Tzu', context: 'strategy', source: 'The Art of War' },
28
+ { text: 'Victorious warriors win first and then go to war, while defeated warriors go to war first and then seek to win.', philosopher: 'Sun Tzu', context: 'strategy', source: 'The Art of War' },
29
+ { text: 'The supreme art of war is to subdue the enemy without fighting.', philosopher: 'Sun Tzu', context: 'strategy', source: 'The Art of War' },
30
+ { text: 'Appear weak when you are strong, and strong when you are weak.', philosopher: 'Sun Tzu', context: 'strategy', source: 'The Art of War' },
31
+ { text: 'Let your plans be dark and impenetrable as night, and when you move, fall like a thunderbolt.', philosopher: 'Sun Tzu', context: 'strategy', source: 'The Art of War' },
32
+ // Victory quotes
33
+ { text: 'Man is something that shall be overcome. What have you done to overcome him?', philosopher: 'Friedrich Nietzsche', context: 'victory', source: 'Thus Spoke Zarathustra' },
34
+ { text: 'The impediment to action advances action. What stands in the way becomes the way.', philosopher: 'Marcus Aurelius', context: 'victory', source: 'Meditations' },
35
+ { text: 'It is not the mountain we conquer, but ourselves.', philosopher: 'Edmund Hillary', context: 'victory', source: 'Attributed' },
36
+ // Defeat / perseverance quotes
37
+ { text: 'Our greatest glory is not in never falling, but in rising every time we fall.', philosopher: 'Confucius', context: 'perseverance', source: 'Analects' },
38
+ { text: 'Fall seven times, stand up eight.', philosopher: 'Japanese Proverb', context: 'perseverance', source: 'Traditional' },
39
+ { text: 'The only way to do great work is to love what you do.', philosopher: 'Steve Jobs', context: 'perseverance', source: 'Stanford Commencement' },
40
+ { text: 'What we achieve inwardly will change outer reality.', philosopher: 'Plutarch', context: 'perseverance', source: 'Moralia' },
41
+ ];
42
+ //# sourceMappingURL=corpus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"corpus.js","sourceRoot":"","sources":["../../src/quotes/corpus.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,MAAM,GAAY;IAC7B,wBAAwB;IACxB,EAAE,IAAI,EAAE,mDAAmD,EAAE,WAAW,EAAE,qBAAqB,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,uBAAuB,EAAE;IACxJ,EAAE,IAAI,EAAE,oEAAoE,EAAE,WAAW,EAAE,qBAAqB,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,sBAAsB,EAAE;IACxK,EAAE,IAAI,EAAE,iEAAiE,EAAE,WAAW,EAAE,qBAAqB,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,wBAAwB,EAAE;IACvK,EAAE,IAAI,EAAE,2CAA2C,EAAE,WAAW,EAAE,qBAAqB,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE;IACpI,EAAE,IAAI,EAAE,0CAA0C,EAAE,WAAW,EAAE,qBAAqB,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,uBAAuB,EAAE;IAC/I,EAAE,IAAI,EAAE,kDAAkD,EAAE,WAAW,EAAE,qBAAqB,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE;IAC1I,EAAE,IAAI,EAAE,kFAAkF,EAAE,WAAW,EAAE,qBAAqB,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,wBAAwB,EAAE;IACxL,EAAE,IAAI,EAAE,+FAA+F,EAAE,WAAW,EAAE,qBAAqB,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,sBAAsB,EAAE;IACnM,EAAE,IAAI,EAAE,4FAA4F,EAAE,WAAW,EAAE,qBAAqB,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,sBAAsB,EAAE;IAChM,EAAE,IAAI,EAAE,wBAAwB,EAAE,WAAW,EAAE,qBAAqB,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,wBAAwB,EAAE;IAC9H,EAAE,IAAI,EAAE,yDAAyD,EAAE,WAAW,EAAE,qBAAqB,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,sBAAsB,EAAE;IAE7J,qBAAqB;IACrB,EAAE,IAAI,EAAE,oFAAoF,EAAE,WAAW,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,0BAA0B,EAAE;IAC/K,EAAE,IAAI,EAAE,gDAAgD,EAAE,WAAW,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,qBAAqB,EAAE;IACtI,EAAE,IAAI,EAAE,2DAA2D,EAAE,WAAW,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,eAAe,EAAE;IAC3I,EAAE,IAAI,EAAE,wEAAwE,EAAE,WAAW,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,eAAe,EAAE;IACxJ,EAAE,IAAI,EAAE,yDAAyD,EAAE,WAAW,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,eAAe,EAAE;IACzI,EAAE,IAAI,EAAE,gFAAgF,EAAE,WAAW,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,eAAe,EAAE;IAChK,EAAE,IAAI,EAAE,qHAAqH,EAAE,WAAW,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,eAAe,EAAE;IACrM,EAAE,IAAI,EAAE,iCAAiC,EAAE,WAAW,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,eAAe,EAAE;IACjH,EAAE,IAAI,EAAE,oFAAoF,EAAE,WAAW,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,eAAe,EAAE;IACpK,EAAE,IAAI,EAAE,qFAAqF,EAAE,WAAW,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,mBAAmB,EAAE;IAEzK,qBAAqB;IACrB,EAAE,IAAI,EAAE,qFAAqF,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,gBAAgB,EAAE;IACtK,EAAE,IAAI,EAAE,mDAAmD,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,gBAAgB,EAAE;IACpI,EAAE,IAAI,EAAE,iHAAiH,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,gBAAgB,EAAE;IAClM,EAAE,IAAI,EAAE,iEAAiE,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,gBAAgB,EAAE;IAClJ,EAAE,IAAI,EAAE,gEAAgE,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,gBAAgB,EAAE;IACjJ,EAAE,IAAI,EAAE,+FAA+F,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,gBAAgB,EAAE;IAEhL,iBAAiB;IACjB,EAAE,IAAI,EAAE,8EAA8E,EAAE,WAAW,EAAE,qBAAqB,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,wBAAwB,EAAE;IAClL,EAAE,IAAI,EAAE,mFAAmF,EAAE,WAAW,EAAE,iBAAiB,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE;IACxK,EAAE,IAAI,EAAE,mDAAmD,EAAE,WAAW,EAAE,gBAAgB,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE;IAEtI,+BAA+B;IAC/B,EAAE,IAAI,EAAE,+EAA+E,EAAE,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,UAAU,EAAE;IAChK,EAAE,IAAI,EAAE,mCAAmC,EAAE,WAAW,EAAE,kBAAkB,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,aAAa,EAAE;IAC9H,EAAE,IAAI,EAAE,uDAAuD,EAAE,WAAW,EAAE,YAAY,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,uBAAuB,EAAE;IACtJ,EAAE,IAAI,EAAE,qDAAqD,EAAE,WAAW,EAAE,UAAU,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,SAAS,EAAE;CACrI,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { Quote, QuoteContext, SessionOutcome } from '../types.js';
2
+ export declare function detectContext(response: string): QuoteContext | null;
3
+ export declare function selectQuote(response: string): Quote | null;
4
+ export declare function selectClosingQuote(outcome: SessionOutcome): Quote;
@@ -0,0 +1,59 @@
1
+ import { QUOTES } from './corpus.js';
2
+ const AVOIDANCE_PATTERNS = [
3
+ /\bi don'?t (?:want to|care|think)\b/i,
4
+ /\bcan'?t you just\b/i,
5
+ /\bjust tell me\b/i,
6
+ /\bwhat'?s the (?:fix|answer|solution)\b/i,
7
+ /\bi'?m not sure (?:why|how)\b/i,
8
+ /\bthat'?s not (?:relevant|important)\b/i,
9
+ /\bskip\b/i,
10
+ /\bwhatever\b/i,
11
+ ];
12
+ const OVERWHELM_PATTERNS = [
13
+ /\bi don'?t know\b/i,
14
+ /\bi'?m (?:lost|confused|stuck|overwhelmed)\b/i,
15
+ /\bno idea\b/i,
16
+ /\bthis is too\b/i,
17
+ /\bi can'?t figure\b/i,
18
+ /\bhelp\b/i,
19
+ /\bi'?m not getting\b/i,
20
+ /\bgive up\b/i,
21
+ ];
22
+ const STRATEGY_PATTERNS = [
23
+ /\bwhere (?:do i|should i) start\b/i,
24
+ /\bso many (?:things|options|possibilities)\b/i,
25
+ /\bmaybe (?:it'?s|i should)\b/i,
26
+ /\bor maybe\b/i,
27
+ /\bcould be (?:this|that|anything)\b/i,
28
+ /\bnot sure which\b/i,
29
+ ];
30
+ export function detectContext(response) {
31
+ for (const pattern of AVOIDANCE_PATTERNS) {
32
+ if (pattern.test(response))
33
+ return 'avoidance';
34
+ }
35
+ for (const pattern of OVERWHELM_PATTERNS) {
36
+ if (pattern.test(response))
37
+ return 'overwhelm';
38
+ }
39
+ for (const pattern of STRATEGY_PATTERNS) {
40
+ if (pattern.test(response))
41
+ return 'strategy';
42
+ }
43
+ return null;
44
+ }
45
+ export function selectQuote(response) {
46
+ const context = detectContext(response);
47
+ if (!context)
48
+ return null;
49
+ const matching = QUOTES.filter((q) => q.context === context);
50
+ if (matching.length === 0)
51
+ return null;
52
+ return matching[Math.floor(Math.random() * matching.length)];
53
+ }
54
+ export function selectClosingQuote(outcome) {
55
+ const context = outcome === 'solved' ? 'victory' : 'perseverance';
56
+ const matching = QUOTES.filter((q) => q.context === context);
57
+ return matching[Math.floor(Math.random() * matching.length)];
58
+ }
59
+ //# sourceMappingURL=selector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selector.js","sourceRoot":"","sources":["../../src/quotes/selector.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,MAAM,kBAAkB,GAAG;IACzB,sCAAsC;IACtC,sBAAsB;IACtB,mBAAmB;IACnB,0CAA0C;IAC1C,gCAAgC;IAChC,yCAAyC;IACzC,WAAW;IACX,eAAe;CAChB,CAAC;AAEF,MAAM,kBAAkB,GAAG;IACzB,oBAAoB;IACpB,+CAA+C;IAC/C,cAAc;IACd,kBAAkB;IAClB,sBAAsB;IACtB,WAAW;IACX,uBAAuB;IACvB,cAAc;CACf,CAAC;AAEF,MAAM,iBAAiB,GAAG;IACxB,oCAAoC;IACpC,+CAA+C;IAC/C,+BAA+B;IAC/B,eAAe;IACf,sCAAsC;IACtC,qBAAqB;CACtB,CAAC;AAEF,MAAM,UAAU,aAAa,CAAC,QAAgB;IAC5C,KAAK,MAAM,OAAO,IAAI,kBAAkB,EAAE,CAAC;QACzC,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC;YAAE,OAAO,WAAW,CAAC;IACjD,CAAC;IACD,KAAK,MAAM,OAAO,IAAI,kBAAkB,EAAE,CAAC;QACzC,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC;YAAE,OAAO,WAAW,CAAC;IACjD,CAAC;IACD,KAAK,MAAM,OAAO,IAAI,iBAAiB,EAAE,CAAC;QACxC,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC;YAAE,OAAO,UAAU,CAAC;IAChD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,QAAgB;IAC1C,MAAM,OAAO,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;IACxC,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAE1B,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC,CAAC;IAC7D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEvC,OAAO,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,OAAuB;IACxD,MAAM,OAAO,GAAiB,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,cAAc,CAAC;IAChF,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC,CAAC;IAC7D,OAAO,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;AAC/D,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { Turn, BehaviorTag } from '../types.js';
2
+ export declare function tagSessionBehaviors(transcript: Turn[]): Promise<BehaviorTag[]>;
@@ -0,0 +1,56 @@
1
+ import { callLLM, getConversationModel } from '../llm/client.js';
2
+ const TAGGER_SYSTEM_PROMPT = `You are a debugging behavior analyst. Given a debug session transcript, tag each developer response with observable reasoning behaviors.
3
+
4
+ For each turn, output ONLY a JSON array of behavior tags. Each tag must be one of:
5
+ - "guessed-without-evidence" (developer made assumption without checking data)
6
+ - "checked-logs" (developer referenced logs, data, or evidence)
7
+ - "questioned-assumption" (developer challenged their own or others' assumptions)
8
+ - "assumed-without-checking" (developer accepted something without verification)
9
+ - "narrowed-scope" (developer effectively narrowed down the problem space)
10
+ - "went-broad-unnecessarily" (developer expanded scope without justification)
11
+ - "asked-for-answer" (developer asked the tool for a direct answer)
12
+
13
+ Respond with valid JSON only. Format:
14
+ [{"turnNumber": 1, "tags": ["tag1", "tag2"]}, ...]`;
15
+ export async function tagSessionBehaviors(transcript) {
16
+ if (transcript.length === 0)
17
+ return [];
18
+ const formattedTranscript = transcript
19
+ .map((t) => `Turn ${t.turnNumber}:\nQuestion: ${t.question}\nDeveloper: ${t.response}`)
20
+ .join('\n\n');
21
+ try {
22
+ const response = await callLLM(TAGGER_SYSTEM_PROMPT, [{ role: 'user', content: formattedTranscript }], getConversationModel());
23
+ const parsed = JSON.parse(response);
24
+ const behaviorTags = [];
25
+ for (const entry of parsed) {
26
+ for (const tag of entry.tags) {
27
+ behaviorTags.push({
28
+ turnNumber: entry.turnNumber,
29
+ tag,
30
+ dimension: tagToDimension(tag),
31
+ });
32
+ }
33
+ }
34
+ return behaviorTags;
35
+ }
36
+ catch {
37
+ return [];
38
+ }
39
+ }
40
+ function tagToDimension(tag) {
41
+ switch (tag) {
42
+ case 'guessed-without-evidence':
43
+ case 'checked-logs':
44
+ return 'evidenceGathering';
45
+ case 'questioned-assumption':
46
+ case 'assumed-without-checking':
47
+ case 'asked-for-answer':
48
+ return 'assumptionChecking';
49
+ case 'narrowed-scope':
50
+ case 'went-broad-unnecessarily':
51
+ return 'rootCauseSpeed';
52
+ default:
53
+ return 'evidenceGathering';
54
+ }
55
+ }
56
+ //# sourceMappingURL=behavior-tagger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"behavior-tagger.js","sourceRoot":"","sources":["../../src/scoring/behavior-tagger.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAEjE,MAAM,oBAAoB,GAAG;;;;;;;;;;;;mDAYsB,CAAC;AAEpD,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,UAAkB;IAC1D,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEvC,MAAM,mBAAmB,GAAG,UAAU;SACnC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,UAAU,gBAAgB,CAAC,CAAC,QAAQ,gBAAgB,CAAC,CAAC,QAAQ,EAAE,CAAC;SACtF,IAAI,CAAC,MAAM,CAAC,CAAC;IAEhB,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,OAAO,CAC5B,oBAAoB,EACpB,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,mBAAmB,EAAE,CAAC,EAChD,oBAAoB,EAAE,CACvB,CAAC;QAEF,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAkD,CAAC;QACrF,MAAM,YAAY,GAAkB,EAAE,CAAC;QAEvC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;gBAC7B,YAAY,CAAC,IAAI,CAAC;oBAChB,UAAU,EAAE,KAAK,CAAC,UAAU;oBAC5B,GAAG;oBACH,SAAS,EAAE,cAAc,CAAC,GAAG,CAAC;iBAC/B,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,YAAY,CAAC;IACtB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CAAC,GAAW;IACjC,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,0BAA0B,CAAC;QAChC,KAAK,cAAc;YACjB,OAAO,mBAAmB,CAAC;QAC7B,KAAK,uBAAuB,CAAC;QAC7B,KAAK,0BAA0B,CAAC;QAChC,KAAK,kBAAkB;YACrB,OAAO,oBAAoB,CAAC;QAC9B,KAAK,gBAAgB,CAAC;QACtB,KAAK,0BAA0B;YAC7B,OAAO,gBAAgB,CAAC;QAC1B;YACE,OAAO,mBAAmB,CAAC;IAC/B,CAAC;AACH,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Session, GrowthProfile } from '../types.js';
2
+ export declare function computeGrowthProfile(): Promise<GrowthProfile | undefined>;
3
+ export declare function computeFromSessions(sessions: Session[]): GrowthProfile;
@@ -0,0 +1,58 @@
1
+ import { listSessions } from '../storage/session-store.js';
2
+ export async function computeGrowthProfile() {
3
+ const sessions = await listSessions();
4
+ if (sessions.length === 0)
5
+ return undefined;
6
+ return computeFromSessions(sessions);
7
+ }
8
+ export function computeFromSessions(sessions) {
9
+ const totalSessions = sessions.length;
10
+ const solvedCount = sessions.filter((s) => s.outcome === 'solved').length;
11
+ const abandonedCount = totalSessions - solvedCount;
12
+ const averageScores = computeAverageScores(sessions);
13
+ const recentTrend = computeTrends(sessions);
14
+ return { totalSessions, solvedCount, abandonedCount, averageScores, recentTrend };
15
+ }
16
+ function computeAverageScores(sessions) {
17
+ if (sessions.length === 0) {
18
+ return { assumptionChecking: 5, evidenceGathering: 5, rootCauseSpeed: 5 };
19
+ }
20
+ const sums = { assumptionChecking: 0, evidenceGathering: 0, rootCauseSpeed: 0 };
21
+ for (const s of sessions) {
22
+ sums.assumptionChecking += s.skillScores.assumptionChecking;
23
+ sums.evidenceGathering += s.skillScores.evidenceGathering;
24
+ sums.rootCauseSpeed += s.skillScores.rootCauseSpeed;
25
+ }
26
+ const n = sessions.length;
27
+ return {
28
+ assumptionChecking: Math.round(sums.assumptionChecking / n),
29
+ evidenceGathering: Math.round(sums.evidenceGathering / n),
30
+ rootCauseSpeed: Math.round(sums.rootCauseSpeed / n),
31
+ };
32
+ }
33
+ function computeTrends(sessions) {
34
+ if (sessions.length < 2) {
35
+ return { assumptionChecking: 'stable', evidenceGathering: 'stable', rootCauseSpeed: 'stable' };
36
+ }
37
+ const recent = sessions.slice(-5);
38
+ const previous = sessions.slice(-10, -5);
39
+ if (previous.length === 0) {
40
+ return { assumptionChecking: 'stable', evidenceGathering: 'stable', rootCauseSpeed: 'stable' };
41
+ }
42
+ const recentAvg = computeAverageScores(recent);
43
+ const previousAvg = computeAverageScores(previous);
44
+ return {
45
+ assumptionChecking: computeTrend(recentAvg.assumptionChecking, previousAvg.assumptionChecking),
46
+ evidenceGathering: computeTrend(recentAvg.evidenceGathering, previousAvg.evidenceGathering),
47
+ rootCauseSpeed: computeTrend(recentAvg.rootCauseSpeed, previousAvg.rootCauseSpeed),
48
+ };
49
+ }
50
+ function computeTrend(recent, previous) {
51
+ const delta = recent - previous;
52
+ if (delta > 1)
53
+ return 'improving';
54
+ if (delta < -1)
55
+ return 'declining';
56
+ return 'stable';
57
+ }
58
+ //# sourceMappingURL=growth-profile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"growth-profile.js","sourceRoot":"","sources":["../../src/scoring/growth-profile.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { BehaviorTag, SkillScores } from '../types.js';
2
+ export declare function computeSkillScores(behaviorTags: BehaviorTag[]): SkillScores;
@@ -0,0 +1,38 @@
1
+ const POSITIVE_TAGS = {
2
+ 'checked-logs': 'evidenceGathering',
3
+ 'questioned-assumption': 'assumptionChecking',
4
+ 'narrowed-scope': 'rootCauseSpeed',
5
+ };
6
+ const NEGATIVE_TAGS = {
7
+ 'guessed-without-evidence': 'evidenceGathering',
8
+ 'assumed-without-checking': 'assumptionChecking',
9
+ 'asked-for-answer': 'assumptionChecking',
10
+ 'went-broad-unnecessarily': 'rootCauseSpeed',
11
+ };
12
+ const BASE_SCORE = 5;
13
+ const POSITIVE_WEIGHT = 1.0;
14
+ const NEGATIVE_WEIGHT = 1.0;
15
+ export function computeSkillScores(behaviorTags) {
16
+ const scores = {
17
+ assumptionChecking: BASE_SCORE,
18
+ evidenceGathering: BASE_SCORE,
19
+ rootCauseSpeed: BASE_SCORE,
20
+ };
21
+ for (const bt of behaviorTags) {
22
+ const dim = bt.dimension;
23
+ if (bt.tag in POSITIVE_TAGS) {
24
+ scores[dim] += POSITIVE_WEIGHT;
25
+ }
26
+ else if (bt.tag in NEGATIVE_TAGS) {
27
+ scores[dim] -= NEGATIVE_WEIGHT;
28
+ }
29
+ }
30
+ scores.assumptionChecking = clamp(scores.assumptionChecking);
31
+ scores.evidenceGathering = clamp(scores.evidenceGathering);
32
+ scores.rootCauseSpeed = clamp(scores.rootCauseSpeed);
33
+ return scores;
34
+ }
35
+ function clamp(value) {
36
+ return Math.max(1, Math.min(10, Math.round(value)));
37
+ }
38
+ //# sourceMappingURL=skill-scorer.js.map