codeep 1.0.6 → 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.
@@ -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)
@@ -372,6 +374,28 @@ export async function runAgent(prompt, projectContext, options = {}) {
372
374
  const messages = [];
373
375
  // Start history session for undo support
374
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
+ }
375
399
  // Gather smart context based on the task
376
400
  const targetFile = extractTargetFile(prompt);
377
401
  const smartContext = gatherSmartContext(targetFile, projectContext, prompt);
@@ -387,8 +411,12 @@ export async function runAgent(prompt, projectContext, options = {}) {
387
411
  if (smartContextStr) {
388
412
  systemPrompt += '\n\n' + smartContextStr;
389
413
  }
390
- // Initial user message
391
- 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 });
392
420
  let iteration = 0;
393
421
  let finalResponse = '';
394
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.6",
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",