@rubytech/taskmaster 1.21.2 → 1.22.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.
@@ -1,14 +1,13 @@
1
1
  import { PRELOADED_SKILL_AGENTS } from "./types.js";
2
2
  const DEFAULT_AGENTS = ["admin", "public"];
3
3
  /**
4
- * Resolve which agents can access a skill.
5
- * Preloaded skills use the hardcoded map (not overridable).
6
- * User/licensed skills use config, defaulting to all agents.
4
+ * Resolve which agents can access a given skill.
5
+ * - Preloaded skills use the hardcoded map (not overridable by users).
6
+ * - User/licensed skills use `config.skills.entries[name].agents`, defaulting to both agents.
7
7
  */
8
8
  export function resolveSkillAgents(skillName, isPreloaded, config) {
9
- if (isPreloaded) {
9
+ if (isPreloaded)
10
10
  return PRELOADED_SKILL_AGENTS[skillName] ?? DEFAULT_AGENTS;
11
- }
12
11
  const entry = config?.skills?.entries?.[skillName];
13
12
  return entry?.agents ?? DEFAULT_AGENTS;
14
13
  }
@@ -1 +1,25 @@
1
- export {};
1
+ /**
2
+ * Hardcoded agent access for preloaded (bundled) skills.
3
+ * Users cannot override these — they are enforced at prompt build time.
4
+ */
5
+ export const PRELOADED_SKILL_AGENTS = {
6
+ "system-admin": ["admin"],
7
+ "log-review": ["admin"],
8
+ "strategic-proactivity": ["admin"],
9
+ "skill-builder": ["admin"],
10
+ anthropic: ["admin"],
11
+ openai: ["admin"],
12
+ "google-ai": ["admin"],
13
+ tavily: ["admin"],
14
+ twilio: ["admin"],
15
+ brevo: ["admin"],
16
+ stripe: ["admin"],
17
+ taskmaster: ["admin", "public"],
18
+ "business-assistant": ["admin", "public"],
19
+ "event-management": ["admin", "public"],
20
+ weather: ["admin", "public"],
21
+ "image-gen": ["admin", "public"],
22
+ "qr-code": ["admin", "public"],
23
+ "sales-closer": ["admin", "public"],
24
+ "zero-to-prototype": ["admin", "public"],
25
+ };
@@ -9,6 +9,7 @@ import { parseFrontmatter, resolveTaskmasterMetadata, resolveSkillInvocationPoli
9
9
  import { resolvePluginSkillDirs } from "./plugin-skills.js";
10
10
  import { hasMarker } from "../../license/skill-pack.js";
11
11
  import { serializeByKey } from "./serialize.js";
12
+ import { resolveSkillAgents } from "./agent-scope.js";
12
13
  const fsp = fs.promises;
13
14
  const skillsLogger = createSubsystemLogger("skills");
14
15
  function escapeXml(str) {
@@ -94,7 +95,21 @@ function debugSkillCommandOnce(messageKey, message, meta) {
94
95
  skillCommandDebugOnce.add(messageKey);
95
96
  skillsLogger.debug(message, meta);
96
97
  }
97
- function filterSkillEntries(entries, config, skillFilter, eligibility) {
98
+ function resolveBundledSkillNamesSet() {
99
+ const dir = resolveBundledSkillsDir();
100
+ if (!dir)
101
+ return new Set();
102
+ try {
103
+ return new Set(fs
104
+ .readdirSync(dir, { withFileTypes: true })
105
+ .filter((d) => d.isDirectory())
106
+ .map((d) => d.name));
107
+ }
108
+ catch {
109
+ return new Set();
110
+ }
111
+ }
112
+ function filterSkillEntries(entries, config, skillFilter, eligibility, agentId) {
98
113
  let filtered = entries.filter((entry) => shouldIncludeSkill({ entry, config, eligibility }));
99
114
  // If skillFilter is provided, only include skills in the filter list.
100
115
  if (skillFilter !== undefined) {
@@ -107,6 +122,15 @@ function filterSkillEntries(entries, config, skillFilter, eligibility) {
107
122
  : [];
108
123
  console.log(`[skills] After filter: ${filtered.map((entry) => entry.skill.name).join(", ")}`);
109
124
  }
125
+ // Agent-level filtering: exclude skills not accessible to this agent.
126
+ if (agentId) {
127
+ const bundledNames = resolveBundledSkillNamesSet();
128
+ filtered = filtered.filter((entry) => {
129
+ const isPreloaded = bundledNames.has(entry.skill.name);
130
+ const allowedAgents = resolveSkillAgents(entry.skill.name, isPreloaded, config);
131
+ return allowedAgents.includes(agentId);
132
+ });
133
+ }
110
134
  return filtered;
111
135
  }
112
136
  const SKILL_COMMAND_MAX_LENGTH = 32;
@@ -214,7 +238,7 @@ function loadSkillEntries(workspaceDir, opts) {
214
238
  }
215
239
  export function buildWorkspaceSkillSnapshot(workspaceDir, opts) {
216
240
  const skillEntries = opts?.entries ?? loadSkillEntries(workspaceDir, opts);
217
- const eligible = filterSkillEntries(skillEntries, opts?.config, opts?.skillFilter, opts?.eligibility);
241
+ const eligible = filterSkillEntries(skillEntries, opts?.config, opts?.skillFilter, opts?.eligibility, opts?.agentId);
218
242
  const promptEntries = eligible.filter((entry) => entry.invocation?.disableModelInvocation !== true);
219
243
  const resolvedSkills = promptEntries.map((entry) => entry.skill);
220
244
  const remoteNote = opts?.eligibility?.remote?.note?.trim();
@@ -233,7 +257,7 @@ export function buildWorkspaceSkillSnapshot(workspaceDir, opts) {
233
257
  }
234
258
  export function buildWorkspaceSkillsPrompt(workspaceDir, opts) {
235
259
  const skillEntries = opts?.entries ?? loadSkillEntries(workspaceDir, opts);
236
- const eligible = filterSkillEntries(skillEntries, opts?.config, opts?.skillFilter, opts?.eligibility);
260
+ const eligible = filterSkillEntries(skillEntries, opts?.config, opts?.skillFilter, opts?.eligibility, opts?.agentId);
237
261
  const promptEntries = eligible.filter((entry) => entry.invocation?.disableModelInvocation !== true);
238
262
  const remoteNote = opts?.eligibility?.remote?.note?.trim();
239
263
  return [remoteNote, formatSkillsForPromptTaskmaster(promptEntries)].filter(Boolean).join("\n");
@@ -354,8 +378,8 @@ export async function syncBundledSkillsToWorkspace(workspaceDir, opts) {
354
378
  }
355
379
  return { synced };
356
380
  }
357
- export function filterWorkspaceSkillEntries(entries, config) {
358
- return filterSkillEntries(entries, config);
381
+ export function filterWorkspaceSkillEntries(entries, config, agentId) {
382
+ return filterSkillEntries(entries, config, undefined, undefined, agentId);
359
383
  }
360
384
  export function buildWorkspaceSkillCommandSpecs(workspaceDir, opts) {
361
385
  const skillEntries = opts?.entries ?? loadSkillEntries(workspaceDir, opts);
@@ -3,6 +3,7 @@ import path from "node:path";
3
3
  import { CONFIG_DIR } from "../utils.js";
4
4
  import { hasMarker } from "../license/skill-pack.js";
5
5
  import { hasBinary, isBundledSkillAllowed, isConfigPathTruthy, loadWorkspaceSkillEntries, resolveBundledAllowlist, resolveBundledSkillsDir, resolveConfigPath, resolveSkillConfig, resolveSkillsInstallPreferences, } from "./skills.js";
6
+ import { resolveSkillAgents } from "./skills/agent-scope.js";
6
7
  function resolveSkillKey(entry) {
7
8
  return entry.taskmaster?.skillKey ?? entry.skill.name;
8
9
  }
@@ -149,6 +150,8 @@ function buildSkillStatus(entry, config, prefs, eligibility, bundledSkillNames)
149
150
  missing.config.length === 0 &&
150
151
  missing.os.length === 0));
151
152
  const preloaded = bundledSkillNames ? bundledSkillNames.has(entry.skill.name) : false;
153
+ const agents = [...resolveSkillAgents(entry.skill.name, preloaded, config)];
154
+ const agentsLocked = preloaded;
152
155
  const isMarketplace = hasMarker(entry.skill.baseDir);
153
156
  let marketplaceVersion;
154
157
  if (isMarketplace) {
@@ -177,6 +180,8 @@ function buildSkillStatus(entry, config, prefs, eligibility, bundledSkillNames)
177
180
  blockedByAllowlist,
178
181
  eligible,
179
182
  preloaded,
183
+ agents,
184
+ agentsLocked,
180
185
  marketplace: isMarketplace,
181
186
  marketplaceVersion,
182
187
  requirements: {
@@ -115,6 +115,7 @@ export async function runPreparedReply(params) {
115
115
  workspaceDir,
116
116
  cfg,
117
117
  skillFilter: opts?.skillFilter,
118
+ agentId: _agentId,
118
119
  });
119
120
  sessionEntry = skillResult.sessionEntry ?? sessionEntry;
120
121
  currentSystemSent = skillResult.systemSent;
@@ -116,7 +116,7 @@ export async function prependSystemEvents(params) {
116
116
  return `${block}\n\n${params.prefixedBodyBase}`;
117
117
  }
118
118
  export async function ensureSkillSnapshot(params) {
119
- const { sessionEntry, sessionStore, sessionKey, storePath, sessionId, isFirstTurnInSession, workspaceDir, cfg, skillFilter, } = params;
119
+ const { sessionEntry, sessionStore, sessionKey, storePath, sessionId, isFirstTurnInSession, workspaceDir, cfg, skillFilter, agentId, } = params;
120
120
  let nextEntry = sessionEntry;
121
121
  let systemSent = sessionEntry?.systemSent ?? false;
122
122
  const remoteEligibility = getRemoteSkillEligibility();
@@ -135,6 +135,7 @@ export async function ensureSkillSnapshot(params) {
135
135
  skillFilter,
136
136
  eligibility: { remote: remoteEligibility },
137
137
  snapshotVersion,
138
+ agentId,
138
139
  })
139
140
  : current.skillsSnapshot;
140
141
  nextEntry = {
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.21.2",
3
- "commit": "ffd98ac2beb01992a243d3dee11c1a5c8dac870c",
4
- "builtAt": "2026-03-06T21:01:06.591Z"
2
+ "version": "1.22.0",
3
+ "commit": "9d1f55fc9452c09c88a79e294df7c30387fdbd3f",
4
+ "builtAt": "2026-03-07T05:55:37.155Z"
5
5
  }