nex-level-code 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 (48) hide show
  1. package/dist/commands/doctor.d.ts +3 -0
  2. package/dist/commands/doctor.d.ts.map +1 -0
  3. package/dist/commands/doctor.js +164 -0
  4. package/dist/commands/doctor.js.map +1 -0
  5. package/dist/commands/install.d.ts +3 -0
  6. package/dist/commands/install.d.ts.map +1 -0
  7. package/dist/commands/install.js +31 -0
  8. package/dist/commands/install.js.map +1 -0
  9. package/dist/commands/mcp.d.ts +3 -0
  10. package/dist/commands/mcp.d.ts.map +1 -0
  11. package/dist/commands/mcp.js +56 -0
  12. package/dist/commands/mcp.js.map +1 -0
  13. package/dist/commands/status.d.ts +3 -0
  14. package/dist/commands/status.d.ts.map +1 -0
  15. package/dist/commands/status.js +142 -0
  16. package/dist/commands/status.js.map +1 -0
  17. package/dist/commands/uninstall.d.ts +3 -0
  18. package/dist/commands/uninstall.d.ts.map +1 -0
  19. package/dist/commands/uninstall.js +114 -0
  20. package/dist/commands/uninstall.js.map +1 -0
  21. package/dist/commands/update.d.ts +3 -0
  22. package/dist/commands/update.d.ts.map +1 -0
  23. package/dist/commands/update.js +26 -0
  24. package/dist/commands/update.js.map +1 -0
  25. package/dist/index.d.ts +3 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +23 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/mcp/server.d.ts +12 -0
  30. package/dist/mcp/server.d.ts.map +1 -0
  31. package/dist/mcp/server.js +270 -0
  32. package/dist/mcp/server.js.map +1 -0
  33. package/dist/registry/tools.d.ts +64 -0
  34. package/dist/registry/tools.d.ts.map +1 -0
  35. package/dist/registry/tools.js +212 -0
  36. package/dist/registry/tools.js.map +1 -0
  37. package/dist/services/install.d.ts +36 -0
  38. package/dist/services/install.d.ts.map +1 -0
  39. package/dist/services/install.js +371 -0
  40. package/dist/services/install.js.map +1 -0
  41. package/package.json +57 -0
  42. package/templates/hooks/dev-logger.js +252 -0
  43. package/templates/hooks/memory-check.js +64 -0
  44. package/templates/hooks/session-start.js +115 -0
  45. package/templates/rules/claude.md +27 -0
  46. package/templates/rules/cursor.md +27 -0
  47. package/templates/starters/MEMORY.md +23 -0
  48. package/templates/starters/session-handoff.md +19 -0
@@ -0,0 +1,252 @@
1
+ #!/usr/bin/env node
2
+ // NLC Development Logger — Installed by Nex Level Code (NLC)
3
+ // Fires on stop: reads latest exchange, asks a small LLM if a task was completed,
4
+ // logs to memory/YYYY-MM-DD.md.
5
+ // DO NOT EDIT — managed by NLC. Run `nlc update` to get latest version.
6
+
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+
10
+ // --- API Key Resolution ---
11
+ function resolveApiKey() {
12
+ if (process.env.ANTHROPIC_API_KEY) return process.env.ANTHROPIC_API_KEY;
13
+
14
+ const home = process.env.USERPROFILE || process.env.HOME || '';
15
+
16
+ // Check common config locations
17
+ const candidates = [
18
+ path.join(home, '.memory-mcp', 'config.json'),
19
+ path.join(home, '.nlc', 'config.json'),
20
+ ];
21
+ for (const p of candidates) {
22
+ if (fs.existsSync(p)) {
23
+ try {
24
+ const cfg = JSON.parse(fs.readFileSync(p, 'utf-8'));
25
+ if (cfg.apiKey) return cfg.apiKey;
26
+ } catch {}
27
+ }
28
+ }
29
+
30
+ // Check file-based key storage
31
+ const keyFiles = [
32
+ path.join(home, '.config', 'anthropic', 'api_key'),
33
+ path.join(home, '.anthropic', 'api_key'),
34
+ ];
35
+ for (const p of keyFiles) {
36
+ if (fs.existsSync(p)) return fs.readFileSync(p, 'utf-8').trim();
37
+ }
38
+
39
+ return null;
40
+ }
41
+
42
+ // --- LLM Call ---
43
+ async function callSmallModel(prompt, maxTokens = 256) {
44
+ const key = resolveApiKey();
45
+ if (!key) return null;
46
+
47
+ const response = await fetch('https://api.anthropic.com/v1/messages', {
48
+ method: 'POST',
49
+ headers: {
50
+ 'Content-Type': 'application/json',
51
+ 'x-api-key': key,
52
+ 'anthropic-version': '2023-06-01',
53
+ },
54
+ body: JSON.stringify({
55
+ model: 'claude-haiku-4-5-20251001',
56
+ max_tokens: maxTokens,
57
+ messages: [{ role: 'user', content: prompt }],
58
+ }),
59
+ });
60
+
61
+ if (!response.ok) return null;
62
+ const data = await response.json();
63
+ return data.content?.[0]?.text || null;
64
+ }
65
+
66
+ // --- Transcript Parsing ---
67
+ function getLatestExchange(transcriptPath, cursorLine) {
68
+ if (!fs.existsSync(transcriptPath)) return null;
69
+
70
+ const content = fs.readFileSync(transcriptPath, 'utf-8');
71
+ const lines = content.split('\n').filter(l => l.trim());
72
+ const newLines = lines.slice(cursorLine);
73
+
74
+ if (newLines.length === 0) return { text: '', hasToolUse: false, totalLines: lines.length };
75
+
76
+ const parts = [];
77
+ let hasToolUse = false;
78
+
79
+ for (const line of newLines) {
80
+ try {
81
+ const entry = JSON.parse(line);
82
+ if (entry.type === 'assistant') {
83
+ const msgContent = entry.message?.content;
84
+ if (typeof msgContent === 'string') {
85
+ parts.push(msgContent);
86
+ } else if (Array.isArray(msgContent)) {
87
+ for (const block of msgContent) {
88
+ if (block.type === 'text' && block.text) {
89
+ parts.push(block.text);
90
+ } else if (block.type === 'tool_use') {
91
+ hasToolUse = true;
92
+ const name = block.name || 'unknown';
93
+ const input = block.input || {};
94
+ if (name === 'Write' || name === 'Edit') {
95
+ parts.push(`[${name}: ${input.file_path || 'unknown file'}]`);
96
+ } else if (name === 'Bash') {
97
+ parts.push(`[Bash: ${(input.command || '').slice(0, 300)}]`);
98
+ } else {
99
+ parts.push(`[${name}: ${input.file_path || input.pattern || ''}]`);
100
+ }
101
+ }
102
+ }
103
+ }
104
+ } else if (entry.type === 'user') {
105
+ const msgContent = entry.message?.content;
106
+ const text = typeof msgContent === 'string'
107
+ ? msgContent
108
+ : Array.isArray(msgContent)
109
+ ? msgContent.filter(b => b.type === 'text').map(b => b.text).join(' ')
110
+ : '';
111
+ if (text) parts.push(`USER: ${text.slice(0, 500)}`);
112
+ }
113
+ } catch {}
114
+ }
115
+
116
+ return { text: parts.join('\n'), hasToolUse, totalLines: lines.length };
117
+ }
118
+
119
+ // --- Cursor (per-session progress tracking) ---
120
+ function getCursorPath(cwd) {
121
+ return path.join(cwd, '.memory', 'dev-cursor.json');
122
+ }
123
+
124
+ function getCursor(cwd, sessionId) {
125
+ const p = getCursorPath(cwd);
126
+ if (!fs.existsSync(p)) return 0;
127
+ try {
128
+ const data = JSON.parse(fs.readFileSync(p, 'utf-8'));
129
+ return data[sessionId] || 0;
130
+ } catch { return 0; }
131
+ }
132
+
133
+ function setCursor(cwd, sessionId, line) {
134
+ const p = getCursorPath(cwd);
135
+ const dir = path.dirname(p);
136
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
137
+
138
+ let data = {};
139
+ if (fs.existsSync(p)) {
140
+ try { data = JSON.parse(fs.readFileSync(p, 'utf-8')); } catch {}
141
+ }
142
+ data[sessionId] = line;
143
+ fs.writeFileSync(p, JSON.stringify(data, null, 2));
144
+ }
145
+
146
+ // --- Memory Directory ---
147
+ function cwdToProjectKey(cwd) {
148
+ return cwd
149
+ .replace(/\\/g, '-')
150
+ .replace(/\//g, '-')
151
+ .replace(/:/g, '-')
152
+ .replace(/_/g, '-')
153
+ .replace(/^([a-z])/, (m) => m.toUpperCase());
154
+ }
155
+
156
+ function getMemoryDir(cwd) {
157
+ const home = process.env.USERPROFILE || process.env.HOME || '';
158
+ const key = cwdToProjectKey(cwd);
159
+ const lowerKey = key.replace(/^([A-Z])/, (m) => m.toLowerCase());
160
+
161
+ const candidates = [
162
+ path.join(home, '.claude', 'projects', lowerKey, 'memory'),
163
+ path.join(home, '.claude', 'projects', key, 'memory'),
164
+ path.join(home, '.nlc', 'projects', lowerKey),
165
+ path.join(home, '.nlc', 'projects', key),
166
+ ];
167
+
168
+ for (const p of candidates) {
169
+ if (fs.existsSync(p)) return p;
170
+ }
171
+ return path.join(home, '.nlc', 'projects', lowerKey);
172
+ }
173
+
174
+ // --- Development Extraction Prompt ---
175
+ function buildDevPrompt(exchange) {
176
+ return `You are a development logger. Analyze the following exchange between a user and an AI coding assistant. Determine if a meaningful task was COMPLETED (not just started or discussed).
177
+
178
+ A "development" is: a feature implemented, bug fixed, config changed, infrastructure deployed, file created/modified with purpose, tool/dependency installed, test written, document created, or script/automation built.
179
+
180
+ NOT a development: reading files, asking questions, explaining without acting, work in progress, minor acknowledgments, or planning without implementing.
181
+
182
+ If a development was completed, respond with ONLY 1-2 concise lines summarizing what was accomplished. Consolidate related steps. Start each line with an action verb. Be specific — include names, paths, or URLs where relevant.
183
+
184
+ If no development was completed, respond with exactly: NONE
185
+
186
+ --- EXCHANGE ---
187
+ ${exchange.slice(0, 6000)}`;
188
+ }
189
+
190
+ // --- Date/Time ---
191
+ function getDateStr() { return new Date().toISOString().split('T')[0]; }
192
+ function getTimeStr() { return new Date().toTimeString().slice(0, 5); }
193
+
194
+ // --- Main ---
195
+ async function main() {
196
+ try {
197
+ let input = '';
198
+ await new Promise((resolve) => {
199
+ process.stdin.setEncoding('utf-8');
200
+ process.stdin.on('data', (chunk) => input += chunk);
201
+ process.stdin.on('end', resolve);
202
+ setTimeout(resolve, 2000);
203
+ });
204
+
205
+ if (!input.trim()) return;
206
+ const hookData = JSON.parse(input);
207
+ const { session_id, transcript_path, cwd } = hookData;
208
+ if (!transcript_path || !cwd || !session_id) return;
209
+
210
+ const cursor = getCursor(cwd, session_id);
211
+ const result = getLatestExchange(transcript_path, cursor);
212
+ if (!result) return;
213
+
214
+ setCursor(cwd, session_id, result.totalLines);
215
+
216
+ // Pre-filter: skip trivially short responses with no tool usage
217
+ if (!result.hasToolUse && result.text.length < 100) return;
218
+
219
+ const prompt = buildDevPrompt(result.text);
220
+ const response = await callSmallModel(prompt);
221
+ if (!response || response.trim() === 'NONE' || response.trim().startsWith('NONE')) return;
222
+
223
+ // Log to memory directory
224
+ const memoryDir = getMemoryDir(cwd);
225
+ if (!fs.existsSync(memoryDir)) fs.mkdirSync(memoryDir, { recursive: true });
226
+
227
+ const dateStr = getDateStr();
228
+ const timeStr = getTimeStr();
229
+ const logFile = path.join(memoryDir, `${dateStr}.md`);
230
+
231
+ let existing = '';
232
+ if (fs.existsSync(logFile)) {
233
+ existing = fs.readFileSync(logFile, 'utf-8');
234
+ } else {
235
+ existing = `# Developments — ${dateStr}\n\n`;
236
+ }
237
+
238
+ const devLines = response.trim().split('\n')
239
+ .map(l => l.trim())
240
+ .filter(l => l && l !== 'NONE');
241
+
242
+ if (devLines.length === 0) return;
243
+
244
+ const entries = devLines.map(line => `- **${timeStr}** — ${line}`).join('\n');
245
+ fs.writeFileSync(logFile, existing + entries + '\n');
246
+
247
+ } catch {
248
+ // Silent failure — never disrupt the agent's work
249
+ }
250
+ }
251
+
252
+ main();
@@ -0,0 +1,64 @@
1
+ // NLC Memory Check Hook — Installed by Nex Level Code (NLC)
2
+ // Fires on user prompt: warns if session-handoff.md is stale (>1 hour).
3
+ // DO NOT EDIT — managed by NLC. Run `nlc update` to get latest version.
4
+
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+
8
+ function getHome() {
9
+ return process.env.USERPROFILE || process.env.HOME || '';
10
+ }
11
+
12
+ function cwdToProjectKey(cwd) {
13
+ return cwd
14
+ .replace(/\\/g, '-')
15
+ .replace(/\//g, '-')
16
+ .replace(/:/g, '-')
17
+ .replace(/_/g, '-')
18
+ .replace(/^([a-z])/, (m) => m.toUpperCase());
19
+ }
20
+
21
+ function findHandoff(cwd) {
22
+ const home = getHome();
23
+ const key = cwdToProjectKey(cwd);
24
+ const lowerKey = key.replace(/^([A-Z])/, (m) => m.toLowerCase());
25
+
26
+ const candidates = [
27
+ path.join(home, '.claude', 'projects', lowerKey, 'memory', 'session-handoff.md'),
28
+ path.join(home, '.claude', 'projects', key, 'memory', 'session-handoff.md'),
29
+ path.join(home, '.nlc', 'projects', lowerKey, 'session-handoff.md'),
30
+ path.join(home, '.nlc', 'projects', key, 'session-handoff.md'),
31
+ ];
32
+
33
+ for (const p of candidates) {
34
+ if (fs.existsSync(p)) return p;
35
+ }
36
+ return null;
37
+ }
38
+
39
+ let input = '';
40
+ process.stdin.setEncoding('utf8');
41
+ process.stdin.on('data', (chunk) => { input += chunk; });
42
+ process.stdin.on('end', () => {
43
+ try {
44
+ const hookData = JSON.parse(input);
45
+ const cwd = hookData.cwd || hookData.session_cwd || process.cwd();
46
+ const handoffPath = findHandoff(cwd);
47
+
48
+ if (!handoffPath) process.exit(0);
49
+
50
+ const stats = fs.statSync(handoffPath);
51
+ const ageMs = Date.now() - stats.mtimeMs;
52
+ const ageHours = ageMs / (1000 * 60 * 60);
53
+
54
+ if (ageHours > 1) {
55
+ const hours = Math.floor(ageHours);
56
+ console.log(
57
+ `[NLC MEMORY] session-handoff.md was last updated ${hours} hour${hours !== 1 ? 's' : ''} ago. ` +
58
+ `Update it with current session progress. Also update MEMORY.md if setup state has changed.`
59
+ );
60
+ }
61
+ } catch (e) {
62
+ process.exit(0);
63
+ }
64
+ });
@@ -0,0 +1,115 @@
1
+ // NLC Session Start Hook — Installed by Nex Level Code (NLC)
2
+ // Fires on session start: injects memory protocol + recent development logs.
3
+ // DO NOT EDIT — managed by NLC. Run `nlc update` to get latest version.
4
+
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+
8
+ // --- NLC Config (set during install) ---
9
+ const NLC_TOOL = '{{TOOL_ID}}'; // e.g., 'claude', 'cursor'
10
+
11
+ // --- Path Resolution ---
12
+ function getHome() {
13
+ return process.env.USERPROFILE || process.env.HOME || '';
14
+ }
15
+
16
+ function cwdToProjectKey(cwd) {
17
+ return cwd
18
+ .replace(/\\/g, '-')
19
+ .replace(/\//g, '-')
20
+ .replace(/:/g, '-')
21
+ .replace(/_/g, '-')
22
+ .replace(/^([a-z])/, (m) => m.toUpperCase());
23
+ }
24
+
25
+ function getMemoryDir(cwd) {
26
+ const home = getHome();
27
+ const key = cwdToProjectKey(cwd);
28
+ const lowerKey = key.replace(/^([A-Z])/, (m) => m.toLowerCase());
29
+
30
+ // Try multiple paths (tools use different directory structures)
31
+ const candidates = [
32
+ path.join(home, '.claude', 'projects', lowerKey, 'memory'),
33
+ path.join(home, '.claude', 'projects', key, 'memory'),
34
+ path.join(home, '.nlc', 'projects', lowerKey),
35
+ path.join(home, '.nlc', 'projects', key),
36
+ ];
37
+
38
+ for (const p of candidates) {
39
+ if (fs.existsSync(p)) return p;
40
+ }
41
+ // Default: NLC's own directory
42
+ return path.join(home, '.nlc', 'projects', lowerKey);
43
+ }
44
+
45
+ function findHandoff(cwd) {
46
+ const memoryDir = getMemoryDir(cwd);
47
+ const handoffPath = path.join(memoryDir, 'session-handoff.md');
48
+ if (fs.existsSync(handoffPath)) return handoffPath;
49
+ return null;
50
+ }
51
+
52
+ function getDateStr(daysAgo = 0) {
53
+ const d = new Date();
54
+ d.setDate(d.getDate() - daysAgo);
55
+ return d.toISOString().split('T')[0];
56
+ }
57
+
58
+ function readDevLog(cwd, dateStr) {
59
+ const memoryDir = getMemoryDir(cwd);
60
+ const logPath = path.join(memoryDir, `${dateStr}.md`);
61
+ if (!fs.existsSync(logPath)) return null;
62
+ try {
63
+ const content = fs.readFileSync(logPath, 'utf-8').trim();
64
+ if (content.split('\n').length <= 2) return null;
65
+ return content;
66
+ } catch { return null; }
67
+ }
68
+
69
+ // --- Main ---
70
+ let input = '';
71
+ process.stdin.setEncoding('utf8');
72
+ process.stdin.on('data', (chunk) => { input += chunk; });
73
+ process.stdin.on('end', () => {
74
+ try {
75
+ const hookData = JSON.parse(input);
76
+ const cwd = hookData.cwd || hookData.session_cwd || process.cwd();
77
+
78
+ const parts = [];
79
+
80
+ // Memory Protocol Reminder
81
+ const handoffPath = findHandoff(cwd);
82
+ if (handoffPath) {
83
+ const stats = fs.statSync(handoffPath);
84
+ const ageHours = (Date.now() - stats.mtimeMs) / (1000 * 60 * 60);
85
+ const staleNote = ageHours > 2
86
+ ? ` WARNING: It was last updated ${Math.floor(ageHours)} hours ago and may be stale.`
87
+ : '';
88
+
89
+ parts.push(
90
+ `[NLC MEMORY PROTOCOL] This project uses session-handoff.md for cross-session context.${staleNote} ` +
91
+ `You MUST update session-handoff.md and MEMORY.md incrementally as you complete tasks — ` +
92
+ `do NOT wait until the end of the session. Context can be lost at any time.`
93
+ );
94
+ }
95
+
96
+ // Recent Development Logs
97
+ const today = getDateStr(0);
98
+ const yesterday = getDateStr(1);
99
+ const todayLog = readDevLog(cwd, today);
100
+ const yesterdayLog = readDevLog(cwd, yesterday);
101
+
102
+ if (yesterdayLog || todayLog) {
103
+ parts.push('');
104
+ parts.push('[NLC RECENT DEVELOPMENTS] Auto-logged task completions:');
105
+ if (yesterdayLog) { parts.push(''); parts.push(yesterdayLog); }
106
+ if (todayLog) { parts.push(''); parts.push(todayLog); }
107
+ }
108
+
109
+ if (parts.length > 0) {
110
+ console.log(parts.join('\n'));
111
+ }
112
+ } catch (e) {
113
+ process.exit(0);
114
+ }
115
+ });
@@ -0,0 +1,27 @@
1
+ # Agent Rules — Installed by Nex Level Code (NLC)
2
+
3
+ ## Memory Protocol (Non-Negotiable)
4
+ 1. **Read session-handoff.md at the start of every session** — it contains cross-session context
5
+ 2. **Update session-handoff.md incrementally as you work** — do NOT wait until the end
6
+ 3. **Update MEMORY.md when setup state changes** — new tools, configs, environment changes
7
+ 4. **Never lose context** — previous sessions have lost all context by failing to write to memory. This is the #1 problem NLC solves.
8
+
9
+ ## Behavioral Rules
10
+ 1. **Never ask for permission to READ anything** — files, URLs, documentation. Just read it.
11
+ 2. **ALWAYS ask the user before EDIT or WRITE** — confirm in conversation before modifying files
12
+ 3. **Log every user decision** — when the user makes a decision, note it in session-handoff.md
13
+ 4. **End responses with a helpful question** — keep the conversation moving forward
14
+
15
+ ## Workflow
16
+ - Follow the PREVC method for non-trivial tasks:
17
+ - **P**lanning → **R**eview → **E**xecution → **V**alidation → **C**onfirmation
18
+ - QUICK (bug fix): E → V
19
+ - SMALL (simple feature): P → E → V
20
+ - MEDIUM (standard feature): P → R → E → V
21
+ - LARGE (complex system): P → R → E → V → C
22
+
23
+ ## Communication Style
24
+ - Be concise and direct
25
+ - Use plain language — avoid jargon unless the user uses it first
26
+ - When presenting options, clearly distinguish proper solutions from workarounds
27
+ - Never make confident assertions about things you're uncertain about
@@ -0,0 +1,27 @@
1
+ # Agent Rules — Installed by Nex Level Code (NLC)
2
+
3
+ ## Memory Protocol (Non-Negotiable)
4
+ 1. Read session-handoff.md at the start of every session — it contains cross-session context
5
+ 2. Update session-handoff.md incrementally as you work — do NOT wait until the end
6
+ 3. Update MEMORY.md when setup state changes — new tools, configs, environment changes
7
+ 4. Never lose context — previous sessions have lost all context by failing to write to memory
8
+
9
+ ## Behavioral Rules
10
+ 1. Never ask for permission to READ anything — files, URLs, documentation. Just read it.
11
+ 2. ALWAYS ask the user before EDIT or WRITE — confirm in conversation before modifying files
12
+ 3. Log every user decision — when the user makes a decision, note it in session-handoff.md
13
+ 4. End responses with a helpful question — keep the conversation moving forward
14
+
15
+ ## Workflow
16
+ - Follow the PREVC method for non-trivial tasks:
17
+ - Planning → Review → Execution → Validation → Confirmation
18
+ - QUICK (bug fix): E → V
19
+ - SMALL (simple feature): P → E → V
20
+ - MEDIUM (standard feature): P → R → E → V
21
+ - LARGE (complex system): P → R → E → V → C
22
+
23
+ ## Communication Style
24
+ - Be concise and direct
25
+ - Use plain language — avoid jargon unless the user uses it first
26
+ - When presenting options, clearly distinguish proper solutions from workarounds
27
+ - Never make confident assertions about things you're uncertain about
@@ -0,0 +1,23 @@
1
+ # Project Memory
2
+
3
+ > Managed by Nex Level Code (NLC). This file persists across sessions.
4
+ > Keep it concise — lines after 200 will be truncated in context.
5
+
6
+ ## Project Overview
7
+ <!-- What is this project? Tech stack? Main directories? -->
8
+
9
+ ## Key Documents
10
+ <!-- Important files the agent should read before making changes. -->
11
+
12
+ ## User Requirements
13
+ <!-- Non-negotiable rules the user has established. -->
14
+
15
+ ## User Preferences
16
+ <!-- How the user likes to work. Communication style. Tool preferences. -->
17
+
18
+ ## Current Setup State
19
+ <!-- What's installed, configured, and running. Update as things change. -->
20
+
21
+ ## Session Protocol
22
+ - **START**: Read `session-handoff.md` first
23
+ - **END**: Update `session-handoff.md` with what was done + what's next
@@ -0,0 +1,19 @@
1
+ # Session Handoff
2
+
3
+ > Updated by: [agent] | Last updated: [date]
4
+ > This file is your cross-session memory. Update it as you work — don't wait until the end.
5
+
6
+ ## What's Working
7
+ <!-- List things that are confirmed working. Remove items that break. -->
8
+
9
+ ## Known Issues
10
+ <!-- Active bugs, blockers, or things that need fixing. Remove when resolved. -->
11
+
12
+ ## Key Decisions
13
+ <!-- Important decisions made by the user. Include reasoning. -->
14
+
15
+ ## Current Focus
16
+ <!-- What the user is working on right now. What's next. -->
17
+
18
+ ## Environment
19
+ <!-- OS, tools, versions, paths — anything the next session needs to know. -->