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.
- package/LICENSE +21 -0
- package/README.md +176 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +36 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/debug.d.ts +2 -0
- package/dist/commands/debug.js +80 -0
- package/dist/commands/debug.js.map +1 -0
- package/dist/core/context-manager.d.ts +22 -0
- package/dist/core/context-manager.js +73 -0
- package/dist/core/context-manager.js.map +1 -0
- package/dist/core/session-loop.d.ts +5 -0
- package/dist/core/session-loop.js +159 -0
- package/dist/core/session-loop.js.map +1 -0
- package/dist/core/session.d.ts +5 -0
- package/dist/core/session.js +46 -0
- package/dist/core/session.js.map +1 -0
- package/dist/llm/client.d.ts +25 -0
- package/dist/llm/client.js +81 -0
- package/dist/llm/client.js.map +1 -0
- package/dist/llm/prompts.d.ts +2 -0
- package/dist/llm/prompts.js +69 -0
- package/dist/llm/prompts.js.map +1 -0
- package/dist/llm/validator.d.ts +10 -0
- package/dist/llm/validator.js +64 -0
- package/dist/llm/validator.js.map +1 -0
- package/dist/quotes/corpus.d.ts +2 -0
- package/dist/quotes/corpus.js +42 -0
- package/dist/quotes/corpus.js.map +1 -0
- package/dist/quotes/selector.d.ts +4 -0
- package/dist/quotes/selector.js +59 -0
- package/dist/quotes/selector.js.map +1 -0
- package/dist/scoring/behavior-tagger.d.ts +2 -0
- package/dist/scoring/behavior-tagger.js +56 -0
- package/dist/scoring/behavior-tagger.js.map +1 -0
- package/dist/scoring/growth-profile.d.ts +3 -0
- package/dist/scoring/growth-profile.js +58 -0
- package/dist/scoring/growth-profile.js.map +1 -0
- package/dist/scoring/skill-scorer.d.ts +2 -0
- package/dist/scoring/skill-scorer.js +38 -0
- package/dist/scoring/skill-scorer.js.map +1 -0
- package/dist/storage/file-reader.d.ts +3 -0
- package/dist/storage/file-reader.js +60 -0
- package/dist/storage/file-reader.js.map +1 -0
- package/dist/storage/session-store.d.ts +5 -0
- package/dist/storage/session-store.js +72 -0
- package/dist/storage/session-store.js.map +1 -0
- package/dist/types.d.ts +56 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/ui/growth-display.d.ts +2 -0
- package/dist/ui/growth-display.js +69 -0
- package/dist/ui/growth-display.js.map +1 -0
- package/dist/ui/renderer.d.ts +12 -0
- package/dist/ui/renderer.js +54 -0
- package/dist/ui/renderer.js.map +1 -0
- 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
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
|
package/dist/cli.js.map
ADDED
|
@@ -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,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":"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"}
|
|
@@ -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,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;
|