codeep 1.0.6 → 1.0.8
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/dist/utils/agent.d.ts +4 -0
- package/dist/utils/agent.js +79 -20
- package/dist/utils/taskPlanner.d.ts +34 -0
- package/dist/utils/taskPlanner.js +160 -0
- package/package.json +1 -1
package/dist/utils/agent.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { ProjectContext } from './project';
|
|
|
5
5
|
import { ToolCall, ToolResult, ActionLog } from './tools';
|
|
6
6
|
import { undoLastAction, undoAllActions, getCurrentSession, getRecentSessions, formatSession, ActionSession } from './history';
|
|
7
7
|
import { VerifyResult } from './verify';
|
|
8
|
+
import { TaskPlan, SubTask } from './taskPlanner';
|
|
8
9
|
export interface AgentOptions {
|
|
9
10
|
maxIterations: number;
|
|
10
11
|
maxDuration: number;
|
|
@@ -13,10 +14,13 @@ export interface AgentOptions {
|
|
|
13
14
|
onIteration?: (iteration: number, message: string) => void;
|
|
14
15
|
onThinking?: (text: string) => void;
|
|
15
16
|
onVerification?: (results: VerifyResult[]) => void;
|
|
17
|
+
onTaskPlan?: (plan: TaskPlan) => void;
|
|
18
|
+
onTaskUpdate?: (task: SubTask) => void;
|
|
16
19
|
abortSignal?: AbortSignal;
|
|
17
20
|
dryRun?: boolean;
|
|
18
21
|
autoVerify?: boolean;
|
|
19
22
|
maxFixAttempts?: number;
|
|
23
|
+
usePlanning?: boolean;
|
|
20
24
|
}
|
|
21
25
|
export interface AgentResult {
|
|
22
26
|
success: boolean;
|
package/dist/utils/agent.js
CHANGED
|
@@ -7,9 +7,11 @@ import { getProviderBaseUrl, getProviderAuthHeader, supportsNativeTools } from '
|
|
|
7
7
|
import { startSession, endSession, undoLastAction, undoAllActions, getCurrentSession, getRecentSessions, formatSession } from './history.js';
|
|
8
8
|
import { runAllVerifications, formatErrorsForAgent, hasVerificationErrors, getVerificationSummary } from './verify.js';
|
|
9
9
|
import { gatherSmartContext, formatSmartContext, extractTargetFile } from './smartContext.js';
|
|
10
|
+
import { planTasks, formatTaskPlan } from './taskPlanner.js';
|
|
10
11
|
const DEFAULT_OPTIONS = {
|
|
11
12
|
maxIterations: 100, // Increased for large tasks
|
|
12
13
|
maxDuration: 20 * 60 * 1000, // 20 minutes
|
|
14
|
+
usePlanning: true, // Enable task planning by default
|
|
13
15
|
};
|
|
14
16
|
/**
|
|
15
17
|
* Generate system prompt for agent mode (used with native tool calling)
|
|
@@ -25,13 +27,15 @@ function getAgentSystemPrompt(projectContext) {
|
|
|
25
27
|
- List directory contents
|
|
26
28
|
|
|
27
29
|
## IMPORTANT: Follow User Instructions Exactly
|
|
28
|
-
- Do EXACTLY what the user asks
|
|
30
|
+
- Do EXACTLY what the user asks - complete the ENTIRE task
|
|
31
|
+
- If user says "create a website" -> create ALL necessary files (HTML, CSS, JS, etc.)
|
|
29
32
|
- If user says "create folder X" -> use create_directory tool to create folder X
|
|
30
33
|
- If user says "delete file X" -> use delete_file tool to delete file X
|
|
31
|
-
- Do NOT
|
|
32
|
-
-
|
|
34
|
+
- Do NOT stop after just 1-2 tool calls unless the task is trivially simple
|
|
35
|
+
- Complex tasks (like creating websites) require MANY tool calls to complete
|
|
33
36
|
- The user may write in any language - understand their request and execute it
|
|
34
37
|
- Tool names and parameters must ALWAYS be in English (e.g., "create_directory", not "kreiraj_direktorij")
|
|
38
|
+
- KEEP WORKING until the entire task is finished - do not stop prematurely
|
|
35
39
|
|
|
36
40
|
## Rules
|
|
37
41
|
1. Always read files before editing them to understand the current content
|
|
@@ -68,12 +72,15 @@ function getFallbackSystemPrompt(projectContext) {
|
|
|
68
72
|
return `You are an AI coding agent with FULL autonomous access to this project.
|
|
69
73
|
|
|
70
74
|
## IMPORTANT: Follow User Instructions Exactly
|
|
71
|
-
- Do EXACTLY what the user asks
|
|
75
|
+
- Do EXACTLY what the user asks - complete the ENTIRE task
|
|
76
|
+
- If user says "create a website" -> create ALL necessary files (HTML, CSS, JS, etc.)
|
|
72
77
|
- If user says "create folder X" -> use create_directory tool
|
|
73
|
-
- If user says "delete file X" -> use delete_file tool
|
|
74
|
-
- Do NOT
|
|
78
|
+
- If user says "delete file X" -> use delete_file tool
|
|
79
|
+
- Do NOT stop after just 1-2 tool calls unless the task is trivially simple
|
|
80
|
+
- Complex tasks (like creating websites) require MANY tool calls to complete
|
|
75
81
|
- The user may write in any language - understand and execute
|
|
76
82
|
- Tool names and parameters must ALWAYS be in English
|
|
83
|
+
- KEEP WORKING until the entire task is finished - do not stop prematurely
|
|
77
84
|
|
|
78
85
|
## Available Tools
|
|
79
86
|
${formatToolDefinitions()}
|
|
@@ -372,6 +379,28 @@ export async function runAgent(prompt, projectContext, options = {}) {
|
|
|
372
379
|
const messages = [];
|
|
373
380
|
// Start history session for undo support
|
|
374
381
|
const sessionId = startSession(prompt, projectContext.root || process.cwd());
|
|
382
|
+
// Task planning phase (if enabled and prompt is complex enough)
|
|
383
|
+
let taskPlan = null;
|
|
384
|
+
if (opts.usePlanning && prompt.split(' ').length > 5) {
|
|
385
|
+
try {
|
|
386
|
+
opts.onIteration?.(0, 'Planning tasks...');
|
|
387
|
+
taskPlan = await planTasks(prompt, {
|
|
388
|
+
name: projectContext.name,
|
|
389
|
+
type: projectContext.type,
|
|
390
|
+
structure: projectContext.structure,
|
|
391
|
+
});
|
|
392
|
+
if (taskPlan.tasks.length > 1) {
|
|
393
|
+
opts.onTaskPlan?.(taskPlan);
|
|
394
|
+
}
|
|
395
|
+
else {
|
|
396
|
+
taskPlan = null; // Single task, no need for planning
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
catch (error) {
|
|
400
|
+
// Planning failed, continue without it
|
|
401
|
+
taskPlan = null;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
375
404
|
// Gather smart context based on the task
|
|
376
405
|
const targetFile = extractTargetFile(prompt);
|
|
377
406
|
const smartContext = gatherSmartContext(targetFile, projectContext, prompt);
|
|
@@ -387,8 +416,12 @@ export async function runAgent(prompt, projectContext, options = {}) {
|
|
|
387
416
|
if (smartContextStr) {
|
|
388
417
|
systemPrompt += '\n\n' + smartContextStr;
|
|
389
418
|
}
|
|
390
|
-
// Initial user message
|
|
391
|
-
|
|
419
|
+
// Initial user message with optional task plan
|
|
420
|
+
let initialPrompt = prompt;
|
|
421
|
+
if (taskPlan) {
|
|
422
|
+
initialPrompt = `${prompt}\n\n## Task Breakdown\nI've broken this down into subtasks. Complete them in order:\n\n${formatTaskPlan(taskPlan)}\n\nStart with task 1.`;
|
|
423
|
+
}
|
|
424
|
+
messages.push({ role: 'user', content: initialPrompt });
|
|
392
425
|
let iteration = 0;
|
|
393
426
|
let finalResponse = '';
|
|
394
427
|
let result;
|
|
@@ -446,18 +479,41 @@ export async function runAgent(prompt, projectContext, options = {}) {
|
|
|
446
479
|
toolCalls = textToolCalls;
|
|
447
480
|
}
|
|
448
481
|
}
|
|
449
|
-
// If no tool calls, this is the final response
|
|
482
|
+
// If no tool calls, check if this is really the final response
|
|
483
|
+
// Don't exit on first iteration without tool calls - agent might be thinking
|
|
450
484
|
if (toolCalls.length === 0) {
|
|
451
|
-
//
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
485
|
+
// Only accept as final response if:
|
|
486
|
+
// 1. We've done at least some work (iteration > 2)
|
|
487
|
+
// 2. Agent explicitly indicates completion
|
|
488
|
+
const completionIndicators = [
|
|
489
|
+
'task is complete',
|
|
490
|
+
'all files have been created',
|
|
491
|
+
'website has been created',
|
|
492
|
+
'successfully completed',
|
|
493
|
+
'everything is ready',
|
|
494
|
+
'all done'
|
|
495
|
+
];
|
|
496
|
+
const lowerContent = content.toLowerCase();
|
|
497
|
+
const indicatesCompletion = completionIndicators.some(indicator => lowerContent.includes(indicator));
|
|
498
|
+
if (iteration > 2 && indicatesCompletion) {
|
|
499
|
+
// Remove <think>...</think> tags from response
|
|
500
|
+
finalResponse = content.replace(/<think>[\s\S]*?<\/think>/gi, '').trim();
|
|
501
|
+
break;
|
|
502
|
+
}
|
|
503
|
+
else if (iteration <= 2) {
|
|
504
|
+
// Too early to quit - remind agent to continue
|
|
505
|
+
messages.push({ role: 'assistant', content });
|
|
506
|
+
messages.push({
|
|
507
|
+
role: 'user',
|
|
508
|
+
content: 'Continue with the task. Use the tools to complete what was requested. Do not stop until all files are created and the task is fully complete.'
|
|
509
|
+
});
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
512
|
+
else {
|
|
513
|
+
// Later iteration without completion indicator - accept as final
|
|
514
|
+
finalResponse = content.replace(/<think>[\s\S]*?<\/think>/gi, '').trim();
|
|
515
|
+
break;
|
|
516
|
+
}
|
|
461
517
|
}
|
|
462
518
|
// Add assistant response to history
|
|
463
519
|
messages.push({ role: 'assistant', content });
|
|
@@ -492,9 +548,12 @@ export async function runAgent(prompt, projectContext, options = {}) {
|
|
|
492
548
|
}
|
|
493
549
|
}
|
|
494
550
|
// Add tool results to messages
|
|
551
|
+
const nextStepPrompt = iteration < 5
|
|
552
|
+
? `Tool results:\n\n${toolResults.join('\n\n')}\n\nGood progress! Continue working on the task. Use more tools to complete what was requested. Only stop when EVERYTHING is finished and working.`
|
|
553
|
+
: `Tool results:\n\n${toolResults.join('\n\n')}\n\nContinue with the task. If the task is fully complete, provide a final summary without any tool calls.`;
|
|
495
554
|
messages.push({
|
|
496
555
|
role: 'user',
|
|
497
|
-
content:
|
|
556
|
+
content: nextStepPrompt,
|
|
498
557
|
});
|
|
499
558
|
}
|
|
500
559
|
// Check if we hit max iterations
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task Planning - breaks down complex tasks into subtasks
|
|
3
|
+
*/
|
|
4
|
+
export interface SubTask {
|
|
5
|
+
id: number;
|
|
6
|
+
description: string;
|
|
7
|
+
status: 'pending' | 'in_progress' | 'completed' | 'failed';
|
|
8
|
+
dependencies?: number[];
|
|
9
|
+
}
|
|
10
|
+
export interface TaskPlan {
|
|
11
|
+
originalPrompt: string;
|
|
12
|
+
tasks: SubTask[];
|
|
13
|
+
estimatedIterations: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Ask AI to break down a complex task into subtasks
|
|
17
|
+
*/
|
|
18
|
+
export declare function planTasks(userPrompt: string, projectContext: {
|
|
19
|
+
name: string;
|
|
20
|
+
type: string;
|
|
21
|
+
structure: string;
|
|
22
|
+
}): Promise<TaskPlan>;
|
|
23
|
+
/**
|
|
24
|
+
* Check if a task's dependencies are completed
|
|
25
|
+
*/
|
|
26
|
+
export declare function canStartTask(task: SubTask, allTasks: SubTask[]): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Get next task to execute
|
|
29
|
+
*/
|
|
30
|
+
export declare function getNextTask(tasks: SubTask[]): SubTask | null;
|
|
31
|
+
/**
|
|
32
|
+
* Format task plan for display
|
|
33
|
+
*/
|
|
34
|
+
export declare function formatTaskPlan(plan: TaskPlan): string;
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task Planning - breaks down complex tasks into subtasks
|
|
3
|
+
*/
|
|
4
|
+
import { config, getApiKey } from '../config/index.js';
|
|
5
|
+
import { getProviderBaseUrl, getProviderAuthHeader } from '../config/providers.js';
|
|
6
|
+
/**
|
|
7
|
+
* Ask AI to break down a complex task into subtasks
|
|
8
|
+
*/
|
|
9
|
+
export async function planTasks(userPrompt, projectContext) {
|
|
10
|
+
const systemPrompt = `You are a task planning expert. Break down user requests into clear, sequential subtasks.
|
|
11
|
+
|
|
12
|
+
RULES:
|
|
13
|
+
1. Create 3-15 subtasks maximum
|
|
14
|
+
2. Each subtask should be specific and actionable
|
|
15
|
+
3. Order tasks logically (dependencies first)
|
|
16
|
+
4. Use simple, clear descriptions
|
|
17
|
+
5. Respond ONLY with a JSON object, no other text
|
|
18
|
+
|
|
19
|
+
Example response format:
|
|
20
|
+
{
|
|
21
|
+
"tasks": [
|
|
22
|
+
{"id": 1, "description": "Create project directory structure", "dependencies": []},
|
|
23
|
+
{"id": 2, "description": "Create index.html with basic structure", "dependencies": [1]},
|
|
24
|
+
{"id": 3, "description": "Create styles.css with base styles", "dependencies": [1]},
|
|
25
|
+
{"id": 4, "description": "Add navigation to all pages", "dependencies": [2, 3]}
|
|
26
|
+
]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
Project Context:
|
|
30
|
+
- Name: ${projectContext.name}
|
|
31
|
+
- Type: ${projectContext.type}
|
|
32
|
+
|
|
33
|
+
User Request: ${userPrompt}
|
|
34
|
+
|
|
35
|
+
Break this down into subtasks. Respond with JSON only.`;
|
|
36
|
+
try {
|
|
37
|
+
const apiKey = await getApiKey();
|
|
38
|
+
if (!apiKey) {
|
|
39
|
+
throw new Error('No API key configured');
|
|
40
|
+
}
|
|
41
|
+
const protocol = config.get('protocol');
|
|
42
|
+
const provider = config.get('provider');
|
|
43
|
+
const model = config.get('model');
|
|
44
|
+
const baseUrl = getProviderBaseUrl(provider, protocol);
|
|
45
|
+
const authHeaderType = getProviderAuthHeader(provider, protocol);
|
|
46
|
+
const messages = [
|
|
47
|
+
{ role: 'user', content: systemPrompt }
|
|
48
|
+
];
|
|
49
|
+
const requestBody = protocol === 'anthropic'
|
|
50
|
+
? {
|
|
51
|
+
model,
|
|
52
|
+
max_tokens: 2048,
|
|
53
|
+
messages,
|
|
54
|
+
system: 'You are a task planning assistant. Respond with JSON only.',
|
|
55
|
+
}
|
|
56
|
+
: {
|
|
57
|
+
model,
|
|
58
|
+
messages: [
|
|
59
|
+
{ role: 'system', content: 'You are a task planning assistant. Respond with JSON only.' },
|
|
60
|
+
...messages
|
|
61
|
+
],
|
|
62
|
+
temperature: 0.3,
|
|
63
|
+
max_tokens: 2048,
|
|
64
|
+
};
|
|
65
|
+
const headers = {
|
|
66
|
+
'Content-Type': 'application/json',
|
|
67
|
+
};
|
|
68
|
+
if (authHeaderType === 'x-api-key') {
|
|
69
|
+
headers['x-api-key'] = apiKey;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
headers['Authorization'] = `Bearer ${apiKey}`;
|
|
73
|
+
}
|
|
74
|
+
const response = await fetch(`${baseUrl}/chat/completions`, {
|
|
75
|
+
method: 'POST',
|
|
76
|
+
headers,
|
|
77
|
+
body: JSON.stringify(requestBody),
|
|
78
|
+
});
|
|
79
|
+
if (!response.ok) {
|
|
80
|
+
throw new Error(`API request failed: ${response.statusText}`);
|
|
81
|
+
}
|
|
82
|
+
const data = await response.json();
|
|
83
|
+
const content = protocol === 'anthropic'
|
|
84
|
+
? data.content?.[0]?.text || ''
|
|
85
|
+
: data.choices?.[0]?.message?.content || '';
|
|
86
|
+
// Extract JSON from response (handle markdown code blocks)
|
|
87
|
+
let jsonStr = content.trim();
|
|
88
|
+
if (jsonStr.startsWith('```json')) {
|
|
89
|
+
jsonStr = jsonStr.replace(/```json\n?/g, '').replace(/```\n?/g, '');
|
|
90
|
+
}
|
|
91
|
+
else if (jsonStr.startsWith('```')) {
|
|
92
|
+
jsonStr = jsonStr.replace(/```\n?/g, '');
|
|
93
|
+
}
|
|
94
|
+
const parsed = JSON.parse(jsonStr);
|
|
95
|
+
// Validate and convert to TaskPlan
|
|
96
|
+
const tasks = parsed.tasks.map((t, index) => ({
|
|
97
|
+
id: t.id || index + 1,
|
|
98
|
+
description: t.description,
|
|
99
|
+
status: 'pending',
|
|
100
|
+
dependencies: t.dependencies || [],
|
|
101
|
+
}));
|
|
102
|
+
return {
|
|
103
|
+
originalPrompt: userPrompt,
|
|
104
|
+
tasks,
|
|
105
|
+
estimatedIterations: tasks.length * 3, // Rough estimate
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
// Fallback: create a single task
|
|
110
|
+
return {
|
|
111
|
+
originalPrompt: userPrompt,
|
|
112
|
+
tasks: [
|
|
113
|
+
{
|
|
114
|
+
id: 1,
|
|
115
|
+
description: userPrompt,
|
|
116
|
+
status: 'pending',
|
|
117
|
+
dependencies: [],
|
|
118
|
+
}
|
|
119
|
+
],
|
|
120
|
+
estimatedIterations: 10,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Check if a task's dependencies are completed
|
|
126
|
+
*/
|
|
127
|
+
export function canStartTask(task, allTasks) {
|
|
128
|
+
if (!task.dependencies || task.dependencies.length === 0) {
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
return task.dependencies.every(depId => {
|
|
132
|
+
const depTask = allTasks.find(t => t.id === depId);
|
|
133
|
+
return depTask?.status === 'completed';
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Get next task to execute
|
|
138
|
+
*/
|
|
139
|
+
export function getNextTask(tasks) {
|
|
140
|
+
return tasks.find(t => t.status === 'pending' && canStartTask(t, tasks)) || null;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Format task plan for display
|
|
144
|
+
*/
|
|
145
|
+
export function formatTaskPlan(plan) {
|
|
146
|
+
const lines = ['Task Plan:', ''];
|
|
147
|
+
plan.tasks.forEach(task => {
|
|
148
|
+
const icon = task.status === 'completed' ? '✓'
|
|
149
|
+
: task.status === 'in_progress' ? '⏳'
|
|
150
|
+
: task.status === 'failed' ? '✗'
|
|
151
|
+
: '⏸';
|
|
152
|
+
const deps = task.dependencies && task.dependencies.length > 0
|
|
153
|
+
? ` (after: ${task.dependencies.join(', ')})`
|
|
154
|
+
: '';
|
|
155
|
+
lines.push(`${icon} ${task.id}. ${task.description}${deps}`);
|
|
156
|
+
});
|
|
157
|
+
lines.push('');
|
|
158
|
+
lines.push(`Estimated iterations: ~${plan.estimatedIterations}`);
|
|
159
|
+
return lines.join('\n');
|
|
160
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeep",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"description": "AI-powered coding assistant built for the terminal. Multiple LLM providers, project-aware context, and a seamless development workflow.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|