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.
@@ -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;
@@ -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, nothing more
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 interpret or expand simple requests into complex tasks
32
- - Simple tasks should be completed in 1-2 tool calls
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, nothing more
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 interpret or expand simple requests into complex tasks
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
- messages.push({ role: 'user', content: prompt });
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
- // Remove <think>...</think> tags from response (some models include thinking)
452
- finalResponse = content.replace(/<think>[\s\S]*?<\/think>/gi, '').trim();
453
- break;
454
- }
455
- // Also check if response indicates completion (even with tool calls)
456
- const completionIndicators = ['task completed', 'finished', 'done with', 'successfully created', 'all set'];
457
- const lowerContent = content.toLowerCase();
458
- if (completionIndicators.some(indicator => lowerContent.includes(indicator))) {
459
- // Agent thinks task is complete, but included tool calls - execute them and finish
460
- // Continue with tool execution but mark this as potentially the last iteration
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: `Tool results:\n\n${toolResults.join('\n\n')}\n\nContinue with the task. If done, provide a final summary without any tool calls.`,
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.6",
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",