codeep 1.0.5 → 1.0.7

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.
@@ -35,6 +35,8 @@ interface ConfigSchema {
35
35
  agentAutoCommitBranch: boolean;
36
36
  agentAutoVerify: boolean;
37
37
  agentMaxFixAttempts: number;
38
+ agentMaxIterations: number;
39
+ agentMaxDuration: number;
38
40
  projectPermissions: ProjectPermission[];
39
41
  providerApiKeys: ProviderApiKey[];
40
42
  }
@@ -66,6 +66,8 @@ export const config = new Conf({
66
66
  agentAutoCommitBranch: false,
67
67
  agentAutoVerify: true, // Auto-verify by default
68
68
  agentMaxFixAttempts: 3,
69
+ agentMaxIterations: 100,
70
+ agentMaxDuration: 20, // minutes
69
71
  protocol: 'openai',
70
72
  plan: 'lite',
71
73
  language: 'en',
@@ -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
- maxIterations: 50, // Increased for complex tasks like website creation
12
- maxDuration: 10 * 60 * 1000, // 10 minutes
12
+ maxIterations: 100, // Increased for large tasks
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)
@@ -358,12 +360,42 @@ async function handleStream(body, protocol, onChunk) {
358
360
  * Run the agent loop
359
361
  */
360
362
  export async function runAgent(prompt, projectContext, options = {}) {
361
- const opts = { ...DEFAULT_OPTIONS, ...options };
363
+ // Load limits from config
364
+ const configMaxIterations = config.get('agentMaxIterations');
365
+ const configMaxDuration = config.get('agentMaxDuration') * 60 * 1000; // convert minutes to ms
366
+ const opts = {
367
+ ...DEFAULT_OPTIONS,
368
+ maxIterations: configMaxIterations,
369
+ maxDuration: configMaxDuration,
370
+ ...options
371
+ };
362
372
  const startTime = Date.now();
363
373
  const actions = [];
364
374
  const messages = [];
365
375
  // Start history session for undo support
366
376
  const sessionId = startSession(prompt, projectContext.root || process.cwd());
377
+ // Task planning phase (if enabled and prompt is complex enough)
378
+ let taskPlan = null;
379
+ if (opts.usePlanning && prompt.split(' ').length > 5) {
380
+ try {
381
+ opts.onIteration?.(0, 'Planning tasks...');
382
+ taskPlan = await planTasks(prompt, {
383
+ name: projectContext.name,
384
+ type: projectContext.type,
385
+ structure: projectContext.structure,
386
+ });
387
+ if (taskPlan.tasks.length > 1) {
388
+ opts.onTaskPlan?.(taskPlan);
389
+ }
390
+ else {
391
+ taskPlan = null; // Single task, no need for planning
392
+ }
393
+ }
394
+ catch (error) {
395
+ // Planning failed, continue without it
396
+ taskPlan = null;
397
+ }
398
+ }
367
399
  // Gather smart context based on the task
368
400
  const targetFile = extractTargetFile(prompt);
369
401
  const smartContext = gatherSmartContext(targetFile, projectContext, prompt);
@@ -379,8 +411,12 @@ export async function runAgent(prompt, projectContext, options = {}) {
379
411
  if (smartContextStr) {
380
412
  systemPrompt += '\n\n' + smartContextStr;
381
413
  }
382
- // Initial user message
383
- messages.push({ role: 'user', content: prompt });
414
+ // Initial user message with optional task plan
415
+ let initialPrompt = prompt;
416
+ if (taskPlan) {
417
+ 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.`;
418
+ }
419
+ messages.push({ role: 'user', content: initialPrompt });
384
420
  let iteration = 0;
385
421
  let finalResponse = '';
386
422
  let result;
@@ -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.5",
3
+ "version": "1.0.7",
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",