@rubytech/taskmaster 1.0.42 → 1.0.43

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.
@@ -32,7 +32,13 @@ function emit(event) {
32
32
  function resolveWatchPaths(workspaceDir, config) {
33
33
  const paths = [];
34
34
  if (workspaceDir.trim()) {
35
- paths.push(path.join(workspaceDir, "skills"));
35
+ // Agent workspace dirs may be subdirs (e.g. ~/taskmaster/agents/public) but
36
+ // skills/ lives at the workspace root (e.g. ~/taskmaster/skills/).
37
+ const rootMatch = workspaceDir.replace(/\/+$/, "").match(/^(.+)\/agents\/[^/]+$/);
38
+ const skillsDir = rootMatch
39
+ ? path.join(rootMatch[1], "skills")
40
+ : path.join(workspaceDir, "skills");
41
+ paths.push(skillsDir);
36
42
  }
37
43
  paths.push(path.join(CONFIG_DIR, "skills"));
38
44
  const extraDirsRaw = config?.skills?.load?.extraDirs ?? [];
@@ -110,8 +110,13 @@ function loadSkillEntries(workspaceDir, opts) {
110
110
  return [];
111
111
  };
112
112
  const managedSkillsDir = opts?.managedSkillsDir ?? path.join(CONFIG_DIR, "skills");
113
- const workspaceSkillsDir = path.join(workspaceDir, "skills");
114
- const bundledSkillsDir = opts?.bundledSkillsDir ?? resolveBundledSkillsDir();
113
+ // Agent workspace dirs may be subdirs (e.g. ~/taskmaster/agents/public) but
114
+ // skills/ lives at the workspace root (e.g. ~/taskmaster/skills/).
115
+ // Resolve the root by stripping a trailing /agents/{id} suffix.
116
+ const rootMatch = workspaceDir.replace(/\/+$/, "").match(/^(.+)\/agents\/[^/]+$/);
117
+ const workspaceSkillsDir = rootMatch
118
+ ? path.join(rootMatch[1], "skills")
119
+ : path.join(workspaceDir, "skills");
115
120
  const extraDirsRaw = opts?.config?.skills?.load?.extraDirs ?? [];
116
121
  const extraDirs = extraDirsRaw
117
122
  .map((d) => (typeof d === "string" ? d.trim() : ""))
@@ -121,12 +126,9 @@ function loadSkillEntries(workspaceDir, opts) {
121
126
  config: opts?.config,
122
127
  });
123
128
  const mergedExtraDirs = [...extraDirs, ...pluginSkillDirs];
124
- const bundledSkills = bundledSkillsDir
125
- ? loadSkills({
126
- dir: bundledSkillsDir,
127
- source: "taskmaster-bundled",
128
- })
129
- : [];
129
+ // Bundled skills are NOT loaded at runtime. They are synced to the workspace
130
+ // on gateway startup (syncBundledSkillsToWorkspace). Agents read skills
131
+ // exclusively from the workspace.
130
132
  const extraSkills = mergedExtraDirs.flatMap((dir) => {
131
133
  const resolved = resolveUserPath(dir);
132
134
  return loadSkills({
@@ -143,11 +145,9 @@ function loadSkillEntries(workspaceDir, opts) {
143
145
  source: "taskmaster-workspace",
144
146
  });
145
147
  const merged = new Map();
146
- // Precedence: extra < bundled < managed < workspace
148
+ // Precedence: extra < managed < workspace
147
149
  for (const skill of extraSkills)
148
150
  merged.set(skill.name, skill);
149
- for (const skill of bundledSkills)
150
- merged.set(skill.name, skill);
151
151
  for (const skill of managedSkills)
152
152
  merged.set(skill.name, skill);
153
153
  for (const skill of workspaceSkills)
@@ -243,6 +243,42 @@ export async function syncSkillsToWorkspace(params) {
243
243
  }
244
244
  });
245
245
  }
246
+ /**
247
+ * Copy bundled skills into a workspace's `skills/` directory.
248
+ * Only copies skills whose target directory does not already exist,
249
+ * preserving any user modifications to existing workspace skills.
250
+ */
251
+ export async function syncBundledSkillsToWorkspace(workspaceDir, opts) {
252
+ const bundledDir = opts?.bundledSkillsDir ?? resolveBundledSkillsDir();
253
+ if (!bundledDir)
254
+ return { synced: [] };
255
+ let entries;
256
+ try {
257
+ entries = await fsp
258
+ .readdir(bundledDir, { withFileTypes: true })
259
+ .then((dirents) => dirents.filter((d) => d.isDirectory()).map((d) => d.name));
260
+ }
261
+ catch {
262
+ return { synced: [] };
263
+ }
264
+ const targetSkillsDir = path.join(workspaceDir, "skills");
265
+ await fsp.mkdir(targetSkillsDir, { recursive: true });
266
+ const synced = [];
267
+ for (const name of entries) {
268
+ const dest = path.join(targetSkillsDir, name);
269
+ if (fs.existsSync(dest))
270
+ continue;
271
+ try {
272
+ await fsp.cp(path.join(bundledDir, name), dest, { recursive: true });
273
+ synced.push(name);
274
+ }
275
+ catch (error) {
276
+ const message = error instanceof Error ? error.message : String(error);
277
+ skillsLogger.warn(`failed to sync bundled skill "${name}" to workspace: ${message}`);
278
+ }
279
+ }
280
+ return { synced };
281
+ }
246
282
  export function filterWorkspaceSkillEntries(entries, config) {
247
283
  return filterSkillEntries(entries, config);
248
284
  }
@@ -25,7 +25,7 @@ function buildMemorySection(params) {
25
25
  }
26
26
  return [
27
27
  "## Memory Recall",
28
- "Your conversation history is primary context. Use memory_search only when the conversation lacks the information you need e.g. prior session context, stored preferences, business data, or facts that may have changed. Use memory_get to pull only the needed lines. If low confidence after search, say you checked.",
28
+ "Memory is your knowledge base customer profiles, business data, preferences, prior interactions, lessons, and instructions all live there. Proactively use memory_search whenever a topic might have stored context: who a person is, what was discussed before, business rules, pricing, product details, or anything that could have been recorded. Do not rely on conversation history alone — it only covers the current session. Use memory_get to pull specific lines when you know the file. If a search returns no results, say you checked.",
29
29
  "",
30
30
  ];
31
31
  }
@@ -120,7 +120,7 @@ export function buildAgentSystemPrompt(params) {
120
120
  ls: "List directory contents",
121
121
  exec: "Run shell commands (pty available for TTY-required CLIs)",
122
122
  process: "Manage background exec sessions",
123
- web_search: "Search the web (Brave API)",
123
+ web_search: "Search the web",
124
124
  web_fetch: "Fetch and extract readable content from a URL",
125
125
  // Channel docking: add login tools here when a channel needs interactive linking.
126
126
  browser: "Control web browser",
@@ -84,7 +84,7 @@ function resolveSearchProvider(search) {
84
84
  return "tavily";
85
85
  if (raw === "brave")
86
86
  return "brave";
87
- return "brave";
87
+ return "tavily";
88
88
  }
89
89
  function resolveTavilyConfig(search) {
90
90
  if (!search || typeof search !== "object")
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.0.42",
3
- "commit": "0bb7245bb438755c6fcb527a5459e21f3c214692",
4
- "builtAt": "2026-02-16T22:06:31.514Z"
2
+ "version": "1.0.43",
3
+ "commit": "7321a0d3197409e1ee8879a287a769ffa2e0905d",
4
+ "builtAt": "2026-02-17T13:12:59.332Z"
5
5
  }