@ebowwa/daemons 0.5.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 +264 -0
- package/dist/bin/discord-cli.js +124118 -0
- package/dist/bin/manager.js +143 -0
- package/dist/bin/telegram-cli.js +124114 -0
- package/dist/index.js +125340 -0
- package/package.json +94 -0
- package/src/agent.ts +111 -0
- package/src/channels/base.ts +573 -0
- package/src/channels/discord.ts +306 -0
- package/src/channels/index.ts +169 -0
- package/src/channels/telegram.ts +315 -0
- package/src/daemon.ts +534 -0
- package/src/hooks.ts +97 -0
- package/src/index.ts +111 -0
- package/src/memory.ts +369 -0
- package/src/skills/coding/commit.ts +202 -0
- package/src/skills/coding/execute-subtask.ts +136 -0
- package/src/skills/coding/fix-issues.ts +126 -0
- package/src/skills/coding/index.ts +26 -0
- package/src/skills/coding/plan-task.ts +158 -0
- package/src/skills/coding/quality-check.ts +155 -0
- package/src/skills/index.ts +65 -0
- package/src/skills/registry.ts +380 -0
- package/src/skills/shared/index.ts +21 -0
- package/src/skills/shared/reflect.ts +156 -0
- package/src/skills/shared/review.ts +201 -0
- package/src/skills/shared/trajectory.ts +319 -0
- package/src/skills/trading/analyze-market.ts +144 -0
- package/src/skills/trading/check-risk.ts +176 -0
- package/src/skills/trading/execute-trade.ts +185 -0
- package/src/skills/trading/generate-signal.ts +160 -0
- package/src/skills/trading/index.ts +26 -0
- package/src/skills/trading/monitor-position.ts +179 -0
- package/src/skills/types.ts +235 -0
- package/src/skills/workflows.ts +340 -0
- package/src/state.ts +77 -0
- package/src/tools.ts +134 -0
- package/src/types.ts +314 -0
- package/src/workflow.ts +341 -0
- package/src/workflows/coding.ts +580 -0
- package/src/workflows/index.ts +61 -0
- package/src/workflows/trading.ts +608 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GLM Daemon - Execute Subtask Skill
|
|
3
|
+
*
|
|
4
|
+
* Work on the current subtask from the plan.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import type { Skill, SkillContext, SkillResult } from "../types.js";
|
|
9
|
+
import { skillRegistry } from "../registry.js";
|
|
10
|
+
import type { Subtask } from "./plan-task.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Execute subtask parameters
|
|
14
|
+
*/
|
|
15
|
+
const ExecuteSubtaskParams = z.object({
|
|
16
|
+
/** Specific subtask ID to execute (defaults to current) */
|
|
17
|
+
subtaskId: z.string().optional(),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Execute subtask result data
|
|
22
|
+
*/
|
|
23
|
+
interface ExecuteSubtaskData {
|
|
24
|
+
subtaskId: string;
|
|
25
|
+
title: string;
|
|
26
|
+
result: string;
|
|
27
|
+
completed: boolean;
|
|
28
|
+
nextSubtaskId: string | null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Execute subtask skill
|
|
33
|
+
*/
|
|
34
|
+
export const executeSubtaskSkill: Skill<z.infer<typeof ExecuteSubtaskParams>, ExecuteSubtaskData> = {
|
|
35
|
+
id: "/execute-subtask",
|
|
36
|
+
name: "Execute Subtask",
|
|
37
|
+
description: "Work on the current subtask using available tools.",
|
|
38
|
+
paramsSchema: ExecuteSubtaskParams,
|
|
39
|
+
tags: ["coding", "execution"],
|
|
40
|
+
parallelizable: false,
|
|
41
|
+
requires: ["subtasks"],
|
|
42
|
+
|
|
43
|
+
async execute(params, context): Promise<SkillResult<ExecuteSubtaskData>> {
|
|
44
|
+
// Get subtask to execute
|
|
45
|
+
const subtaskId = params?.subtaskId || context.state.slam.currentSubtask;
|
|
46
|
+
|
|
47
|
+
if (!subtaskId) {
|
|
48
|
+
console.log("[/execute-subtask] No current subtask, all complete");
|
|
49
|
+
return {
|
|
50
|
+
success: true,
|
|
51
|
+
data: {
|
|
52
|
+
subtaskId: "",
|
|
53
|
+
title: "",
|
|
54
|
+
result: "All subtasks complete",
|
|
55
|
+
completed: true,
|
|
56
|
+
nextSubtaskId: null,
|
|
57
|
+
},
|
|
58
|
+
nextSkill: "/quality-check",
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const subtask = context.state.slam.subtasks.find((st) => st.id === subtaskId) as Subtask | undefined;
|
|
63
|
+
|
|
64
|
+
if (!subtask) {
|
|
65
|
+
return {
|
|
66
|
+
success: false,
|
|
67
|
+
error: `Subtask not found: ${subtaskId}`,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
console.log(`[/execute-subtask] Working on: ${subtask.title}`);
|
|
72
|
+
|
|
73
|
+
const prompt = `Work on this subtask: ${subtask.title}
|
|
74
|
+
|
|
75
|
+
${subtask.description}
|
|
76
|
+
|
|
77
|
+
Use available tools to complete the work.
|
|
78
|
+
Focus on making concrete progress.
|
|
79
|
+
|
|
80
|
+
When done, briefly summarize what was accomplished.`;
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
const result = await context.agent.execute(prompt);
|
|
84
|
+
|
|
85
|
+
// Mark subtask as completed
|
|
86
|
+
const completedSubtasks = [...new Set([...context.state.slam.completedSubtasks, subtaskId])];
|
|
87
|
+
const updatedSubtasks = context.state.slam.subtasks.map((st) =>
|
|
88
|
+
st.id === subtaskId ? { ...st, status: "completed" as const, result } : st
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
// Find next subtask (that isn't completed and has dependencies met)
|
|
92
|
+
const nextSubtask = context.state.slam.subtasks.find(
|
|
93
|
+
(st) => !completedSubtasks.includes(st.id) && (!st.dependencies || st.dependencies.every((d) => completedSubtasks.includes(d)))
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const allComplete = completedSubtasks.length === context.state.slam.subtasks.length;
|
|
97
|
+
|
|
98
|
+
console.log(`[/execute-subtask] Completed: ${subtask.title}`);
|
|
99
|
+
console.log(`[/execute-subtask] Progress: ${completedSubtasks.length}/${context.state.slam.subtasks.length}`);
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
success: true,
|
|
103
|
+
data: {
|
|
104
|
+
subtaskId,
|
|
105
|
+
title: subtask.title,
|
|
106
|
+
result: result.substring(0, 500),
|
|
107
|
+
completed: allComplete,
|
|
108
|
+
nextSubtaskId: nextSubtask?.id || null,
|
|
109
|
+
},
|
|
110
|
+
stateUpdates: {
|
|
111
|
+
slam: {
|
|
112
|
+
...context.state.slam,
|
|
113
|
+
subtasks: updatedSubtasks,
|
|
114
|
+
currentSubtask: nextSubtask?.id || null,
|
|
115
|
+
completedSubtasks,
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
contextUpdates: {
|
|
119
|
+
currentSubtask: nextSubtask?.id || null,
|
|
120
|
+
lastCompletedSubtask: subtaskId,
|
|
121
|
+
},
|
|
122
|
+
nextSkill: allComplete ? "/quality-check" : undefined,
|
|
123
|
+
};
|
|
124
|
+
} catch (error) {
|
|
125
|
+
return {
|
|
126
|
+
success: false,
|
|
127
|
+
error: error instanceof Error ? error.message : String(error),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
skillRegistry.register(executeSubtaskSkill);
|
|
134
|
+
|
|
135
|
+
export { ExecuteSubtaskParams };
|
|
136
|
+
export type { ExecuteSubtaskData };
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GLM Daemon - Fix Issues Skill
|
|
3
|
+
*
|
|
4
|
+
* Fix issues found during quality check or review.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import type { Skill, SkillContext, SkillResult } from "../types.js";
|
|
9
|
+
import { skillRegistry } from "../registry.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Fix issues parameters
|
|
13
|
+
*/
|
|
14
|
+
const FixIssuesParams = z.object({
|
|
15
|
+
/** Specific issues to fix (defaults to all) */
|
|
16
|
+
issues: z.array(z.string()).optional(),
|
|
17
|
+
/** Maximum fixes to attempt */
|
|
18
|
+
maxFixes: z.number().optional(),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Fix issues result data
|
|
23
|
+
*/
|
|
24
|
+
interface FixIssuesData {
|
|
25
|
+
fixedCount: number;
|
|
26
|
+
remainingIssues: string[];
|
|
27
|
+
fixes: string[];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Fix issues skill
|
|
32
|
+
*/
|
|
33
|
+
export const fixIssuesSkill: Skill<z.infer<typeof FixIssuesParams>, FixIssuesData> = {
|
|
34
|
+
id: "/fix-issues",
|
|
35
|
+
name: "Fix Issues",
|
|
36
|
+
description: "Fix issues identified during quality check or review.",
|
|
37
|
+
paramsSchema: FixIssuesParams,
|
|
38
|
+
tags: ["coding", "fixing"],
|
|
39
|
+
parallelizable: false,
|
|
40
|
+
requires: ["qualityIssues"],
|
|
41
|
+
|
|
42
|
+
async execute(params, context): Promise<SkillResult<FixIssuesData>> {
|
|
43
|
+
const issues = params?.issues || (context.custom.qualityIssues as string[]) || [];
|
|
44
|
+
const maxFixes = params?.maxFixes || 10;
|
|
45
|
+
|
|
46
|
+
if (issues.length === 0) {
|
|
47
|
+
console.log("[/fix-issues] No issues to fix");
|
|
48
|
+
return {
|
|
49
|
+
success: true,
|
|
50
|
+
data: {
|
|
51
|
+
fixedCount: 0,
|
|
52
|
+
remainingIssues: [],
|
|
53
|
+
fixes: [],
|
|
54
|
+
},
|
|
55
|
+
nextSkill: "/review",
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
console.log(`[/fix-issues] Fixing ${issues.length} issues`);
|
|
60
|
+
|
|
61
|
+
const prompt = `Fix the following issues:
|
|
62
|
+
|
|
63
|
+
${issues.map((issue, i) => `${i + 1}. ${issue}`).join("\n")}
|
|
64
|
+
|
|
65
|
+
Context:
|
|
66
|
+
- Task: ${context.state.prompt}
|
|
67
|
+
- Files changed: ${context.state.filesChanged.join(", ") || "none"}
|
|
68
|
+
|
|
69
|
+
Use tools to make the necessary fixes.
|
|
70
|
+
Test your changes after fixing.
|
|
71
|
+
Report what was fixed.`;
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
const response = await context.agent.execute(prompt);
|
|
75
|
+
|
|
76
|
+
// Parse what was fixed from response
|
|
77
|
+
const fixes: string[] = [];
|
|
78
|
+
const fixedMatch = response.match(/fixed:?\s*([\s\S]*?)(?=remaining|still|$)/i);
|
|
79
|
+
if (fixedMatch) {
|
|
80
|
+
fixes.push(...fixedMatch[1].split("\n").filter((l) => l.trim()));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Check if issues are resolved (simplified - would need actual validation)
|
|
84
|
+
const remainingIssues: string[] = [];
|
|
85
|
+
const lowerResponse = response.toLowerCase();
|
|
86
|
+
for (const issue of issues) {
|
|
87
|
+
if (
|
|
88
|
+
!lowerResponse.includes("fixed") &&
|
|
89
|
+
!lowerResponse.includes("resolved") &&
|
|
90
|
+
lowerResponse.includes("still")
|
|
91
|
+
) {
|
|
92
|
+
remainingIssues.push(issue);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const fixedCount = issues.length - remainingIssues.length;
|
|
97
|
+
|
|
98
|
+
console.log(`[/fix-issues] Fixed ${fixedCount}/${issues.length} issues`);
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
success: true,
|
|
102
|
+
data: {
|
|
103
|
+
fixedCount,
|
|
104
|
+
remainingIssues,
|
|
105
|
+
fixes: fixes.length > 0 ? fixes : [response.substring(0, 200)],
|
|
106
|
+
},
|
|
107
|
+
contextUpdates: {
|
|
108
|
+
qualityIssues: remainingIssues,
|
|
109
|
+
hasIssues: remainingIssues.length > 0,
|
|
110
|
+
lastFixes: fixes,
|
|
111
|
+
},
|
|
112
|
+
nextSkill: "/quality-check", // Re-check after fixes
|
|
113
|
+
};
|
|
114
|
+
} catch (error) {
|
|
115
|
+
return {
|
|
116
|
+
success: false,
|
|
117
|
+
error: error instanceof Error ? error.message : String(error),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
skillRegistry.register(fixIssuesSkill);
|
|
124
|
+
|
|
125
|
+
export { FixIssuesParams };
|
|
126
|
+
export type { FixIssuesData };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GLM Daemon - Coding Skills
|
|
3
|
+
*
|
|
4
|
+
* Skills for software development workflows.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Import to auto-register
|
|
8
|
+
import "./plan-task.js";
|
|
9
|
+
import "./execute-subtask.js";
|
|
10
|
+
import "./quality-check.js";
|
|
11
|
+
import "./fix-issues.js";
|
|
12
|
+
import "./commit.js";
|
|
13
|
+
|
|
14
|
+
// Re-export for explicit imports
|
|
15
|
+
export { planTaskSkill } from "./plan-task.js";
|
|
16
|
+
export { executeSubtaskSkill } from "./execute-subtask.js";
|
|
17
|
+
export { qualityCheckSkill } from "./quality-check.js";
|
|
18
|
+
export { fixIssuesSkill } from "./fix-issues.js";
|
|
19
|
+
export { commitSkill } from "./commit.js";
|
|
20
|
+
|
|
21
|
+
// Re-export types
|
|
22
|
+
export type { PlanTaskData, Subtask } from "./plan-task.js";
|
|
23
|
+
export type { ExecuteSubtaskData } from "./execute-subtask.js";
|
|
24
|
+
export type { QualityCheckData } from "./quality-check.js";
|
|
25
|
+
export type { FixIssuesData } from "./fix-issues.js";
|
|
26
|
+
export type { CommitData } from "./commit.js";
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GLM Daemon - Plan Task Skill
|
|
3
|
+
*
|
|
4
|
+
* Break down a task into subtasks for execution.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import type { Skill, SkillContext, SkillResult } from "../types.js";
|
|
9
|
+
import { skillRegistry } from "../registry.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Plan task parameters
|
|
13
|
+
*/
|
|
14
|
+
const PlanTaskParams = z.object({
|
|
15
|
+
/** Max number of subtasks to create */
|
|
16
|
+
maxSubtasks: z.number().min(2).max(10).optional(),
|
|
17
|
+
/** Force replanning even if subtasks exist */
|
|
18
|
+
force: z.boolean().optional(),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Subtask structure
|
|
23
|
+
*/
|
|
24
|
+
interface Subtask {
|
|
25
|
+
id: string;
|
|
26
|
+
title: string;
|
|
27
|
+
description: string;
|
|
28
|
+
status: "pending" | "in_progress" | "completed" | "blocked" | "failed";
|
|
29
|
+
dependencies?: string[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Plan task result data
|
|
34
|
+
*/
|
|
35
|
+
interface PlanTaskData {
|
|
36
|
+
subtasks: Subtask[];
|
|
37
|
+
firstSubtaskId: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Plan task skill
|
|
42
|
+
*/
|
|
43
|
+
export const planTaskSkill: Skill<z.infer<typeof PlanTaskParams>, PlanTaskData> = {
|
|
44
|
+
id: "/plan-task",
|
|
45
|
+
name: "Plan Task",
|
|
46
|
+
description: "Break down a task into concrete subtasks with dependencies.",
|
|
47
|
+
paramsSchema: PlanTaskParams,
|
|
48
|
+
tags: ["coding", "planning"],
|
|
49
|
+
parallelizable: false,
|
|
50
|
+
produces: ["subtasks", "currentSubtask"],
|
|
51
|
+
|
|
52
|
+
async execute(params, context): Promise<SkillResult<PlanTaskData>> {
|
|
53
|
+
const maxSubtasks = params?.maxSubtasks ?? 7;
|
|
54
|
+
const force = params?.force ?? false;
|
|
55
|
+
|
|
56
|
+
// Skip if subtasks already exist and not forcing
|
|
57
|
+
if (!force && context.state.slam.subtasks.length > 0) {
|
|
58
|
+
console.log("[/plan-task] Subtasks already exist, skipping");
|
|
59
|
+
return {
|
|
60
|
+
success: true,
|
|
61
|
+
data: {
|
|
62
|
+
subtasks: context.state.slam.subtasks as Subtask[],
|
|
63
|
+
firstSubtaskId: context.state.slam.subtasks[0]?.id,
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
console.log("[/plan-task] Breaking down task into subtasks");
|
|
69
|
+
|
|
70
|
+
const prompt = `${context.state.prompt}
|
|
71
|
+
|
|
72
|
+
Break this task down into ${maxSubtasks} or fewer concrete subtasks.
|
|
73
|
+
For each subtask, specify:
|
|
74
|
+
- Title (short description)
|
|
75
|
+
- Description (what needs to be done)
|
|
76
|
+
- Dependencies (which subtasks must complete first, by ID)
|
|
77
|
+
|
|
78
|
+
Respond with a JSON array:
|
|
79
|
+
[
|
|
80
|
+
{
|
|
81
|
+
"id": "subtask-0",
|
|
82
|
+
"title": "Short title",
|
|
83
|
+
"description": "Detailed description",
|
|
84
|
+
"dependencies": []
|
|
85
|
+
}
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
Keep subtasks small and actionable. Each should be completable in one iteration.`;
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
const response = await context.agent.execute(prompt);
|
|
92
|
+
|
|
93
|
+
// Parse JSON from response
|
|
94
|
+
const jsonMatch = response.match(/\[[\s\S]*\]/);
|
|
95
|
+
let subtasks: Subtask[] = [];
|
|
96
|
+
|
|
97
|
+
if (jsonMatch) {
|
|
98
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
99
|
+
subtasks = parsed.map((st: any, idx: number) => ({
|
|
100
|
+
id: st.id || `subtask-${idx}`,
|
|
101
|
+
title: st.title || st.task || `Subtask ${idx + 1}`,
|
|
102
|
+
description: st.description || "",
|
|
103
|
+
status: "pending" as const,
|
|
104
|
+
dependencies: st.dependencies || [],
|
|
105
|
+
}));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Fallback if parsing failed
|
|
109
|
+
if (subtasks.length === 0) {
|
|
110
|
+
subtasks = [
|
|
111
|
+
{
|
|
112
|
+
id: "subtask-0",
|
|
113
|
+
title: context.state.prompt.substring(0, 50),
|
|
114
|
+
description: context.state.prompt,
|
|
115
|
+
status: "pending",
|
|
116
|
+
},
|
|
117
|
+
];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
console.log(`[/plan-task] Created ${subtasks.length} subtasks`);
|
|
121
|
+
subtasks.forEach((st, i) => {
|
|
122
|
+
console.log(` ${i + 1}. ${st.title}`);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
const firstSubtaskId = subtasks[0]?.id;
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
success: true,
|
|
129
|
+
data: {
|
|
130
|
+
subtasks,
|
|
131
|
+
firstSubtaskId,
|
|
132
|
+
},
|
|
133
|
+
stateUpdates: {
|
|
134
|
+
slam: {
|
|
135
|
+
...context.state.slam,
|
|
136
|
+
subtasks,
|
|
137
|
+
currentSubtask: firstSubtaskId || null,
|
|
138
|
+
completedSubtasks: [],
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
contextUpdates: {
|
|
142
|
+
subtasks,
|
|
143
|
+
currentSubtask: firstSubtaskId,
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
} catch (error) {
|
|
147
|
+
return {
|
|
148
|
+
success: false,
|
|
149
|
+
error: error instanceof Error ? error.message : String(error),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
skillRegistry.register(planTaskSkill);
|
|
156
|
+
|
|
157
|
+
export { PlanTaskParams };
|
|
158
|
+
export type { PlanTaskData, Subtask };
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GLM Daemon - Quality Check Skill
|
|
3
|
+
*
|
|
4
|
+
* Paranoid review of changes made during execution.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import type { Skill, SkillContext, SkillResult } from "../types.js";
|
|
9
|
+
import { skillRegistry } from "../registry.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Quality check parameters
|
|
13
|
+
*/
|
|
14
|
+
const QualityCheckParams = z.object({
|
|
15
|
+
/** Focus areas for the check */
|
|
16
|
+
focusAreas: z.array(z.string()).optional(),
|
|
17
|
+
/** Strictness level */
|
|
18
|
+
strictness: z.enum(["relaxed", "normal", "strict"]).optional(),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Quality check result data
|
|
23
|
+
*/
|
|
24
|
+
interface QualityCheckData {
|
|
25
|
+
passed: boolean;
|
|
26
|
+
hasIssues: boolean;
|
|
27
|
+
issues: string[];
|
|
28
|
+
warnings: string[];
|
|
29
|
+
assessment: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Default focus areas
|
|
34
|
+
*/
|
|
35
|
+
const DEFAULT_FOCUS_AREAS = [
|
|
36
|
+
"Bugs or errors",
|
|
37
|
+
"Edge cases not handled",
|
|
38
|
+
"Security vulnerabilities",
|
|
39
|
+
"Performance issues",
|
|
40
|
+
"Breaking changes",
|
|
41
|
+
"Type errors",
|
|
42
|
+
"Missing error handling",
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Quality check skill
|
|
47
|
+
*/
|
|
48
|
+
export const qualityCheckSkill: Skill<z.infer<typeof QualityCheckParams>, QualityCheckData> = {
|
|
49
|
+
id: "/quality-check",
|
|
50
|
+
name: "Quality Check",
|
|
51
|
+
description: "Paranoid review of all changes for bugs, security issues, and quality problems.",
|
|
52
|
+
paramsSchema: QualityCheckParams,
|
|
53
|
+
tags: ["coding", "quality", "validation"],
|
|
54
|
+
parallelizable: false,
|
|
55
|
+
produces: ["hasIssues", "issues"],
|
|
56
|
+
|
|
57
|
+
async execute(params, context): Promise<SkillResult<QualityCheckData>> {
|
|
58
|
+
const focusAreas = params?.focusAreas || DEFAULT_FOCUS_AREAS;
|
|
59
|
+
const strictness = params?.strictness || "normal";
|
|
60
|
+
|
|
61
|
+
console.log("[/quality-check] Running paranoid review");
|
|
62
|
+
|
|
63
|
+
const filesChanged = context.state.filesChanged.length > 0
|
|
64
|
+
? context.state.filesChanged.join("\n- ")
|
|
65
|
+
: "No files changed";
|
|
66
|
+
|
|
67
|
+
const prompt = `Review the changes made in this session for quality issues.
|
|
68
|
+
|
|
69
|
+
Task: ${context.state.prompt}
|
|
70
|
+
|
|
71
|
+
Files changed:
|
|
72
|
+
- ${filesChanged}
|
|
73
|
+
|
|
74
|
+
Focus areas (${strictness} mode):
|
|
75
|
+
${focusAreas.map((a, i) => `${i + 1}. ${a}`).join("\n")}
|
|
76
|
+
|
|
77
|
+
Be thorough but fair. In "relaxed" mode, only flag critical issues.
|
|
78
|
+
In "strict" mode, flag even minor issues.
|
|
79
|
+
|
|
80
|
+
Respond in JSON format:
|
|
81
|
+
{
|
|
82
|
+
"passed": true|false,
|
|
83
|
+
"hasIssues": true|false,
|
|
84
|
+
"issues": ["critical problems that MUST be fixed"],
|
|
85
|
+
"warnings": ["minor issues or suggestions"],
|
|
86
|
+
"assessment": "overall assessment"
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
Set hasIssues to true only if there are critical problems in the "issues" array.
|
|
90
|
+
Minor concerns go in "warnings".`;
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
const response = await context.agent.execute(prompt);
|
|
94
|
+
|
|
95
|
+
// Parse JSON from response
|
|
96
|
+
const jsonMatch = response.match(/\{[\s\S]*\}/);
|
|
97
|
+
let data: QualityCheckData;
|
|
98
|
+
|
|
99
|
+
if (jsonMatch) {
|
|
100
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
101
|
+
data = {
|
|
102
|
+
passed: parsed.passed ?? !parsed.hasIssues,
|
|
103
|
+
hasIssues: parsed.hasIssues ?? (parsed.issues?.length > 0),
|
|
104
|
+
issues: parsed.issues || [],
|
|
105
|
+
warnings: parsed.warnings || [],
|
|
106
|
+
assessment: parsed.assessment || "",
|
|
107
|
+
};
|
|
108
|
+
} else {
|
|
109
|
+
// Fallback: detect issues from response
|
|
110
|
+
const lowerResponse = response.toLowerCase();
|
|
111
|
+
const issueKeywords = ["bug", "error", "critical", "must fix", "problem", "vulnerability"];
|
|
112
|
+
const hasIssues = issueKeywords.some((kw) => lowerResponse.includes(kw));
|
|
113
|
+
data = {
|
|
114
|
+
passed: !hasIssues,
|
|
115
|
+
hasIssues,
|
|
116
|
+
issues: hasIssues ? ["Issues detected in review"] : [],
|
|
117
|
+
warnings: [],
|
|
118
|
+
assessment: response.substring(0, 500),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
console.log(`[/quality-check] Result: ${data.passed ? "PASSED" : "ISSUES FOUND"}`);
|
|
123
|
+
if (data.issues.length > 0) {
|
|
124
|
+
console.log(`[/quality-check] Issues: ${data.issues.length}`);
|
|
125
|
+
}
|
|
126
|
+
if (data.warnings.length > 0) {
|
|
127
|
+
console.log(`[/quality-check] Warnings: ${data.warnings.length}`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
success: true,
|
|
132
|
+
data,
|
|
133
|
+
contextUpdates: {
|
|
134
|
+
hasIssues: data.hasIssues,
|
|
135
|
+
qualityIssues: data.issues,
|
|
136
|
+
qualityWarnings: data.warnings,
|
|
137
|
+
},
|
|
138
|
+
nextSkill: data.hasIssues ? "/fix-issues" : "/review",
|
|
139
|
+
};
|
|
140
|
+
} catch (error) {
|
|
141
|
+
return {
|
|
142
|
+
success: false,
|
|
143
|
+
error: error instanceof Error ? error.message : String(error),
|
|
144
|
+
contextUpdates: {
|
|
145
|
+
hasIssues: false,
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
skillRegistry.register(qualityCheckSkill);
|
|
153
|
+
|
|
154
|
+
export { QualityCheckParams };
|
|
155
|
+
export type { QualityCheckData };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Daemons - Skills Module
|
|
3
|
+
*
|
|
4
|
+
* Composable skill system for workflow phases.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* ```typescript
|
|
8
|
+
* import { skillRegistry, defineSkill } from '@ebowwa/daemons/skills';
|
|
9
|
+
*
|
|
10
|
+
* // Define a skill (auto-registers)
|
|
11
|
+
* const mySkill = defineSkill({
|
|
12
|
+
* id: 'my-skill',
|
|
13
|
+
* name: 'My Skill',
|
|
14
|
+
* description: 'Does something useful',
|
|
15
|
+
* }, async (params, context) => {
|
|
16
|
+
* // Do work
|
|
17
|
+
* return { success: true, data: { result: 'done' } };
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* // Execute a skill
|
|
21
|
+
* const result = await skillRegistry.execute('my-skill', context, { foo: 'bar' });
|
|
22
|
+
*
|
|
23
|
+
* // Use built-in workflows
|
|
24
|
+
* import { codingWorkflow, executeWorkflowSkill } from '@ebowwa/daemons/skills';
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* Skills are auto-registered on import. To use all built-in skills:
|
|
28
|
+
* ```typescript
|
|
29
|
+
* import '@ebowwa/daemons/skills'; // Core types + registry + workflows
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
// Core types
|
|
34
|
+
export type {
|
|
35
|
+
Skill,
|
|
36
|
+
SkillConfig,
|
|
37
|
+
SkillContext,
|
|
38
|
+
SkillResult,
|
|
39
|
+
SkillData,
|
|
40
|
+
SkillParams,
|
|
41
|
+
SkillTransition,
|
|
42
|
+
SkillTransitionCondition,
|
|
43
|
+
SkillWorkflowConfig,
|
|
44
|
+
SkillExecutionMeta,
|
|
45
|
+
SkillExecutionOptions,
|
|
46
|
+
} from "./types.js";
|
|
47
|
+
|
|
48
|
+
// Registry
|
|
49
|
+
export { SkillRegistry, skillRegistry, defineSkill } from "./registry.js";
|
|
50
|
+
|
|
51
|
+
// Shared skills (auto-register on import)
|
|
52
|
+
export * from "./shared/index.js";
|
|
53
|
+
|
|
54
|
+
// Domain-specific skills (auto-register on import)
|
|
55
|
+
export * from "./coding/index.js";
|
|
56
|
+
export * from "./trading/index.js";
|
|
57
|
+
|
|
58
|
+
// Workflow definitions and executor
|
|
59
|
+
export {
|
|
60
|
+
codingWorkflow,
|
|
61
|
+
tradingWorkflow,
|
|
62
|
+
workflowConfigRegistry,
|
|
63
|
+
executeWorkflowSkill,
|
|
64
|
+
isWorkflowComplete,
|
|
65
|
+
} from "./workflows.js";
|