@limo-labs/limo-cli 0.1.0-alpha.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 +238 -0
- package/dist/agents/analyst.d.ts +24 -0
- package/dist/agents/analyst.js +128 -0
- package/dist/agents/editor.d.ts +26 -0
- package/dist/agents/editor.js +157 -0
- package/dist/agents/planner-validator.d.ts +7 -0
- package/dist/agents/planner-validator.js +125 -0
- package/dist/agents/planner.d.ts +56 -0
- package/dist/agents/planner.js +186 -0
- package/dist/agents/writer.d.ts +25 -0
- package/dist/agents/writer.js +164 -0
- package/dist/commands/analyze.d.ts +14 -0
- package/dist/commands/analyze.js +562 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +41 -0
- package/dist/report/diagrams.d.ts +27 -0
- package/dist/report/diagrams.js +74 -0
- package/dist/report/graphCompiler.d.ts +37 -0
- package/dist/report/graphCompiler.js +277 -0
- package/dist/report/markdownGenerator.d.ts +71 -0
- package/dist/report/markdownGenerator.js +148 -0
- package/dist/tools/additional.d.ts +116 -0
- package/dist/tools/additional.js +349 -0
- package/dist/tools/extended.d.ts +101 -0
- package/dist/tools/extended.js +586 -0
- package/dist/tools/index.d.ts +86 -0
- package/dist/tools/index.js +362 -0
- package/dist/types/agents.types.d.ts +139 -0
- package/dist/types/agents.types.js +6 -0
- package/dist/types/graphSemantics.d.ts +99 -0
- package/dist/types/graphSemantics.js +104 -0
- package/dist/utils/debug.d.ts +28 -0
- package/dist/utils/debug.js +125 -0
- package/dist/utils/limoConfigParser.d.ts +21 -0
- package/dist/utils/limoConfigParser.js +274 -0
- package/dist/utils/reviewMonitor.d.ts +20 -0
- package/dist/utils/reviewMonitor.js +121 -0
- package/package.json +62 -0
- package/prompts/analyst.md +343 -0
- package/prompts/editor.md +196 -0
- package/prompts/planner.md +388 -0
- package/prompts/writer.md +218 -0
|
@@ -0,0 +1,586 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extended tool implementations for Limo CLI
|
|
3
|
+
* Implements all missing tools from VSCode extension
|
|
4
|
+
*/
|
|
5
|
+
import * as fs from 'fs/promises';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
import { glob } from 'glob';
|
|
8
|
+
import { z } from 'zod';
|
|
9
|
+
import { exec } from 'child_process';
|
|
10
|
+
import { promisify } from 'util';
|
|
11
|
+
const execAsync = promisify(exec);
|
|
12
|
+
// ===== FILE OPERATIONS =====
|
|
13
|
+
const FileSearchContentInputSchema = z.object({
|
|
14
|
+
root_path: z.string().describe('Root directory to search from'),
|
|
15
|
+
pattern: z.string().describe('Regex pattern to search for'),
|
|
16
|
+
file_pattern: z.string().optional().describe('Glob pattern for files to search'),
|
|
17
|
+
exclude_pattern: z.string().optional().describe('Glob pattern for files to exclude'),
|
|
18
|
+
max_results: z.number().optional().describe('Maximum number of results to return')
|
|
19
|
+
});
|
|
20
|
+
export function createFileSearchContentTool(context) {
|
|
21
|
+
return {
|
|
22
|
+
name: 'file_search_content',
|
|
23
|
+
description: 'Search for content in files using regex pattern',
|
|
24
|
+
inputSchema: FileSearchContentInputSchema,
|
|
25
|
+
execute: async (params) => {
|
|
26
|
+
const rootPath = path.resolve(context.workspaceRoot, params.root_path || '.');
|
|
27
|
+
const maxResults = params.max_results || 50;
|
|
28
|
+
try {
|
|
29
|
+
const filePattern = params.file_pattern || '**/*';
|
|
30
|
+
const files = await glob(filePattern, {
|
|
31
|
+
cwd: rootPath,
|
|
32
|
+
ignore: [
|
|
33
|
+
'**/node_modules/**',
|
|
34
|
+
'**/.git/**',
|
|
35
|
+
'**/.limo/**',
|
|
36
|
+
...(params.exclude_pattern ? [params.exclude_pattern] : [])
|
|
37
|
+
],
|
|
38
|
+
nodir: true
|
|
39
|
+
});
|
|
40
|
+
const regex = new RegExp(params.pattern, 'gi');
|
|
41
|
+
const results = [];
|
|
42
|
+
for (const file of files) {
|
|
43
|
+
if (results.length >= maxResults)
|
|
44
|
+
break;
|
|
45
|
+
const filePath = path.join(rootPath, file);
|
|
46
|
+
try {
|
|
47
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
48
|
+
const lines = content.split('\n');
|
|
49
|
+
for (let i = 0; i < lines.length; i++) {
|
|
50
|
+
if (results.length >= maxResults)
|
|
51
|
+
break;
|
|
52
|
+
const match = regex.exec(lines[i]);
|
|
53
|
+
if (match) {
|
|
54
|
+
results.push({
|
|
55
|
+
file: file,
|
|
56
|
+
line: i + 1,
|
|
57
|
+
content: lines[i].trim(),
|
|
58
|
+
match: match[0]
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
// Skip files that can't be read
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
success: true,
|
|
69
|
+
data: {
|
|
70
|
+
results,
|
|
71
|
+
total: results.length,
|
|
72
|
+
truncated: results.length >= maxResults
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
return {
|
|
78
|
+
success: false,
|
|
79
|
+
error: `Search failed: ${error instanceof Error ? error.message : String(error)}`
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
// ===== TERMINAL OPERATIONS =====
|
|
86
|
+
const TerminalExecuteInputSchema = z.object({
|
|
87
|
+
command: z.string().describe('Command to execute'),
|
|
88
|
+
timeout: z.number().optional().describe('Timeout in milliseconds'),
|
|
89
|
+
cwd: z.string().optional().describe('Working directory')
|
|
90
|
+
});
|
|
91
|
+
export function createTerminalExecuteTool(context) {
|
|
92
|
+
return {
|
|
93
|
+
name: 'terminal_execute',
|
|
94
|
+
description: 'Execute a shell command',
|
|
95
|
+
inputSchema: TerminalExecuteInputSchema,
|
|
96
|
+
execute: async (params) => {
|
|
97
|
+
const timeout = params.timeout || 30000;
|
|
98
|
+
const cwd = params.cwd || context.workspaceRoot;
|
|
99
|
+
try {
|
|
100
|
+
const { stdout, stderr } = await execAsync(params.command, {
|
|
101
|
+
cwd,
|
|
102
|
+
timeout,
|
|
103
|
+
maxBuffer: 1024 * 1024 * 10 // 10MB buffer
|
|
104
|
+
});
|
|
105
|
+
return {
|
|
106
|
+
success: true,
|
|
107
|
+
data: {
|
|
108
|
+
stdout: stdout.trim(),
|
|
109
|
+
stderr: stderr.trim(),
|
|
110
|
+
exitCode: 0
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
return {
|
|
116
|
+
success: false,
|
|
117
|
+
error: `Command failed: ${error.message}`,
|
|
118
|
+
data: {
|
|
119
|
+
stdout: error.stdout || '',
|
|
120
|
+
stderr: error.stderr || '',
|
|
121
|
+
exitCode: error.code || 1
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
// ===== MEMORY OPERATIONS (Extended) =====
|
|
129
|
+
const MemoryListInputSchema = z.object({});
|
|
130
|
+
export function createMemoryListTool(context) {
|
|
131
|
+
return {
|
|
132
|
+
name: 'memory_list',
|
|
133
|
+
description: 'List all memory keys',
|
|
134
|
+
inputSchema: MemoryListInputSchema,
|
|
135
|
+
execute: async () => {
|
|
136
|
+
const keys = Array.from(context.memory.keys());
|
|
137
|
+
return {
|
|
138
|
+
success: true,
|
|
139
|
+
data: {
|
|
140
|
+
keys,
|
|
141
|
+
total: keys.length
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
const MemorySearchInputSchema = z.object({
|
|
148
|
+
query: z.string().optional().describe('Search query'),
|
|
149
|
+
tags: z.array(z.string()).optional().describe('Filter by tags'),
|
|
150
|
+
categories: z.array(z.string()).optional().describe('Filter by categories'),
|
|
151
|
+
min_importance: z.number().optional().describe('Minimum importance level'),
|
|
152
|
+
max_results: z.number().optional().describe('Maximum results to return')
|
|
153
|
+
});
|
|
154
|
+
export function createMemorySearchTool(context) {
|
|
155
|
+
return {
|
|
156
|
+
name: 'memory_search',
|
|
157
|
+
description: 'Search memory by query, tags, or categories',
|
|
158
|
+
inputSchema: MemorySearchInputSchema,
|
|
159
|
+
execute: async (params) => {
|
|
160
|
+
const maxResults = params.max_results || 10;
|
|
161
|
+
const memories = Array.from(context.memory.entries())
|
|
162
|
+
.filter(([key, value]) => {
|
|
163
|
+
if (params.query && !key.includes(params.query) && !value.content.includes(params.query)) {
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
if (params.tags && params.tags.length > 0) {
|
|
167
|
+
const memoryTags = value.tags || [];
|
|
168
|
+
if (!params.tags.every(tag => memoryTags.includes(tag))) {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (params.categories && params.categories.length > 0) {
|
|
173
|
+
if (!params.categories.includes(value.category)) {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (params.min_importance && value.importance < params.min_importance) {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
return true;
|
|
181
|
+
})
|
|
182
|
+
.slice(0, maxResults)
|
|
183
|
+
.map(([key, value]) => ({ key, ...value }));
|
|
184
|
+
return {
|
|
185
|
+
success: true,
|
|
186
|
+
data: {
|
|
187
|
+
memories,
|
|
188
|
+
total: memories.length
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
const MemoryUpdateInputSchema = z.object({
|
|
195
|
+
key: z.string().describe('Memory key to update'),
|
|
196
|
+
content: z.string().describe('New content'),
|
|
197
|
+
importance: z.number().optional().describe('Updated importance level'),
|
|
198
|
+
merge_mode: z.enum(['replace', 'append']).optional().describe('How to merge content')
|
|
199
|
+
});
|
|
200
|
+
export function createMemoryUpdateTool(context) {
|
|
201
|
+
return {
|
|
202
|
+
name: 'memory_update',
|
|
203
|
+
description: 'Update existing memory content',
|
|
204
|
+
inputSchema: MemoryUpdateInputSchema,
|
|
205
|
+
execute: async (params) => {
|
|
206
|
+
const existing = context.memory.get(params.key);
|
|
207
|
+
if (!existing) {
|
|
208
|
+
return {
|
|
209
|
+
success: false,
|
|
210
|
+
error: `Memory key not found: ${params.key}`
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
const mergeMode = params.merge_mode || 'replace';
|
|
214
|
+
const newContent = mergeMode === 'append'
|
|
215
|
+
? `${existing.content}\n\n${params.content}`
|
|
216
|
+
: params.content;
|
|
217
|
+
context.memory.set(params.key, {
|
|
218
|
+
...existing,
|
|
219
|
+
content: newContent,
|
|
220
|
+
importance: params.importance !== undefined ? params.importance : existing.importance,
|
|
221
|
+
timestamp: new Date().toISOString()
|
|
222
|
+
});
|
|
223
|
+
return {
|
|
224
|
+
success: true,
|
|
225
|
+
data: { key: params.key }
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
// ===== WEB OPERATIONS =====
|
|
231
|
+
const WebSearchInputSchema = z.object({
|
|
232
|
+
query: z.string().describe('Search query'),
|
|
233
|
+
max_results: z.number().optional().describe('Maximum number of results')
|
|
234
|
+
});
|
|
235
|
+
export function createWebSearchTool(_context) {
|
|
236
|
+
return {
|
|
237
|
+
name: 'web_search',
|
|
238
|
+
description: 'Search the web for information',
|
|
239
|
+
inputSchema: WebSearchInputSchema,
|
|
240
|
+
execute: async (params) => {
|
|
241
|
+
// Note: This is a stub implementation
|
|
242
|
+
// In production, you would integrate with a search API
|
|
243
|
+
return {
|
|
244
|
+
success: true,
|
|
245
|
+
data: {
|
|
246
|
+
results: [],
|
|
247
|
+
message: 'Web search not implemented in CLI version. Use VSCode extension for web search.'
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
const WebFetchInputSchema = z.object({
|
|
254
|
+
url: z.string().describe('URL to fetch'),
|
|
255
|
+
parse_html: z.boolean().optional().describe('Parse HTML to extract text')
|
|
256
|
+
});
|
|
257
|
+
export function createWebFetchTool(_context) {
|
|
258
|
+
return {
|
|
259
|
+
name: 'web_fetch',
|
|
260
|
+
description: 'Fetch and parse content from a URL',
|
|
261
|
+
inputSchema: WebFetchInputSchema,
|
|
262
|
+
execute: async (params) => {
|
|
263
|
+
// Note: This is a stub implementation
|
|
264
|
+
return {
|
|
265
|
+
success: false,
|
|
266
|
+
error: 'Web fetch not implemented in CLI version. Use VSCode extension for web fetch.'
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
// ===== CONTEXT OPERATIONS =====
|
|
272
|
+
const ContextResetInputSchema = z.object({
|
|
273
|
+
reason: z.string().describe('Reason for context reset'),
|
|
274
|
+
memories_stored: z.array(z.string()).optional().describe('Memory keys stored')
|
|
275
|
+
});
|
|
276
|
+
export function createContextResetTool(_context) {
|
|
277
|
+
return {
|
|
278
|
+
name: 'context_reset',
|
|
279
|
+
description: 'Request a context reset to clear conversation history',
|
|
280
|
+
inputSchema: ContextResetInputSchema,
|
|
281
|
+
execute: async (params) => {
|
|
282
|
+
// In CLI, this is informational only - actual reset happens at framework level
|
|
283
|
+
return {
|
|
284
|
+
success: true,
|
|
285
|
+
data: {
|
|
286
|
+
message: 'Context reset requested',
|
|
287
|
+
reason: params.reason,
|
|
288
|
+
memories_preserved: params.memories_stored?.length || 0
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
const ContextStatsInputSchema = z.object({});
|
|
295
|
+
export function createContextStatsTool(_context) {
|
|
296
|
+
return {
|
|
297
|
+
name: 'context_stats',
|
|
298
|
+
description: 'Get statistics about current conversation context',
|
|
299
|
+
inputSchema: ContextStatsInputSchema,
|
|
300
|
+
execute: async () => {
|
|
301
|
+
// CLI stub - actual stats would come from deity framework
|
|
302
|
+
return {
|
|
303
|
+
success: true,
|
|
304
|
+
data: {
|
|
305
|
+
message: 'Context stats not available in CLI version',
|
|
306
|
+
approximate_tokens: 0
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
// ===== SUBTASK OPERATIONS =====
|
|
313
|
+
const SubtaskCreatePlanInputSchema = z.object({
|
|
314
|
+
parent_task_id: z.string().describe('Parent task ID'),
|
|
315
|
+
subtasks: z.array(z.object({
|
|
316
|
+
title: z.string().describe('Subtask title'),
|
|
317
|
+
description: z.string().optional().describe('Subtask description')
|
|
318
|
+
})).describe('Array of subtasks')
|
|
319
|
+
});
|
|
320
|
+
export function createSubtaskCreatePlanTool(context) {
|
|
321
|
+
return {
|
|
322
|
+
name: 'subtask_create_plan',
|
|
323
|
+
description: 'Create a plan by breaking down a task into subtasks',
|
|
324
|
+
inputSchema: SubtaskCreatePlanInputSchema,
|
|
325
|
+
execute: async (params) => {
|
|
326
|
+
// Store subtasks in context (simplified implementation)
|
|
327
|
+
const subtasksKey = `subtasks_${params.parent_task_id}`;
|
|
328
|
+
const subtasksWithStatus = params.subtasks.map((st, idx) => ({
|
|
329
|
+
...st,
|
|
330
|
+
id: `${params.parent_task_id}_sub_${idx + 1}`,
|
|
331
|
+
status: 'pending'
|
|
332
|
+
}));
|
|
333
|
+
context.memory.set(subtasksKey, {
|
|
334
|
+
content: JSON.stringify(subtasksWithStatus),
|
|
335
|
+
importance: 5,
|
|
336
|
+
category: 'planning',
|
|
337
|
+
timestamp: new Date().toISOString()
|
|
338
|
+
});
|
|
339
|
+
return {
|
|
340
|
+
success: true,
|
|
341
|
+
data: {
|
|
342
|
+
parent_task_id: params.parent_task_id,
|
|
343
|
+
subtasks: subtasksWithStatus,
|
|
344
|
+
total: subtasksWithStatus.length
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
const SubtaskGetCurrentInputSchema = z.object({
|
|
351
|
+
parent_task_id: z.string().describe('Parent task ID')
|
|
352
|
+
});
|
|
353
|
+
export function createSubtaskGetCurrentTool(context) {
|
|
354
|
+
return {
|
|
355
|
+
name: 'subtask_get_current',
|
|
356
|
+
description: 'Get the current subtask to work on',
|
|
357
|
+
inputSchema: SubtaskGetCurrentInputSchema,
|
|
358
|
+
execute: async (params) => {
|
|
359
|
+
const subtasksKey = `subtasks_${params.parent_task_id}`;
|
|
360
|
+
const stored = context.memory.get(subtasksKey);
|
|
361
|
+
if (!stored) {
|
|
362
|
+
return {
|
|
363
|
+
success: false,
|
|
364
|
+
error: `No subtasks found for task: ${params.parent_task_id}`
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
const subtasks = JSON.parse(stored.content);
|
|
368
|
+
const current = subtasks.find((st) => st.status === 'pending');
|
|
369
|
+
return {
|
|
370
|
+
success: true,
|
|
371
|
+
data: {
|
|
372
|
+
current,
|
|
373
|
+
remaining: subtasks.filter((st) => st.status === 'pending').length
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
const SubtaskCompleteInputSchema = z.object({
|
|
380
|
+
parent_task_id: z.string().describe('Parent task ID'),
|
|
381
|
+
result: z.string().optional().describe('Summary of accomplishment')
|
|
382
|
+
});
|
|
383
|
+
export function createSubtaskCompleteTool(context) {
|
|
384
|
+
return {
|
|
385
|
+
name: 'subtask_complete',
|
|
386
|
+
description: 'Mark current subtask as completed',
|
|
387
|
+
inputSchema: SubtaskCompleteInputSchema,
|
|
388
|
+
execute: async (params) => {
|
|
389
|
+
const subtasksKey = `subtasks_${params.parent_task_id}`;
|
|
390
|
+
const stored = context.memory.get(subtasksKey);
|
|
391
|
+
if (!stored) {
|
|
392
|
+
return {
|
|
393
|
+
success: false,
|
|
394
|
+
error: `No subtasks found for task: ${params.parent_task_id}`
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
const subtasks = JSON.parse(stored.content);
|
|
398
|
+
const currentIdx = subtasks.findIndex((st) => st.status === 'pending');
|
|
399
|
+
if (currentIdx === -1) {
|
|
400
|
+
return {
|
|
401
|
+
success: false,
|
|
402
|
+
error: 'No pending subtasks to complete'
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
subtasks[currentIdx].status = 'completed';
|
|
406
|
+
subtasks[currentIdx].result = params.result;
|
|
407
|
+
context.memory.set(subtasksKey, {
|
|
408
|
+
...stored,
|
|
409
|
+
content: JSON.stringify(subtasks),
|
|
410
|
+
timestamp: new Date().toISOString()
|
|
411
|
+
});
|
|
412
|
+
const nextSubtask = subtasks.find((st) => st.status === 'pending');
|
|
413
|
+
return {
|
|
414
|
+
success: true,
|
|
415
|
+
data: {
|
|
416
|
+
completed: subtasks[currentIdx],
|
|
417
|
+
next: nextSubtask,
|
|
418
|
+
remaining: subtasks.filter((st) => st.status === 'pending').length
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
const SubtaskSkipInputSchema = z.object({
|
|
425
|
+
parent_task_id: z.string().describe('Parent task ID'),
|
|
426
|
+
reason: z.string().describe('Reason for skipping')
|
|
427
|
+
});
|
|
428
|
+
export function createSubtaskSkipTool(context) {
|
|
429
|
+
return {
|
|
430
|
+
name: 'subtask_skip',
|
|
431
|
+
description: 'Skip current subtask',
|
|
432
|
+
inputSchema: SubtaskSkipInputSchema,
|
|
433
|
+
execute: async (params) => {
|
|
434
|
+
const subtasksKey = `subtasks_${params.parent_task_id}`;
|
|
435
|
+
const stored = context.memory.get(subtasksKey);
|
|
436
|
+
if (!stored) {
|
|
437
|
+
return {
|
|
438
|
+
success: false,
|
|
439
|
+
error: `No subtasks found for task: ${params.parent_task_id}`
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
const subtasks = JSON.parse(stored.content);
|
|
443
|
+
const currentIdx = subtasks.findIndex((st) => st.status === 'pending');
|
|
444
|
+
if (currentIdx === -1) {
|
|
445
|
+
return {
|
|
446
|
+
success: false,
|
|
447
|
+
error: 'No pending subtasks to skip'
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
subtasks[currentIdx].status = 'skipped';
|
|
451
|
+
subtasks[currentIdx].skip_reason = params.reason;
|
|
452
|
+
context.memory.set(subtasksKey, {
|
|
453
|
+
...stored,
|
|
454
|
+
content: JSON.stringify(subtasks),
|
|
455
|
+
timestamp: new Date().toISOString()
|
|
456
|
+
});
|
|
457
|
+
return {
|
|
458
|
+
success: true,
|
|
459
|
+
data: {
|
|
460
|
+
skipped: subtasks[currentIdx],
|
|
461
|
+
reason: params.reason
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
// ===== TASK OPERATIONS =====
|
|
468
|
+
const TaskGetCurrentInputSchema = z.object({});
|
|
469
|
+
export function createTaskGetCurrentTool(context) {
|
|
470
|
+
return {
|
|
471
|
+
name: 'task_get_current',
|
|
472
|
+
description: 'Get the current task that should be executed',
|
|
473
|
+
inputSchema: TaskGetCurrentInputSchema,
|
|
474
|
+
execute: async () => {
|
|
475
|
+
if (!context.plan) {
|
|
476
|
+
return {
|
|
477
|
+
success: false,
|
|
478
|
+
error: 'No plan created yet'
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
// Prefer in_progress tasks (already started), then pending tasks
|
|
482
|
+
let currentTask = context.plan.tasks.find((t) => t.status === 'in_progress');
|
|
483
|
+
if (!currentTask) {
|
|
484
|
+
currentTask = context.plan.tasks.find((t) => t.status === 'pending');
|
|
485
|
+
if (currentTask) {
|
|
486
|
+
currentTask.status = 'in_progress';
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
return {
|
|
490
|
+
success: true,
|
|
491
|
+
data: {
|
|
492
|
+
current: currentTask,
|
|
493
|
+
total_tasks: context.plan.tasks.length,
|
|
494
|
+
completed_tasks: context.plan.tasks.filter((t) => t.status === 'completed').length
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
const TaskExecuteNextInputSchema = z.object({
|
|
501
|
+
current_task_id: z.string().describe('ID of completed task'),
|
|
502
|
+
outputs_generated: z.object({}).describe('Outputs generated')
|
|
503
|
+
});
|
|
504
|
+
export function createTaskExecuteNextTool(context) {
|
|
505
|
+
return {
|
|
506
|
+
name: 'task_execute_next',
|
|
507
|
+
description: 'Mark current task as complete and get next task',
|
|
508
|
+
inputSchema: TaskExecuteNextInputSchema,
|
|
509
|
+
execute: async (params) => {
|
|
510
|
+
if (!context.plan) {
|
|
511
|
+
return {
|
|
512
|
+
success: false,
|
|
513
|
+
error: 'No plan exists'
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
let taskIdx = context.plan.tasks.findIndex((t) => t.task_id === params.current_task_id);
|
|
517
|
+
// Fallback: if exact task_id not found, complete in_progress or first pending task
|
|
518
|
+
if (taskIdx === -1) {
|
|
519
|
+
taskIdx = context.plan.tasks.findIndex((t) => t.status === 'in_progress');
|
|
520
|
+
}
|
|
521
|
+
if (taskIdx === -1) {
|
|
522
|
+
taskIdx = context.plan.tasks.findIndex((t) => t.status === 'pending');
|
|
523
|
+
}
|
|
524
|
+
if (taskIdx === -1) {
|
|
525
|
+
return {
|
|
526
|
+
success: false,
|
|
527
|
+
error: `Task not found: ${params.current_task_id}. All ${context.plan.tasks.length} tasks are already completed or failed.`
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
context.plan.tasks[taskIdx].status = 'completed';
|
|
531
|
+
context.plan.tasks[taskIdx].outputs = params.outputs_generated;
|
|
532
|
+
const nextTask = context.plan.tasks.find((t) => t.status === 'pending');
|
|
533
|
+
return {
|
|
534
|
+
success: true,
|
|
535
|
+
data: {
|
|
536
|
+
completed_task_id: params.current_task_id,
|
|
537
|
+
next_task: nextTask,
|
|
538
|
+
progress: {
|
|
539
|
+
completed: context.plan.tasks.filter((t) => t.status === 'completed').length,
|
|
540
|
+
total: context.plan.tasks.length
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
// ===== UI OPERATIONS (Stubs for CLI) =====
|
|
548
|
+
const StartAnalysisStepInputSchema = z.object({
|
|
549
|
+
module: z.string().describe('Analysis module'),
|
|
550
|
+
title: z.string().describe('Step title'),
|
|
551
|
+
description: z.string().describe('Step description'),
|
|
552
|
+
phase: z.enum(['discovery', 'analysis', 'assessment', 'reporting']).optional()
|
|
553
|
+
});
|
|
554
|
+
export function createStartAnalysisStepTool(_context) {
|
|
555
|
+
return {
|
|
556
|
+
name: 'start_analysis_step',
|
|
557
|
+
description: 'Start a new analysis step (UI operation - stub in CLI)',
|
|
558
|
+
inputSchema: StartAnalysisStepInputSchema,
|
|
559
|
+
execute: async (params) => {
|
|
560
|
+
console.log(`[STEP] ${params.module}: ${params.title}`);
|
|
561
|
+
return {
|
|
562
|
+
success: true,
|
|
563
|
+
data: { message: 'Step started (CLI mode - no UI)' }
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
// Export all extended tools creator function
|
|
569
|
+
export function createExtendedTools(context) {
|
|
570
|
+
return [
|
|
571
|
+
createFileSearchContentTool(context),
|
|
572
|
+
createTerminalExecuteTool(context),
|
|
573
|
+
// Memory tools - REMOVED: Now handled by Deity's built-in memory_store and memory_recall
|
|
574
|
+
createWebSearchTool(context),
|
|
575
|
+
createWebFetchTool(context),
|
|
576
|
+
createContextResetTool(context),
|
|
577
|
+
createContextStatsTool(context),
|
|
578
|
+
createSubtaskCreatePlanTool(context),
|
|
579
|
+
createSubtaskGetCurrentTool(context),
|
|
580
|
+
createSubtaskCompleteTool(context),
|
|
581
|
+
createSubtaskSkipTool(context),
|
|
582
|
+
createTaskGetCurrentTool(context),
|
|
583
|
+
createTaskExecuteNextTool(context),
|
|
584
|
+
createStartAnalysisStepTool(context)
|
|
585
|
+
];
|
|
586
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool implementations for Limo CLI
|
|
3
|
+
*/
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import type { Tool, ExecutionContext } from '@limo-labs/deity';
|
|
6
|
+
export interface ToolContext {
|
|
7
|
+
workspaceRoot: string;
|
|
8
|
+
outputDir: string;
|
|
9
|
+
memory: Map<string, any>;
|
|
10
|
+
plan?: any;
|
|
11
|
+
reportSections: Map<string, string>;
|
|
12
|
+
diagrams: Map<string, any>;
|
|
13
|
+
}
|
|
14
|
+
declare const FileListInputSchema: z.ZodObject<{
|
|
15
|
+
root_path: z.ZodString;
|
|
16
|
+
pattern: z.ZodString;
|
|
17
|
+
exclude_pattern: z.ZodOptional<z.ZodString>;
|
|
18
|
+
max_results: z.ZodOptional<z.ZodNumber>;
|
|
19
|
+
}, z.core.$strip>;
|
|
20
|
+
export declare function createFileListTool(context: ToolContext): Tool<z.infer<typeof FileListInputSchema>, any>;
|
|
21
|
+
declare const FileReadInputSchema: z.ZodObject<{
|
|
22
|
+
file_path: z.ZodString;
|
|
23
|
+
}, z.core.$strip>;
|
|
24
|
+
export declare function createFileReadTool(context: ToolContext): Tool<z.infer<typeof FileReadInputSchema>, any>;
|
|
25
|
+
declare const PlanningCreateInputSchema: z.ZodObject<{
|
|
26
|
+
project_complexity: z.ZodEnum<{
|
|
27
|
+
small: "small";
|
|
28
|
+
medium: "medium";
|
|
29
|
+
large: "large";
|
|
30
|
+
}>;
|
|
31
|
+
estimated_loc: z.ZodNumber;
|
|
32
|
+
estimated_files: z.ZodNumber;
|
|
33
|
+
tasks: z.ZodArray<z.ZodObject<{
|
|
34
|
+
task_id: z.ZodString;
|
|
35
|
+
module: z.ZodString;
|
|
36
|
+
title: z.ZodString;
|
|
37
|
+
description: z.ZodString;
|
|
38
|
+
required_outputs: z.ZodObject<{
|
|
39
|
+
report_sections: z.ZodNumber;
|
|
40
|
+
code_examples: z.ZodNumber;
|
|
41
|
+
diagrams: z.ZodNumber;
|
|
42
|
+
min_word_count: z.ZodNumber;
|
|
43
|
+
}, z.core.$strip>;
|
|
44
|
+
memory_keys_to_generate: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
45
|
+
dependencies: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
46
|
+
status: z.ZodOptional<z.ZodString>;
|
|
47
|
+
}, z.core.$strip>>;
|
|
48
|
+
}, z.core.$strip>;
|
|
49
|
+
export declare function createPlanningCreateTool(context: ToolContext): Tool<z.infer<typeof PlanningCreateInputSchema>, any>;
|
|
50
|
+
declare const ReportWriteInputSchema: z.ZodObject<{
|
|
51
|
+
section_id: z.ZodString;
|
|
52
|
+
title: z.ZodString;
|
|
53
|
+
content: z.ZodString;
|
|
54
|
+
level: z.ZodOptional<z.ZodNumber>;
|
|
55
|
+
}, z.core.$strip>;
|
|
56
|
+
export declare function createReportWriteTool(context: ToolContext): Tool<z.infer<typeof ReportWriteInputSchema>, any>;
|
|
57
|
+
declare const ReportAddDiagramInputSchema: z.ZodObject<{
|
|
58
|
+
diagram_id: z.ZodString;
|
|
59
|
+
title: z.ZodString;
|
|
60
|
+
description: z.ZodOptional<z.ZodString>;
|
|
61
|
+
section_id: z.ZodOptional<z.ZodString>;
|
|
62
|
+
nodes: z.ZodArray<z.ZodAny>;
|
|
63
|
+
edges: z.ZodArray<z.ZodAny>;
|
|
64
|
+
groups: z.ZodOptional<z.ZodArray<z.ZodAny>>;
|
|
65
|
+
format: z.ZodOptional<z.ZodEnum<{
|
|
66
|
+
svg: "svg";
|
|
67
|
+
}>>;
|
|
68
|
+
source: z.ZodOptional<z.ZodString>;
|
|
69
|
+
related_files: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
70
|
+
metadata: z.ZodOptional<z.ZodAny>;
|
|
71
|
+
}, z.core.$strip>;
|
|
72
|
+
export declare function createReportAddDiagramTool(context: ToolContext): Tool<z.infer<typeof ReportAddDiagramInputSchema>, any>;
|
|
73
|
+
declare const ReportFinalizeInputSchema: z.ZodObject<{
|
|
74
|
+
executive_summary: z.ZodString;
|
|
75
|
+
recommendations: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
76
|
+
}, z.core.$strip>;
|
|
77
|
+
export declare function createReportFinalizeTool(context: ToolContext): Tool<z.infer<typeof ReportFinalizeInputSchema>, any>;
|
|
78
|
+
/**
|
|
79
|
+
* Create all tools including Deity's memory tools
|
|
80
|
+
*
|
|
81
|
+
* @param context Tool context
|
|
82
|
+
* @param ctx ExecutionContext with memory enabled
|
|
83
|
+
* @returns Array of all tools
|
|
84
|
+
*/
|
|
85
|
+
export declare function createAllTools(context: ToolContext, ctx: ExecutionContext): Tool[];
|
|
86
|
+
export {};
|