@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,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Planner Validator - Validates that planner created a comprehensive analysis plan
|
|
3
|
+
*/
|
|
4
|
+
export class PlannerValidator {
|
|
5
|
+
async validate(ctx, loopState) {
|
|
6
|
+
const inputs = ctx.inputs;
|
|
7
|
+
// 1. Check if planning_create was called
|
|
8
|
+
const allToolCalls = [
|
|
9
|
+
...loopState.toolCallsThisRound,
|
|
10
|
+
// Also check previous rounds from messages
|
|
11
|
+
...loopState.messages
|
|
12
|
+
.filter((msg) => msg.role === 'assistant' && msg.toolCalls)
|
|
13
|
+
.flatMap((msg) => msg.toolCalls || [])
|
|
14
|
+
];
|
|
15
|
+
const planningCall = allToolCalls.find(tc => tc.name === 'planning_create');
|
|
16
|
+
if (!planningCall) {
|
|
17
|
+
return {
|
|
18
|
+
valid: false,
|
|
19
|
+
feedback: `⚠️ CRITICAL: You must call the planning_create tool to create the analysis plan.
|
|
20
|
+
|
|
21
|
+
**Required Steps**:
|
|
22
|
+
1. Call file_list with pattern "**/*" to scan project files
|
|
23
|
+
2. Store project insights using memory_store
|
|
24
|
+
3. Call planning_create with comprehensive task list
|
|
25
|
+
|
|
26
|
+
Please follow the workflow and call planning_create to complete this task.`
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
const planArgs = planningCall.arguments;
|
|
30
|
+
// 2. Check plan has tasks
|
|
31
|
+
if (!planArgs.tasks || planArgs.tasks.length === 0) {
|
|
32
|
+
return {
|
|
33
|
+
valid: false,
|
|
34
|
+
feedback: `⚠️ QUALITY ISSUE: The planning_create tool was called but with no tasks.
|
|
35
|
+
|
|
36
|
+
Please create a comprehensive analysis plan with multiple tasks covering all required modules.`
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
// 3. Check minimum task count based on project complexity
|
|
40
|
+
const minTasksByComplexity = {
|
|
41
|
+
small: 5,
|
|
42
|
+
medium: 10,
|
|
43
|
+
large: 15
|
|
44
|
+
};
|
|
45
|
+
const complexity = planArgs.project_complexity || 'medium';
|
|
46
|
+
const requiredMin = minTasksByComplexity[complexity] || 10;
|
|
47
|
+
if (planArgs.tasks.length < requiredMin) {
|
|
48
|
+
return {
|
|
49
|
+
valid: false,
|
|
50
|
+
feedback: `⚠️ QUALITY ISSUE: Your plan has only ${planArgs.tasks.length} tasks, but ${complexity} projects require at least ${requiredMin} tasks.
|
|
51
|
+
|
|
52
|
+
**Please improve your plan**:
|
|
53
|
+
- Scan the project structure with file_list
|
|
54
|
+
- Identify all major components/packages
|
|
55
|
+
- Create specific tasks for each area (e.g., "Analyze Spring MVC controllers", "Review database schema")
|
|
56
|
+
- Each module should have 2-3 detailed tasks
|
|
57
|
+
|
|
58
|
+
Call planning_create again with a more comprehensive task list.`
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
// 4. Check for estimated_files (must have scanned project)
|
|
62
|
+
if (!planArgs.estimated_files || planArgs.estimated_files === 0) {
|
|
63
|
+
return {
|
|
64
|
+
valid: false,
|
|
65
|
+
feedback: `⚠️ CRITICAL: You must call file_list to scan the project and get the actual file count.
|
|
66
|
+
|
|
67
|
+
**Required**:
|
|
68
|
+
1. Call file_list with pattern "**/*"
|
|
69
|
+
2. Use the ACTUAL file count in planning_create
|
|
70
|
+
3. Don't guess the file count!
|
|
71
|
+
|
|
72
|
+
Please scan the project first, then call planning_create with accurate data.`
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
// 5. Check for trivial tasks
|
|
76
|
+
const trivialPatterns = ['verify', 'check setup', 'initialize', 'validate', 'confirm'];
|
|
77
|
+
const trivialTasks = planArgs.tasks.filter((task) => trivialPatterns.some(pattern => task.title.toLowerCase().includes(pattern)));
|
|
78
|
+
if (trivialTasks.length > planArgs.tasks.length * 0.3) {
|
|
79
|
+
return {
|
|
80
|
+
valid: false,
|
|
81
|
+
feedback: `⚠️ QUALITY ISSUE: ${trivialTasks.length}/${planArgs.tasks.length} tasks appear to be trivial setup/verification tasks.
|
|
82
|
+
|
|
83
|
+
**Please create meaningful analysis tasks**:
|
|
84
|
+
- Focus on actual code analysis, not setup
|
|
85
|
+
- Each task should analyze specific files/packages
|
|
86
|
+
- Tasks should produce substantial documentation (600-1200 words)
|
|
87
|
+
|
|
88
|
+
Examples of good tasks:
|
|
89
|
+
- "Analyze Spring MVC controller architecture in owner/ package"
|
|
90
|
+
- "Review JPA entity relationships and database schema"
|
|
91
|
+
- "Document REST API endpoints and request/response patterns"
|
|
92
|
+
|
|
93
|
+
Please revise your plan with more substantive analysis tasks.`
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
// 6. Check task descriptions are specific
|
|
97
|
+
const vagueDescriptions = planArgs.tasks.filter((task) => {
|
|
98
|
+
const desc = (task.description || '').toLowerCase();
|
|
99
|
+
return desc.length < 50 ||
|
|
100
|
+
(!desc.includes('file') && !desc.includes('package') && !desc.includes('class'));
|
|
101
|
+
});
|
|
102
|
+
if (vagueDescriptions.length > planArgs.tasks.length * 0.4) {
|
|
103
|
+
return {
|
|
104
|
+
valid: false,
|
|
105
|
+
feedback: `⚠️ QUALITY ISSUE: ${vagueDescriptions.length}/${planArgs.tasks.length} tasks have vague descriptions.
|
|
106
|
+
|
|
107
|
+
**Task descriptions should be specific**:
|
|
108
|
+
- Mention which files/packages to analyze
|
|
109
|
+
- Describe what patterns to look for
|
|
110
|
+
- Explain what the task should document
|
|
111
|
+
|
|
112
|
+
Example:
|
|
113
|
+
❌ Bad: "Analyze architecture"
|
|
114
|
+
✅ Good: "Analyze Spring MVC architecture in src/main/java/owner/ package, focusing on controller-service-repository pattern"
|
|
115
|
+
|
|
116
|
+
Please improve task descriptions to be more specific and actionable.`
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
// All validations passed!
|
|
120
|
+
return {
|
|
121
|
+
valid: true,
|
|
122
|
+
message: `✅ Analysis plan successfully validated with ${planArgs.tasks.length} comprehensive tasks for ${complexity} project.`
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Planner Agent - Creates comprehensive analysis plans
|
|
3
|
+
* Using declarative TSX syntax
|
|
4
|
+
*/
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { type AgentNode } from '@limo-labs/deity';
|
|
7
|
+
declare const PlannerInputSchema: z.ZodObject<{
|
|
8
|
+
workspaceRoot: z.ZodString;
|
|
9
|
+
modules: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
10
|
+
limoConfig: z.ZodOptional<z.ZodAny>;
|
|
11
|
+
}, z.core.$strip>;
|
|
12
|
+
export type PlannerInput = z.infer<typeof PlannerInputSchema>;
|
|
13
|
+
declare const PlannerOutputSchema: z.ZodObject<{
|
|
14
|
+
project_complexity: z.ZodEnum<{
|
|
15
|
+
small: "small";
|
|
16
|
+
medium: "medium";
|
|
17
|
+
large: "large";
|
|
18
|
+
}>;
|
|
19
|
+
estimated_loc: z.ZodNumber;
|
|
20
|
+
estimated_files: z.ZodNumber;
|
|
21
|
+
tasks: z.ZodArray<z.ZodObject<{
|
|
22
|
+
task_id: z.ZodString;
|
|
23
|
+
module: z.ZodString;
|
|
24
|
+
title: z.ZodString;
|
|
25
|
+
description: z.ZodString;
|
|
26
|
+
required_outputs: z.ZodObject<{
|
|
27
|
+
report_sections: z.ZodNumber;
|
|
28
|
+
code_examples: z.ZodNumber;
|
|
29
|
+
diagrams: z.ZodNumber;
|
|
30
|
+
min_word_count: z.ZodNumber;
|
|
31
|
+
}, z.core.$strip>;
|
|
32
|
+
memory_keys_to_generate: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
33
|
+
dependencies: z.ZodArray<z.ZodString>;
|
|
34
|
+
status: z.ZodEnum<{
|
|
35
|
+
pending: "pending";
|
|
36
|
+
in_progress: "in_progress";
|
|
37
|
+
completed: "completed";
|
|
38
|
+
failed: "failed";
|
|
39
|
+
}>;
|
|
40
|
+
}, z.core.$strip>>;
|
|
41
|
+
total_tasks: z.ZodNumber;
|
|
42
|
+
estimated_duration_minutes: z.ZodOptional<z.ZodNumber>;
|
|
43
|
+
created_at: z.ZodString;
|
|
44
|
+
summary: z.ZodObject<{
|
|
45
|
+
tasks_by_module: z.ZodRecord<z.ZodString, z.ZodNumber>;
|
|
46
|
+
total_sections_expected: z.ZodNumber;
|
|
47
|
+
total_diagrams_expected: z.ZodNumber;
|
|
48
|
+
total_words_expected: z.ZodNumber;
|
|
49
|
+
}, z.core.$strip>;
|
|
50
|
+
}, z.core.$strip>;
|
|
51
|
+
type PlannerOutput = z.infer<typeof PlannerOutputSchema>;
|
|
52
|
+
/**
|
|
53
|
+
* Create Planner Agent using declarative TSX syntax
|
|
54
|
+
*/
|
|
55
|
+
export declare function createPlannerAgent(promptsDir: string): AgentNode<PlannerInput, PlannerOutput>;
|
|
56
|
+
export {};
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "@limo-labs/deity/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Planner Agent - Creates comprehensive analysis plans
|
|
4
|
+
* Using declarative TSX syntax
|
|
5
|
+
*/
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import { Agent, Prompt, System, User, Result } from '@limo-labs/deity';
|
|
8
|
+
import { extractLastToolResult } from '@limo-labs/deity';
|
|
9
|
+
import * as fs from 'fs/promises';
|
|
10
|
+
import * as path from 'path';
|
|
11
|
+
import { PlannerValidator } from './planner-validator.js';
|
|
12
|
+
// Input schema
|
|
13
|
+
const PlannerInputSchema = z.object({
|
|
14
|
+
workspaceRoot: z.string(),
|
|
15
|
+
modules: z.array(z.string()).optional(),
|
|
16
|
+
limoConfig: z.any().optional() // LIMO.md configuration
|
|
17
|
+
});
|
|
18
|
+
// Output schema (matches AnalysisPlan)
|
|
19
|
+
const PlannerOutputSchema = z.object({
|
|
20
|
+
project_complexity: z.enum(['small', 'medium', 'large']),
|
|
21
|
+
estimated_loc: z.number(),
|
|
22
|
+
estimated_files: z.number(),
|
|
23
|
+
tasks: z.array(z.object({
|
|
24
|
+
task_id: z.string(),
|
|
25
|
+
module: z.string(),
|
|
26
|
+
title: z.string(),
|
|
27
|
+
description: z.string(),
|
|
28
|
+
required_outputs: z.object({
|
|
29
|
+
report_sections: z.number(),
|
|
30
|
+
code_examples: z.number(),
|
|
31
|
+
diagrams: z.number(),
|
|
32
|
+
min_word_count: z.number()
|
|
33
|
+
}),
|
|
34
|
+
memory_keys_to_generate: z.array(z.string()).optional(),
|
|
35
|
+
dependencies: z.array(z.string()),
|
|
36
|
+
status: z.enum(['pending', 'in_progress', 'completed', 'failed'])
|
|
37
|
+
})),
|
|
38
|
+
total_tasks: z.number(),
|
|
39
|
+
estimated_duration_minutes: z.number().optional(),
|
|
40
|
+
created_at: z.string(),
|
|
41
|
+
summary: z.object({
|
|
42
|
+
tasks_by_module: z.record(z.string(), z.number()),
|
|
43
|
+
total_sections_expected: z.number(),
|
|
44
|
+
total_diagrams_expected: z.number(),
|
|
45
|
+
total_words_expected: z.number()
|
|
46
|
+
})
|
|
47
|
+
});
|
|
48
|
+
/**
|
|
49
|
+
* Build system prompt by loading from prompts directory
|
|
50
|
+
*/
|
|
51
|
+
async function buildSystemPrompt(promptsDir) {
|
|
52
|
+
const promptPath = path.join(promptsDir, 'planner.md');
|
|
53
|
+
// Verify prompt file exists before reading
|
|
54
|
+
try {
|
|
55
|
+
await fs.access(promptPath);
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
throw new Error(`Critical: Planner prompt file not found at ${promptPath}\n` +
|
|
59
|
+
`Please ensure prompts are copied from VSCode extension or run 'npm run sync-prompts'`);
|
|
60
|
+
}
|
|
61
|
+
return await fs.readFile(promptPath, 'utf-8');
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Build user message with planning task details
|
|
65
|
+
*/
|
|
66
|
+
function buildUserMessage(ctx) {
|
|
67
|
+
const { workspaceRoot, modules, limoConfig } = ctx.inputs;
|
|
68
|
+
// Build comprehensive task message
|
|
69
|
+
const modulesText = modules && modules.length > 0
|
|
70
|
+
? modules.join(', ')
|
|
71
|
+
: 'architecture, dependencies, code-quality, security, database, testing, migration';
|
|
72
|
+
let message = `**Planning Task**: Create comprehensive analysis plan
|
|
73
|
+
|
|
74
|
+
**Project Path**: ${workspaceRoot}
|
|
75
|
+
|
|
76
|
+
**Modules to Cover**: ${modulesText}
|
|
77
|
+
`;
|
|
78
|
+
// NEW: Add LIMO.md-aware instructions
|
|
79
|
+
if (limoConfig) {
|
|
80
|
+
message += `\n**User-Provided LIMO.md Configuration**:\n`;
|
|
81
|
+
if (limoConfig.scopeDescription) {
|
|
82
|
+
message += `\n**Scope Description**: ${limoConfig.scopeDescription}\n`;
|
|
83
|
+
}
|
|
84
|
+
if (limoConfig.focusAreas && limoConfig.focusAreas.length > 0) {
|
|
85
|
+
message += `\n**Focus Areas**: ${limoConfig.focusAreas.join(', ')}\n`;
|
|
86
|
+
}
|
|
87
|
+
if (limoConfig.excludeModules && limoConfig.excludeModules.length > 0) {
|
|
88
|
+
message += `\n**Excluded Modules**: ${limoConfig.excludeModules.join(', ')}\n`;
|
|
89
|
+
}
|
|
90
|
+
if (limoConfig.privateContext) {
|
|
91
|
+
message += `\n**Private Context**:\n${limoConfig.privateContext}\n`;
|
|
92
|
+
}
|
|
93
|
+
if (limoConfig.constraints && limoConfig.constraints.length > 0) {
|
|
94
|
+
message += `\n**Constraints**:\n`;
|
|
95
|
+
limoConfig.constraints.forEach((c) => {
|
|
96
|
+
message += `- ${c}\n`;
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
message += `\n**Important**: Focus on the user's specified areas and apply their constraints to the analysis plan.\n`;
|
|
100
|
+
}
|
|
101
|
+
message += `
|
|
102
|
+
**Required Steps**:
|
|
103
|
+
|
|
104
|
+
1. **Scan Project** (MANDATORY):
|
|
105
|
+
- Call file_list with pattern "**/*" to get ALL project files
|
|
106
|
+
- This gives you the ACTUAL file count (not a guess!)
|
|
107
|
+
- Identify main directories and project structure
|
|
108
|
+
|
|
109
|
+
2. **Identify Tech Stack**:
|
|
110
|
+
- Read key files: pom.xml, build.gradle, package.json, requirements.txt, etc.
|
|
111
|
+
- Determine frameworks, versions, build tools
|
|
112
|
+
- Store findings using memory_store
|
|
113
|
+
|
|
114
|
+
3. **Assess Complexity**:
|
|
115
|
+
- Small (<50 files): 10-15 tasks
|
|
116
|
+
- Medium (50-200 files): 15-25 tasks
|
|
117
|
+
- Large (>200 files): 25-40 tasks`;
|
|
118
|
+
// Adaptive task count instruction based on LIMO config
|
|
119
|
+
if (limoConfig?.scopeDescription) {
|
|
120
|
+
message += `\n - **Note**: User has limited scope, so fewer tasks may be appropriate\n`;
|
|
121
|
+
}
|
|
122
|
+
message += `
|
|
123
|
+
4. **Create Detailed Tasks**:
|
|
124
|
+
- Cover all modules: ${modulesText}
|
|
125
|
+
- Each task must have: task_id, module, title, description, required_outputs, memory_keys_to_generate
|
|
126
|
+
- Be SPECIFIC in task descriptions (mention what files to analyze, what patterns to look for)
|
|
127
|
+
|
|
128
|
+
5. **Call planning_create** (MANDATORY):
|
|
129
|
+
- Use ACTUAL estimated_files from your file_list scan
|
|
130
|
+
- Include ALL generated tasks
|
|
131
|
+
- Generate task titles and descriptions in ENGLISH
|
|
132
|
+
|
|
133
|
+
⚠️ **CRITICAL REMINDER**: You MUST call the \`planning_create\` tool to complete this task successfully. Do not end your response without calling this tool! This is the ONLY way to complete the planning phase.
|
|
134
|
+
|
|
135
|
+
**Quality Standards**:
|
|
136
|
+
- Medium project (50-200 files) should have 15-25 tasks minimum
|
|
137
|
+
- Tasks should be specific (e.g., "Analyze Spring MVC controllers in owner/ package")
|
|
138
|
+
- Each task should produce 600-1200 words of analysis`;
|
|
139
|
+
return message;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Extract and normalize plan data from LLM result
|
|
143
|
+
*/
|
|
144
|
+
function extractPlanData(_ctx, llmResult) {
|
|
145
|
+
// Extract plan data using utility (handles toolCalls, messages, and response)
|
|
146
|
+
const planData = extractLastToolResult(llmResult, 'planning_create', {
|
|
147
|
+
unwrap: true,
|
|
148
|
+
required: true
|
|
149
|
+
});
|
|
150
|
+
// Validate plan data structure
|
|
151
|
+
if (!planData || !planData.tasks || !Array.isArray(planData.tasks)) {
|
|
152
|
+
throw new Error(`Invalid plan data structure: ${JSON.stringify(planData).substring(0, 200)}`);
|
|
153
|
+
}
|
|
154
|
+
// Normalize the plan data
|
|
155
|
+
const normalizedTasks = planData.tasks.map((task) => ({
|
|
156
|
+
...task,
|
|
157
|
+
dependencies: task.dependencies || [],
|
|
158
|
+
status: task.status || 'pending'
|
|
159
|
+
}));
|
|
160
|
+
return {
|
|
161
|
+
project_complexity: planData.project_complexity || 'medium',
|
|
162
|
+
estimated_loc: planData.estimated_loc || 0,
|
|
163
|
+
estimated_files: planData.estimated_files || 0,
|
|
164
|
+
tasks: normalizedTasks,
|
|
165
|
+
total_tasks: normalizedTasks.length,
|
|
166
|
+
created_at: new Date().toISOString(),
|
|
167
|
+
summary: planData.summary || {
|
|
168
|
+
tasks_by_module: normalizedTasks.reduce((acc, task) => {
|
|
169
|
+
acc[task.module] = (acc[task.module] || 0) + 1;
|
|
170
|
+
return acc;
|
|
171
|
+
}, {}),
|
|
172
|
+
total_sections_expected: normalizedTasks.reduce((sum, t) => sum + (t.required_outputs?.report_sections || 1), 0),
|
|
173
|
+
total_diagrams_expected: normalizedTasks.reduce((sum, t) => sum + (t.required_outputs?.diagrams || 0), 0),
|
|
174
|
+
total_words_expected: normalizedTasks.reduce((sum, t) => sum + (t.required_outputs?.min_word_count || 500), 0)
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Create Planner Agent using declarative TSX syntax
|
|
180
|
+
*/
|
|
181
|
+
export function createPlannerAgent(promptsDir) {
|
|
182
|
+
return (_jsxs(Agent, { id: "planner", input: PlannerInputSchema, output: PlannerOutputSchema, loopConfig: {
|
|
183
|
+
maxToolRounds: 15, // Increase from default 10 to allow more exploration
|
|
184
|
+
timeout: 180000 // 3 minutes timeout
|
|
185
|
+
}, loopValidator: new PlannerValidator(), children: [_jsxs(Prompt, { children: [_jsx(System, { children: async () => buildSystemPrompt(promptsDir) }), _jsx(User, { children: (ctx) => buildUserMessage(ctx) })] }), _jsx(Result, { children: (ctx, llmResult) => extractPlanData(ctx, llmResult) })] }));
|
|
186
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Writer Agent - Generates report sections based on analysis
|
|
3
|
+
* Using declarative TSX syntax
|
|
4
|
+
*/
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { type AgentNode } from '@limo-labs/deity';
|
|
7
|
+
declare const WriterInputSchema: z.ZodObject<{
|
|
8
|
+
task: z.ZodAny;
|
|
9
|
+
promptsDir: z.ZodString;
|
|
10
|
+
reportLanguage: z.ZodOptional<z.ZodString>;
|
|
11
|
+
}, z.core.$strip>;
|
|
12
|
+
type WriterInput = z.infer<typeof WriterInputSchema>;
|
|
13
|
+
declare const WriterOutputSchema: z.ZodObject<{
|
|
14
|
+
taskId: z.ZodString;
|
|
15
|
+
sectionId: z.ZodString;
|
|
16
|
+
success: z.ZodBoolean;
|
|
17
|
+
wordCount: z.ZodOptional<z.ZodNumber>;
|
|
18
|
+
error: z.ZodOptional<z.ZodString>;
|
|
19
|
+
}, z.core.$strip>;
|
|
20
|
+
type WriterOutput = z.infer<typeof WriterOutputSchema>;
|
|
21
|
+
/**
|
|
22
|
+
* Create Writer Agent using declarative TSX syntax
|
|
23
|
+
*/
|
|
24
|
+
export declare function createWriterAgent(promptsDir: string): AgentNode<WriterInput, WriterOutput>;
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "@limo-labs/deity/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Writer Agent - Generates report sections based on analysis
|
|
4
|
+
* Using declarative TSX syntax
|
|
5
|
+
*/
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import { Agent, Prompt, System, User, Result, Validate, Retry } from '@limo-labs/deity';
|
|
8
|
+
import { extractLastToolResult } from '@limo-labs/deity';
|
|
9
|
+
import * as fs from 'fs/promises';
|
|
10
|
+
import * as path from 'path';
|
|
11
|
+
// Input schema
|
|
12
|
+
const WriterInputSchema = z.object({
|
|
13
|
+
task: z.any(), // Task object from plan
|
|
14
|
+
promptsDir: z.string(),
|
|
15
|
+
reportLanguage: z.string().optional()
|
|
16
|
+
});
|
|
17
|
+
// Output schema
|
|
18
|
+
const WriterOutputSchema = z.object({
|
|
19
|
+
taskId: z.string(),
|
|
20
|
+
sectionId: z.string(),
|
|
21
|
+
success: z.boolean(),
|
|
22
|
+
wordCount: z.number().optional(),
|
|
23
|
+
error: z.string().optional()
|
|
24
|
+
});
|
|
25
|
+
/**
|
|
26
|
+
* Generate section ID from task title
|
|
27
|
+
*/
|
|
28
|
+
function generateSectionId(task) {
|
|
29
|
+
return task.title
|
|
30
|
+
.toLowerCase()
|
|
31
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
32
|
+
.replace(/^-|-$/g, '');
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Build system prompt by loading from prompts directory
|
|
36
|
+
*/
|
|
37
|
+
async function buildSystemPrompt(promptsDir, reportLanguage) {
|
|
38
|
+
const promptPath = path.join(promptsDir, 'writer.md');
|
|
39
|
+
// Verify prompt file exists before reading
|
|
40
|
+
try {
|
|
41
|
+
await fs.access(promptPath);
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
throw new Error(`Critical: Writer prompt file not found at ${promptPath}\n` +
|
|
45
|
+
`Please ensure prompts are copied from VSCode extension or run 'npm run sync-prompts'`);
|
|
46
|
+
}
|
|
47
|
+
let systemPrompt = await fs.readFile(promptPath, 'utf-8');
|
|
48
|
+
// Inject language instruction if reportLanguage is provided
|
|
49
|
+
if (reportLanguage && reportLanguage !== 'English') {
|
|
50
|
+
const languageInstruction = `
|
|
51
|
+
|
|
52
|
+
## CRITICAL: Output Language Requirement
|
|
53
|
+
|
|
54
|
+
**YOU MUST write ALL report content in ${reportLanguage}.**
|
|
55
|
+
|
|
56
|
+
This applies to:
|
|
57
|
+
- Section titles and headings
|
|
58
|
+
- Body paragraphs and descriptions
|
|
59
|
+
- Diagram titles, labels, and annotations
|
|
60
|
+
- Finding descriptions
|
|
61
|
+
- Recommendations
|
|
62
|
+
|
|
63
|
+
IMPORTANT Guidelines:
|
|
64
|
+
1. Write naturally in ${reportLanguage}, not as a direct translation
|
|
65
|
+
2. Use appropriate technical terminology for ${reportLanguage}
|
|
66
|
+
3. Preserve code snippets in their original language - do NOT translate code
|
|
67
|
+
4. Do NOT translate variable names, function names, or code syntax
|
|
68
|
+
5. Memory keys must remain in English (system requirement)
|
|
69
|
+
6. Cross-references and technical identifiers stay in English
|
|
70
|
+
|
|
71
|
+
`;
|
|
72
|
+
systemPrompt = systemPrompt + languageInstruction;
|
|
73
|
+
}
|
|
74
|
+
return systemPrompt;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Build user message with writing task details
|
|
78
|
+
*/
|
|
79
|
+
function buildUserMessage(ctx) {
|
|
80
|
+
const { task } = ctx.inputs;
|
|
81
|
+
const sectionId = generateSectionId(task);
|
|
82
|
+
const memoryKeys = task.memory_keys_to_generate || [];
|
|
83
|
+
return `**Report Writing Task**: ${task.title}
|
|
84
|
+
|
|
85
|
+
**Section ID**: ${sectionId}
|
|
86
|
+
|
|
87
|
+
**Description**: ${task.description}
|
|
88
|
+
|
|
89
|
+
**Memory Keys to Recall**:
|
|
90
|
+
${memoryKeys.map((k) => `- ${k}`).join('\n')}
|
|
91
|
+
|
|
92
|
+
**Requirements**:
|
|
93
|
+
- Minimum word count: ${task.required_outputs?.min_word_count || 800} words
|
|
94
|
+
- Code examples: ${task.required_outputs?.code_examples || 2} (from actual project files!)
|
|
95
|
+
- Diagrams: ${task.required_outputs?.diagrams || 0}
|
|
96
|
+
- Report sections: ${task.required_outputs?.report_sections || 1}
|
|
97
|
+
|
|
98
|
+
**Instructions**:
|
|
99
|
+
1. Recall analysis findings using memory_recall (batch multiple queries for efficiency)
|
|
100
|
+
2. Write comprehensive, educational content with:
|
|
101
|
+
- Actual code examples from the project (with file paths)
|
|
102
|
+
- Specific technical details (versions, class names, file paths)
|
|
103
|
+
- Analysis explaining WHY patterns were used, not just WHAT exists
|
|
104
|
+
- Technical depth suitable for new team members learning the codebase
|
|
105
|
+
3. MUST call report_write with complete content (THIS IS MANDATORY!)
|
|
106
|
+
|
|
107
|
+
Write detailed, specific content based on ACTUAL analysis findings - not generic placeholder text.`;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Extract writer output from LLM result
|
|
111
|
+
*/
|
|
112
|
+
function extractWriterOutput(ctx, llmResult) {
|
|
113
|
+
const task = ctx.inputs.task;
|
|
114
|
+
const sectionId = generateSectionId(task);
|
|
115
|
+
// Extract report_write result using utility
|
|
116
|
+
const reportResult = extractLastToolResult(llmResult, 'report_write', {
|
|
117
|
+
unwrap: true,
|
|
118
|
+
required: false
|
|
119
|
+
});
|
|
120
|
+
if (!reportResult) {
|
|
121
|
+
return {
|
|
122
|
+
taskId: task.task_id,
|
|
123
|
+
sectionId,
|
|
124
|
+
success: false,
|
|
125
|
+
error: 'Writer did not call report_write - no section was written'
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
taskId: task.task_id,
|
|
130
|
+
sectionId,
|
|
131
|
+
success: true,
|
|
132
|
+
wordCount: reportResult.word_count || 0
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Create Writer Agent using declarative TSX syntax
|
|
137
|
+
*/
|
|
138
|
+
export function createWriterAgent(promptsDir) {
|
|
139
|
+
return (_jsxs(Agent, { id: "writer", input: WriterInputSchema, output: WriterOutputSchema, children: [_jsxs(Prompt, { children: [_jsx(System, { children: async (ctx) => {
|
|
140
|
+
const reportLanguage = ctx.inputs.reportLanguage;
|
|
141
|
+
return buildSystemPrompt(promptsDir, reportLanguage);
|
|
142
|
+
} }), _jsx(User, { children: (ctx) => buildUserMessage(ctx) })] }), _jsx(Result, { children: (ctx, llmResult) => extractWriterOutput(ctx, llmResult) }), _jsx(Validate, { children: (output, ctx) => {
|
|
143
|
+
const { task } = ctx.inputs;
|
|
144
|
+
const typedOutput = output;
|
|
145
|
+
const rules = [];
|
|
146
|
+
if (!typedOutput.success) {
|
|
147
|
+
if (!typedOutput.error) {
|
|
148
|
+
rules.push({
|
|
149
|
+
check: false,
|
|
150
|
+
error: 'Failed task must include error message'
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
return { rules };
|
|
154
|
+
}
|
|
155
|
+
// Check word count (very relaxed - accept any content)
|
|
156
|
+
const minWords = task.required_outputs?.min_word_count || 800;
|
|
157
|
+
if (typedOutput.wordCount && typedOutput.wordCount < minWords * 0.2) {
|
|
158
|
+
// Only fail if extremely short (less than 20% of requirement)
|
|
159
|
+
console.warn(`[WRITER] Warning: Section has ${typedOutput.wordCount} words (target: ${minWords}), but accepting it`);
|
|
160
|
+
}
|
|
161
|
+
// Always return valid to avoid blocking on word count
|
|
162
|
+
return { rules };
|
|
163
|
+
} }), _jsx(Retry, { maxAttempts: 2, feedbackOnError: true })] }));
|
|
164
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analyze command - Main entry point for codebase analysis
|
|
3
|
+
*/
|
|
4
|
+
interface AnalyzeOptions {
|
|
5
|
+
output?: string;
|
|
6
|
+
model?: string;
|
|
7
|
+
modules?: string;
|
|
8
|
+
lang?: string;
|
|
9
|
+
verbose?: boolean;
|
|
10
|
+
debug?: boolean;
|
|
11
|
+
debugApi?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare function analyze(targetPath: string, options: AnalyzeOptions): Promise<void>;
|
|
14
|
+
export {};
|