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.
Files changed (123) hide show
  1. package/LICENSE +663 -0
  2. package/README.md +69 -19
  3. package/SOUL.md +29 -26
  4. package/config/mcp.json +126 -66
  5. package/daemora-ui/README.md +11 -0
  6. package/package.json +12 -2
  7. package/skills/api-development.md +35 -0
  8. package/skills/artifacts-builder/SKILL.md +74 -0
  9. package/skills/artifacts-builder/scripts/bundle-artifact.sh +54 -0
  10. package/skills/artifacts-builder/scripts/init-artifact.sh +322 -0
  11. package/skills/artifacts-builder/scripts/shadcn-components.tar.gz +0 -0
  12. package/skills/brand-guidelines.md +73 -0
  13. package/skills/browser.md +77 -0
  14. package/skills/changelog-generator.md +104 -0
  15. package/skills/coding.md +26 -10
  16. package/skills/content-research-writer.md +538 -0
  17. package/skills/data-analysis.md +27 -0
  18. package/skills/debugging.md +33 -0
  19. package/skills/devops.md +37 -0
  20. package/skills/document-docx.md +197 -0
  21. package/skills/document-pdf.md +294 -0
  22. package/skills/document-pptx.md +484 -0
  23. package/skills/document-xlsx.md +289 -0
  24. package/skills/domain-name-brainstormer.md +212 -0
  25. package/skills/file-organizer.md +433 -0
  26. package/skills/frontend-design.md +42 -0
  27. package/skills/image-enhancer.md +99 -0
  28. package/skills/invoice-organizer.md +446 -0
  29. package/skills/lead-research-assistant.md +199 -0
  30. package/skills/mcp-builder/SKILL.md +328 -0
  31. package/skills/mcp-builder/reference/evaluation.md +602 -0
  32. package/skills/mcp-builder/reference/mcp_best_practices.md +915 -0
  33. package/skills/mcp-builder/reference/node_mcp_server.md +916 -0
  34. package/skills/mcp-builder/reference/python_mcp_server.md +752 -0
  35. package/skills/mcp-builder/scripts/connections.py +151 -0
  36. package/skills/mcp-builder/scripts/evaluation.py +373 -0
  37. package/skills/mcp-builder/scripts/example_evaluation.xml +22 -0
  38. package/skills/mcp-builder/scripts/requirements.txt +2 -0
  39. package/skills/meeting-insights-analyzer.md +327 -0
  40. package/skills/orchestration.md +93 -0
  41. package/skills/raffle-winner-picker.md +159 -0
  42. package/skills/slack-gif-creator/SKILL.md +646 -0
  43. package/skills/slack-gif-creator/core/color_palettes.py +302 -0
  44. package/skills/slack-gif-creator/core/easing.py +230 -0
  45. package/skills/slack-gif-creator/core/frame_composer.py +469 -0
  46. package/skills/slack-gif-creator/core/gif_builder.py +246 -0
  47. package/skills/slack-gif-creator/core/typography.py +357 -0
  48. package/skills/slack-gif-creator/core/validators.py +264 -0
  49. package/skills/slack-gif-creator/core/visual_effects.py +494 -0
  50. package/skills/slack-gif-creator/requirements.txt +4 -0
  51. package/skills/slack-gif-creator/templates/bounce.py +106 -0
  52. package/skills/slack-gif-creator/templates/explode.py +331 -0
  53. package/skills/slack-gif-creator/templates/fade.py +329 -0
  54. package/skills/slack-gif-creator/templates/flip.py +291 -0
  55. package/skills/slack-gif-creator/templates/kaleidoscope.py +211 -0
  56. package/skills/slack-gif-creator/templates/morph.py +329 -0
  57. package/skills/slack-gif-creator/templates/move.py +293 -0
  58. package/skills/slack-gif-creator/templates/pulse.py +268 -0
  59. package/skills/slack-gif-creator/templates/shake.py +127 -0
  60. package/skills/slack-gif-creator/templates/slide.py +291 -0
  61. package/skills/slack-gif-creator/templates/spin.py +269 -0
  62. package/skills/slack-gif-creator/templates/wiggle.py +300 -0
  63. package/skills/slack-gif-creator/templates/zoom.py +312 -0
  64. package/skills/system-admin.md +44 -0
  65. package/skills/tailored-resume-generator.md +345 -0
  66. package/skills/theme-factory/SKILL.md +59 -0
  67. package/skills/theme-factory/theme-showcase.pdf +0 -0
  68. package/skills/theme-factory/themes/arctic-frost.md +19 -0
  69. package/skills/theme-factory/themes/botanical-garden.md +19 -0
  70. package/skills/theme-factory/themes/desert-rose.md +19 -0
  71. package/skills/theme-factory/themes/forest-canopy.md +19 -0
  72. package/skills/theme-factory/themes/golden-hour.md +19 -0
  73. package/skills/theme-factory/themes/midnight-galaxy.md +19 -0
  74. package/skills/theme-factory/themes/modern-minimalist.md +19 -0
  75. package/skills/theme-factory/themes/ocean-depths.md +19 -0
  76. package/skills/theme-factory/themes/sunset-boulevard.md +19 -0
  77. package/skills/theme-factory/themes/tech-innovation.md +19 -0
  78. package/skills/video-downloader.md +99 -0
  79. package/skills/web-development.md +32 -0
  80. package/skills/webapp-testing/SKILL.md +96 -0
  81. package/skills/webapp-testing/examples/console_logging.py +35 -0
  82. package/skills/webapp-testing/examples/element_discovery.py +40 -0
  83. package/skills/webapp-testing/examples/static_html_automation.py +33 -0
  84. package/skills/webapp-testing/scripts/with_server.py +106 -0
  85. package/src/agents/SubAgentManager.js +134 -16
  86. package/src/agents/systemPrompt.js +427 -0
  87. package/src/api/openai-compat.js +212 -0
  88. package/src/channels/TelegramChannel.js +5 -2
  89. package/src/channels/index.js +7 -10
  90. package/src/cli.js +281 -55
  91. package/src/config/agentProfiles.js +1 -0
  92. package/src/config/default.js +15 -1
  93. package/src/config/models.js +314 -78
  94. package/src/config/permissions.js +12 -0
  95. package/src/core/AgentLoop.js +70 -50
  96. package/src/core/Compaction.js +111 -11
  97. package/src/core/MessageQueue.js +90 -0
  98. package/src/core/Task.js +13 -0
  99. package/src/core/TaskQueue.js +1 -1
  100. package/src/core/TaskRunner.js +81 -6
  101. package/src/index.js +725 -59
  102. package/src/mcp/MCPAgentRunner.js +48 -11
  103. package/src/mcp/MCPManager.js +40 -2
  104. package/src/models/ModelRouter.js +74 -4
  105. package/src/safety/DockerSandbox.js +212 -0
  106. package/src/safety/ExecApproval.js +118 -0
  107. package/src/scheduler/Heartbeat.js +56 -21
  108. package/src/services/cleanup.js +106 -0
  109. package/src/services/sessions.js +39 -1
  110. package/src/setup/wizard.js +125 -75
  111. package/src/skills/SkillLoader.js +132 -17
  112. package/src/storage/TaskStore.js +19 -1
  113. package/src/tools/browserAutomation.js +615 -104
  114. package/src/tools/executeCommand.js +19 -1
  115. package/src/tools/index.js +7 -1
  116. package/src/tools/manageAgents.js +55 -4
  117. package/src/tools/replyWithFile.js +62 -0
  118. package/src/tools/screenCapture.js +12 -1
  119. package/src/tools/taskManager.js +164 -0
  120. package/src/tools/useMCP.js +3 -1
  121. package/src/utils/Embeddings.js +236 -12
  122. package/src/webhooks/WebhookHandler.js +107 -0
  123. 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
  */
@@ -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.