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
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dilawar Gopang
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,176 @@
1
+ # Nietzschian Debugger
2
+
3
+ > "What doesn't kill your code makes it stronger."
4
+
5
+ The anti-AI. Every other tool gives you answers. Nietzschian Debugger **confronts you with questions**.
6
+
7
+ You paste your bug. It doesn't fix it. Instead, it:
8
+
9
+ - Challenges your assumptions about what's happening
10
+ - Forces you to confront the evidence you're avoiding
11
+ - Tears apart your weak reasoning until only truth remains
12
+ - Guides you to the "aha!" moment — YOUR moment, not the AI's
13
+
14
+ ## Why?
15
+
16
+ AI is making developers weaker. Copy-paste from ChatGPT, ship, repeat. Nobody understands their own code anymore. **Nietzschian Debugger forges stronger developers** — through struggle, not shortcuts.
17
+
18
+ > "He who has a why to debug can bear almost any how."
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ # Install globally
24
+ npm install -g nietzschian-debugger
25
+
26
+ # Or use directly with npx
27
+ npx nietzschian-debugger
28
+ ```
29
+
30
+ **Requirements:**
31
+ - Node.js >= 20.0.0
32
+ - An Anthropic API key (BYOK — bring your own key)
33
+
34
+ ## Setup
35
+
36
+ Set your Anthropic API key:
37
+
38
+ ```bash
39
+ # Linux / macOS
40
+ export ANTHROPIC_API_KEY="sk-ant-..."
41
+
42
+ # Windows (PowerShell)
43
+ $env:ANTHROPIC_API_KEY = "sk-ant-..."
44
+
45
+ # Windows (CMD)
46
+ set ANTHROPIC_API_KEY=sk-ant-...
47
+ ```
48
+
49
+ ## Quick Start
50
+
51
+ ```bash
52
+ # Start a debugging session (default: nietzsche intensity)
53
+ nietzschian debug "My API returns 500 but only on Tuesdays"
54
+
55
+ # Gentle mode — for when you need guidance, not punishment
56
+ nietzschian debug --intensity socrates "My login endpoint fails randomly"
57
+
58
+ # Maximum intensity — for seniors who want pain
59
+ nietzschian debug --intensity zarathustra "Memory leak in production"
60
+
61
+ # Point it at specific code
62
+ nietzschian debug "Auth fails in ./src/middleware/auth.ts"
63
+ ```
64
+
65
+ ## Three Intensity Levels
66
+
67
+ | Level | Style | Best For |
68
+ |-------|-------|----------|
69
+ | `socrates` | Warm, guiding, scaffolded questions | Juniors, learning fundamentals |
70
+ | `nietzsche` | Direct, confrontational, no hand-holding | Daily debugging (default) |
71
+ | `zarathustra` | Brutal, adversarial, hostile to weak reasoning | Seniors who want real pain |
72
+
73
+ ## How a Session Looks
74
+
75
+ ```
76
+ Nietzschian Debugger v0.1.0 — intensity: nietzsche
77
+
78
+ You: My login endpoint returns 403 for some users
79
+
80
+ Nietzschian: You say "some users." That's vague. What separates
81
+ the ones who fail from the ones who don't?
82
+
83
+ You: I'm not sure, it seems random
84
+
85
+ Nietzschian: "Random" is the word we use when we haven't looked
86
+ hard enough. What do the server logs say for the 403
87
+ responses? Go look. I'll wait.
88
+
89
+ You: Let me check... oh, they all have expired JWT tokens
90
+
91
+ Nietzschian: Now we're getting somewhere. Why would some users
92
+ have expired tokens while others don't? What changed
93
+ recently in your auth system?
94
+
95
+ You: ... oh. We changed the token TTL last week and didn't
96
+ invalidate existing sessions.
97
+
98
+ Nietzschian: There it is. You didn't need me to tell you — you
99
+ needed to be forced to look.
100
+
101
+ Session Complete — 4 questions to root cause (Solved)
102
+
103
+ Your Debugging Profile:
104
+ ┣ Assumption-checking ██████████ strong
105
+ ┣ Evidence-gathering ██████░░░░ moderate
106
+ ┗ Root cause speed ████████░░ strong
107
+
108
+ "Man is something that shall be overcome."
109
+ — Friedrich Nietzsche
110
+ ```
111
+
112
+ ## Session Commands
113
+
114
+ | Command | Effect |
115
+ |---------|--------|
116
+ | `solved` or `found it` | End session — you found the root cause |
117
+ | `I give up` | Get one lifeline question, then exit |
118
+ | `exit` or `quit` | Abandon session immediately |
119
+ | `Ctrl+C` / `Ctrl+D` | Force quit |
120
+
121
+ ## Features
122
+
123
+ - **Never Gives Answers** — Every response is a question. The tool will NEVER tell you the fix.
124
+ - **Real Code Reading** — Reference a file path and it reads YOUR actual code, not generic examples.
125
+ - **Contextual Philosophy** — Nietzsche when you're avoiding, Seneca when overwhelmed, Sun Tzu when you need strategy.
126
+ - **Growth Score** — Track your debugging skills across sessions with visual bar charts.
127
+ - **Session History** — All sessions saved locally in `.nietzschian/sessions/` for trend tracking.
128
+ - **Sliding Context** — Long sessions don't crash; older turns are transparently summarized.
129
+
130
+ ## The Philosophy
131
+
132
+ | Traditional AI | Nietzschian Debugger |
133
+ |---------------|---------------------|
134
+ | "Here's the fix" | "What have you tried?" |
135
+ | Makes you dependent | Makes you dangerous |
136
+ | You learn nothing | You learn everything |
137
+ | Fast but shallow | Painful but permanent |
138
+ | AI gets smarter | YOU get smarter |
139
+
140
+ ## Who Is This For?
141
+
142
+ - **Junior developers** who want to actually learn, not just copy-paste
143
+ - **Senior developers** who miss the struggle that made them good
144
+ - **Engineering managers** who want their team thinking, not just prompting
145
+ - **Bootcamp students** building real debugging intuition
146
+ - **Anyone** who believes the best developer is a self-reliant one
147
+
148
+ ## Development
149
+
150
+ ```bash
151
+ # Clone and install
152
+ git clone https://github.com/your-username/nietzschian-debugger.git
153
+ cd nietzschian-debugger
154
+ npm install
155
+
156
+ # Build
157
+ npm run build
158
+
159
+ # Run tests (121 tests)
160
+ npm test
161
+
162
+ # Run CLI locally
163
+ node dist/cli.js debug "test problem"
164
+ ```
165
+
166
+ ## Tech Stack
167
+
168
+ - TypeScript (strict mode)
169
+ - Node.js >= 20
170
+ - Claude API (Haiku for conversation, Sonnet for code analysis)
171
+ - Commander.js for CLI
172
+ - Vitest for testing
173
+
174
+ ## License
175
+
176
+ MIT
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { readFileSync } from 'node:fs';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { dirname, join } from 'node:path';
6
+ import { debugCommand } from './commands/debug.js';
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = dirname(__filename);
9
+ let version = '0.1.0';
10
+ try {
11
+ const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));
12
+ version = pkg.version;
13
+ }
14
+ catch {
15
+ // Fallback version
16
+ }
17
+ const program = new Command();
18
+ program
19
+ .name('nietzschian')
20
+ .description('The anti-AI debugger. What doesn\'t kill your code makes it stronger.')
21
+ .version(version);
22
+ program
23
+ .command('debug')
24
+ .description('Start an interactive debugging session')
25
+ .argument('<problem>', 'Description of the bug or problem to debug')
26
+ .option('-i, --intensity <level>', 'Questioning intensity: socrates, nietzsche, zarathustra', 'nietzsche')
27
+ .action(async (problem, options) => {
28
+ const validIntensities = ['socrates', 'nietzsche', 'zarathustra'];
29
+ if (!validIntensities.includes(options.intensity)) {
30
+ console.error(`Invalid intensity: "${options.intensity}". Choose from: ${validIntensities.join(', ')}`);
31
+ process.exit(2);
32
+ }
33
+ await debugCommand(problem, options.intensity);
34
+ });
35
+ program.parse();
36
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGnD,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtC,IAAI,OAAO,GAAG,OAAO,CAAC;AACtB,IAAI,CAAC;IACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;IACrF,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC;AACxB,CAAC;AAAC,MAAM,CAAC;IACP,mBAAmB;AACrB,CAAC;AAED,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,aAAa,CAAC;KACnB,WAAW,CAAC,uEAAuE,CAAC;KACpF,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,wCAAwC,CAAC;KACrD,QAAQ,CAAC,WAAW,EAAE,4CAA4C,CAAC;KACnE,MAAM,CACL,yBAAyB,EACzB,yDAAyD,EACzD,WAAW,CACZ;KACA,MAAM,CAAC,KAAK,EAAE,OAAe,EAAE,OAA8B,EAAE,EAAE;IAChE,MAAM,gBAAgB,GAAG,CAAC,UAAU,EAAE,WAAW,EAAE,aAAa,CAAC,CAAC;IAClE,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QAClD,OAAO,CAAC,KAAK,CACX,uBAAuB,OAAO,CAAC,SAAS,mBAAmB,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACzF,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,SAAsB,CAAC,CAAC;AAC9D,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { Intensity } from '../types.js';
2
+ export declare function debugCommand(problemDescription: string, intensity: Intensity): Promise<void>;
@@ -0,0 +1,80 @@
1
+ import Anthropic from '@anthropic-ai/sdk';
2
+ import { getClient, MissingApiKeyError } from '../llm/client.js';
3
+ import { createSession, finalizeSession } from '../core/session.js';
4
+ import { runSessionLoop } from '../core/session-loop.js';
5
+ import { saveSession } from '../storage/session-store.js';
6
+ import { tagSessionBehaviors } from '../scoring/behavior-tagger.js';
7
+ import { computeSkillScores } from '../scoring/skill-scorer.js';
8
+ import { computeGrowthProfile } from '../scoring/growth-profile.js';
9
+ import { selectClosingQuote } from '../quotes/selector.js';
10
+ import { renderGrowthScore } from '../ui/growth-display.js';
11
+ import { displayError, displayApiKeyHelp } from '../ui/renderer.js';
12
+ export async function debugCommand(problemDescription, intensity) {
13
+ // Validate API key
14
+ try {
15
+ getClient();
16
+ }
17
+ catch (error) {
18
+ if (error instanceof MissingApiKeyError) {
19
+ displayApiKeyHelp();
20
+ process.exit(1);
21
+ }
22
+ throw error;
23
+ }
24
+ // Validate problem description
25
+ if (!problemDescription || !problemDescription.trim()) {
26
+ displayError('Please provide a problem description.');
27
+ console.log('\nUsage: nietzschian debug "Your problem description here"');
28
+ process.exit(2);
29
+ }
30
+ // Create session
31
+ const session = createSession(problemDescription.trim(), intensity);
32
+ try {
33
+ // Run interactive session
34
+ const result = await runSessionLoop(session);
35
+ // Finalize session
36
+ let behaviorTags = session.behaviorTags;
37
+ let skillScores = session.skillScores;
38
+ try {
39
+ behaviorTags = await tagSessionBehaviors(session.transcript);
40
+ skillScores = computeSkillScores(behaviorTags);
41
+ }
42
+ catch {
43
+ // Scoring is best-effort — don't crash if it fails
44
+ }
45
+ finalizeSession(session, result.outcome, skillScores, behaviorTags);
46
+ // Save session
47
+ try {
48
+ await saveSession(session);
49
+ }
50
+ catch {
51
+ // Persistence is best-effort
52
+ }
53
+ // Display growth score
54
+ const growthProfile = await computeGrowthProfile();
55
+ const closingQuote = selectClosingQuote(result.outcome);
56
+ const display = renderGrowthScore(session, growthProfile, closingQuote);
57
+ console.log(display);
58
+ process.exit(0);
59
+ }
60
+ catch (error) {
61
+ if (error instanceof Anthropic.AuthenticationError) {
62
+ displayError('Invalid API key. Check your ANTHROPIC_API_KEY.');
63
+ process.exit(1);
64
+ }
65
+ if (error instanceof Anthropic.RateLimitError) {
66
+ displayError('Rate limit reached. Try again in a moment.');
67
+ process.exit(3);
68
+ }
69
+ if (error instanceof Anthropic.APIConnectionError) {
70
+ displayError('Cannot reach Claude API. Check your connection.');
71
+ process.exit(3);
72
+ }
73
+ if (error instanceof Anthropic.APIError) {
74
+ displayError(`API error: ${error.message}`);
75
+ process.exit(3);
76
+ }
77
+ throw error;
78
+ }
79
+ }
80
+ //# sourceMappingURL=debug.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"debug.js","sourceRoot":"","sources":["../../src/commands/debug.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAE1C,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAEpE,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,kBAA0B,EAC1B,SAAoB;IAEpB,mBAAmB;IACnB,IAAI,CAAC;QACH,SAAS,EAAE,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,kBAAkB,EAAE,CAAC;YACxC,iBAAiB,EAAE,CAAC;YACpB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;IAED,+BAA+B;IAC/B,IAAI,CAAC,kBAAkB,IAAI,CAAC,kBAAkB,CAAC,IAAI,EAAE,EAAE,CAAC;QACtD,YAAY,CAAC,uCAAuC,CAAC,CAAC;QACtD,OAAO,CAAC,GAAG,CAAC,4DAA4D,CAAC,CAAC;QAC1E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,iBAAiB;IACjB,MAAM,OAAO,GAAG,aAAa,CAAC,kBAAkB,CAAC,IAAI,EAAE,EAAE,SAAS,CAAC,CAAC;IAEpE,IAAI,CAAC;QACH,0BAA0B;QAC1B,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC,CAAC;QAE7C,mBAAmB;QACnB,IAAI,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;QACxC,IAAI,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;QAEtC,IAAI,CAAC;YACH,YAAY,GAAG,MAAM,mBAAmB,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAC7D,WAAW,GAAG,kBAAkB,CAAC,YAAY,CAAC,CAAC;QACjD,CAAC;QAAC,MAAM,CAAC;YACP,mDAAmD;QACrD,CAAC;QAED,eAAe,CAAC,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC;QAEpE,eAAe;QACf,IAAI,CAAC;YACH,MAAM,WAAW,CAAC,OAAO,CAAC,CAAC;QAC7B,CAAC;QAAC,MAAM,CAAC;YACP,6BAA6B;QAC/B,CAAC;QAED,uBAAuB;QACvB,MAAM,aAAa,GAAG,MAAM,oBAAoB,EAAE,CAAC;QACnD,MAAM,YAAY,GAAG,kBAAkB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACxD,MAAM,OAAO,GAAG,iBAAiB,CAAC,OAAO,EAAE,aAAa,EAAE,YAAY,CAAC,CAAC;QACxE,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAErB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,SAAS,CAAC,mBAAmB,EAAE,CAAC;YACnD,YAAY,CAAC,gDAAgD,CAAC,CAAC;YAC/D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,IAAI,KAAK,YAAY,SAAS,CAAC,cAAc,EAAE,CAAC;YAC9C,YAAY,CAAC,4CAA4C,CAAC,CAAC;YAC3D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,IAAI,KAAK,YAAY,SAAS,CAAC,kBAAkB,EAAE,CAAC;YAClD,YAAY,CAAC,iDAAiD,CAAC,CAAC;YAChE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,IAAI,KAAK,YAAY,SAAS,CAAC,QAAQ,EAAE,CAAC;YACxC,YAAY,CAAC,cAAc,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YAC5C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { Turn } from '../types.js';
2
+ export interface MessageArray {
3
+ messages: Array<{
4
+ role: 'user' | 'assistant';
5
+ content: string;
6
+ }>;
7
+ rollingSummary?: string;
8
+ }
9
+ export declare function buildMessageArray(turns: Turn[], rollingSummary?: string): MessageArray;
10
+ export declare function estimateTokens(systemPrompt: string, messages: Array<{
11
+ role: 'user' | 'assistant';
12
+ content: string;
13
+ }>): number;
14
+ export declare function shouldSummarize(estimatedTokens: number): boolean;
15
+ export declare function summarizeOldTurns(turns: Turn[], existingSummary?: string): Promise<{
16
+ summary: string;
17
+ recentTurns: Turn[];
18
+ }>;
19
+ export declare function checkAndSummarize(systemPrompt: string, turns: Turn[], existingSummary?: string): Promise<{
20
+ turns: Turn[];
21
+ summary?: string;
22
+ }>;
@@ -0,0 +1,73 @@
1
+ import { callLLM, getConversationModel, countTokens } from '../llm/client.js';
2
+ const TOKEN_THRESHOLD_RATIO = 0.8;
3
+ const MAX_CONTEXT_TOKENS = 200_000;
4
+ const TOKEN_THRESHOLD = MAX_CONTEXT_TOKENS * TOKEN_THRESHOLD_RATIO; // 160K
5
+ const CHARS_PER_TOKEN = 4;
6
+ const PRESERVE_RECENT_TURNS = 8;
7
+ const SUMMARY_SYSTEM_PROMPT = `You are a precise conversation summarizer. Compress the following debug session turns into a single paragraph that preserves:
8
+ - The developer's key hypotheses (stated and rejected)
9
+ - Observable behaviors (guessing without evidence, checking assumptions, etc.)
10
+ - The current state of understanding at the end of these turns
11
+ - Any files or code elements mentioned
12
+
13
+ Do not editorialize. Do not add new information. Output only the summary paragraph.`;
14
+ export function buildMessageArray(turns, rollingSummary) {
15
+ const messages = [];
16
+ if (rollingSummary) {
17
+ // Summary is implicitly available via system prompt, not as a message
18
+ }
19
+ for (const turn of turns) {
20
+ if (turn.question) {
21
+ messages.push({ role: 'assistant', content: turn.question });
22
+ }
23
+ if (turn.response) {
24
+ messages.push({ role: 'user', content: turn.response });
25
+ }
26
+ }
27
+ return { messages, rollingSummary };
28
+ }
29
+ export function estimateTokens(systemPrompt, messages) {
30
+ let totalChars = systemPrompt.length;
31
+ for (const msg of messages) {
32
+ totalChars += msg.content.length;
33
+ }
34
+ return Math.ceil(totalChars / CHARS_PER_TOKEN);
35
+ }
36
+ export function shouldSummarize(estimatedTokens) {
37
+ return estimatedTokens > TOKEN_THRESHOLD;
38
+ }
39
+ export async function summarizeOldTurns(turns, existingSummary) {
40
+ if (turns.length <= PRESERVE_RECENT_TURNS) {
41
+ return { summary: existingSummary ?? '', recentTurns: turns };
42
+ }
43
+ const oldTurns = turns.slice(0, -PRESERVE_RECENT_TURNS);
44
+ const recentTurns = turns.slice(-PRESERVE_RECENT_TURNS);
45
+ const turnsText = oldTurns
46
+ .map((t) => `Turn ${t.turnNumber}:\nQuestion: ${t.question}\nDeveloper: ${t.response}`)
47
+ .join('\n\n');
48
+ const toSummarize = existingSummary
49
+ ? `Previous summary: ${existingSummary}\n\nNew turns to incorporate:\n${turnsText}`
50
+ : turnsText;
51
+ const summary = await callLLM(SUMMARY_SYSTEM_PROMPT, [{ role: 'user', content: toSummarize }], getConversationModel());
52
+ return { summary, recentTurns };
53
+ }
54
+ export async function checkAndSummarize(systemPrompt, turns, existingSummary) {
55
+ const msgArray = buildMessageArray(turns, existingSummary);
56
+ const estimated = estimateTokens(systemPrompt, msgArray.messages);
57
+ if (!shouldSummarize(estimated)) {
58
+ return { turns, summary: existingSummary };
59
+ }
60
+ // Verify with exact count
61
+ try {
62
+ const exact = await countTokens(systemPrompt, msgArray.messages, getConversationModel());
63
+ if (exact <= TOKEN_THRESHOLD) {
64
+ return { turns, summary: existingSummary };
65
+ }
66
+ }
67
+ catch {
68
+ // If count_tokens fails, proceed with summarization based on estimate
69
+ }
70
+ const result = await summarizeOldTurns(turns, existingSummary);
71
+ return { turns: result.recentTurns, summary: result.summary };
72
+ }
73
+ //# sourceMappingURL=context-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-manager.js","sourceRoot":"","sources":["../../src/core/context-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { Session, SessionOutcome } from '../types.js';
2
+ export interface SessionLoopResult {
3
+ outcome: SessionOutcome;
4
+ }
5
+ export declare function runSessionLoop(session: Session): Promise<SessionLoopResult>;
@@ -0,0 +1,159 @@
1
+ import { createInterface } from 'node:readline/promises';
2
+ import { stdin, stdout } from 'node:process';
3
+ import { streamQuestion, getConversationModel, getModelForFile } from '../llm/client.js';
4
+ import { getSystemPrompt } from '../llm/prompts.js';
5
+ import { validateResponse, repromptIfInvalid, getFallbackQuestion } from '../llm/validator.js';
6
+ import { detectFilePaths, readCodeFile, formatCodeContext } from '../storage/file-reader.js';
7
+ import { addTurn, addCodeFile } from './session.js';
8
+ import { checkAndSummarize } from './context-manager.js';
9
+ import { selectQuote } from '../quotes/selector.js';
10
+ import { streamToTerminal, newLine, displaySessionHeader, displayDim, } from '../ui/renderer.js';
11
+ const EXIT_COMMANDS = new Set(['exit', 'quit']);
12
+ const SOLVE_COMMANDS = new Set(['solved', 'found it']);
13
+ export async function runSessionLoop(session) {
14
+ const rl = createInterface({ input: stdin, output: stdout });
15
+ const codeFiles = new Map();
16
+ let rollingSummary;
17
+ let lifelineOffered = false;
18
+ displaySessionHeader(session.intensity);
19
+ // Read initial code files from problem description
20
+ const initialPaths = detectFilePaths(session.problemDescription);
21
+ for (const filePath of initialPaths) {
22
+ const content = await readCodeFile(filePath);
23
+ if (content) {
24
+ codeFiles.set(filePath, content);
25
+ addCodeFile(session, filePath);
26
+ }
27
+ }
28
+ // Generate opening question
29
+ let model = initialPaths.length > 0 ? getModelForFile(initialPaths[0]) : getConversationModel();
30
+ const codeContext = formatCodeContext(codeFiles);
31
+ let systemPrompt = getSystemPrompt(session.intensity, session.problemDescription, codeContext || undefined, rollingSummary, 1);
32
+ const openingQuestion = await generateValidQuestion(systemPrompt, [{ role: 'user', content: session.problemDescription }], model, session.intensity);
33
+ newLine();
34
+ displayDim(`[Turn 1]`);
35
+ console.log(openingQuestion);
36
+ newLine();
37
+ // Add opening turn with empty response (to be filled when user responds)
38
+ let currentQuestion = openingQuestion;
39
+ try {
40
+ while (true) {
41
+ const input = await rl.question('> ');
42
+ const trimmed = input.trim();
43
+ if (!trimmed)
44
+ continue;
45
+ const lower = trimmed.toLowerCase();
46
+ // Check exit commands
47
+ if (EXIT_COMMANDS.has(lower)) {
48
+ addTurn(session, currentQuestion, '[exited]', model);
49
+ rl.close();
50
+ return { outcome: 'abandoned' };
51
+ }
52
+ // Check solve commands
53
+ if (SOLVE_COMMANDS.has(lower)) {
54
+ addTurn(session, currentQuestion, '[solved]', model);
55
+ rl.close();
56
+ return { outcome: 'solved' };
57
+ }
58
+ // Check "I give up"
59
+ if (lower === 'i give up') {
60
+ if (!lifelineOffered) {
61
+ lifelineOffered = true;
62
+ addTurn(session, currentQuestion, trimmed, model);
63
+ // Generate one lifeline question
64
+ const lifelinePrompt = getSystemPrompt(session.intensity, session.problemDescription, codeContext || undefined, rollingSummary, session.transcript.length + 1);
65
+ const messages = buildConversationMessages(session, trimmed);
66
+ const lifelineQ = await generateValidQuestion(lifelinePrompt, [...messages, { role: 'user', content: 'I give up. I cannot figure this out.' }], getConversationModel(), session.intensity);
67
+ newLine();
68
+ displayDim(`[Lifeline — one more question before you go]`);
69
+ console.log(lifelineQ);
70
+ newLine();
71
+ currentQuestion = lifelineQ;
72
+ continue;
73
+ }
74
+ else {
75
+ addTurn(session, currentQuestion, '[gave up]', model);
76
+ rl.close();
77
+ return { outcome: 'abandoned' };
78
+ }
79
+ }
80
+ // Normal response — process it
81
+ addTurn(session, currentQuestion, trimmed, model);
82
+ // Check for new file paths in response
83
+ const newPaths = detectFilePaths(trimmed);
84
+ for (const filePath of newPaths) {
85
+ if (!codeFiles.has(filePath)) {
86
+ const content = await readCodeFile(filePath);
87
+ if (content) {
88
+ codeFiles.set(filePath, content);
89
+ addCodeFile(session, filePath);
90
+ }
91
+ }
92
+ }
93
+ // Check context window
94
+ const contextResult = await checkAndSummarize(systemPrompt, session.transcript, rollingSummary);
95
+ rollingSummary = contextResult.summary;
96
+ // Select contextual quote
97
+ const quote = selectQuote(trimmed);
98
+ const suggestedQuoteText = quote ? quote.text : undefined;
99
+ // Build next turn
100
+ const turnNumber = session.transcript.length + 1;
101
+ const updatedCodeContext = formatCodeContext(codeFiles);
102
+ systemPrompt = getSystemPrompt(session.intensity, session.problemDescription, updatedCodeContext || undefined, rollingSummary, turnNumber, suggestedQuoteText);
103
+ // Determine model — Sonnet for first analysis of new files
104
+ model = newPaths.length > 0 ? getModelForFile(newPaths[0]) : getConversationModel();
105
+ const messages = buildConversationMessages(session);
106
+ const nextQuestion = await generateValidQuestion(systemPrompt, messages, model, session.intensity);
107
+ newLine();
108
+ displayDim(`[Turn ${turnNumber}]`);
109
+ console.log(nextQuestion);
110
+ newLine();
111
+ currentQuestion = nextQuestion;
112
+ }
113
+ }
114
+ catch (error) {
115
+ // Handle Ctrl+C / Ctrl+D / readline close
116
+ if (error.code === 'ERR_USE_AFTER_CLOSE') {
117
+ return { outcome: 'abandoned' };
118
+ }
119
+ rl.close();
120
+ return { outcome: 'abandoned' };
121
+ }
122
+ }
123
+ async function generateValidQuestion(systemPrompt, messages, model, intensity) {
124
+ let fullText = '';
125
+ await streamQuestion(systemPrompt, messages, model, {
126
+ onText: (text) => streamToTerminal(text),
127
+ onComplete: (text) => {
128
+ fullText = text;
129
+ },
130
+ onError: () => { },
131
+ });
132
+ // Clear the streamed output line
133
+ process.stdout.write('\r\x1b[K');
134
+ const validation = validateResponse(fullText);
135
+ if (validation.valid)
136
+ return fullText;
137
+ // Try reprompt
138
+ const reprompted = await repromptIfInvalid(systemPrompt, messages, fullText);
139
+ if (reprompted)
140
+ return reprompted;
141
+ // Fallback
142
+ return getFallbackQuestion(intensity);
143
+ }
144
+ function buildConversationMessages(session, currentResponse) {
145
+ const messages = [
146
+ { role: 'user', content: session.problemDescription },
147
+ ];
148
+ for (const turn of session.transcript) {
149
+ messages.push({ role: 'assistant', content: turn.question });
150
+ if (turn.response && !turn.response.startsWith('[')) {
151
+ messages.push({ role: 'user', content: turn.response });
152
+ }
153
+ }
154
+ if (currentResponse) {
155
+ messages.push({ role: 'user', content: currentResponse });
156
+ }
157
+ return messages;
158
+ }
159
+ //# sourceMappingURL=session-loop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-loop.js","sourceRoot":"","sources":["../../src/core/session-loop.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { Session, Intensity, SessionOutcome, Turn, SkillScores, BehaviorTag, Quote } from '../types.js';
2
+ export declare function createSession(problemDescription: string, intensity: Intensity): Session;
3
+ export declare function addTurn(session: Session, question: string, response: string, model: string, behaviorTags?: string[], quoteUsed?: Quote | null): Turn;
4
+ export declare function finalizeSession(session: Session, outcome: SessionOutcome, skillScores?: SkillScores, behaviorTags?: BehaviorTag[]): void;
5
+ export declare function addCodeFile(session: Session, filePath: string): void;