@rubytech/taskmaster 1.0.83 → 1.0.85

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.
@@ -112,6 +112,18 @@ export function resolveAgentWorkspaceDir(cfg, agentId) {
112
112
  }
113
113
  return path.join(os.homedir(), `taskmaster-${id}`);
114
114
  }
115
+ /**
116
+ * Resolve the workspace ROOT directory for an agent.
117
+ * Multi-agent workspaces set each agent's workspace to a subdirectory
118
+ * (e.g. ~/taskmaster/agents/admin). Shared resources — memory/, skills/,
119
+ * files — live at the workspace root (~/taskmaster), not the agent subdir.
120
+ */
121
+ export function resolveAgentWorkspaceRoot(cfg, agentId) {
122
+ const agentDir = resolveAgentWorkspaceDir(cfg, agentId);
123
+ const normalised = agentDir.replace(/\/+$/, "");
124
+ const match = normalised.match(/^(.+)\/agents\/[^/]+$/);
125
+ return match ? match[1] : normalised;
126
+ }
115
127
  export function resolveAgentDir(cfg, agentId) {
116
128
  const id = normalizeAgentId(agentId);
117
129
  const configured = resolveAgentConfig(cfg, id)?.agentDir?.trim();
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.0.83",
3
- "commit": "76b1166a34fd749231748a19a48975bb182fa6d6",
4
- "builtAt": "2026-02-20T22:57:58.675Z"
2
+ "version": "1.0.85",
3
+ "commit": "cc77f20e23c104d39b5dba8c5a09d7a303377186",
4
+ "builtAt": "2026-02-20T23:10:31.117Z"
5
5
  }
@@ -1,24 +1,14 @@
1
1
  import fsp from "node:fs/promises";
2
2
  import path from "node:path";
3
- import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../../agents/agent-scope.js";
3
+ import { resolveAgentWorkspaceRoot, resolveDefaultAgentId } from "../../agents/agent-scope.js";
4
4
  import { loadConfig } from "../../config/config.js";
5
5
  import { ErrorCodes, errorShape } from "../protocol/index.js";
6
6
  const MAX_PREVIEW_BYTES = 256 * 1024; // 256 KB for preview
7
7
  const MAX_DOWNLOAD_BYTES = 5 * 1024 * 1024; // 5 MB for download
8
8
  const MAX_UPLOAD_BYTES = 5 * 1024 * 1024; // 5 MB for upload
9
- /**
10
- * Multi-agent workspaces set each agent's workspace to a subdirectory
11
- * (e.g. ~/taskmaster/agents/admin). The files page should show the
12
- * workspace root (~/taskmaster), not the agent subdir.
13
- */
14
- function stripAgentSubdir(agentWorkspaceDir) {
15
- const normalised = agentWorkspaceDir.replace(/\/+$/, "");
16
- const match = normalised.match(/^(.+)\/agents\/[^/]+$/);
17
- return match ? match[1] : normalised;
18
- }
19
9
  function resolveWorkspaceRoot() {
20
10
  const cfg = loadConfig();
21
- return stripAgentSubdir(resolveAgentWorkspaceDir(cfg, resolveDefaultAgentId(cfg)));
11
+ return resolveAgentWorkspaceRoot(cfg, resolveDefaultAgentId(cfg));
22
12
  }
23
13
  /**
24
14
  * Resolve workspace root from request params.
@@ -30,7 +20,7 @@ function resolveWorkspaceForRequest(params) {
30
20
  if (!agentId)
31
21
  return resolveWorkspaceRoot();
32
22
  const cfg = loadConfig();
33
- return stripAgentSubdir(resolveAgentWorkspaceDir(cfg, agentId));
23
+ return resolveAgentWorkspaceRoot(cfg, agentId);
34
24
  }
35
25
  /**
36
26
  * Validate and resolve a relative path within the workspace.
@@ -1,4 +1,4 @@
1
- import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../../agents/agent-scope.js";
1
+ import { resolveAgentWorkspaceRoot, resolveDefaultAgentId } from "../../agents/agent-scope.js";
2
2
  import { loadConfig } from "../../config/config.js";
3
3
  import { clearAuditEntries, getUnreviewedEntries } from "../../memory/audit.js";
4
4
  import { getMemorySearchManager } from "../../memory/index.js";
@@ -69,7 +69,7 @@ export const memoryHandlers = {
69
69
  const agentId = typeof params.agentId === "string" && params.agentId.trim()
70
70
  ? params.agentId.trim()
71
71
  : resolveDefaultAgentId(cfg);
72
- const workspaceDir = resolveAgentWorkspaceDir(cfg, agentId);
72
+ const workspaceDir = resolveAgentWorkspaceRoot(cfg, agentId);
73
73
  const entries = getUnreviewedEntries(workspaceDir);
74
74
  respond(true, { ok: true, entries });
75
75
  }
@@ -119,7 +119,7 @@ export const memoryHandlers = {
119
119
  const agentId = typeof params.agentId === "string" && params.agentId.trim()
120
120
  ? params.agentId.trim()
121
121
  : resolveDefaultAgentId(cfg);
122
- const workspaceDir = resolveAgentWorkspaceDir(cfg, agentId);
122
+ const workspaceDir = resolveAgentWorkspaceRoot(cfg, agentId);
123
123
  const result = clearAuditEntries(workspaceDir);
124
124
  respond(true, { ok: true, cleared: result.cleared });
125
125
  }
@@ -1,4 +1,4 @@
1
- import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../../agents/agent-scope.js";
1
+ import { resolveAgentWorkspaceRoot, resolveDefaultAgentId } from "../../agents/agent-scope.js";
2
2
  import { installSkill } from "../../agents/skills-install.js";
3
3
  import { buildWorkspaceSkillStatus } from "../../agents/skills-status.js";
4
4
  import { loadWorkspaceSkillEntries } from "../../agents/skills.js";
@@ -11,11 +11,11 @@ function listWorkspaceDirs(cfg) {
11
11
  if (Array.isArray(list)) {
12
12
  for (const entry of list) {
13
13
  if (entry && typeof entry === "object" && typeof entry.id === "string") {
14
- dirs.add(resolveAgentWorkspaceDir(cfg, entry.id));
14
+ dirs.add(resolveAgentWorkspaceRoot(cfg, entry.id));
15
15
  }
16
16
  }
17
17
  }
18
- dirs.add(resolveAgentWorkspaceDir(cfg, resolveDefaultAgentId(cfg)));
18
+ dirs.add(resolveAgentWorkspaceRoot(cfg, resolveDefaultAgentId(cfg)));
19
19
  return [...dirs];
20
20
  }
21
21
  function collectSkillBins(entries) {
@@ -52,7 +52,7 @@ export const skillsHandlers = {
52
52
  return;
53
53
  }
54
54
  const cfg = loadConfig();
55
- const workspaceDir = resolveAgentWorkspaceDir(cfg, resolveDefaultAgentId(cfg));
55
+ const workspaceDir = resolveAgentWorkspaceRoot(cfg, resolveDefaultAgentId(cfg));
56
56
  const report = buildWorkspaceSkillStatus(workspaceDir, {
57
57
  config: cfg,
58
58
  eligibility: { remote: getRemoteSkillEligibility() },
@@ -81,7 +81,7 @@ export const skillsHandlers = {
81
81
  }
82
82
  const p = params;
83
83
  const cfg = loadConfig();
84
- const workspaceDirRaw = resolveAgentWorkspaceDir(cfg, resolveDefaultAgentId(cfg));
84
+ const workspaceDirRaw = resolveAgentWorkspaceRoot(cfg, resolveDefaultAgentId(cfg));
85
85
  const result = await installSkill({
86
86
  workspaceDir: workspaceDirRaw,
87
87
  skillName: p.name,
@@ -3,7 +3,7 @@ import fsSync from "node:fs";
3
3
  import fs from "node:fs/promises";
4
4
  import path from "node:path";
5
5
  import chokidar from "chokidar";
6
- import { resolveAgentDir, resolveAgentWorkspaceDir } from "../agents/agent-scope.js";
6
+ import { resolveAgentDir, resolveAgentWorkspaceRoot } from "../agents/agent-scope.js";
7
7
  import { resolveMemorySearchConfig } from "../agents/memory-search.js";
8
8
  import { createInternalHookEvent, triggerInternalHook } from "../hooks/internal-hooks.js";
9
9
  import { resolveSessionTranscriptsDirForAgent } from "../config/sessions/paths.js";
@@ -299,7 +299,7 @@ export class MemoryIndexManager {
299
299
  const settings = resolveMemorySearchConfig(cfg, agentId);
300
300
  if (!settings)
301
301
  return null;
302
- const workspaceDir = resolveAgentWorkspaceDir(cfg, agentId);
302
+ const workspaceDir = resolveAgentWorkspaceRoot(cfg, agentId);
303
303
  // Ensure standard memory folder structure exists
304
304
  await MemoryIndexManager.ensureMemoryStructure(workspaceDir);
305
305
  const key = `${agentId}:${workspaceDir}:${JSON.stringify(settings)}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/taskmaster",
3
- "version": "1.0.83",
3
+ "version": "1.0.85",
4
4
  "description": "AI-powered business assistant for small businesses",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -254,12 +254,70 @@ function applyPatchFile({ patchPath, targetDir }) {
254
254
  applyPatchSet({ patchText, targetDir });
255
255
  }
256
256
 
257
+ /**
258
+ * On Linux global installs (including upgrades), ensure platform dependencies
259
+ * that the original install may have missed are present. Runs as root since
260
+ * `sudo npm install -g` passes root to postinstall.
261
+ */
262
+ function ensurePlatformDeps() {
263
+ if (process.platform !== "linux") return;
264
+ // Only run for global installs (deployed devices), not local dev
265
+ const isGlobal =
266
+ process.env.npm_config_global === "true" ||
267
+ // npm 9+ sets this instead
268
+ (getRepoRoot().includes("/lib/node_modules/") && !fs.existsSync(path.join(getRepoRoot(), ".git")));
269
+ if (!isGlobal) return;
270
+
271
+ const deps = [
272
+ {
273
+ name: "tailscale",
274
+ check: "tailscale",
275
+ install: ["sh", "-c", "curl -fsSL https://tailscale.com/install.sh | sh"],
276
+ },
277
+ {
278
+ name: "avahi-daemon",
279
+ check: "avahi-browse",
280
+ install: ["apt-get", "install", "-y", "avahi-daemon", "avahi-utils"],
281
+ },
282
+ ];
283
+
284
+ for (const dep of deps) {
285
+ const exists = spawnSync("command", ["-v", dep.check], { shell: true, stdio: "ignore" });
286
+ if (exists.status === 0) continue;
287
+ console.log(`[postinstall] Installing ${dep.name}...`);
288
+ const result = spawnSync(dep.install[0], dep.install.slice(1), {
289
+ stdio: "inherit",
290
+ timeout: 120_000,
291
+ });
292
+ if (result.status === 0) {
293
+ console.log(`[postinstall] ${dep.name} installed`);
294
+ } else {
295
+ console.warn(`[postinstall] ${dep.name} install failed (continuing)`);
296
+ }
297
+ }
298
+
299
+ // Ensure hostname resolves in /etc/hosts (prevents sudo timeouts)
300
+ try {
301
+ const hostname = spawnSync("hostname", [], { encoding: "utf-8" }).stdout.trim();
302
+ if (hostname) {
303
+ const hosts = fs.readFileSync("/etc/hosts", "utf-8");
304
+ if (!hosts.includes(hostname)) {
305
+ fs.appendFileSync("/etc/hosts", `\n127.0.1.1\t${hostname}\n`);
306
+ console.log(`[postinstall] Added ${hostname} to /etc/hosts`);
307
+ }
308
+ }
309
+ } catch {
310
+ // non-critical
311
+ }
312
+ }
313
+
257
314
  function main() {
258
315
  const repoRoot = getRepoRoot();
259
316
  process.chdir(repoRoot);
260
317
 
261
318
  ensureExecutable(path.join(repoRoot, "dist", "entry.js"));
262
319
  setupGitHooks({ repoRoot });
320
+ ensurePlatformDeps();
263
321
 
264
322
  if (!shouldApplyPnpmPatchedDependenciesFallback()) {
265
323
  return;