daemora 1.0.1 → 1.0.3
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 +106 -76
- package/SOUL.md +100 -28
- package/config/mcp.json +9 -9
- package/package.json +15 -8
- package/skills/apple-notes.md +0 -52
- package/skills/apple-reminders.md +1 -87
- package/skills/camsnap.md +20 -144
- package/skills/coding.md +7 -7
- package/skills/documents.md +6 -6
- package/skills/email.md +6 -6
- package/skills/gif-search.md +28 -171
- package/skills/healthcheck.md +21 -203
- package/skills/image-gen.md +24 -123
- package/skills/model-usage.md +18 -165
- package/skills/obsidian.md +28 -174
- package/skills/pdf.md +30 -181
- package/skills/research.md +6 -6
- package/skills/skill-creator.md +35 -111
- package/skills/spotify.md +2 -17
- package/skills/summarize.md +36 -193
- package/skills/things.md +23 -175
- package/skills/tmux.md +1 -91
- package/skills/trello.md +32 -157
- package/skills/video-frames.md +26 -166
- package/skills/weather.md +6 -6
- package/src/a2a/A2AClient.js +2 -2
- package/src/a2a/A2AServer.js +6 -6
- package/src/a2a/AgentCard.js +2 -2
- package/src/agents/SubAgentManager.js +61 -19
- package/src/agents/Supervisor.js +4 -4
- package/src/channels/BaseChannel.js +6 -6
- package/src/channels/BlueBubblesChannel.js +112 -0
- package/src/channels/DiscordChannel.js +8 -8
- package/src/channels/EmailChannel.js +54 -26
- package/src/channels/FeishuChannel.js +140 -0
- package/src/channels/GoogleChatChannel.js +8 -8
- package/src/channels/HttpChannel.js +2 -2
- package/src/channels/IRCChannel.js +144 -0
- package/src/channels/LineChannel.js +13 -13
- package/src/channels/MatrixChannel.js +97 -0
- package/src/channels/MattermostChannel.js +119 -0
- package/src/channels/NextcloudChannel.js +133 -0
- package/src/channels/NostrChannel.js +175 -0
- package/src/channels/SignalChannel.js +9 -9
- package/src/channels/SlackChannel.js +10 -10
- package/src/channels/TeamsChannel.js +10 -10
- package/src/channels/TelegramChannel.js +8 -8
- package/src/channels/TwitchChannel.js +128 -0
- package/src/channels/WhatsAppChannel.js +10 -10
- package/src/channels/ZaloChannel.js +119 -0
- package/src/channels/iMessageChannel.js +150 -0
- package/src/channels/index.js +241 -11
- package/src/cli.js +835 -38
- package/src/config/agentProfiles.js +19 -19
- package/src/config/channels.js +1 -1
- package/src/config/default.js +12 -7
- package/src/config/models.js +3 -3
- package/src/config/permissions.js +2 -2
- package/src/core/AgentLoop.js +13 -13
- package/src/core/Compaction.js +3 -3
- package/src/core/CostTracker.js +2 -2
- package/src/core/EventBus.js +15 -15
- package/src/core/TaskQueue.js +24 -7
- package/src/core/TaskRunner.js +19 -6
- package/src/daemon/DaemonManager.js +4 -4
- package/src/hooks/HookRunner.js +4 -4
- package/src/index.js +6 -2
- package/src/mcp/MCPAgentRunner.js +3 -3
- package/src/mcp/MCPClient.js +9 -9
- package/src/mcp/MCPManager.js +14 -14
- package/src/models/ModelRouter.js +2 -2
- package/src/safety/AuditLog.js +3 -3
- package/src/safety/CircuitBreaker.js +2 -2
- package/src/safety/CommandGuard.js +132 -0
- package/src/safety/FilesystemGuard.js +23 -3
- package/src/safety/GitRollback.js +5 -5
- package/src/safety/HumanApproval.js +9 -9
- package/src/safety/InputSanitizer.js +81 -8
- package/src/safety/PermissionGuard.js +2 -2
- package/src/safety/Sandbox.js +1 -1
- package/src/safety/SecretScanner.js +90 -28
- package/src/safety/SecretVault.js +2 -2
- package/src/scheduler/Heartbeat.js +3 -3
- package/src/scheduler/Scheduler.js +6 -6
- package/src/setup/theme.js +171 -66
- package/src/setup/wizard.js +432 -57
- package/src/skills/SkillLoader.js +145 -8
- package/src/storage/TaskStore.js +39 -15
- package/src/systemPrompt.js +45 -43
- package/src/tenants/TenantManager.js +79 -22
- package/src/tools/ToolRegistry.js +3 -3
- package/src/tools/applyPatch.js +2 -2
- package/src/tools/browserAutomation.js +4 -4
- package/src/tools/calendar.js +155 -0
- package/src/tools/clipboard.js +71 -0
- package/src/tools/contacts.js +138 -0
- package/src/tools/createDocument.js +2 -2
- package/src/tools/cronTool.js +14 -14
- package/src/tools/database.js +165 -0
- package/src/tools/editFile.js +10 -10
- package/src/tools/executeCommand.js +11 -3
- package/src/tools/generateImage.js +79 -0
- package/src/tools/gitTool.js +141 -0
- package/src/tools/glob.js +1 -1
- package/src/tools/googlePlaces.js +136 -0
- package/src/tools/grep.js +2 -2
- package/src/tools/iMessageTool.js +86 -0
- package/src/tools/imageAnalysis.js +3 -3
- package/src/tools/index.js +56 -2
- package/src/tools/makeVoiceCall.js +283 -0
- package/src/tools/manageAgents.js +2 -2
- package/src/tools/manageMCP.js +38 -20
- package/src/tools/memory.js +25 -32
- package/src/tools/messageChannel.js +1 -1
- package/src/tools/notification.js +90 -0
- package/src/tools/philipsHue.js +147 -0
- package/src/tools/projectTracker.js +8 -8
- package/src/tools/readFile.js +1 -1
- package/src/tools/readPDF.js +73 -0
- package/src/tools/screenCapture.js +6 -6
- package/src/tools/searchContent.js +2 -2
- package/src/tools/searchFiles.js +1 -1
- package/src/tools/sendEmail.js +79 -24
- package/src/tools/sendFile.js +4 -4
- package/src/tools/sonos.js +137 -0
- package/src/tools/sshTool.js +130 -0
- package/src/tools/textToSpeech.js +5 -5
- package/src/tools/transcribeAudio.js +4 -4
- package/src/tools/useMCP.js +4 -4
- package/src/tools/webFetch.js +2 -2
- package/src/tools/webSearch.js +1 -1
- package/src/utils/Embeddings.js +79 -0
- package/src/voice/VoiceSessionManager.js +170 -0
- package/src/voice/VoiceWebhook.js +188 -0
|
@@ -1,15 +1,19 @@
|
|
|
1
|
-
import { readFileSync,
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync, readdirSync, mkdirSync } from "fs";
|
|
2
2
|
import { join } from "path";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
3
4
|
import { config } from "../config/default.js";
|
|
5
|
+
import { generateEmbedding, getEmbeddingProvider } from "../utils/Embeddings.js";
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
|
-
* Skill Loader
|
|
8
|
+
* Skill Loader - auto-discovers .md skill files from the skills/ directory.
|
|
7
9
|
*
|
|
8
10
|
* Each skill file has YAML frontmatter (name, description, triggers)
|
|
9
11
|
* followed by the skill prompt content.
|
|
10
12
|
*
|
|
11
|
-
* Skills are
|
|
12
|
-
*
|
|
13
|
+
* Skills are matched to tasks via:
|
|
14
|
+
* 1. Semantic (embeddings) - cosine similarity on OpenAI text-embedding-3-small vectors.
|
|
15
|
+
* Vectors are cached in data/skill-embeddings.json, recomputed only when skill content changes.
|
|
16
|
+
* 2. Keyword fallback - simple trigger/description word matching when no API key is set.
|
|
13
17
|
*
|
|
14
18
|
* Format:
|
|
15
19
|
* ```markdown
|
|
@@ -22,10 +26,14 @@ import { config } from "../config/default.js";
|
|
|
22
26
|
* ```
|
|
23
27
|
*/
|
|
24
28
|
|
|
29
|
+
const SKILL_EMBEDDINGS_PATH = join(config.dataDir, "skill-embeddings.json");
|
|
30
|
+
const EMBED_THRESHOLD = 0.32; // Lower than memory (0.40) - skill descriptions are shorter
|
|
31
|
+
|
|
25
32
|
class SkillLoader {
|
|
26
33
|
constructor() {
|
|
27
34
|
this.skills = new Map();
|
|
28
35
|
this.loaded = false;
|
|
36
|
+
this._skillVectors = {}; // { skillName: { hash, vector } }
|
|
29
37
|
}
|
|
30
38
|
|
|
31
39
|
/**
|
|
@@ -56,6 +64,7 @@ class SkillLoader {
|
|
|
56
64
|
}
|
|
57
65
|
|
|
58
66
|
this.loaded = true;
|
|
67
|
+
this._loadSkillVectors();
|
|
59
68
|
console.log(
|
|
60
69
|
`[SkillLoader] Loaded ${this.skills.size} skills: ${[...this.skills.keys()].join(", ") || "(none)"}`
|
|
61
70
|
);
|
|
@@ -67,7 +76,6 @@ class SkillLoader {
|
|
|
67
76
|
parseSkill(content, filename) {
|
|
68
77
|
const fmMatch = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/);
|
|
69
78
|
if (!fmMatch) {
|
|
70
|
-
// No frontmatter — use filename as name
|
|
71
79
|
return {
|
|
72
80
|
name: filename.replace(".md", ""),
|
|
73
81
|
description: "",
|
|
@@ -100,6 +108,137 @@ class SkillLoader {
|
|
|
100
108
|
};
|
|
101
109
|
}
|
|
102
110
|
|
|
111
|
+
// ── Embedding helpers ────────────────────────────────────────────────────────
|
|
112
|
+
|
|
113
|
+
_contentHash(skill) {
|
|
114
|
+
// Include provider so cache auto-invalidates when user switches embedding provider
|
|
115
|
+
const provider = getEmbeddingProvider() || "none";
|
|
116
|
+
return createHash("sha1")
|
|
117
|
+
.update(`${provider}|${skill.name}|${skill.description}|${skill.triggers.join(",")}|${skill.content}`)
|
|
118
|
+
.digest("hex")
|
|
119
|
+
.slice(0, 16);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
_loadSkillVectors() {
|
|
123
|
+
if (!existsSync(SKILL_EMBEDDINGS_PATH)) return;
|
|
124
|
+
try {
|
|
125
|
+
this._skillVectors = JSON.parse(readFileSync(SKILL_EMBEDDINGS_PATH, "utf-8"));
|
|
126
|
+
} catch {
|
|
127
|
+
this._skillVectors = {};
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
_saveSkillVectors() {
|
|
132
|
+
try {
|
|
133
|
+
mkdirSync(config.dataDir, { recursive: true });
|
|
134
|
+
writeFileSync(SKILL_EMBEDDINGS_PATH, JSON.stringify(this._skillVectors));
|
|
135
|
+
} catch {}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
_generateEmbedding(text) {
|
|
139
|
+
return generateEmbedding(text);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
_cosineSim(a, b) {
|
|
143
|
+
let dot = 0, na = 0, nb = 0;
|
|
144
|
+
for (let i = 0; i < a.length; i++) {
|
|
145
|
+
dot += a[i] * b[i];
|
|
146
|
+
na += a[i] * a[i];
|
|
147
|
+
nb += b[i] * b[i];
|
|
148
|
+
}
|
|
149
|
+
if (!na || !nb) return 0;
|
|
150
|
+
return dot / (Math.sqrt(na) * Math.sqrt(nb));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// ── Public async API ─────────────────────────────────────────────────────────
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Pre-compute and cache embeddings for all loaded skills.
|
|
157
|
+
* Called once at startup as fire-and-forget. Only re-embeds skills whose content changed.
|
|
158
|
+
*/
|
|
159
|
+
async embedSkills() {
|
|
160
|
+
if (!getEmbeddingProvider()) return;
|
|
161
|
+
if (!this.loaded) this.load();
|
|
162
|
+
|
|
163
|
+
let changed = false;
|
|
164
|
+
|
|
165
|
+
for (const [name, skill] of this.skills) {
|
|
166
|
+
const hash = this._contentHash(skill);
|
|
167
|
+
if (this._skillVectors[name]?.hash === hash) continue; // Cache hit - skip
|
|
168
|
+
|
|
169
|
+
// Text to embed: name + description + triggers + first 500 chars of body
|
|
170
|
+
const text = [
|
|
171
|
+
`Skill: ${skill.name}`,
|
|
172
|
+
`Description: ${skill.description}`,
|
|
173
|
+
`Triggers: ${skill.triggers.join(", ")}`,
|
|
174
|
+
skill.content.slice(0, 500),
|
|
175
|
+
].join("\n");
|
|
176
|
+
|
|
177
|
+
const vector = await this._generateEmbedding(text);
|
|
178
|
+
if (vector) {
|
|
179
|
+
this._skillVectors[name] = { hash, vector };
|
|
180
|
+
changed = true;
|
|
181
|
+
console.log(`[SkillLoader] Embedded skill: ${name}`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Remove cached vectors for skills that no longer exist
|
|
186
|
+
for (const name of Object.keys(this._skillVectors)) {
|
|
187
|
+
if (!this.skills.has(name)) {
|
|
188
|
+
delete this._skillVectors[name];
|
|
189
|
+
changed = true;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (changed) this._saveSkillVectors();
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Semantic skill matching - embeds the task input and returns skills above cosine threshold.
|
|
198
|
+
* Falls back to keyword matching if OPENAI_API_KEY is absent or embeddings aren't ready.
|
|
199
|
+
*
|
|
200
|
+
* Returns top-5 matched skills, sorted by relevance score (highest first).
|
|
201
|
+
*/
|
|
202
|
+
async getSkillPromptsAsync(taskInput) {
|
|
203
|
+
if (!taskInput) return "";
|
|
204
|
+
if (!this.loaded) this.load();
|
|
205
|
+
|
|
206
|
+
const vectorsAvailable = Object.keys(this._skillVectors).length > 0;
|
|
207
|
+
|
|
208
|
+
if (getEmbeddingProvider() && vectorsAvailable) {
|
|
209
|
+
const queryVector = await this._generateEmbedding(taskInput);
|
|
210
|
+
if (queryVector) {
|
|
211
|
+
const scored = [];
|
|
212
|
+
|
|
213
|
+
for (const [name, skill] of this.skills) {
|
|
214
|
+
const cached = this._skillVectors[name];
|
|
215
|
+
if (!cached?.vector) continue;
|
|
216
|
+
const score = this._cosineSim(queryVector, cached.vector);
|
|
217
|
+
if (score >= EMBED_THRESHOLD) {
|
|
218
|
+
scored.push({ skill, score });
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
scored.sort((a, b) => b.score - a.score);
|
|
223
|
+
const matched = scored.slice(0, 5).map((s) => s.skill);
|
|
224
|
+
|
|
225
|
+
if (matched.length > 0) {
|
|
226
|
+
console.log(
|
|
227
|
+
`[SkillLoader] Semantic match (${matched.length}): ${matched.map((s) => s.name).join(", ")}`
|
|
228
|
+
);
|
|
229
|
+
return matched
|
|
230
|
+
.map((s) => `\n--- Skill: ${s.name} ---\n${s.content}\n--- End Skill ---`)
|
|
231
|
+
.join("\n");
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Fallback: keyword matching
|
|
237
|
+
return this.getSkillPrompts(taskInput);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// ── Sync keyword API (fallback) ───────────────────────────────────────────────
|
|
241
|
+
|
|
103
242
|
/**
|
|
104
243
|
* Match skills relevant to a given task/message.
|
|
105
244
|
* Returns skill contents to inject into the system prompt.
|
|
@@ -111,10 +250,8 @@ class SkillLoader {
|
|
|
111
250
|
const matched = [];
|
|
112
251
|
|
|
113
252
|
for (const [name, skill] of this.skills) {
|
|
114
|
-
// Check triggers
|
|
115
253
|
const triggerMatch = skill.triggers.some((t) => input.includes(t));
|
|
116
254
|
|
|
117
|
-
// Check description keywords
|
|
118
255
|
const descWords = skill.description.toLowerCase().split(/\s+/);
|
|
119
256
|
const descMatch = descWords.some(
|
|
120
257
|
(w) => w.length > 3 && input.includes(w)
|
|
@@ -129,7 +266,7 @@ class SkillLoader {
|
|
|
129
266
|
}
|
|
130
267
|
|
|
131
268
|
/**
|
|
132
|
-
* Get skill prompt text for matched skills.
|
|
269
|
+
* Get skill prompt text for matched skills (sync keyword version).
|
|
133
270
|
*/
|
|
134
271
|
getSkillPrompts(taskInput) {
|
|
135
272
|
const matched = this.matchSkills(taskInput);
|
package/src/storage/TaskStore.js
CHANGED
|
@@ -43,27 +43,51 @@ export function listTasks({ limit = 20, status = null } = {}) {
|
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
45
|
* On startup, find tasks stuck in "running" state and reset to "pending".
|
|
46
|
+
* Returns the count of tasks reset so the caller can log accordingly.
|
|
46
47
|
*/
|
|
47
48
|
export function recoverStaleTasks() {
|
|
48
|
-
const files = readdirSync(TASKS_DIR).filter((f) => f.endsWith(".json"));
|
|
49
49
|
let recovered = 0;
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
task.status
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
50
|
+
try {
|
|
51
|
+
const files = readdirSync(TASKS_DIR).filter((f) => f.endsWith(".json"));
|
|
52
|
+
for (const f of files) {
|
|
53
|
+
try {
|
|
54
|
+
const task = JSON.parse(readFileSync(`${TASKS_DIR}/${f}`, "utf-8"));
|
|
55
|
+
if (task.status === "running") {
|
|
56
|
+
task.status = "pending";
|
|
57
|
+
task.startedAt = null;
|
|
58
|
+
writeFileSync(`${TASKS_DIR}/${f}`, JSON.stringify(task, null, 2));
|
|
59
|
+
recovered++;
|
|
60
|
+
console.log(`[TaskStore] Recovered stale task: ${task.id}`);
|
|
61
|
+
}
|
|
62
|
+
} catch {
|
|
63
|
+
// skip corrupt files
|
|
60
64
|
}
|
|
61
|
-
} catch {
|
|
62
|
-
// skip corrupt files
|
|
63
65
|
}
|
|
66
|
+
} catch {
|
|
67
|
+
// TASKS_DIR may not exist yet on first run
|
|
64
68
|
}
|
|
69
|
+
return recovered;
|
|
70
|
+
}
|
|
65
71
|
|
|
66
|
-
|
|
67
|
-
|
|
72
|
+
/**
|
|
73
|
+
* Load all tasks currently in "pending" state from disk, sorted oldest-first.
|
|
74
|
+
* Used by TaskQueue.init() to re-hydrate the in-memory queue after a restart.
|
|
75
|
+
*/
|
|
76
|
+
export function loadPendingTasks() {
|
|
77
|
+
try {
|
|
78
|
+
const files = readdirSync(TASKS_DIR).filter((f) => f.endsWith(".json"));
|
|
79
|
+
return files
|
|
80
|
+
.map((f) => {
|
|
81
|
+
try { return JSON.parse(readFileSync(`${TASKS_DIR}/${f}`, "utf-8")); }
|
|
82
|
+
catch { return null; }
|
|
83
|
+
})
|
|
84
|
+
.filter((t) => t && t.status === "pending")
|
|
85
|
+
.sort((a, b) => {
|
|
86
|
+
// Sort by priority first (lower number = higher priority), then by createdAt (oldest first)
|
|
87
|
+
if (a.priority !== b.priority) return (a.priority || 5) - (b.priority || 5);
|
|
88
|
+
return new Date(a.createdAt) - new Date(b.createdAt);
|
|
89
|
+
});
|
|
90
|
+
} catch {
|
|
91
|
+
return [];
|
|
68
92
|
}
|
|
69
93
|
}
|
package/src/systemPrompt.js
CHANGED
|
@@ -6,6 +6,7 @@ import mcpManager from "./mcp/MCPManager.js";
|
|
|
6
6
|
import tenantContext from "./tenants/TenantContext.js";
|
|
7
7
|
|
|
8
8
|
skillLoader.load();
|
|
9
|
+
skillLoader.embedSkills().catch(() => {}); // Pre-compute skill embeddings at startup (fire-and-forget)
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Build the system prompt dynamically by composing modular sections.
|
|
@@ -48,7 +49,7 @@ function _getContextMemoryPaths() {
|
|
|
48
49
|
/**
|
|
49
50
|
* Inject the top-k most semantically relevant memories for this specific task.
|
|
50
51
|
* Only runs when OPENAI_API_KEY is set and the embeddings store has entries.
|
|
51
|
-
* Falls back silently
|
|
52
|
+
* Falls back silently - never blocks startup or errors out.
|
|
52
53
|
*/
|
|
53
54
|
async function renderSemanticRecall(taskInput) {
|
|
54
55
|
if (!taskInput || taskInput.length < 10) return null;
|
|
@@ -101,7 +102,7 @@ You MUST respond with a JSON object matching this exact schema on every turn:
|
|
|
101
102
|
- Set finalResponse to true
|
|
102
103
|
|
|
103
104
|
## CRITICAL RULES:
|
|
104
|
-
1. NEVER set finalResponse to true unless the work is VERIFIED complete
|
|
105
|
+
1. NEVER set finalResponse to true unless the work is VERIFIED complete - not just written, but confirmed working.
|
|
105
106
|
2. If the user asks you to DO something (fix, create, edit, build, search, etc.), your FIRST response MUST be type "tool_call". Not text. Not a plan. A tool call.
|
|
106
107
|
3. Chain multiple tool calls across turns. After each tool result, decide: need more tools? Call another. Done with verification? Set finalResponse true.
|
|
107
108
|
4. If a tool fails, try an alternative approach. Do NOT give up and ask the user to do it manually.
|
|
@@ -245,15 +246,15 @@ Append to today's daily log. Use to track task progress and decisions.
|
|
|
245
246
|
### spawnAgent(taskDescription, optionsJson?)
|
|
246
247
|
Spawn a sub-agent for a single task.
|
|
247
248
|
- optionsJson: {"profile":"coder","extraTools":["sendEmail"],"parentContext":"spec string","model":"openai:gpt-4.1-mini"}
|
|
248
|
-
- profile: "researcher" | "coder" | "writer" | "analyst"
|
|
249
|
+
- profile: "researcher" | "coder" | "writer" | "analyst" - focused tool set for the task type
|
|
249
250
|
- extraTools: add specific tools on top of the profile (e.g. researcher that also needs writeFile)
|
|
250
|
-
- tools: explicit tool list
|
|
251
|
-
- taskDescription must be comprehensive
|
|
251
|
+
- tools: explicit tool list - overrides profile entirely when you need exact control
|
|
252
|
+
- taskDescription must be comprehensive - sub-agent has no other context
|
|
252
253
|
|
|
253
254
|
### parallelAgents(tasksJson, sharedOptionsJson?)
|
|
254
255
|
Spawn multiple sub-agents in parallel. Each task can have its own profile.
|
|
255
|
-
- tasksJson: array of {"description":"...","options":{"profile":"coder"}}
|
|
256
|
-
- sharedOptionsJson: {"sharedContext":"..."}
|
|
256
|
+
- tasksJson: array of {"description":"...","options":{"profile":"coder"}} - each must be self-contained
|
|
257
|
+
- sharedOptionsJson: {"sharedContext":"..."} - spec/contract shared with ALL agents before their task
|
|
257
258
|
- Always pass workspace path in sharedContext when agents need to share artifacts via filesystem
|
|
258
259
|
|
|
259
260
|
### manageAgents(action, paramsJson?)
|
|
@@ -261,14 +262,14 @@ List, kill, or steer running sub-agents. action: "list"|"kill"|"steer". paramsJs
|
|
|
261
262
|
|
|
262
263
|
### useMCP(serverName, taskDescription)
|
|
263
264
|
Delegate a task to a specialist agent for the named MCP server.
|
|
264
|
-
- serverName: the MCP server to use
|
|
265
|
-
- taskDescription: full task spec
|
|
265
|
+
- serverName: the MCP server to use - check "Connected MCP Servers" section for available servers
|
|
266
|
+
- taskDescription: full task spec - the specialist has zero other context, include all details
|
|
266
267
|
- The specialist gets ONLY that server's tools: lean context, no noise from built-in tools
|
|
267
268
|
|
|
268
269
|
### manageMCP(action, paramsJson?)
|
|
269
270
|
Inspect connected MCP servers and their available tools at runtime.
|
|
270
|
-
- list / status: no params
|
|
271
|
-
- tools: paramsJson {"server":"github"}
|
|
271
|
+
- list / status: no params - all servers with connection status and tool names
|
|
272
|
+
- tools: paramsJson {"server":"github"} - full tool list for one server, or {} for all
|
|
272
273
|
|
|
273
274
|
### delegateToAgent(agentUrl, taskInput)
|
|
274
275
|
Delegate to an external AI agent via A2A protocol.
|
|
@@ -276,11 +277,11 @@ Delegate to an external AI agent via A2A protocol.
|
|
|
276
277
|
## Project Tracking
|
|
277
278
|
|
|
278
279
|
### projectTracker(action, paramsJson?)
|
|
279
|
-
Track multi-step project progress. Persisted to disk
|
|
280
|
+
Track multi-step project progress. Persisted to disk - survives crashes and timeouts.
|
|
280
281
|
- createProject: paramsJson {"name":"...","description":"...","tasks":["step 1","step 2",...]}
|
|
281
282
|
- addTask: paramsJson {"projectId":"...","title":"...","description":"..."}
|
|
282
283
|
- updateTask: paramsJson {"projectId":"...","taskId":"t1","status":"in_progress|done|failed|skipped","notes":"..."}
|
|
283
|
-
- getProject: paramsJson {"projectId":"..."}
|
|
284
|
+
- getProject: paramsJson {"projectId":"..."} - shows ✅⬜🔄❌ status per task
|
|
284
285
|
- listProjects: paramsJson {} or {"status":"in_progress|done"}
|
|
285
286
|
- deleteProject: paramsJson {"projectId":"..."}
|
|
286
287
|
|
|
@@ -296,7 +297,7 @@ function renderMCPTools() {
|
|
|
296
297
|
|
|
297
298
|
const serverList = servers
|
|
298
299
|
.map((s) => {
|
|
299
|
-
const desc = s.description ? `
|
|
300
|
+
const desc = s.description ? ` - ${s.description}` : "";
|
|
300
301
|
return `- **${s.name}**${desc} (${s.toolCount} tools: ${s.toolNames.join(", ")})`;
|
|
301
302
|
})
|
|
302
303
|
.join("\n");
|
|
@@ -308,7 +309,7 @@ The following MCP servers are connected. Use \`useMCP(serverName, taskDescriptio
|
|
|
308
309
|
${serverList}
|
|
309
310
|
|
|
310
311
|
Use \`manageMCP("list")\` to check server connection status at any time.
|
|
311
|
-
Do NOT call mcp__ tools directly
|
|
312
|
+
Do NOT call mcp__ tools directly - always route through \`useMCP\`. The specialist agent receives only that server's tools for focused, efficient execution.`;
|
|
312
313
|
}
|
|
313
314
|
|
|
314
315
|
function renderToolUsageRules() {
|
|
@@ -322,7 +323,7 @@ function renderToolUsageRules() {
|
|
|
322
323
|
## Choose the Right Tool
|
|
323
324
|
- **Small, targeted change** (fix a line, rename a variable, replace a block): use editFile
|
|
324
325
|
- **Major rewrite or adding lots of content** (new CSS sections, restructuring): use writeFile to rewrite the entire file
|
|
325
|
-
- **editFile keeps failing?** Switch to writeFile
|
|
326
|
+
- **editFile keeps failing?** Switch to writeFile - read the full file, modify the content, write it all back
|
|
326
327
|
- **Need to find something?** Use searchContent before reading multiple files
|
|
327
328
|
- **Need file list?** Use listDirectory or searchFiles, not executeCommand("ls")
|
|
328
329
|
|
|
@@ -330,7 +331,7 @@ function renderToolUsageRules() {
|
|
|
330
331
|
- If editFile fails because oldString wasn't found: re-read the file to get the exact current content, then retry
|
|
331
332
|
- If editFile fails because params are wrong: remember it needs EXACTLY 3 string params (filePath, oldString, newString)
|
|
332
333
|
- If a command fails: read the error, diagnose, try a different approach
|
|
333
|
-
- NEVER tell the user to do something manually. You have tools
|
|
334
|
+
- NEVER tell the user to do something manually. You have tools - use them.
|
|
334
335
|
|
|
335
336
|
## Don't Over-Engineer
|
|
336
337
|
- Only make changes that are directly requested or clearly necessary.
|
|
@@ -338,7 +339,7 @@ function renderToolUsageRules() {
|
|
|
338
339
|
- Don't add docstrings, comments, or type annotations to code you didn't change. Only add comments where the logic isn't self-evident.
|
|
339
340
|
- Don't add error handling, fallbacks, or validation for scenarios that can't happen. Trust internal code and framework guarantees. Only validate at system boundaries (user input, external APIs).
|
|
340
341
|
- Don't use feature flags or backwards-compatibility shims when you can just change the code.
|
|
341
|
-
- Don't create helpers, utilities, or abstractions for one-time operations. Don't design for hypothetical future requirements. The right amount of complexity is the minimum needed
|
|
342
|
+
- Don't create helpers, utilities, or abstractions for one-time operations. Don't design for hypothetical future requirements. The right amount of complexity is the minimum needed - three similar lines of code is better than a premature abstraction.
|
|
342
343
|
- Avoid backwards-compatibility hacks like renaming unused _vars, re-exporting types, adding "// removed" comments for removed code. If something is unused, delete it completely.
|
|
343
344
|
- Do not create new files unless absolutely necessary. Prefer editing an existing file over creating a new one.
|
|
344
345
|
|
|
@@ -350,7 +351,7 @@ function renderToolUsageRules() {
|
|
|
350
351
|
|
|
351
352
|
## Quality Standards
|
|
352
353
|
- Follow existing code conventions (naming, formatting, indentation, style).
|
|
353
|
-
- Match the existing project's patterns
|
|
354
|
+
- Match the existing project's patterns - check surrounding code first.
|
|
354
355
|
- Never assume a library is available without checking package.json or imports.
|
|
355
356
|
- Prefer the simplest correct solution. Complexity is a cost, not a feature.
|
|
356
357
|
|
|
@@ -361,21 +362,21 @@ function renderToolUsageRules() {
|
|
|
361
362
|
- Heavy task (3+ files, multi-agent, research then build, unclear scope): plan first using projectTracker, then execute.
|
|
362
363
|
|
|
363
364
|
### Planning workflow for heavy tasks
|
|
364
|
-
1. Call projectTracker("createProject")
|
|
365
|
-
2. The workspace path is returned
|
|
365
|
+
1. Call projectTracker("createProject") - breaks work into tasks AND creates a shared workspace directory.
|
|
366
|
+
2. The workspace path is returned - pass it in sharedContext so all sub-agents know where to write artifacts.
|
|
366
367
|
3. Define the shared contract (API schema, DOM structure, naming conventions) before spawning agents.
|
|
367
368
|
4. Mark each task in_progress before starting, done with notes when finished.
|
|
368
369
|
5. If interrupted, call projectTracker("listProjects") to find and resume.
|
|
369
370
|
|
|
370
|
-
### Parallel vs sequential
|
|
371
|
+
### Parallel vs sequential - the decision rule
|
|
371
372
|
- Ask: does task B need output from task A? Yes → sequential. No → parallel.
|
|
372
373
|
- Never run agents in parallel when they have data dependencies. Define contracts upfront and make them independent.
|
|
373
374
|
- Parallel agents communicate through the shared workspace (files), NOT through messages or return values.
|
|
374
375
|
|
|
375
376
|
### Choosing a profile for sub-agents
|
|
376
|
-
- researcher: gather info, browse web, write findings
|
|
377
|
-
- coder: read/write/run full loop
|
|
378
|
-
- writer: produce documents and reports
|
|
377
|
+
- researcher: gather info, browse web, write findings - no shell execution
|
|
378
|
+
- coder: read/write/run full loop - for building, fixing, testing
|
|
379
|
+
- writer: produce documents and reports - no shell, no browser
|
|
379
380
|
- analyst: data processing with shell scripts + web + vision
|
|
380
381
|
- No profile: gets the default 27-tool set (safe general-purpose)
|
|
381
382
|
- Add extraTools when a profile is almost right but needs one more tool
|
|
@@ -385,8 +386,8 @@ When projectTracker creates a project, it returns a workspace path (data/workspa
|
|
|
385
386
|
Include this in sharedContext for ALL parallel agents on that project:
|
|
386
387
|
- Sub-agents write output files to workspace/ (code, reports, schemas, notes)
|
|
387
388
|
- Parent reads from workspace/ to build context for the next phase
|
|
388
|
-
- Artifacts survive crashes
|
|
389
|
-
- Do NOT pass full file contents back as return values
|
|
389
|
+
- Artifacts survive crashes - work is never lost
|
|
390
|
+
- Do NOT pass full file contents back as return values - write to workspace and return a summary
|
|
390
391
|
|
|
391
392
|
### Structured return convention
|
|
392
393
|
End every sub-agent response with a structured summary block so the parent can parse results:
|
|
@@ -400,7 +401,7 @@ A sub-agent has NO context except what you give it. Write as if handing off to a
|
|
|
400
401
|
|
|
401
402
|
A comprehensive task description includes:
|
|
402
403
|
- Exact file path(s) to create or modify
|
|
403
|
-
- The full spec, schema, or contract to follow (do not summarize
|
|
404
|
+
- The full spec, schema, or contract to follow (do not summarize - paste the actual names, endpoints, fields)
|
|
404
405
|
- Expected behavior and output, not just the file name
|
|
405
406
|
- Any constraints (no external libraries, match existing patterns, specific format)
|
|
406
407
|
|
|
@@ -409,11 +410,12 @@ Good: "Create /project/style.css. Style these DOM elements from the shared spec:
|
|
|
409
410
|
|
|
410
411
|
### Sequential vs parallel agents
|
|
411
412
|
- Sequential: use spawnAgent multiple times when each step needs the previous step's output (research → write → test).
|
|
412
|
-
- Parallel: use parallelAgents when steps can run simultaneously
|
|
413
|
+
- Parallel: use parallelAgents when steps can run simultaneously - always provide sharedContext so agents share the same contract.`;
|
|
413
414
|
}
|
|
414
415
|
|
|
415
|
-
function renderSkills(taskInput) {
|
|
416
|
-
|
|
416
|
+
async function renderSkills(taskInput) {
|
|
417
|
+
if (!taskInput) return "";
|
|
418
|
+
const skillPrompts = await skillLoader.getSkillPromptsAsync(taskInput);
|
|
417
419
|
if (!skillPrompts) return "";
|
|
418
420
|
return `# Active Skills\n\n${skillPrompts}`;
|
|
419
421
|
}
|
|
@@ -449,17 +451,17 @@ function renderOperationalGuidelines() {
|
|
|
449
451
|
- Report what you DID in past tense: "Updated styles.css with hover effects" not "I will update the styles".
|
|
450
452
|
- Don't narrate your tool calls. Just call the tool.
|
|
451
453
|
- Don't explain what you're about to do. Just do it.
|
|
452
|
-
- Don't ask "shall I proceed?" or "would you like me to...?"
|
|
454
|
+
- Don't ask "shall I proceed?" or "would you like me to...?" - just do the work.
|
|
453
455
|
- Only ask for confirmation before DELETING files or running destructive commands.
|
|
454
456
|
|
|
455
457
|
## Understanding Requirements
|
|
456
|
-
- Read between the lines. Vague requests have implied intent
|
|
458
|
+
- Read between the lines. Vague requests have implied intent - infer it.
|
|
457
459
|
- "make it look better" → proper spacing, typography, color contrast, responsive layout
|
|
458
460
|
- "fix the bug" → find root cause, fix it properly, verify it's gone
|
|
459
461
|
- "add more features" → add meaningful features that fit the context of the app
|
|
460
462
|
- If truly ambiguous (two valid interpretations with different outcomes), ask ONE focused question. Otherwise just do the most sensible thing.
|
|
461
463
|
- Don't take requirements hyper-literally. "Fix the login" means fix the whole login flow, not just the one line mentioned.
|
|
462
|
-
- Match the existing code style, patterns, and conventions
|
|
464
|
+
- Match the existing code style, patterns, and conventions - check surrounding code first.
|
|
463
465
|
|
|
464
466
|
## Workflow: Read → Understand → Act → Verify → Fix → Report
|
|
465
467
|
1. **Read:** Read every file you will touch BEFORE touching it. Never edit blind.
|
|
@@ -501,26 +503,26 @@ When you build or modify any UI (web app, landing page, dashboard, component):
|
|
|
501
503
|
|
|
502
504
|
## When Blocked
|
|
503
505
|
- If your approach is blocked, do NOT brute force. Read the error, understand the root cause, try a different approach.
|
|
504
|
-
- If a tool fails twice with the same params, stop and diagnose
|
|
506
|
+
- If a tool fails twice with the same params, stop and diagnose - don't retry the same thing.
|
|
505
507
|
- If editFile keeps failing to match, re-read the file to get the exact current content, then retry.
|
|
506
|
-
- Never use destructive workarounds (deleting files, force-pushing, wiping state) to clear a blocker
|
|
508
|
+
- Never use destructive workarounds (deleting files, force-pushing, wiping state) to clear a blocker - investigate first.
|
|
507
509
|
|
|
508
510
|
## What NOT To Do
|
|
509
511
|
- NEVER claim you "fixed" or "updated" something without actually calling writeFile or editFile
|
|
510
|
-
- NEVER describe a plan and stop
|
|
512
|
+
- NEVER describe a plan and stop - execute the plan using tools
|
|
511
513
|
- NEVER ask the user to copy-paste code or make changes manually
|
|
512
|
-
- NEVER ask the user to run tests, start a server, or open a browser
|
|
513
|
-
- NEVER tell the user "you should..." or "you can..."
|
|
514
|
-
- NEVER give up after one failed attempt
|
|
514
|
+
- NEVER ask the user to run tests, start a server, or open a browser - do it yourself
|
|
515
|
+
- NEVER tell the user "you should..." or "you can..." - you do it
|
|
516
|
+
- NEVER give up after one failed attempt - try alternative approaches
|
|
515
517
|
- NEVER set finalResponse to true without having verified the result
|
|
516
|
-
- NEVER output text explaining what you will do next between tool calls
|
|
518
|
+
- NEVER output text explaining what you will do next between tool calls - just call the next tool
|
|
517
519
|
- NEVER over-engineer. Only make changes that are directly requested or clearly necessary.
|
|
518
520
|
- NEVER add features, refactor, or "improve" code beyond what was asked.
|
|
519
521
|
- NEVER add comments, docstrings, or type annotations to code you didn't touch.
|
|
520
|
-
- NEVER introduce security vulnerabilities
|
|
522
|
+
- NEVER introduce security vulnerabilities - if you spot one you created, fix it immediately.
|
|
521
523
|
- NEVER ask a question you can answer yourself with a tool call.`;
|
|
522
524
|
}
|
|
523
525
|
|
|
524
526
|
// Note: buildSystemPrompt is now async. Use `await buildSystemPrompt(taskInput)` at call sites.
|
|
525
527
|
// This legacy sync export is kept for any import that doesn't need task-specific recall.
|
|
526
|
-
export const systemPrompt = { role: "system", content: "" }; // placeholder
|
|
528
|
+
export const systemPrompt = { role: "system", content: "" }; // placeholder - rebuilt per-task
|