daemora 1.0.4 → 1.0.6
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/LICENSE +663 -0
- package/README.md +69 -19
- package/SOUL.md +29 -26
- package/config/mcp.json +126 -66
- package/daemora-ui/README.md +11 -0
- package/package.json +12 -2
- package/skills/api-development.md +35 -0
- package/skills/artifacts-builder/SKILL.md +74 -0
- package/skills/artifacts-builder/scripts/bundle-artifact.sh +54 -0
- package/skills/artifacts-builder/scripts/init-artifact.sh +322 -0
- package/skills/artifacts-builder/scripts/shadcn-components.tar.gz +0 -0
- package/skills/brand-guidelines.md +73 -0
- package/skills/browser.md +77 -0
- package/skills/changelog-generator.md +104 -0
- package/skills/coding.md +26 -10
- package/skills/content-research-writer.md +538 -0
- package/skills/data-analysis.md +27 -0
- package/skills/debugging.md +33 -0
- package/skills/devops.md +37 -0
- package/skills/document-docx.md +197 -0
- package/skills/document-pdf.md +294 -0
- package/skills/document-pptx.md +484 -0
- package/skills/document-xlsx.md +289 -0
- package/skills/domain-name-brainstormer.md +212 -0
- package/skills/file-organizer.md +433 -0
- package/skills/frontend-design.md +42 -0
- package/skills/image-enhancer.md +99 -0
- package/skills/invoice-organizer.md +446 -0
- package/skills/lead-research-assistant.md +199 -0
- package/skills/mcp-builder/SKILL.md +328 -0
- package/skills/mcp-builder/reference/evaluation.md +602 -0
- package/skills/mcp-builder/reference/mcp_best_practices.md +915 -0
- package/skills/mcp-builder/reference/node_mcp_server.md +916 -0
- package/skills/mcp-builder/reference/python_mcp_server.md +752 -0
- package/skills/mcp-builder/scripts/connections.py +151 -0
- package/skills/mcp-builder/scripts/evaluation.py +373 -0
- package/skills/mcp-builder/scripts/example_evaluation.xml +22 -0
- package/skills/mcp-builder/scripts/requirements.txt +2 -0
- package/skills/meeting-insights-analyzer.md +327 -0
- package/skills/orchestration.md +93 -0
- package/skills/raffle-winner-picker.md +159 -0
- package/skills/slack-gif-creator/SKILL.md +646 -0
- package/skills/slack-gif-creator/core/color_palettes.py +302 -0
- package/skills/slack-gif-creator/core/easing.py +230 -0
- package/skills/slack-gif-creator/core/frame_composer.py +469 -0
- package/skills/slack-gif-creator/core/gif_builder.py +246 -0
- package/skills/slack-gif-creator/core/typography.py +357 -0
- package/skills/slack-gif-creator/core/validators.py +264 -0
- package/skills/slack-gif-creator/core/visual_effects.py +494 -0
- package/skills/slack-gif-creator/requirements.txt +4 -0
- package/skills/slack-gif-creator/templates/bounce.py +106 -0
- package/skills/slack-gif-creator/templates/explode.py +331 -0
- package/skills/slack-gif-creator/templates/fade.py +329 -0
- package/skills/slack-gif-creator/templates/flip.py +291 -0
- package/skills/slack-gif-creator/templates/kaleidoscope.py +211 -0
- package/skills/slack-gif-creator/templates/morph.py +329 -0
- package/skills/slack-gif-creator/templates/move.py +293 -0
- package/skills/slack-gif-creator/templates/pulse.py +268 -0
- package/skills/slack-gif-creator/templates/shake.py +127 -0
- package/skills/slack-gif-creator/templates/slide.py +291 -0
- package/skills/slack-gif-creator/templates/spin.py +269 -0
- package/skills/slack-gif-creator/templates/wiggle.py +300 -0
- package/skills/slack-gif-creator/templates/zoom.py +312 -0
- package/skills/system-admin.md +44 -0
- package/skills/tailored-resume-generator.md +345 -0
- package/skills/theme-factory/SKILL.md +59 -0
- package/skills/theme-factory/theme-showcase.pdf +0 -0
- package/skills/theme-factory/themes/arctic-frost.md +19 -0
- package/skills/theme-factory/themes/botanical-garden.md +19 -0
- package/skills/theme-factory/themes/desert-rose.md +19 -0
- package/skills/theme-factory/themes/forest-canopy.md +19 -0
- package/skills/theme-factory/themes/golden-hour.md +19 -0
- package/skills/theme-factory/themes/midnight-galaxy.md +19 -0
- package/skills/theme-factory/themes/modern-minimalist.md +19 -0
- package/skills/theme-factory/themes/ocean-depths.md +19 -0
- package/skills/theme-factory/themes/sunset-boulevard.md +19 -0
- package/skills/theme-factory/themes/tech-innovation.md +19 -0
- package/skills/video-downloader.md +99 -0
- package/skills/web-development.md +32 -0
- package/skills/webapp-testing/SKILL.md +96 -0
- package/skills/webapp-testing/examples/console_logging.py +35 -0
- package/skills/webapp-testing/examples/element_discovery.py +40 -0
- package/skills/webapp-testing/examples/static_html_automation.py +33 -0
- package/skills/webapp-testing/scripts/with_server.py +106 -0
- package/src/agents/SubAgentManager.js +134 -16
- package/src/agents/systemPrompt.js +427 -0
- package/src/api/openai-compat.js +212 -0
- package/src/channels/TelegramChannel.js +5 -2
- package/src/channels/index.js +7 -10
- package/src/cli.js +281 -55
- package/src/config/agentProfiles.js +1 -0
- package/src/config/default.js +15 -1
- package/src/config/models.js +314 -78
- package/src/config/permissions.js +12 -0
- package/src/core/AgentLoop.js +70 -50
- package/src/core/Compaction.js +111 -11
- package/src/core/MessageQueue.js +90 -0
- package/src/core/Task.js +13 -0
- package/src/core/TaskQueue.js +1 -1
- package/src/core/TaskRunner.js +81 -6
- package/src/index.js +725 -59
- package/src/mcp/MCPAgentRunner.js +48 -11
- package/src/mcp/MCPManager.js +40 -2
- package/src/models/ModelRouter.js +74 -4
- package/src/safety/DockerSandbox.js +212 -0
- package/src/safety/ExecApproval.js +118 -0
- package/src/scheduler/Heartbeat.js +56 -21
- package/src/services/cleanup.js +106 -0
- package/src/services/sessions.js +39 -1
- package/src/setup/wizard.js +125 -75
- package/src/skills/SkillLoader.js +132 -17
- package/src/storage/TaskStore.js +19 -1
- package/src/tools/browserAutomation.js +615 -104
- package/src/tools/executeCommand.js +19 -1
- package/src/tools/index.js +7 -1
- package/src/tools/manageAgents.js +55 -4
- package/src/tools/replyWithFile.js +62 -0
- package/src/tools/screenCapture.js +12 -1
- package/src/tools/taskManager.js +164 -0
- package/src/tools/useMCP.js +3 -1
- package/src/utils/Embeddings.js +236 -12
- package/src/webhooks/WebhookHandler.js +107 -0
- package/src/systemPrompt.js +0 -528
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync, existsSync, readdirSync, mkdirSync } from "fs";
|
|
2
|
-
import { join } from "path";
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync, readdirSync, mkdirSync, statSync, accessSync, constants } from "fs";
|
|
2
|
+
import { join, delimiter } from "path";
|
|
3
3
|
import { createHash } from "node:crypto";
|
|
4
4
|
import { config } from "../config/default.js";
|
|
5
|
-
import { generateEmbedding, getEmbeddingProvider } from "../utils/Embeddings.js";
|
|
5
|
+
import { generateEmbedding, getEmbeddingProvider, buildTfidfVocab } from "../utils/Embeddings.js";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Skill Loader - auto-discovers .md skill files from the skills/ directory.
|
|
@@ -47,24 +47,12 @@ class SkillLoader {
|
|
|
47
47
|
return;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
const files = readdirSync(skillsDir).filter((f) => f.endsWith(".md"));
|
|
51
50
|
this.skills.clear();
|
|
52
|
-
|
|
53
|
-
for (const file of files) {
|
|
54
|
-
try {
|
|
55
|
-
const filePath = join(skillsDir, file);
|
|
56
|
-
const content = readFileSync(filePath, "utf-8");
|
|
57
|
-
const skill = this.parseSkill(content, file);
|
|
58
|
-
if (skill) {
|
|
59
|
-
this.skills.set(skill.name, skill);
|
|
60
|
-
}
|
|
61
|
-
} catch (error) {
|
|
62
|
-
console.log(`[SkillLoader] Error loading ${file}: ${error.message}`);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
51
|
+
this._loadFromDir(skillsDir);
|
|
65
52
|
|
|
66
53
|
this.loaded = true;
|
|
67
54
|
this._loadSkillVectors();
|
|
55
|
+
this._buildTfidfIndex();
|
|
68
56
|
console.log(
|
|
69
57
|
`[SkillLoader] Loaded ${this.skills.size} skills: ${[...this.skills.keys()].join(", ") || "(none)"}`
|
|
70
58
|
);
|
|
@@ -104,10 +92,56 @@ class SkillLoader {
|
|
|
104
92
|
triggers: meta.triggers
|
|
105
93
|
? meta.triggers.split(",").map((t) => t.trim().toLowerCase())
|
|
106
94
|
: [],
|
|
95
|
+
// Eligibility fields (OpenClaw-style filtering)
|
|
96
|
+
os: meta.os ? meta.os.split(",").map((s) => s.trim().toLowerCase()) : [],
|
|
97
|
+
requires: meta.requires ? meta.requires.split(",").map((s) => s.trim()) : [],
|
|
98
|
+
env: meta.env ? meta.env.split(",").map((s) => s.trim()) : [],
|
|
107
99
|
content: body,
|
|
108
100
|
};
|
|
109
101
|
}
|
|
110
102
|
|
|
103
|
+
/**
|
|
104
|
+
* Load skills from a directory — supports flat .md files and subdirectories with SKILL.md.
|
|
105
|
+
* Scans one level deep: skills/foo.md and skills/bar/SKILL.md both work.
|
|
106
|
+
*/
|
|
107
|
+
_loadFromDir(dir) {
|
|
108
|
+
const entries = readdirSync(dir);
|
|
109
|
+
for (const entry of entries) {
|
|
110
|
+
try {
|
|
111
|
+
const entryPath = join(dir, entry);
|
|
112
|
+
const stat = statSync(entryPath);
|
|
113
|
+
|
|
114
|
+
if (stat.isFile() && entry.endsWith(".md")) {
|
|
115
|
+
// Flat file: skills/coding.md
|
|
116
|
+
const content = readFileSync(entryPath, "utf-8");
|
|
117
|
+
const skill = this.parseSkill(content, entry);
|
|
118
|
+
if (skill) this.skills.set(skill.name, skill);
|
|
119
|
+
} else if (stat.isDirectory()) {
|
|
120
|
+
// Subdirectory: skills/webapp-testing/SKILL.md
|
|
121
|
+
const skillMd = join(entryPath, "SKILL.md");
|
|
122
|
+
if (existsSync(skillMd)) {
|
|
123
|
+
const content = readFileSync(skillMd, "utf-8");
|
|
124
|
+
const skill = this.parseSkill(content, `${entry}/SKILL.md`);
|
|
125
|
+
if (skill) this.skills.set(skill.name, skill);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
} catch (error) {
|
|
129
|
+
console.log(`[SkillLoader] Error loading ${entry}: ${error.message}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Build TF-IDF vocabulary from all loaded skills (zero-cost local embeddings fallback).
|
|
136
|
+
*/
|
|
137
|
+
_buildTfidfIndex() {
|
|
138
|
+
const docs = [];
|
|
139
|
+
for (const [, skill] of this.skills) {
|
|
140
|
+
docs.push(`${skill.name} ${skill.description} ${skill.triggers.join(" ")} ${skill.content.slice(0, 500)}`);
|
|
141
|
+
}
|
|
142
|
+
buildTfidfVocab(docs);
|
|
143
|
+
}
|
|
144
|
+
|
|
111
145
|
// ── Embedding helpers ────────────────────────────────────────────────────────
|
|
112
146
|
|
|
113
147
|
_contentHash(skill) {
|
|
@@ -237,6 +271,59 @@ class SkillLoader {
|
|
|
237
271
|
return this.getSkillPrompts(taskInput);
|
|
238
272
|
}
|
|
239
273
|
|
|
274
|
+
/**
|
|
275
|
+
* Get matched skill summaries (name + description + path) for lazy loading.
|
|
276
|
+
* Uses hybrid ranking: embeddings (API or local) → keyword fallback → list all.
|
|
277
|
+
* Returns up to `limit` skills, sorted by relevance.
|
|
278
|
+
*/
|
|
279
|
+
async getMatchedSkillSummaries(taskInput, limit = 20) {
|
|
280
|
+
if (!this.loaded) this.load();
|
|
281
|
+
if (this.skills.size === 0) return [];
|
|
282
|
+
|
|
283
|
+
const toSummary = (skill) => ({
|
|
284
|
+
name: skill.name,
|
|
285
|
+
description: skill.description,
|
|
286
|
+
path: `skills/${skill.name}.md`,
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
// 1. Embedding match (OpenAI/Google/Ollama/TF-IDF — whatever is available)
|
|
290
|
+
if (taskInput) {
|
|
291
|
+
const vectorsAvailable = Object.keys(this._skillVectors).length > 0;
|
|
292
|
+
if (getEmbeddingProvider() && vectorsAvailable) {
|
|
293
|
+
const queryVector = await this._generateEmbedding(taskInput);
|
|
294
|
+
if (queryVector) {
|
|
295
|
+
const scored = [];
|
|
296
|
+
for (const [name, skill] of this.skills) {
|
|
297
|
+
const cached = this._skillVectors[name];
|
|
298
|
+
if (!cached?.vector) continue;
|
|
299
|
+
const score = this._cosineSim(queryVector, cached.vector);
|
|
300
|
+
scored.push({ skill, score });
|
|
301
|
+
}
|
|
302
|
+
scored.sort((a, b) => b.score - a.score);
|
|
303
|
+
const top = scored.slice(0, limit);
|
|
304
|
+
if (top.length > 0) {
|
|
305
|
+
console.log(`[SkillLoader] Ranked top ${top.length}/${this.skills.size} skills (embedding)`);
|
|
306
|
+
return top.map((s) => toSummary(s.skill));
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// 2. Keyword fallback — matched first, then fill remaining up to limit
|
|
312
|
+
const keywordMatched = this.matchSkills(taskInput);
|
|
313
|
+
if (keywordMatched.length > 0) {
|
|
314
|
+
const matchedNames = new Set(keywordMatched.map((s) => s.name));
|
|
315
|
+
const rest = [...this.skills.values()].filter((s) => !matchedNames.has(s.name));
|
|
316
|
+
const combined = [...keywordMatched, ...rest].slice(0, limit);
|
|
317
|
+
console.log(`[SkillLoader] Ranked top ${combined.length}/${this.skills.size} skills (keyword)`);
|
|
318
|
+
return combined.map(toSummary);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// 3. No match or no input — return first N alphabetically
|
|
323
|
+
const all = [...this.skills.values()].slice(0, limit);
|
|
324
|
+
return all.map(toSummary);
|
|
325
|
+
}
|
|
326
|
+
|
|
240
327
|
// ── Sync keyword API (fallback) ───────────────────────────────────────────────
|
|
241
328
|
|
|
242
329
|
/**
|
|
@@ -280,6 +367,34 @@ class SkillLoader {
|
|
|
280
367
|
return `\n\n## Active Skills\n${sections.join("\n")}`;
|
|
281
368
|
}
|
|
282
369
|
|
|
370
|
+
/**
|
|
371
|
+
* Get a skill by name or path. Supports:
|
|
372
|
+
* - Exact name: "coding"
|
|
373
|
+
* - Path: "skills/coding.md", "skills/webapp-testing/SKILL.md"
|
|
374
|
+
* - Partial path: "coding.md"
|
|
375
|
+
* Returns full skill object or null.
|
|
376
|
+
*/
|
|
377
|
+
getSkill(nameOrPath) {
|
|
378
|
+
if (!this.loaded) this.load();
|
|
379
|
+
// Direct name lookup
|
|
380
|
+
if (this.skills.has(nameOrPath)) return this.skills.get(nameOrPath);
|
|
381
|
+
|
|
382
|
+
// Strip common path prefixes and .md extension for matching
|
|
383
|
+
let normalized = nameOrPath
|
|
384
|
+
.replace(/^skills\//, "")
|
|
385
|
+
.replace(/\/SKILL\.md$/i, "")
|
|
386
|
+
.replace(/\.md$/, "");
|
|
387
|
+
|
|
388
|
+
if (this.skills.has(normalized)) return this.skills.get(normalized);
|
|
389
|
+
|
|
390
|
+
// Case-insensitive fallback
|
|
391
|
+
const lower = normalized.toLowerCase();
|
|
392
|
+
for (const [key, skill] of this.skills) {
|
|
393
|
+
if (key.toLowerCase() === lower) return skill;
|
|
394
|
+
}
|
|
395
|
+
return null;
|
|
396
|
+
}
|
|
397
|
+
|
|
283
398
|
/**
|
|
284
399
|
* List all loaded skills.
|
|
285
400
|
*/
|
package/src/storage/TaskStore.js
CHANGED
|
@@ -23,7 +23,7 @@ export function loadTask(taskId) {
|
|
|
23
23
|
/**
|
|
24
24
|
* List recent tasks (sorted by createdAt descending).
|
|
25
25
|
*/
|
|
26
|
-
export function listTasks({ limit = 20, status = null } = {}) {
|
|
26
|
+
export function listTasks({ limit = 20, status = null, type = null } = {}) {
|
|
27
27
|
const files = readdirSync(TASKS_DIR).filter((f) => f.endsWith(".json"));
|
|
28
28
|
let tasks = files.map((f) => {
|
|
29
29
|
try {
|
|
@@ -37,10 +37,28 @@ export function listTasks({ limit = 20, status = null } = {}) {
|
|
|
37
37
|
tasks = tasks.filter((t) => t.status === status);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
if (type) {
|
|
41
|
+
tasks = tasks.filter((t) => (t.type || "chat") === type);
|
|
42
|
+
}
|
|
43
|
+
|
|
40
44
|
tasks.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));
|
|
41
45
|
return tasks.slice(0, limit);
|
|
42
46
|
}
|
|
43
47
|
|
|
48
|
+
/**
|
|
49
|
+
* List child tasks of a given parent task.
|
|
50
|
+
*/
|
|
51
|
+
export function listChildTasks(parentTaskId) {
|
|
52
|
+
const files = readdirSync(TASKS_DIR).filter((f) => f.endsWith(".json"));
|
|
53
|
+
return files
|
|
54
|
+
.map((f) => {
|
|
55
|
+
try { return JSON.parse(readFileSync(`${TASKS_DIR}/${f}`, "utf-8")); }
|
|
56
|
+
catch { return null; }
|
|
57
|
+
})
|
|
58
|
+
.filter((t) => t && t.parentTaskId === parentTaskId)
|
|
59
|
+
.sort((a, b) => new Date(a.createdAt) - new Date(b.createdAt));
|
|
60
|
+
}
|
|
61
|
+
|
|
44
62
|
/**
|
|
45
63
|
* On startup, find tasks stuck in "running" state and reset to "pending".
|
|
46
64
|
* Returns the count of tasks reset so the caller can log accordingly.
|