lynkr 3.2.1 → 4.0.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/README.md +770 -25
- package/ROUTER_COMPARISON.md +173 -0
- package/TIER_ROUTING_PLAN.md +771 -0
- package/docs/GSD_LEARNINGS.md +1116 -0
- package/docs/LOCAL_EMBEDDINGS_PLAN.md +1024 -0
- package/docs/index.md +49 -5
- package/final-test.js +33 -0
- package/package.json +2 -2
- package/src/api/openai-router.js +755 -0
- package/src/api/router.js +4 -0
- package/src/clients/bedrock-utils.js +298 -0
- package/src/clients/databricks.js +265 -0
- package/src/clients/databricks.js.backup +1036 -0
- package/src/clients/openai-format.js +393 -0
- package/src/clients/routing.js +12 -0
- package/src/config/index.js +55 -3
- package/src/orchestrator/index.js +8 -1
- package/src/tools/smart-selection.js +1 -1
- package/test/bedrock-integration.test.js +471 -0
- package/test/cursor-integration.test.js +484 -0
- package/test/llamacpp-integration.test.js +13 -34
- package/test/lmstudio-integration.test.js +335 -0
|
@@ -0,0 +1,1116 @@
|
|
|
1
|
+
# Learnings from GET SHIT DONE Repository
|
|
2
|
+
|
|
3
|
+
**Source:** https://github.com/glittercowboy/get-shit-done
|
|
4
|
+
**Analyzed:** 2026-01-11
|
|
5
|
+
**Stars:** 1.4k | **Forks:** 191
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Executive Summary
|
|
10
|
+
|
|
11
|
+
GET SHIT DONE (GSD) is a meta-prompting system for Claude Code that prevents quality degradation through structured workflows and fresh context management. Key innovation: **breaking work into atomic tasks executed by fresh subagents** rather than long single-session conversations.
|
|
12
|
+
|
|
13
|
+
**What Lynkr does better:**
|
|
14
|
+
- ✅ Multi-provider support (9 providers vs Claude-only)
|
|
15
|
+
- ✅ Token optimization (60-80% reduction via caching, tool selection)
|
|
16
|
+
- ✅ IDE integration (Cursor support, OpenAI API compatibility)
|
|
17
|
+
- ✅ Hybrid routing (local + cloud fallback)
|
|
18
|
+
|
|
19
|
+
**What GSD does better:**
|
|
20
|
+
- ✅ Context management (fresh agents per task)
|
|
21
|
+
- ✅ Atomic task planning (enforced 2-3 task breakdown)
|
|
22
|
+
- ✅ Codebase mapping (7-document parallel analysis)
|
|
23
|
+
- ✅ Git integration (automatic commits per task)
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Key Patterns & Missing Features
|
|
28
|
+
|
|
29
|
+
### 1. Fresh Context Per Task (Multi-Agent Pattern)
|
|
30
|
+
|
|
31
|
+
#### Problem Statement
|
|
32
|
+
**Current behavior in Lynkr:**
|
|
33
|
+
- Single session conversation grows indefinitely
|
|
34
|
+
- As context fills (150k+ tokens), quality degrades
|
|
35
|
+
- Claude starts saying "Due to context limits, I'll be more concise..."
|
|
36
|
+
- User must manually start new session
|
|
37
|
+
|
|
38
|
+
**GSD's solution:**
|
|
39
|
+
- Each task spawns fresh subagent with 200k clean tokens
|
|
40
|
+
- Essential state passed as summary
|
|
41
|
+
- Prevents context pollution
|
|
42
|
+
- Maintains consistent quality
|
|
43
|
+
|
|
44
|
+
#### Implementation Plan
|
|
45
|
+
|
|
46
|
+
**Files to modify:**
|
|
47
|
+
- `src/orchestrator/index.js` - Add context monitoring
|
|
48
|
+
- `src/agents/index.js` - Add task-based spawning
|
|
49
|
+
- `src/config/index.js` - Add context thresholds
|
|
50
|
+
|
|
51
|
+
**Configuration additions:**
|
|
52
|
+
```javascript
|
|
53
|
+
// .env
|
|
54
|
+
CONTEXT_BUDGET_WARNING=150000 # Warn at 150k tokens
|
|
55
|
+
CONTEXT_BUDGET_MAX=180000 # Hard limit at 180k tokens
|
|
56
|
+
AUTO_SPAWN_SUBAGENT=false # Auto-spawn when hitting limit (default: false)
|
|
57
|
+
SUBAGENT_CONTEXT_SUMMARY_LENGTH=5000 # Summary size for new agent
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Code implementation:**
|
|
61
|
+
```javascript
|
|
62
|
+
// src/orchestrator/index.js (after line 1500)
|
|
63
|
+
|
|
64
|
+
async function checkContextBudget(session, estimatedTokens) {
|
|
65
|
+
const warningThreshold = config.CONTEXT_BUDGET_WARNING || 150000;
|
|
66
|
+
const maxThreshold = config.CONTEXT_BUDGET_MAX || 180000;
|
|
67
|
+
|
|
68
|
+
if (estimatedTokens > warningThreshold) {
|
|
69
|
+
const utilization = Math.round(estimatedTokens / maxThreshold * 100);
|
|
70
|
+
|
|
71
|
+
logger.warn({
|
|
72
|
+
estimatedTokens,
|
|
73
|
+
warningThreshold,
|
|
74
|
+
maxThreshold,
|
|
75
|
+
utilization: `${utilization}%`
|
|
76
|
+
}, "⚠️ Context approaching budget limit");
|
|
77
|
+
|
|
78
|
+
// Option A: Auto-spawn (aggressive)
|
|
79
|
+
if (config.AUTO_SPAWN_SUBAGENT && estimatedTokens > maxThreshold) {
|
|
80
|
+
return await spawnFreshSubagent(session);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Option B: Inject warning (conservative)
|
|
84
|
+
return {
|
|
85
|
+
warning: true,
|
|
86
|
+
message: `Context budget at ${utilization}%. Consider:
|
|
87
|
+
1. Breaking current task into smaller subtasks
|
|
88
|
+
2. Completing current work before starting new tasks
|
|
89
|
+
3. Starting fresh session for complex work`
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return { warning: false };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function spawnFreshSubagent(session) {
|
|
97
|
+
const summary = await summarizeSessionState(session, {
|
|
98
|
+
maxLength: config.SUBAGENT_CONTEXT_SUMMARY_LENGTH || 5000,
|
|
99
|
+
include: {
|
|
100
|
+
currentTask: true,
|
|
101
|
+
recentDecisions: true,
|
|
102
|
+
activeFiles: true,
|
|
103
|
+
blockers: true
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
logger.info({
|
|
108
|
+
parentSession: session.id,
|
|
109
|
+
summaryTokens: summary.tokens
|
|
110
|
+
}, "Spawning fresh subagent with summarized context");
|
|
111
|
+
|
|
112
|
+
const { executeAgent } = require("./agents");
|
|
113
|
+
return await executeAgent({
|
|
114
|
+
agentType: "general-purpose",
|
|
115
|
+
task: summary.currentTask,
|
|
116
|
+
context: summary.text,
|
|
117
|
+
maxSteps: 15,
|
|
118
|
+
parentSession: session.id
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function summarizeSessionState(session, options = {}) {
|
|
123
|
+
const recentTurns = session.messages.slice(-10); // Last 10 turns
|
|
124
|
+
const currentFiles = extractActiveFiles(session.messages);
|
|
125
|
+
const decisions = extractDecisions(session.messages);
|
|
126
|
+
const blockers = extractBlockers(session.messages);
|
|
127
|
+
|
|
128
|
+
const summary = `
|
|
129
|
+
<session-summary>
|
|
130
|
+
<current-task>
|
|
131
|
+
${extractCurrentTask(recentTurns)}
|
|
132
|
+
</current-task>
|
|
133
|
+
|
|
134
|
+
<active-files>
|
|
135
|
+
${currentFiles.map(f => `<file>${f}</file>`).join('\n ')}
|
|
136
|
+
</active-files>
|
|
137
|
+
|
|
138
|
+
<recent-decisions>
|
|
139
|
+
${decisions.map(d => `<decision>${d}</decision>`).join('\n ')}
|
|
140
|
+
</recent-decisions>
|
|
141
|
+
|
|
142
|
+
<blockers>
|
|
143
|
+
${blockers.map(b => `<blocker>${b}</blocker>`).join('\n ')}
|
|
144
|
+
</blockers>
|
|
145
|
+
|
|
146
|
+
<next-steps>
|
|
147
|
+
${suggestNextSteps(recentTurns)}
|
|
148
|
+
</next-steps>
|
|
149
|
+
</session-summary>`;
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
text: summary,
|
|
153
|
+
tokens: estimateTokens(summary)
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Priority:** HIGH
|
|
159
|
+
**Difficulty:** 5/10 (medium)
|
|
160
|
+
**Effort:** 4-6 hours
|
|
161
|
+
**Value:** Prevents quality degradation in long sessions
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
### 2. Atomic Task Planning (Enforced Breakdown)
|
|
166
|
+
|
|
167
|
+
#### Problem Statement
|
|
168
|
+
**Current behavior in Lynkr:**
|
|
169
|
+
- TodoWrite tool exists but not enforced
|
|
170
|
+
- Users can request complex multi-step tasks
|
|
171
|
+
- No automatic breakdown into atomic units
|
|
172
|
+
- No verification of task completeness
|
|
173
|
+
|
|
174
|
+
**GSD's solution:**
|
|
175
|
+
- Enforces 2-3 task maximum per phase
|
|
176
|
+
- Each task has clear completion criteria
|
|
177
|
+
- Tasks are atomic (single focus, discrete)
|
|
178
|
+
- XML-structured for precision
|
|
179
|
+
|
|
180
|
+
#### Implementation Plan
|
|
181
|
+
|
|
182
|
+
**Files to modify:**
|
|
183
|
+
- `src/orchestrator/index.js` - Add task complexity detection
|
|
184
|
+
- `src/utils/task-planner.js` - New file for atomic planning
|
|
185
|
+
- `src/prompts/task-breakdown.js` - Task breakdown prompt
|
|
186
|
+
|
|
187
|
+
**Code implementation:**
|
|
188
|
+
```javascript
|
|
189
|
+
// src/utils/task-planner.js (NEW FILE)
|
|
190
|
+
|
|
191
|
+
const logger = require("../logger");
|
|
192
|
+
const { estimateTokenCount } = require("./tokens");
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Detect if user request is complex and needs breakdown
|
|
196
|
+
*/
|
|
197
|
+
function isComplexTask(userMessage) {
|
|
198
|
+
const complexityIndicators = [
|
|
199
|
+
// Multiple verbs
|
|
200
|
+
/\b(and|then|also|after|before)\b/gi,
|
|
201
|
+
// Multiple files mentioned
|
|
202
|
+
/\b(file|files|folder|folders|directory|directories)\b.*\b(file|files|folder|folders|directory|directories)\b/gi,
|
|
203
|
+
// Multiple actions
|
|
204
|
+
/\b(create|add|update|fix|refactor|implement|write)\b.*\b(create|add|update|fix|refactor|implement|write)\b/gi,
|
|
205
|
+
// Long descriptions (>200 words)
|
|
206
|
+
userMessage.split(/\s+/).length > 200,
|
|
207
|
+
// Multiple features/components
|
|
208
|
+
/\b(feature|component|module|service)\b.*\b(feature|component|module|service)\b/gi
|
|
209
|
+
];
|
|
210
|
+
|
|
211
|
+
return complexityIndicators.some(indicator =>
|
|
212
|
+
typeof indicator === 'boolean' ? indicator : indicator.test(userMessage)
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Generate atomic task breakdown using LLM
|
|
218
|
+
*/
|
|
219
|
+
async function generateAtomicTasks(userMessage, context = {}) {
|
|
220
|
+
const prompt = `
|
|
221
|
+
<task-breakdown-request>
|
|
222
|
+
<user-request>
|
|
223
|
+
${userMessage}
|
|
224
|
+
</user-request>
|
|
225
|
+
|
|
226
|
+
<context>
|
|
227
|
+
<current-files>
|
|
228
|
+
${context.files?.join(', ') || 'Unknown'}
|
|
229
|
+
</current-files>
|
|
230
|
+
<tech-stack>
|
|
231
|
+
${context.stack || 'Unknown'}
|
|
232
|
+
</tech-stack>
|
|
233
|
+
</context>
|
|
234
|
+
|
|
235
|
+
<instructions>
|
|
236
|
+
Break down the user's request into atomic tasks following these rules:
|
|
237
|
+
|
|
238
|
+
1. Each task must be ATOMIC (single focus, discrete action)
|
|
239
|
+
2. Maximum 2-3 tasks per phase
|
|
240
|
+
3. If more than 3 tasks needed, create multiple phases
|
|
241
|
+
4. Each task must have:
|
|
242
|
+
- Clear name (verb + object, e.g., "Add authentication middleware")
|
|
243
|
+
- Specific files to modify
|
|
244
|
+
- Concrete action description
|
|
245
|
+
- Verification steps
|
|
246
|
+
- Completion criteria
|
|
247
|
+
|
|
248
|
+
Return in XML format:
|
|
249
|
+
<task-plan>
|
|
250
|
+
<phase name="Phase 1 Name">
|
|
251
|
+
<task>
|
|
252
|
+
<name>Task name</name>
|
|
253
|
+
<files>file1.js, file2.js</files>
|
|
254
|
+
<action>What to do</action>
|
|
255
|
+
<verify>How to verify</verify>
|
|
256
|
+
<done>Completion criteria</done>
|
|
257
|
+
</task>
|
|
258
|
+
</phase>
|
|
259
|
+
</task-plan>
|
|
260
|
+
</instructions>
|
|
261
|
+
</task-breakdown-request>`;
|
|
262
|
+
|
|
263
|
+
// Call LLM to generate plan (use fast model like Haiku)
|
|
264
|
+
const response = await invokeModel({
|
|
265
|
+
model: "haiku", // Fast, cheap for planning
|
|
266
|
+
messages: [{ role: "user", content: prompt }],
|
|
267
|
+
max_tokens: 2000
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
return parseTaskPlan(response.content);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function parseTaskPlan(xmlContent) {
|
|
274
|
+
// Parse XML into structured task plan
|
|
275
|
+
// Returns: { phases: [{ name, tasks: [{ name, files, action, verify, done }] }] }
|
|
276
|
+
// Implementation uses XML parser or regex extraction
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
module.exports = {
|
|
280
|
+
isComplexTask,
|
|
281
|
+
generateAtomicTasks
|
|
282
|
+
};
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
**Integration in orchestrator:**
|
|
286
|
+
```javascript
|
|
287
|
+
// src/orchestrator/index.js
|
|
288
|
+
|
|
289
|
+
const { isComplexTask, generateAtomicTasks } = require("../utils/task-planner");
|
|
290
|
+
|
|
291
|
+
async function processMessage({ payload, headers, session, options }) {
|
|
292
|
+
const userMessage = payload.messages[payload.messages.length - 1].content;
|
|
293
|
+
|
|
294
|
+
// Check if task is complex
|
|
295
|
+
if (isComplexTask(userMessage)) {
|
|
296
|
+
logger.info("Detected complex task, suggesting atomic breakdown");
|
|
297
|
+
|
|
298
|
+
// Generate task plan
|
|
299
|
+
const taskPlan = await generateAtomicTasks(userMessage, {
|
|
300
|
+
files: extractFilesFromContext(session),
|
|
301
|
+
stack: detectTechStack(session)
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
// Present to user for approval
|
|
305
|
+
const approval = await askUserApproval({
|
|
306
|
+
message: "This looks complex! I suggest breaking it into these atomic tasks:",
|
|
307
|
+
plan: taskPlan,
|
|
308
|
+
options: [
|
|
309
|
+
{ label: "Approve & execute", value: "approve" },
|
|
310
|
+
{ label: "Modify plan", value: "modify" },
|
|
311
|
+
{ label: "Execute without planning", value: "skip" }
|
|
312
|
+
]
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
if (approval === "approve") {
|
|
316
|
+
// Execute tasks one by one with fresh context
|
|
317
|
+
return await executeAtomicTasks(taskPlan, session);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Continue with normal execution
|
|
322
|
+
// ...
|
|
323
|
+
}
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
**Priority:** MEDIUM
|
|
327
|
+
**Difficulty:** 6/10 (medium-high)
|
|
328
|
+
**Effort:** 6-8 hours
|
|
329
|
+
**Value:** Improves success rate for complex requests
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
### 3. XML-Structured Prompts (Better Parsing)
|
|
334
|
+
|
|
335
|
+
#### Problem Statement
|
|
336
|
+
**Current behavior in Lynkr:**
|
|
337
|
+
- All tool parameters use JSON
|
|
338
|
+
- Natural language instructions in system prompts
|
|
339
|
+
- Can be ambiguous with nested structures
|
|
340
|
+
|
|
341
|
+
**GSD's solution:**
|
|
342
|
+
- Uses XML for semantic structure
|
|
343
|
+
- Claude parses XML more reliably than JSON
|
|
344
|
+
- Tags provide meaning (e.g., `<verify>`, `<done>`)
|
|
345
|
+
|
|
346
|
+
#### Implementation Plan
|
|
347
|
+
|
|
348
|
+
**Use cases for XML in Lynkr:**
|
|
349
|
+
1. Complex tool calls with nested parameters
|
|
350
|
+
2. Task definitions in TodoWrite
|
|
351
|
+
3. Memory injection format
|
|
352
|
+
4. State summaries for subagents
|
|
353
|
+
|
|
354
|
+
**Example conversion:**
|
|
355
|
+
|
|
356
|
+
**Current (JSON):**
|
|
357
|
+
```json
|
|
358
|
+
{
|
|
359
|
+
"todos": [
|
|
360
|
+
{
|
|
361
|
+
"content": "Add authentication",
|
|
362
|
+
"status": "pending",
|
|
363
|
+
"activeForm": "Adding authentication"
|
|
364
|
+
}
|
|
365
|
+
]
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
**Proposed (XML):**
|
|
370
|
+
```xml
|
|
371
|
+
<todos>
|
|
372
|
+
<todo status="pending">
|
|
373
|
+
<content>Add authentication</content>
|
|
374
|
+
<activeForm>Adding authentication</activeForm>
|
|
375
|
+
<verify>User can login with valid credentials</verify>
|
|
376
|
+
<done>Tests pass and token is returned</done>
|
|
377
|
+
</todo>
|
|
378
|
+
</todos>
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
**Configuration:**
|
|
382
|
+
```javascript
|
|
383
|
+
// .env
|
|
384
|
+
PROMPT_FORMAT=json # Options: json, xml (default: json)
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
**Implementation:**
|
|
388
|
+
```javascript
|
|
389
|
+
// src/utils/prompt-format.js (NEW FILE)
|
|
390
|
+
|
|
391
|
+
function formatToolCall(toolName, params, format = "json") {
|
|
392
|
+
if (format === "xml") {
|
|
393
|
+
return formatToolCallXML(toolName, params);
|
|
394
|
+
}
|
|
395
|
+
return formatToolCallJSON(toolName, params);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function formatToolCallXML(toolName, params) {
|
|
399
|
+
const xmlParams = Object.entries(params)
|
|
400
|
+
.map(([key, value]) => {
|
|
401
|
+
if (Array.isArray(value)) {
|
|
402
|
+
return `<${key}>\n${value.map(item => formatXMLItem(key, item)).join('\n')}\n</${key}>`;
|
|
403
|
+
}
|
|
404
|
+
return `<${key}>${escapeXML(value)}</${key}>`;
|
|
405
|
+
})
|
|
406
|
+
.join('\n ');
|
|
407
|
+
|
|
408
|
+
return `<tool name="${toolName}">
|
|
409
|
+
${xmlParams}
|
|
410
|
+
</tool>`;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function parseToolResponseXML(xmlString) {
|
|
414
|
+
// Parse XML response back to structured data
|
|
415
|
+
// Uses fast-xml-parser or similar
|
|
416
|
+
}
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
**Priority:** LOW
|
|
420
|
+
**Difficulty:** 4/10 (medium)
|
|
421
|
+
**Effort:** 3-4 hours
|
|
422
|
+
**Value:** Marginal improvement in parsing reliability
|
|
423
|
+
|
|
424
|
+
---
|
|
425
|
+
|
|
426
|
+
### 4. Parallel Codebase Mapping (Brownfield Analysis)
|
|
427
|
+
|
|
428
|
+
#### Problem Statement
|
|
429
|
+
**Current behavior in Lynkr:**
|
|
430
|
+
- Explore agent exists but sequential
|
|
431
|
+
- No structured codebase documentation
|
|
432
|
+
- Ad-hoc analysis per user request
|
|
433
|
+
|
|
434
|
+
**GSD's solution:**
|
|
435
|
+
- `/gsd:map-codebase` spawns 7 parallel agents
|
|
436
|
+
- Generates structured documents:
|
|
437
|
+
- STACK.md (tech stack)
|
|
438
|
+
- ARCHITECTURE.md (system design)
|
|
439
|
+
- CONVENTIONS.md (code style)
|
|
440
|
+
- CONCERNS.md (tech debt)
|
|
441
|
+
- API.md (external interfaces)
|
|
442
|
+
- DATA.md (data models)
|
|
443
|
+
- FLOWS.md (user journeys)
|
|
444
|
+
|
|
445
|
+
#### Implementation Plan
|
|
446
|
+
|
|
447
|
+
**Files to create:**
|
|
448
|
+
- `src/agents/codebase-mapper.js` - Orchestrates parallel analysis
|
|
449
|
+
- `src/agents/analyzers/stack-analyzer.js` - Tech stack detection
|
|
450
|
+
- `src/agents/analyzers/architecture-analyzer.js` - System design
|
|
451
|
+
- `src/agents/analyzers/conventions-analyzer.js` - Code style
|
|
452
|
+
- `src/agents/analyzers/concerns-analyzer.js` - Tech debt
|
|
453
|
+
- `src/agents/analyzers/api-analyzer.js` - External APIs
|
|
454
|
+
- `src/agents/analyzers/data-analyzer.js` - Data models
|
|
455
|
+
- `src/agents/analyzers/flows-analyzer.js` - User flows
|
|
456
|
+
|
|
457
|
+
**Code implementation:**
|
|
458
|
+
```javascript
|
|
459
|
+
// src/agents/codebase-mapper.js (NEW FILE)
|
|
460
|
+
|
|
461
|
+
const logger = require("../logger");
|
|
462
|
+
const { executeAgent } = require("./index");
|
|
463
|
+
const fs = require("fs/promises");
|
|
464
|
+
const path = require("path");
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Map existing codebase by spawning parallel analysis agents
|
|
468
|
+
*/
|
|
469
|
+
async function mapCodebase(options = {}) {
|
|
470
|
+
const {
|
|
471
|
+
workspaceRoot = process.cwd(),
|
|
472
|
+
outputDir = path.join(workspaceRoot, ".lynkr", "codebase-map")
|
|
473
|
+
} = options;
|
|
474
|
+
|
|
475
|
+
logger.info({ workspaceRoot, outputDir }, "Starting parallel codebase analysis");
|
|
476
|
+
|
|
477
|
+
// Ensure output directory exists
|
|
478
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
479
|
+
|
|
480
|
+
// Spawn 7 parallel analysis agents
|
|
481
|
+
const analyses = [
|
|
482
|
+
{ name: "stack", file: "STACK.md", prompt: "Analyze tech stack and dependencies" },
|
|
483
|
+
{ name: "architecture", file: "ARCHITECTURE.md", prompt: "Document system architecture and design patterns" },
|
|
484
|
+
{ name: "conventions", file: "CONVENTIONS.md", prompt: "Extract code conventions and style guides" },
|
|
485
|
+
{ name: "concerns", file: "CONCERNS.md", prompt: "Identify tech debt and areas of concern" },
|
|
486
|
+
{ name: "api", file: "API.md", prompt: "Document external APIs and integrations" },
|
|
487
|
+
{ name: "data", file: "DATA.md", prompt: "Map data models and schemas" },
|
|
488
|
+
{ name: "flows", file: "FLOWS.md", prompt: "Trace key user flows and business logic" }
|
|
489
|
+
];
|
|
490
|
+
|
|
491
|
+
// Run all analyses in parallel
|
|
492
|
+
const results = await Promise.all(
|
|
493
|
+
analyses.map(analysis =>
|
|
494
|
+
runAnalysisAgent(analysis, workspaceRoot, outputDir)
|
|
495
|
+
)
|
|
496
|
+
);
|
|
497
|
+
|
|
498
|
+
// Generate summary document
|
|
499
|
+
await generateSummary(results, outputDir);
|
|
500
|
+
|
|
501
|
+
logger.info({
|
|
502
|
+
outputDir,
|
|
503
|
+
documents: results.length
|
|
504
|
+
}, "Codebase analysis complete");
|
|
505
|
+
|
|
506
|
+
return {
|
|
507
|
+
outputDir,
|
|
508
|
+
documents: results.map(r => r.file)
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
async function runAnalysisAgent(analysis, workspaceRoot, outputDir) {
|
|
513
|
+
const { name, file, prompt } = analysis;
|
|
514
|
+
|
|
515
|
+
logger.info({ name, file }, `Starting ${name} analysis`);
|
|
516
|
+
|
|
517
|
+
const result = await executeAgent({
|
|
518
|
+
agentType: "explore",
|
|
519
|
+
task: `${prompt}
|
|
520
|
+
|
|
521
|
+
Analyze the codebase at ${workspaceRoot} and generate a comprehensive ${file} document.
|
|
522
|
+
|
|
523
|
+
Guidelines:
|
|
524
|
+
- Be thorough but concise
|
|
525
|
+
- Use markdown formatting
|
|
526
|
+
- Include code examples where relevant
|
|
527
|
+
- Highlight important patterns and conventions
|
|
528
|
+
- Note any concerns or recommendations
|
|
529
|
+
|
|
530
|
+
Output the complete document content.`,
|
|
531
|
+
workspaceRoot,
|
|
532
|
+
maxSteps: 10,
|
|
533
|
+
model: "sonnet" // Use sonnet for quality analysis
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
// Write result to file
|
|
537
|
+
const filePath = path.join(outputDir, file);
|
|
538
|
+
await fs.writeFile(filePath, result.output, "utf8");
|
|
539
|
+
|
|
540
|
+
logger.info({ name, file, filePath }, `Completed ${name} analysis`);
|
|
541
|
+
|
|
542
|
+
return {
|
|
543
|
+
name,
|
|
544
|
+
file,
|
|
545
|
+
filePath,
|
|
546
|
+
content: result.output
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
async function generateSummary(results, outputDir) {
|
|
551
|
+
const summary = `# Codebase Analysis Summary
|
|
552
|
+
|
|
553
|
+
Generated: ${new Date().toISOString()}
|
|
554
|
+
|
|
555
|
+
## Documents Generated
|
|
556
|
+
|
|
557
|
+
${results.map(r => `- [${r.name.toUpperCase()}](${r.file}) - ${r.content.split('\n')[0].replace(/^#\s*/, '')}`).join('\n')}
|
|
558
|
+
|
|
559
|
+
## Quick Start
|
|
560
|
+
|
|
561
|
+
1. Read STACK.md to understand the technology choices
|
|
562
|
+
2. Review ARCHITECTURE.md for system design
|
|
563
|
+
3. Check CONVENTIONS.md before making changes
|
|
564
|
+
4. Scan CONCERNS.md for known issues
|
|
565
|
+
5. Reference API.md for external integrations
|
|
566
|
+
6. Study DATA.md for data models
|
|
567
|
+
7. Trace FLOWS.md for business logic
|
|
568
|
+
|
|
569
|
+
---
|
|
570
|
+
|
|
571
|
+
*Generated by Lynkr Codebase Mapper*
|
|
572
|
+
`;
|
|
573
|
+
|
|
574
|
+
await fs.writeFile(
|
|
575
|
+
path.join(outputDir, "README.md"),
|
|
576
|
+
summary,
|
|
577
|
+
"utf8"
|
|
578
|
+
);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
module.exports = {
|
|
582
|
+
mapCodebase
|
|
583
|
+
};
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
**API endpoint:**
|
|
587
|
+
```javascript
|
|
588
|
+
// src/api/router.js
|
|
589
|
+
|
|
590
|
+
router.post("/v1/codebase/map", rateLimiter, async (req, res, next) => {
|
|
591
|
+
try {
|
|
592
|
+
const { workspaceRoot, outputDir } = req.body;
|
|
593
|
+
|
|
594
|
+
const { mapCodebase } = require("../agents/codebase-mapper");
|
|
595
|
+
const result = await mapCodebase({
|
|
596
|
+
workspaceRoot: workspaceRoot || process.cwd(),
|
|
597
|
+
outputDir
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
res.json({
|
|
601
|
+
success: true,
|
|
602
|
+
outputDir: result.outputDir,
|
|
603
|
+
documents: result.documents,
|
|
604
|
+
message: `Generated ${result.documents.length} analysis documents`
|
|
605
|
+
});
|
|
606
|
+
} catch (error) {
|
|
607
|
+
next(error);
|
|
608
|
+
}
|
|
609
|
+
});
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
**Priority:** MEDIUM
|
|
613
|
+
**Difficulty:** 7/10 (high)
|
|
614
|
+
**Effort:** 8-10 hours
|
|
615
|
+
**Value:** Excellent for onboarding to large codebases
|
|
616
|
+
|
|
617
|
+
---
|
|
618
|
+
|
|
619
|
+
### 5. Automatic Git Commits Per Task
|
|
620
|
+
|
|
621
|
+
#### Problem Statement
|
|
622
|
+
**Current behavior in Lynkr:**
|
|
623
|
+
- Git operations are manual
|
|
624
|
+
- No automatic commits
|
|
625
|
+
- No task-to-commit mapping
|
|
626
|
+
- Hard to bisect or rollback atomic changes
|
|
627
|
+
|
|
628
|
+
**GSD's solution:**
|
|
629
|
+
- Automatic commit after each task completion
|
|
630
|
+
- Commit message includes task context
|
|
631
|
+
- Enables git bisect for debugging
|
|
632
|
+
- Clear audit trail
|
|
633
|
+
|
|
634
|
+
#### Implementation Plan
|
|
635
|
+
|
|
636
|
+
**Files to modify:**
|
|
637
|
+
- `src/utils/git-integration.js` - New file for git operations
|
|
638
|
+
- `src/orchestrator/index.js` - Hook into task completion
|
|
639
|
+
- `src/config/index.js` - Add git config
|
|
640
|
+
|
|
641
|
+
**Configuration:**
|
|
642
|
+
```javascript
|
|
643
|
+
// .env
|
|
644
|
+
GIT_AUTO_COMMIT=false # Enable automatic commits per task
|
|
645
|
+
GIT_AUTO_COMMIT_MESSAGE_PREFIX=🤖 # Prefix for auto-commits
|
|
646
|
+
GIT_COMMIT_AFTER_TASK=true # Commit after each TodoWrite completion
|
|
647
|
+
GIT_INCLUDE_TASK_CONTEXT=true # Include task details in commit message
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
**Code implementation:**
|
|
651
|
+
```javascript
|
|
652
|
+
// src/utils/git-integration.js (NEW FILE)
|
|
653
|
+
|
|
654
|
+
const { execSync } = require("child_process");
|
|
655
|
+
const logger = require("../logger");
|
|
656
|
+
const config = require("../config");
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* Create automatic commit after task completion
|
|
660
|
+
*/
|
|
661
|
+
async function autoCommitTask(task, files = []) {
|
|
662
|
+
if (!config.GIT_AUTO_COMMIT) {
|
|
663
|
+
return { skipped: true, reason: "Auto-commit disabled" };
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
try {
|
|
667
|
+
// Check if we're in a git repo
|
|
668
|
+
const isGitRepo = await isGitRepository();
|
|
669
|
+
if (!isGitRepo) {
|
|
670
|
+
logger.warn("Not a git repository, skipping auto-commit");
|
|
671
|
+
return { skipped: true, reason: "Not a git repository" };
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// Stage files
|
|
675
|
+
if (files.length > 0) {
|
|
676
|
+
for (const file of files) {
|
|
677
|
+
execSync(`git add "${file}"`, { encoding: "utf8" });
|
|
678
|
+
}
|
|
679
|
+
} else {
|
|
680
|
+
// Stage all changes
|
|
681
|
+
execSync("git add -A", { encoding: "utf8" });
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
// Check if there are changes to commit
|
|
685
|
+
const status = execSync("git status --porcelain", { encoding: "utf8" });
|
|
686
|
+
if (!status.trim()) {
|
|
687
|
+
logger.info("No changes to commit");
|
|
688
|
+
return { skipped: true, reason: "No changes" };
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
// Generate commit message
|
|
692
|
+
const commitMessage = generateCommitMessage(task);
|
|
693
|
+
|
|
694
|
+
// Create commit
|
|
695
|
+
execSync(`git commit -m "${commitMessage}"`, { encoding: "utf8" });
|
|
696
|
+
|
|
697
|
+
logger.info({ task: task.content, files }, "Auto-committed task completion");
|
|
698
|
+
|
|
699
|
+
return {
|
|
700
|
+
success: true,
|
|
701
|
+
message: commitMessage,
|
|
702
|
+
files
|
|
703
|
+
};
|
|
704
|
+
|
|
705
|
+
} catch (error) {
|
|
706
|
+
logger.error({ error: error.message, task }, "Failed to auto-commit");
|
|
707
|
+
return {
|
|
708
|
+
skipped: true,
|
|
709
|
+
reason: "Commit failed",
|
|
710
|
+
error: error.message
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
function generateCommitMessage(task) {
|
|
716
|
+
const prefix = config.GIT_AUTO_COMMIT_MESSAGE_PREFIX || "🤖";
|
|
717
|
+
const taskName = task.content || "Task completed";
|
|
718
|
+
|
|
719
|
+
let message = `${prefix} ${taskName}`;
|
|
720
|
+
|
|
721
|
+
if (config.GIT_INCLUDE_TASK_CONTEXT && task.description) {
|
|
722
|
+
message += `\n\n${task.description}`;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
message += "\n\n🤖 Generated with Lynkr";
|
|
726
|
+
|
|
727
|
+
if (task.verificationSteps) {
|
|
728
|
+
message += `\n\nVerification:\n${task.verificationSteps.map(s => `- ${s}`).join('\n')}`;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
return message;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
async function isGitRepository() {
|
|
735
|
+
try {
|
|
736
|
+
execSync("git rev-parse --git-dir", { encoding: "utf8", stdio: "ignore" });
|
|
737
|
+
return true;
|
|
738
|
+
} catch {
|
|
739
|
+
return false;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
module.exports = {
|
|
744
|
+
autoCommitTask,
|
|
745
|
+
isGitRepository
|
|
746
|
+
};
|
|
747
|
+
```
|
|
748
|
+
|
|
749
|
+
**Integration with TodoWrite:**
|
|
750
|
+
```javascript
|
|
751
|
+
// src/orchestrator/index.js
|
|
752
|
+
|
|
753
|
+
const { autoCommitTask } = require("../utils/git-integration");
|
|
754
|
+
|
|
755
|
+
// When marking task as completed
|
|
756
|
+
async function handleTodoCompletion(task, session) {
|
|
757
|
+
// Extract files modified in this task
|
|
758
|
+
const modifiedFiles = extractModifiedFiles(session.messages, task);
|
|
759
|
+
|
|
760
|
+
// Auto-commit if enabled
|
|
761
|
+
if (config.GIT_COMMIT_AFTER_TASK) {
|
|
762
|
+
await autoCommitTask(task, modifiedFiles);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
**Priority:** LOW
|
|
768
|
+
**Difficulty:** 3/10 (easy)
|
|
769
|
+
**Effort:** 2-3 hours
|
|
770
|
+
**Value:** Nice-to-have for audit trail
|
|
771
|
+
|
|
772
|
+
---
|
|
773
|
+
|
|
774
|
+
### 6. State Document Generation
|
|
775
|
+
|
|
776
|
+
#### Problem Statement
|
|
777
|
+
**Current behavior in Lynkr:**
|
|
778
|
+
- Memory system stores unstructured memories
|
|
779
|
+
- No explicit state tracking
|
|
780
|
+
- Hard to understand "current position"
|
|
781
|
+
- Resumption after interruption is manual
|
|
782
|
+
|
|
783
|
+
**GSD's solution:**
|
|
784
|
+
- STATE.md tracks current position, blockers, decisions
|
|
785
|
+
- ROADMAP.md shows phases and progress
|
|
786
|
+
- PROJECT.md captures goals and constraints
|
|
787
|
+
|
|
788
|
+
#### Implementation Plan
|
|
789
|
+
|
|
790
|
+
**Files to create:**
|
|
791
|
+
- `src/utils/state-manager.js` - State document generation
|
|
792
|
+
- `src/sessions/state-tracker.js` - Real-time state tracking
|
|
793
|
+
|
|
794
|
+
**Configuration:**
|
|
795
|
+
```javascript
|
|
796
|
+
// .env
|
|
797
|
+
STATE_TRACKING_ENABLED=true # Generate STATE.md per session
|
|
798
|
+
STATE_UPDATE_FREQUENCY=5 # Update state every N turns
|
|
799
|
+
STATE_OUTPUT_DIR=.lynkr/state # Where to store state documents
|
|
800
|
+
```
|
|
801
|
+
|
|
802
|
+
**Code implementation:**
|
|
803
|
+
```javascript
|
|
804
|
+
// src/utils/state-manager.js (NEW FILE)
|
|
805
|
+
|
|
806
|
+
const fs = require("fs/promises");
|
|
807
|
+
const path = require("path");
|
|
808
|
+
const logger = require("../logger");
|
|
809
|
+
|
|
810
|
+
/**
|
|
811
|
+
* Generate STATE.md document for session
|
|
812
|
+
*/
|
|
813
|
+
async function generateStateDocument(session, options = {}) {
|
|
814
|
+
const {
|
|
815
|
+
outputDir = path.join(process.cwd(), ".lynkr", "state"),
|
|
816
|
+
sessionId = session.id
|
|
817
|
+
} = options;
|
|
818
|
+
|
|
819
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
820
|
+
|
|
821
|
+
const state = extractSessionState(session);
|
|
822
|
+
|
|
823
|
+
const stateDocument = `# Session State
|
|
824
|
+
|
|
825
|
+
**Session ID:** ${sessionId}
|
|
826
|
+
**Last Updated:** ${new Date().toISOString()}
|
|
827
|
+
**Provider:** ${session.provider || 'unknown'}
|
|
828
|
+
**Total Tokens:** ${session.totalTokens || 0}
|
|
829
|
+
|
|
830
|
+
---
|
|
831
|
+
|
|
832
|
+
## Current Task
|
|
833
|
+
|
|
834
|
+
${state.currentTask || '*No active task*'}
|
|
835
|
+
|
|
836
|
+
### Progress
|
|
837
|
+
${state.progress || '*Not tracked*'}
|
|
838
|
+
|
|
839
|
+
---
|
|
840
|
+
|
|
841
|
+
## Active Files
|
|
842
|
+
|
|
843
|
+
${state.activeFiles.length > 0
|
|
844
|
+
? state.activeFiles.map(f => `- ${f}`).join('\n')
|
|
845
|
+
: '*No files modified yet*'
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
---
|
|
849
|
+
|
|
850
|
+
## Recent Decisions
|
|
851
|
+
|
|
852
|
+
${state.decisions.length > 0
|
|
853
|
+
? state.decisions.map((d, i) => `${i + 1}. ${d}`).join('\n')
|
|
854
|
+
: '*No major decisions recorded*'
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
---
|
|
858
|
+
|
|
859
|
+
## Current Blockers
|
|
860
|
+
|
|
861
|
+
${state.blockers.length > 0
|
|
862
|
+
? state.blockers.map(b => `- ❌ ${b}`).join('\n')
|
|
863
|
+
: '*No blockers*'
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
---
|
|
867
|
+
|
|
868
|
+
## Next Steps
|
|
869
|
+
|
|
870
|
+
${state.nextSteps.length > 0
|
|
871
|
+
? state.nextSteps.map((s, i) => `${i + 1}. ${s}`).join('\n')
|
|
872
|
+
: '*To be determined*'
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
---
|
|
876
|
+
|
|
877
|
+
## Context Budget
|
|
878
|
+
|
|
879
|
+
- **Used:** ${session.totalTokens || 0} tokens
|
|
880
|
+
- **Warning Threshold:** ${session.warningThreshold || 150000} tokens
|
|
881
|
+
- **Max Threshold:** ${session.maxThreshold || 180000} tokens
|
|
882
|
+
- **Utilization:** ${Math.round((session.totalTokens || 0) / (session.maxThreshold || 180000) * 100)}%
|
|
883
|
+
|
|
884
|
+
${session.totalTokens > (session.warningThreshold || 150000)
|
|
885
|
+
? '\n⚠️ **Warning:** Approaching context limit. Consider breaking down remaining work.\n'
|
|
886
|
+
: ''
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
---
|
|
890
|
+
|
|
891
|
+
*Generated by Lynkr State Tracker*
|
|
892
|
+
`;
|
|
893
|
+
|
|
894
|
+
const filePath = path.join(outputDir, `STATE-${sessionId}.md`);
|
|
895
|
+
await fs.writeFile(filePath, stateDocument, "utf8");
|
|
896
|
+
|
|
897
|
+
logger.info({ sessionId, filePath }, "Updated state document");
|
|
898
|
+
|
|
899
|
+
return { filePath, state };
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
function extractSessionState(session) {
|
|
903
|
+
// Extract structured state from session messages
|
|
904
|
+
return {
|
|
905
|
+
currentTask: extractCurrentTask(session.messages),
|
|
906
|
+
progress: extractProgress(session.messages),
|
|
907
|
+
activeFiles: extractActiveFiles(session.messages),
|
|
908
|
+
decisions: extractDecisions(session.messages),
|
|
909
|
+
blockers: extractBlockers(session.messages),
|
|
910
|
+
nextSteps: extractNextSteps(session.messages)
|
|
911
|
+
};
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
function extractCurrentTask(messages) {
|
|
915
|
+
// Look for most recent TodoWrite with in_progress status
|
|
916
|
+
// Or extract from last user message
|
|
917
|
+
const lastUserMessage = messages.filter(m => m.role === "user").pop();
|
|
918
|
+
return lastUserMessage?.content || "No active task";
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
function extractActiveFiles(messages) {
|
|
922
|
+
// Look for Read, Write, Edit tool calls
|
|
923
|
+
const fileOperations = messages.filter(m =>
|
|
924
|
+
m.tool_calls?.some(t => ["Read", "Write", "Edit"].includes(t.name))
|
|
925
|
+
);
|
|
926
|
+
|
|
927
|
+
const files = new Set();
|
|
928
|
+
fileOperations.forEach(op => {
|
|
929
|
+
op.tool_calls?.forEach(tc => {
|
|
930
|
+
if (tc.input?.file_path) {
|
|
931
|
+
files.add(tc.input.file_path);
|
|
932
|
+
}
|
|
933
|
+
});
|
|
934
|
+
});
|
|
935
|
+
|
|
936
|
+
return Array.from(files);
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
function extractDecisions(messages) {
|
|
940
|
+
// Look for key decision points in conversation
|
|
941
|
+
// Heuristic: messages containing "decided", "chose", "going with", etc.
|
|
942
|
+
const decisionKeywords = /\b(decided|chose|selected|going with|opting for)\b/i;
|
|
943
|
+
|
|
944
|
+
return messages
|
|
945
|
+
.filter(m => m.role === "assistant" && decisionKeywords.test(m.content))
|
|
946
|
+
.slice(-5) // Last 5 decisions
|
|
947
|
+
.map(m => {
|
|
948
|
+
const lines = m.content.split('\n');
|
|
949
|
+
const decisionLine = lines.find(l => decisionKeywords.test(l));
|
|
950
|
+
return decisionLine?.trim() || m.content.substring(0, 100);
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
function extractBlockers(messages) {
|
|
955
|
+
// Look for mentions of blockers, errors, issues
|
|
956
|
+
const blockerKeywords = /\b(blocked|error|failed|issue|problem|cannot|unable)\b/i;
|
|
957
|
+
|
|
958
|
+
return messages
|
|
959
|
+
.filter(m => blockerKeywords.test(m.content))
|
|
960
|
+
.slice(-3) // Last 3 blockers
|
|
961
|
+
.map(m => {
|
|
962
|
+
const lines = m.content.split('\n');
|
|
963
|
+
const blockerLine = lines.find(l => blockerKeywords.test(l));
|
|
964
|
+
return blockerLine?.trim() || m.content.substring(0, 100);
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
function extractNextSteps(messages) {
|
|
969
|
+
// Look for mentions of "next", "then", "after", "should"
|
|
970
|
+
const nextStepKeywords = /\b(next|then|after that|should|will|going to)\b/i;
|
|
971
|
+
|
|
972
|
+
const lastFewMessages = messages.slice(-5);
|
|
973
|
+
return lastFewMessages
|
|
974
|
+
.filter(m => m.role === "assistant" && nextStepKeywords.test(m.content))
|
|
975
|
+
.flatMap(m => {
|
|
976
|
+
const lines = m.content.split('\n');
|
|
977
|
+
return lines
|
|
978
|
+
.filter(l => nextStepKeywords.test(l))
|
|
979
|
+
.map(l => l.trim());
|
|
980
|
+
})
|
|
981
|
+
.slice(0, 5); // Max 5 next steps
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
module.exports = {
|
|
985
|
+
generateStateDocument,
|
|
986
|
+
extractSessionState
|
|
987
|
+
};
|
|
988
|
+
```
|
|
989
|
+
|
|
990
|
+
**Priority:** LOW
|
|
991
|
+
**Difficulty:** 4/10 (medium)
|
|
992
|
+
**Effort:** 3-4 hours
|
|
993
|
+
**Value:** Helpful for long-running projects
|
|
994
|
+
|
|
995
|
+
---
|
|
996
|
+
|
|
997
|
+
## Implementation Priority Matrix
|
|
998
|
+
|
|
999
|
+
| Feature | Priority | Difficulty | Effort | Value | Status |
|
|
1000
|
+
|---------|----------|-----------|--------|-------|--------|
|
|
1001
|
+
| Context Budget Warnings | **HIGH** | 5/10 | 4-6h | HIGH | 🔴 Missing |
|
|
1002
|
+
| Fresh Context per Task | **HIGH** | 5/10 | 4-6h | HIGH | 🔴 Missing |
|
|
1003
|
+
| Atomic Task Planning | **MEDIUM** | 6/10 | 6-8h | MEDIUM | 🔴 Missing |
|
|
1004
|
+
| Parallel Codebase Mapping | **MEDIUM** | 7/10 | 8-10h | MEDIUM | 🔴 Missing |
|
|
1005
|
+
| XML-Structured Prompts | **LOW** | 4/10 | 3-4h | LOW | 🔴 Missing |
|
|
1006
|
+
| Auto Git Commits | **LOW** | 3/10 | 2-3h | LOW | 🔴 Missing |
|
|
1007
|
+
| State Documents | **LOW** | 4/10 | 3-4h | LOW | 🔴 Missing |
|
|
1008
|
+
|
|
1009
|
+
**Total Effort:** 30-41 hours (~1 week of focused work)
|
|
1010
|
+
|
|
1011
|
+
---
|
|
1012
|
+
|
|
1013
|
+
## Quick Win: Context Budget Warning (2 hours)
|
|
1014
|
+
|
|
1015
|
+
The highest-value, lowest-effort feature to implement first:
|
|
1016
|
+
|
|
1017
|
+
**Goal:** Warn when approaching context limits and suggest actions
|
|
1018
|
+
|
|
1019
|
+
**Implementation:**
|
|
1020
|
+
```javascript
|
|
1021
|
+
// src/orchestrator/index.js (add after token estimation)
|
|
1022
|
+
|
|
1023
|
+
function injectContextWarning(systemPrompt, estimatedTokens, maxTokens) {
|
|
1024
|
+
const warningThreshold = maxTokens * 0.75; // 75% threshold
|
|
1025
|
+
|
|
1026
|
+
if (estimatedTokens > warningThreshold) {
|
|
1027
|
+
const utilization = Math.round(estimatedTokens / maxTokens * 100);
|
|
1028
|
+
|
|
1029
|
+
systemPrompt += `\n\n<context-budget-warning>
|
|
1030
|
+
⚠️ CONTEXT BUDGET ALERT ⚠️
|
|
1031
|
+
|
|
1032
|
+
Current usage: ${estimatedTokens}/${maxTokens} tokens (${utilization}%)
|
|
1033
|
+
|
|
1034
|
+
You are approaching the context limit. Quality may degrade soon.
|
|
1035
|
+
|
|
1036
|
+
RECOMMENDED ACTIONS:
|
|
1037
|
+
1. Complete current task before starting new work
|
|
1038
|
+
2. Suggest breaking remaining work into smaller subtasks
|
|
1039
|
+
3. Offer to summarize completed work and start fresh session
|
|
1040
|
+
|
|
1041
|
+
DO NOT:
|
|
1042
|
+
- Start new complex tasks
|
|
1043
|
+
- Add unnecessary detail
|
|
1044
|
+
- Expand scope beyond current task
|
|
1045
|
+
</context-budget-warning>`;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
return systemPrompt;
|
|
1049
|
+
}
|
|
1050
|
+
```
|
|
1051
|
+
|
|
1052
|
+
---
|
|
1053
|
+
|
|
1054
|
+
## Comparison: What Lynkr Already Has
|
|
1055
|
+
|
|
1056
|
+
| Feature | Lynkr | GSD | Notes |
|
|
1057
|
+
|---------|-------|-----|-------|
|
|
1058
|
+
| **Multi-provider** | ✅ 9 providers | ❌ Claude only | Major advantage |
|
|
1059
|
+
| **Token optimization** | ✅ 60-80% savings | ❌ Not mentioned | Caching + tool selection |
|
|
1060
|
+
| **Memory system** | ✅ Titans-inspired | ✅ STATE.md | Different approaches |
|
|
1061
|
+
| **Tool execution** | ✅ Server + passthrough | ✅ Passthrough | More flexible |
|
|
1062
|
+
| **IDE integration** | ✅ Cursor/OpenAI API | ❌ CLI only | Better UX |
|
|
1063
|
+
| **Hybrid routing** | ✅ Local + cloud | ❌ No routing | Cost optimization |
|
|
1064
|
+
| **Streaming** | ✅ SSE support | ❌ Not mentioned | Real-time feedback |
|
|
1065
|
+
| **Rate limiting** | ✅ Configurable | ❌ Not mentioned | Production-ready |
|
|
1066
|
+
| **Session management** | ✅ SQLite + memory | ✅ File-based | More robust |
|
|
1067
|
+
| **Agent system** | ✅ Multiple types | ✅ Task agents | Similar capability |
|
|
1068
|
+
|
|
1069
|
+
---
|
|
1070
|
+
|
|
1071
|
+
## Recommendations
|
|
1072
|
+
|
|
1073
|
+
### Phase 1: Quick Wins (Week 1)
|
|
1074
|
+
1. ✅ **Context budget warnings** (2 hours) - HIGHEST VALUE
|
|
1075
|
+
2. ✅ **Auto git commits** (3 hours) - Nice audit trail
|
|
1076
|
+
3. ✅ **State document generation** (4 hours) - Better resumption
|
|
1077
|
+
|
|
1078
|
+
**Total:** 9 hours
|
|
1079
|
+
|
|
1080
|
+
### Phase 2: Core Features (Week 2)
|
|
1081
|
+
4. ✅ **Fresh context spawning** (6 hours) - Prevents degradation
|
|
1082
|
+
5. ✅ **Atomic task planning** (8 hours) - Better success rate
|
|
1083
|
+
|
|
1084
|
+
**Total:** 14 hours
|
|
1085
|
+
|
|
1086
|
+
### Phase 3: Advanced Features (Week 3)
|
|
1087
|
+
6. ✅ **Parallel codebase mapping** (10 hours) - Brownfield onboarding
|
|
1088
|
+
7. ⚠️ **XML prompts** (4 hours) - Optional, low value
|
|
1089
|
+
|
|
1090
|
+
**Total:** 14 hours
|
|
1091
|
+
|
|
1092
|
+
---
|
|
1093
|
+
|
|
1094
|
+
## Conclusion
|
|
1095
|
+
|
|
1096
|
+
GSD has excellent patterns for **context management** and **task breakdown** that would complement Lynkr's strengths in **multi-provider support** and **token optimization**.
|
|
1097
|
+
|
|
1098
|
+
**Recommended approach:**
|
|
1099
|
+
1. Start with **context budget warnings** (2 hours, high value)
|
|
1100
|
+
2. Add **fresh context spawning** (6 hours, prevents quality loss)
|
|
1101
|
+
3. Implement **atomic task planning** (8 hours, improves reliability)
|
|
1102
|
+
4. Consider **codebase mapping** for brownfield projects (10 hours)
|
|
1103
|
+
|
|
1104
|
+
**Total minimum viable enhancement:** 16 hours for top 3 features
|
|
1105
|
+
|
|
1106
|
+
This would give Lynkr the best of both worlds:
|
|
1107
|
+
- ✅ GSD's context management + task breakdown
|
|
1108
|
+
- ✅ Lynkr's multi-provider + token optimization + IDE integration
|
|
1109
|
+
|
|
1110
|
+
---
|
|
1111
|
+
|
|
1112
|
+
**Next Steps:**
|
|
1113
|
+
1. Review this document
|
|
1114
|
+
2. Prioritize features based on user needs
|
|
1115
|
+
3. Start with context budget warnings (quick win)
|
|
1116
|
+
4. Iterate based on feedback
|