@mclawnet/agent 0.5.9 → 0.6.2

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 (81) hide show
  1. package/cli.js +168 -61
  2. package/dist/__tests__/cli.test.d.ts +2 -0
  3. package/dist/__tests__/cli.test.d.ts.map +1 -0
  4. package/dist/__tests__/service-config.test.d.ts +2 -0
  5. package/dist/__tests__/service-config.test.d.ts.map +1 -0
  6. package/dist/__tests__/service-linux.test.d.ts +2 -0
  7. package/dist/__tests__/service-linux.test.d.ts.map +1 -0
  8. package/dist/__tests__/service-macos.test.d.ts +2 -0
  9. package/dist/__tests__/service-macos.test.d.ts.map +1 -0
  10. package/dist/__tests__/service-windows.test.d.ts +2 -0
  11. package/dist/__tests__/service-windows.test.d.ts.map +1 -0
  12. package/dist/backend-adapter.d.ts +2 -0
  13. package/dist/backend-adapter.d.ts.map +1 -1
  14. package/dist/chunk-CBZIH6FY.js +93 -0
  15. package/dist/chunk-CBZIH6FY.js.map +1 -0
  16. package/dist/{chunk-KHPEQTWF.js → chunk-GLO5OZAY.js} +203 -213
  17. package/dist/chunk-GLO5OZAY.js.map +1 -0
  18. package/dist/chunk-RO47ET27.js +88 -0
  19. package/dist/chunk-RO47ET27.js.map +1 -0
  20. package/dist/hub-connection.d.ts.map +1 -1
  21. package/dist/index.js +5 -3
  22. package/dist/index.js.map +1 -1
  23. package/dist/linux-6AR7SXHW.js +176 -0
  24. package/dist/linux-6AR7SXHW.js.map +1 -0
  25. package/dist/macos-XVPWIH4C.js +174 -0
  26. package/dist/macos-XVPWIH4C.js.map +1 -0
  27. package/dist/service/config.d.ts +19 -0
  28. package/dist/service/config.d.ts.map +1 -0
  29. package/dist/service/index.d.ts +6 -0
  30. package/dist/service/index.d.ts.map +1 -0
  31. package/dist/service/index.js +47 -0
  32. package/dist/service/index.js.map +1 -0
  33. package/dist/service/linux.d.ts +18 -0
  34. package/dist/service/linux.d.ts.map +1 -0
  35. package/dist/service/macos.d.ts +18 -0
  36. package/dist/service/macos.d.ts.map +1 -0
  37. package/dist/service/types.d.ts +19 -0
  38. package/dist/service/types.d.ts.map +1 -0
  39. package/dist/service/windows.d.ts +18 -0
  40. package/dist/service/windows.d.ts.map +1 -0
  41. package/dist/session-manager.d.ts +4 -7
  42. package/dist/session-manager.d.ts.map +1 -1
  43. package/dist/skill-loader.d.ts +8 -0
  44. package/dist/skill-loader.d.ts.map +1 -0
  45. package/dist/start.d.ts.map +1 -1
  46. package/dist/start.js +2 -1
  47. package/dist/windows-NLONSCDA.js +165 -0
  48. package/dist/windows-NLONSCDA.js.map +1 -0
  49. package/package.json +7 -5
  50. package/skills/academic-search/SKILL.md +147 -0
  51. package/skills/architecture/SKILL.md +294 -0
  52. package/skills/changelog-generator/SKILL.md +112 -0
  53. package/skills/chart-visualization/SKILL.md +183 -0
  54. package/skills/code-review/SKILL.md +304 -0
  55. package/skills/codebase-health/SKILL.md +281 -0
  56. package/skills/consulting-analysis/SKILL.md +584 -0
  57. package/skills/content-research-writer/SKILL.md +546 -0
  58. package/skills/data-analysis/SKILL.md +194 -0
  59. package/skills/deep-research/SKILL.md +198 -0
  60. package/skills/docx/SKILL.md +211 -0
  61. package/skills/github-deep-research/SKILL.md +207 -0
  62. package/skills/image-generation/SKILL.md +209 -0
  63. package/skills/lead-research-assistant/SKILL.md +207 -0
  64. package/skills/mcp-builder/SKILL.md +304 -0
  65. package/skills/meeting-insights-analyzer/SKILL.md +335 -0
  66. package/skills/pair-programming/SKILL.md +196 -0
  67. package/skills/pdf/SKILL.md +309 -0
  68. package/skills/performance-analysis/SKILL.md +261 -0
  69. package/skills/podcast-generation/SKILL.md +224 -0
  70. package/skills/pptx/SKILL.md +497 -0
  71. package/skills/project-learnings/SKILL.md +280 -0
  72. package/skills/security-audit/SKILL.md +211 -0
  73. package/skills/skill-creator/SKILL.md +200 -0
  74. package/skills/technical-writing/SKILL.md +286 -0
  75. package/skills/testing/SKILL.md +363 -0
  76. package/skills/video-generation/SKILL.md +247 -0
  77. package/skills/web-design-guidelines/SKILL.md +203 -0
  78. package/skills/webapp-testing/SKILL.md +162 -0
  79. package/skills/workflow-automation/SKILL.md +299 -0
  80. package/skills/xlsx/SKILL.md +305 -0
  81. package/dist/chunk-KHPEQTWF.js.map +0 -1
@@ -1,90 +1,6 @@
1
- // src/config.ts
2
- import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
3
- import { join } from "path";
4
- import { homedir, hostname } from "os";
5
- import { createLogger } from "@mclawnet/logger";
6
- var log = createLogger({ module: "config" });
7
- var CONFIG_DIR = join(homedir(), ".clawnet");
8
- var SETTINGS_FILE = join(CONFIG_DIR, "settings.json");
9
- var DEFAULT_HUB_URL = process.env.CLAWNET_DEFAULT_HUB_URL || "ws://localhost:3000/ws/agent";
10
- var DEFAULTS = {
11
- hubUrl: DEFAULT_HUB_URL,
12
- token: "",
13
- name: hostname(),
14
- backendType: "claude-code"
15
- };
16
- function normalizeHubUrl(url) {
17
- url = url.replace(/^https:\/\//, "wss://").replace(/^http:\/\//, "ws://");
18
- if (!/^wss?:\/\//.test(url)) url = "wss://" + url;
19
- if (url.endsWith("/ws/agent")) return url;
20
- return url.replace(/\/+$/, "") + "/ws/agent";
21
- }
22
- function applyEmbeddingConfig(embedding) {
23
- const mapping = [
24
- ["provider", "CLAWNET_EMBEDDING_PROVIDER"],
25
- ["openaiBaseUrl", "CLAWNET_OPENAI_BASE_URL"],
26
- ["openaiApiKey", "CLAWNET_OPENAI_API_KEY"],
27
- ["openaiModel", "CLAWNET_OPENAI_EMBEDDING_MODEL"],
28
- ["ollamaUrl", "CLAWNET_OLLAMA_URL"],
29
- ["ollamaModel", "CLAWNET_OLLAMA_MODEL"]
30
- ];
31
- for (const [configKey, envKey] of mapping) {
32
- const value = embedding[configKey];
33
- if (value !== void 0 && process.env[envKey] === void 0) {
34
- process.env[envKey] = value;
35
- }
36
- }
37
- }
38
- function loadConfig(cliOpts = {}) {
39
- let fileConfig = {};
40
- log.info({ path: SETTINGS_FILE }, "loading config");
41
- if (existsSync(SETTINGS_FILE)) {
42
- try {
43
- fileConfig = JSON.parse(readFileSync(SETTINGS_FILE, "utf-8"));
44
- log.info(
45
- { hubUrl: fileConfig.hubUrl, hasToken: !!fileConfig.token, name: fileConfig.name },
46
- "settings.json loaded"
47
- );
48
- } catch (e) {
49
- log.warn({ err: e }, "failed to parse settings.json");
50
- }
51
- } else {
52
- log.warn("settings.json not found");
53
- }
54
- if (fileConfig.embedding) {
55
- applyEmbeddingConfig(fileConfig.embedding);
56
- }
57
- const hubUrl = cliOpts.hubUrl ?? process.env.CLAWNET_HUB_URL ?? fileConfig.hubUrl ?? DEFAULTS.hubUrl;
58
- const resolved = {
59
- hubUrl: normalizeHubUrl(hubUrl),
60
- token: cliOpts.token ?? process.env.CLAWNET_TOKEN ?? fileConfig.token ?? DEFAULTS.token,
61
- name: cliOpts.name ?? process.env.CLAWNET_NAME ?? fileConfig.name ?? DEFAULTS.name,
62
- backendType: cliOpts.backendType ?? process.env.CLAWNET_BACKEND_TYPE ?? fileConfig.backendType ?? DEFAULTS.backendType,
63
- embedding: fileConfig.embedding
64
- };
65
- log.info(
66
- {
67
- hubUrl: resolved.hubUrl,
68
- hubUrlSource: cliOpts.hubUrl ? "cli" : process.env.CLAWNET_HUB_URL ? "env" : fileConfig.hubUrl ? "file" : "default",
69
- tokenSource: cliOpts.token ? "cli" : process.env.CLAWNET_TOKEN ? "env" : fileConfig.token ? "file" : "default",
70
- hasToken: !!resolved.token
71
- },
72
- "config resolved"
73
- );
74
- return resolved;
75
- }
76
- function saveConfig(config) {
77
- mkdirSync(CONFIG_DIR, { recursive: true });
78
- let existing = {};
79
- if (existsSync(SETTINGS_FILE)) {
80
- try {
81
- existing = JSON.parse(readFileSync(SETTINGS_FILE, "utf-8"));
82
- } catch {
83
- }
84
- }
85
- const merged = { ...existing, ...config };
86
- writeFileSync(SETTINGS_FILE, JSON.stringify(merged, null, 2) + "\n");
87
- }
1
+ import {
2
+ loadConfig
3
+ } from "./chunk-CBZIH6FY.js";
88
4
 
89
5
  // src/hub-connection.ts
90
6
  import { hostname as osHostname } from "os";
@@ -95,16 +11,16 @@ import {
95
11
  MAX_RECONNECT_MS,
96
12
  WS_CLOSE_INVALID_TOKEN
97
13
  } from "@mclawnet/shared";
98
- import { listRecoverableSwarms, recoverSwarm, listRoles, loadRole, listRecoverableSwarmIds, deleteSwarmSnapshot } from "@mclawnet/swarm";
14
+ import { listRecoverableSwarms, recoverSwarm, listRoles, loadRole, listRecoverableSwarmIds, deleteSwarmSnapshot, listTemplates, loadTemplate } from "@mclawnet/swarm";
99
15
 
100
16
  // src/fs-handler.ts
101
17
  import { readdir } from "fs/promises";
102
- import { existsSync as existsSync2, readdirSync, statSync, readFileSync as readFileSync2 } from "fs";
103
- import { homedir as homedir2 } from "os";
104
- import { join as join2 } from "path";
18
+ import { existsSync, readdirSync, statSync, readFileSync } from "fs";
19
+ import { homedir } from "os";
20
+ import { join } from "path";
105
21
  var SKIP_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", "__pycache__", ".next", ".nuxt", ".cache"]);
106
22
  async function handleListDir(path) {
107
- const target = path || homedir2();
23
+ const target = path || homedir();
108
24
  const dirents = await readdir(target, { withFileTypes: true });
109
25
  const entries = dirents.filter((d) => !(d.isDirectory() && SKIP_DIRS.has(d.name))).map((d) => ({
110
26
  name: d.name,
@@ -116,14 +32,14 @@ async function handleListDir(path) {
116
32
  return { path: target, entries };
117
33
  }
118
34
  function getClaudeProjectsDir() {
119
- return join2(homedir2(), ".claude", "projects");
35
+ return join(homedir(), ".claude", "projects");
120
36
  }
121
37
  function pathToProjectFolder(workDir) {
122
38
  return workDir.replace(/:/g, "-").replace(/[/\\]/g, "-");
123
39
  }
124
40
  function extractWorkDirFromSessionFile(filePath) {
125
41
  try {
126
- const content = readFileSync2(filePath, "utf-8");
42
+ const content = readFileSync(filePath, "utf-8");
127
43
  const lines = content.split("\n").filter((l) => l.trim());
128
44
  for (const line of lines.slice(0, 5)) {
129
45
  try {
@@ -141,7 +57,7 @@ function getWorkDirFromProjectFolder(folderPath, folderName) {
141
57
  const files = readdirSync(folderPath);
142
58
  for (const file of files) {
143
59
  if (file.endsWith(".jsonl")) {
144
- const workDir = extractWorkDirFromSessionFile(join2(folderPath, file));
60
+ const workDir = extractWorkDirFromSessionFile(join(folderPath, file));
145
61
  if (workDir) return workDir;
146
62
  }
147
63
  }
@@ -158,10 +74,10 @@ function getWorkDirFromProjectFolder(folderPath, folderName) {
158
74
  async function handleListFolders() {
159
75
  const projectsDir = getClaudeProjectsDir();
160
76
  const folders = [];
161
- if (!existsSync2(projectsDir)) return { folders };
77
+ if (!existsSync(projectsDir)) return { folders };
162
78
  const entries = readdirSync(projectsDir);
163
79
  for (const entry of entries) {
164
- const entryPath = join2(projectsDir, entry);
80
+ const entryPath = join(projectsDir, entry);
165
81
  let entryStat;
166
82
  try {
167
83
  entryStat = statSync(entryPath);
@@ -179,7 +95,7 @@ async function handleListFolders() {
179
95
  if (file.endsWith(".jsonl")) {
180
96
  sessionCount++;
181
97
  try {
182
- const fileStats = statSync(join2(entryPath, file));
98
+ const fileStats = statSync(join(entryPath, file));
183
99
  if (fileStats.mtime.getTime() > lastModified) {
184
100
  lastModified = fileStats.mtime.getTime();
185
101
  }
@@ -197,8 +113,8 @@ async function handleListFolders() {
197
113
  async function handleListHistorySessions(workDir) {
198
114
  const projectsDir = getClaudeProjectsDir();
199
115
  const projectFolder = pathToProjectFolder(workDir);
200
- const projectPath = join2(projectsDir, projectFolder);
201
- if (!existsSync2(projectPath)) {
116
+ const projectPath = join(projectsDir, projectFolder);
117
+ if (!existsSync(projectPath)) {
202
118
  return { workDir, sessions: [] };
203
119
  }
204
120
  const sessions = [];
@@ -206,7 +122,7 @@ async function handleListHistorySessions(workDir) {
206
122
  for (const file of files) {
207
123
  if (!file.endsWith(".jsonl")) continue;
208
124
  const sessionId = file.replace(".jsonl", "");
209
- const filePath = join2(projectPath, file);
125
+ const filePath = join(projectPath, file);
210
126
  let fileStats;
211
127
  try {
212
128
  fileStats = statSync(filePath);
@@ -218,7 +134,7 @@ async function handleListHistorySessions(workDir) {
218
134
  let customTitle = "";
219
135
  let jsonlSummary = "";
220
136
  try {
221
- const content = readFileSync2(filePath, "utf-8");
137
+ const content = readFileSync(filePath, "utf-8");
222
138
  const lines = content.split("\n").filter((l) => l.trim());
223
139
  for (const line of lines) {
224
140
  try {
@@ -256,13 +172,13 @@ async function handleListHistorySessions(workDir) {
256
172
  async function handleLoadSessionHistory(workDir, claudeSessionId) {
257
173
  const projectsDir = getClaudeProjectsDir();
258
174
  const projectFolder = pathToProjectFolder(workDir);
259
- const filePath = join2(projectsDir, projectFolder, `${claudeSessionId}.jsonl`);
260
- if (!existsSync2(filePath)) {
175
+ const filePath = join(projectsDir, projectFolder, `${claudeSessionId}.jsonl`);
176
+ if (!existsSync(filePath)) {
261
177
  return { messages: [] };
262
178
  }
263
179
  const messages = [];
264
180
  try {
265
- const content = readFileSync2(filePath, "utf-8");
181
+ const content = readFileSync(filePath, "utf-8");
266
182
  const lines = content.split("\n").filter((l) => l.trim());
267
183
  for (const line of lines) {
268
184
  try {
@@ -324,8 +240,8 @@ async function handleLoadSessionHistory(workDir, claudeSessionId) {
324
240
  }
325
241
 
326
242
  // src/hub-connection.ts
327
- import { createLogger as createLogger2 } from "@mclawnet/logger";
328
- var log2 = createLogger2({ module: "agent" });
243
+ import { createLogger } from "@mclawnet/logger";
244
+ var log = createLogger({ module: "agent" });
329
245
  var HubConnection = class {
330
246
  ws = null;
331
247
  heartbeatTimer = null;
@@ -407,7 +323,7 @@ var HubConnection = class {
407
323
  return;
408
324
  }
409
325
  if (this.authState === "pending" && data.type === "auth_required") {
410
- log2.info("hub requires auth, sending credentials");
326
+ log.info("hub requires auth, sending credentials");
411
327
  this.authState = "authenticating";
412
328
  this.sendRaw({
413
329
  type: "auth",
@@ -419,7 +335,7 @@ var HubConnection = class {
419
335
  return;
420
336
  }
421
337
  if (this.authState === "authenticating" && data.type === "registered") {
422
- log2.info({ agentId: data.agentId }, "registered with hub");
338
+ log.info({ agentId: data.agentId }, "registered with hub");
423
339
  this.authState = "authenticated";
424
340
  this.agentId = data.agentId ?? null;
425
341
  this.startHeartbeat();
@@ -433,18 +349,18 @@ var HubConnection = class {
433
349
  }
434
350
  });
435
351
  this.ws.on("close", (code, reason) => {
436
- log2.warn({ code, reason: reason.toString() }, "disconnected from hub");
352
+ log.warn({ code, reason: reason.toString() }, "disconnected from hub");
437
353
  this.stopHeartbeat();
438
354
  this.authState = "pending";
439
355
  this.onDisconnect?.(code, reason.toString());
440
356
  if (code === WS_CLOSE_INVALID_TOKEN) {
441
- log2.error("auth failed \u2014 not reconnecting, check your token");
357
+ log.error("auth failed \u2014 not reconnecting, check your token");
442
358
  return;
443
359
  }
444
360
  this.scheduleReconnect();
445
361
  });
446
362
  this.ws.on("error", (err) => {
447
- log2.error({ err }, "ws connection error");
363
+ log.error({ err }, "ws connection error");
448
364
  this.onError?.(err);
449
365
  });
450
366
  }
@@ -462,11 +378,11 @@ var HubConnection = class {
462
378
  // ── Session message handling ─────────────────────────────────────
463
379
  handleSessionMessage(msg) {
464
380
  if (msg.type === "fs.list_dir") {
465
- log2.info({ path: msg.path }, "fs.list_dir");
381
+ log.info({ path: msg.path }, "fs.list_dir");
466
382
  handleListDir(msg.path).then((result) => {
467
383
  this.send({ type: "fs.list_dir_result", requestId: msg.requestId, ...result });
468
384
  }).catch((err) => {
469
- log2.error({ err, path: msg.path }, "fs.list_dir failed");
385
+ log.error({ err, path: msg.path }, "fs.list_dir failed");
470
386
  this.send({
471
387
  type: "fs.list_dir_result",
472
388
  requestId: msg.requestId,
@@ -477,21 +393,21 @@ var HubConnection = class {
477
393
  return true;
478
394
  }
479
395
  if (msg.type === "list_folders") {
480
- log2.info("list_folders");
396
+ log.info("list_folders");
481
397
  handleListFolders().then((result) => {
482
398
  this.send({ type: "folders_list_result", requestId: msg.requestId, ...result });
483
399
  }).catch((err) => {
484
- log2.error({ err }, "list_folders failed");
400
+ log.error({ err }, "list_folders failed");
485
401
  this.send({ type: "folders_list_result", requestId: msg.requestId, folders: [] });
486
402
  });
487
403
  return true;
488
404
  }
489
405
  if (msg.type === "list_history_sessions") {
490
- log2.info({ workDir: msg.workDir }, "list_history_sessions");
406
+ log.info({ workDir: msg.workDir }, "list_history_sessions");
491
407
  handleListHistorySessions(msg.workDir).then((result) => {
492
408
  this.send({ type: "history_sessions_result", requestId: msg.requestId, ...result });
493
409
  }).catch((err) => {
494
- log2.error({ err, workDir: msg.workDir }, "list_history_sessions failed");
410
+ log.error({ err, workDir: msg.workDir }, "list_history_sessions failed");
495
411
  this.send({
496
412
  type: "history_sessions_result",
497
413
  requestId: msg.requestId,
@@ -502,26 +418,28 @@ var HubConnection = class {
502
418
  return true;
503
419
  }
504
420
  if (msg.type === "load_session_history") {
505
- log2.info({ workDir: msg.workDir, claudeSessionId: msg.claudeSessionId }, "load_session_history");
421
+ log.info({ workDir: msg.workDir, claudeSessionId: msg.claudeSessionId }, "load_session_history");
506
422
  handleLoadSessionHistory(msg.workDir, msg.claudeSessionId).then((result) => {
507
423
  this.send({ type: "session_history_result", requestId: msg.requestId, ...result });
508
424
  }).catch((err) => {
509
- log2.error({ err, workDir: msg.workDir }, "load_session_history failed");
425
+ log.error({ err, workDir: msg.workDir }, "load_session_history failed");
510
426
  this.send({ type: "session_history_result", requestId: msg.requestId, messages: [] });
511
427
  });
512
428
  return true;
513
429
  }
514
430
  if (msg.type === "list_roles") {
515
- log2.info("list_roles");
431
+ log.info("list_roles");
516
432
  const roleNames = listRoles();
517
433
  const roles = roleNames.map((name) => {
518
434
  try {
519
435
  const def = loadRole(name);
520
436
  return {
521
437
  name: def.name,
522
- displayName: def.shortName || def.name,
438
+ displayName: def.displayName || def.description || def.name,
523
439
  description: def.description || "",
524
440
  capabilities: def.capabilities || [],
441
+ type: def.type,
442
+ color: def.color,
525
443
  promptBody: def.promptBody || ""
526
444
  };
527
445
  } catch {
@@ -535,12 +453,41 @@ var HubConnection = class {
535
453
  });
536
454
  return true;
537
455
  }
456
+ if (msg.type === "list_templates") {
457
+ log.info("list_templates");
458
+ try {
459
+ const names = listTemplates();
460
+ const templates = names.map((name) => {
461
+ const tpl = loadTemplate(name);
462
+ return {
463
+ name: tpl.name,
464
+ displayName: tpl.displayName,
465
+ description: tpl.description,
466
+ icon: tpl.icon,
467
+ roles: tpl.roles
468
+ };
469
+ });
470
+ this.send({
471
+ type: "templates_list_result",
472
+ sessionId: msg.sessionId,
473
+ templates
474
+ });
475
+ } catch (err) {
476
+ log.error({ err }, "list_templates failed");
477
+ this.send({
478
+ type: "templates_list_result",
479
+ sessionId: msg.sessionId,
480
+ templates: []
481
+ });
482
+ }
483
+ return true;
484
+ }
538
485
  if (msg.type === "generic.request") {
539
486
  const handler = this.namespaceHandlers.get(msg.namespace);
540
487
  if (handler) {
541
- log2.info({ namespace: msg.namespace, action: msg.action, requestId: msg.requestId }, "generic.request received");
488
+ log.info({ namespace: msg.namespace, action: msg.action, requestId: msg.requestId }, "generic.request received");
542
489
  handler(msg).then((result) => {
543
- log2.info({ namespace: msg.namespace, action: msg.action, requestId: msg.requestId }, "generic.request handled OK");
490
+ log.info({ namespace: msg.namespace, action: msg.action, requestId: msg.requestId }, "generic.request handled OK");
544
491
  this.send({
545
492
  type: "generic.response",
546
493
  namespace: msg.namespace,
@@ -549,7 +496,7 @@ var HubConnection = class {
549
496
  requestId: msg.requestId
550
497
  });
551
498
  }).catch((err) => {
552
- log2.error({ namespace: msg.namespace, action: msg.action, requestId: msg.requestId, err }, "generic.request handler error");
499
+ log.error({ namespace: msg.namespace, action: msg.action, requestId: msg.requestId, err }, "generic.request handler error");
553
500
  this.send({
554
501
  type: "generic.response",
555
502
  namespace: msg.namespace,
@@ -561,7 +508,7 @@ var HubConnection = class {
561
508
  });
562
509
  return true;
563
510
  }
564
- log2.warn({ namespace: msg.namespace, action: msg.action, requestId: msg.requestId }, "generic.request unknown namespace");
511
+ log.warn({ namespace: msg.namespace, action: msg.action, requestId: msg.requestId }, "generic.request unknown namespace");
565
512
  this.send({
566
513
  type: "generic.response",
567
514
  namespace: msg.namespace,
@@ -575,7 +522,7 @@ var HubConnection = class {
575
522
  if (msg.type === "swarm.execute" && this.swarmCoordinator) {
576
523
  const { sessionId, content, workDir, targetInstance, crewConfig } = msg;
577
524
  if (this.swarmCoordinator.hasSwarm(sessionId)) {
578
- log2.info({ sessionId, targetInstance }, "swarm.execute: forwarding to existing swarm");
525
+ log.info({ sessionId, targetInstance }, "swarm.execute: forwarding to existing swarm");
579
526
  this.swarmCoordinator.handleUserMessage(sessionId, content, targetInstance).catch((err) => {
580
527
  this.send({
581
528
  type: "session.error",
@@ -584,16 +531,17 @@ var HubConnection = class {
584
531
  });
585
532
  });
586
533
  } else if (this.finishedSwarms.has(sessionId)) {
587
- log2.info({ sessionId }, "swarm.execute ignored: swarm already finished");
534
+ log.info({ sessionId }, "swarm.execute ignored: swarm already finished");
588
535
  this.send({
589
536
  type: "session.error",
590
537
  sessionId,
591
538
  error: "\u8702\u7FA4\u4EFB\u52A1\u5DF2\u5B8C\u6210\uFF0C\u65E0\u6CD5\u7EE7\u7EED\u64CD\u4F5C\u3002\u8BF7\u521B\u5EFA\u65B0\u7684\u8702\u7FA4\u4EFB\u52A1\u3002"
592
539
  });
593
- } else if (crewConfig?.roles) {
594
- log2.info({ sessionId, rolesCount: crewConfig.roles.length }, "swarm.execute: creating new swarm");
540
+ } else if (crewConfig?.templateName || crewConfig?.roles) {
541
+ const templateName = crewConfig.templateName;
595
542
  const roles = crewConfig.roles;
596
- this.swarmCoordinator.create(sessionId, { workDir, roles, task: content }).catch((err) => {
543
+ log.info({ sessionId, templateName, rolesCount: roles?.length }, "swarm.execute: creating new swarm");
544
+ this.swarmCoordinator.create(sessionId, { workDir, templateName, roles, task: content }).catch((err) => {
597
545
  this.send({
598
546
  type: "session.error",
599
547
  sessionId,
@@ -601,7 +549,7 @@ var HubConnection = class {
601
549
  });
602
550
  });
603
551
  } else {
604
- log2.info({ sessionId }, "swarm.execute ignored: swarm not found, no config");
552
+ log.info({ sessionId }, "swarm.execute ignored: swarm not found, no config");
605
553
  this.send({
606
554
  type: "session.error",
607
555
  sessionId,
@@ -613,7 +561,7 @@ var HubConnection = class {
613
561
  if (!this.sessionManager) return false;
614
562
  if (msg.type === "abort_execution") {
615
563
  const { sessionId } = msg;
616
- log2.info({ sessionId }, "abort_execution");
564
+ log.info({ sessionId }, "abort_execution");
617
565
  if (this.sessionManager?.hasSession(sessionId)) {
618
566
  this.sessionManager.abortSession(sessionId).then(() => {
619
567
  this.send({ type: "execution_aborted", sessionId });
@@ -628,10 +576,10 @@ var HubConnection = class {
628
576
  if (msg.type === "claude.execute") {
629
577
  const { sessionId, content, workDir, claudeSessionId, useBrainCore } = msg;
630
578
  if (this.sessionManager.hasSession(sessionId)) {
631
- log2.info({ sessionId }, "claude.execute: reusing existing session");
579
+ log.info({ sessionId }, "claude.execute: reusing existing session");
632
580
  this.sessionManager.sendInput(sessionId, content);
633
581
  } else {
634
- log2.info({ sessionId, workDir, roleId: "role-__assistant__" }, "claude.execute: creating new session with memory injection");
582
+ log.info({ sessionId, workDir, roleId: "role-__assistant__" }, "claude.execute: creating new session with memory injection");
635
583
  this.sessionManager.createSession({ sessionId, workDir, resumeId: claudeSessionId, useBrainCore, roleId: "role-__assistant__" }).then(() => {
636
584
  this.sessionManager.sendInput(sessionId, content);
637
585
  }).catch((err) => {
@@ -645,7 +593,7 @@ var HubConnection = class {
645
593
  return true;
646
594
  }
647
595
  if (msg.type === "session.create") {
648
- log2.info({ sessionId: msg.sessionId, roleId: "role-__assistant__" }, "session.create with memory injection");
596
+ log.info({ sessionId: msg.sessionId, roleId: "role-__assistant__" }, "session.create with memory injection");
649
597
  this.sessionManager.createSession({
650
598
  sessionId: msg.sessionId,
651
599
  workDir: msg.workDir,
@@ -667,13 +615,13 @@ var HubConnection = class {
667
615
  return true;
668
616
  }
669
617
  if (msg.type === "session.close") {
670
- log2.info({ sessionId: msg.sessionId }, "session.close");
618
+ log.info({ sessionId: msg.sessionId }, "session.close");
671
619
  this.sessionManager.closeSession(msg.sessionId).catch(() => {
672
620
  });
673
621
  return true;
674
622
  }
675
623
  if (msg.type === "claude.input") {
676
- log2.info({ sessionId: msg.sessionId }, "claude.input");
624
+ log.info({ sessionId: msg.sessionId }, "claude.input");
677
625
  this.sessionManager.sendInput(msg.sessionId, msg.content);
678
626
  return true;
679
627
  }
@@ -689,17 +637,17 @@ var HubConnection = class {
689
637
  for (const id of allIds) {
690
638
  if (!recoverableIds.has(id)) {
691
639
  deleteSwarmSnapshot(id);
692
- log2.info({ swarmId: id }, "cleaned up non-recoverable swarm snapshot");
640
+ log.info({ swarmId: id }, "cleaned up non-recoverable swarm snapshot");
693
641
  }
694
642
  }
695
643
  for (const snap of snapshots) {
696
- log2.info({ swarmId: snap.id }, "recovering swarm");
644
+ log.info({ swarmId: snap.id }, "recovering swarm");
697
645
  recoverSwarm(this.swarmCoordinator, snap).catch((err) => {
698
- log2.error({ err, swarmId: snap.id }, "failed to recover swarm");
646
+ log.error({ err, swarmId: snap.id }, "failed to recover swarm");
699
647
  });
700
648
  }
701
649
  } catch (err) {
702
- log2.error({ err }, "swarm recovery failed");
650
+ log.error({ err }, "swarm recovery failed");
703
651
  }
704
652
  }
705
653
  sendRaw(data) {
@@ -721,7 +669,7 @@ var HubConnection = class {
721
669
  }
722
670
  scheduleReconnect() {
723
671
  if (this.destroyed) return;
724
- log2.warn({ delayMs: this.reconnectDelay }, "reconnecting to hub...");
672
+ log.warn({ delayMs: this.reconnectDelay }, "reconnecting to hub...");
725
673
  this.reconnectTimer = setTimeout(() => {
726
674
  this.connect();
727
675
  }, this.reconnectDelay);
@@ -746,17 +694,14 @@ var HubConnection = class {
746
694
  };
747
695
 
748
696
  // src/session-manager.ts
749
- import { writeFileSync as writeFileSync2, unlinkSync } from "fs";
750
- import { tmpdir } from "os";
751
- import { join as join3, dirname } from "path";
752
- import { createRequire } from "module";
753
- import { createLogger as createLogger3 } from "@mclawnet/logger";
697
+ import { createLogger as createLogger2 } from "@mclawnet/logger";
754
698
  import { buildMemorySection } from "@mclawnet/memory";
755
- var log3 = createLogger3({ module: "agent/session-manager" });
699
+ var log2 = createLogger2({ module: "agent/session-manager" });
700
+ var DEFAULT_MAX_PROCESSES = 10;
701
+ var MAX_PROCESSES = Number(process.env.CLAWNET_MAX_PROCESSES) || DEFAULT_MAX_PROCESSES;
756
702
  var SessionManager = class {
757
703
  sessions = /* @__PURE__ */ new Map();
758
704
  conversationBuffer = /* @__PURE__ */ new Map();
759
- mcpConfigPaths = /* @__PURE__ */ new Map();
760
705
  adapter;
761
706
  onOutput;
762
707
  onTurnComplete;
@@ -773,23 +718,21 @@ var SessionManager = class {
773
718
  if (this.sessions.has(options.sessionId)) {
774
719
  throw new Error(`Session ${options.sessionId} already exists`);
775
720
  }
721
+ if (this.sessions.size >= MAX_PROCESSES) {
722
+ throw new Error(`Process limit reached (${MAX_PROCESSES}). Cannot create new session.`);
723
+ }
776
724
  if (options.roleId) {
777
725
  try {
778
726
  const memorySection = buildMemorySection(options.roleId);
779
- options.systemPrompt = options.systemPrompt ? `${memorySection}
727
+ const roleHint = `
780
728
 
781
- ${options.systemPrompt}` : memorySection;
782
- log3.debug({ roleId: options.roleId, sessionId: options.sessionId }, "memory prompt injected");
729
+ [Memory Context] Your roleId is "${options.roleId}". Always use this roleId when calling memory_search, memory_store, memory_stats, or memory_reflect.`;
730
+ options.systemPrompt = options.systemPrompt ? `${memorySection}${roleHint}
731
+
732
+ ${options.systemPrompt}` : `${memorySection}${roleHint}`;
733
+ log2.debug({ roleId: options.roleId, sessionId: options.sessionId }, "memory prompt + roleId hint injected");
783
734
  } catch (err) {
784
- log3.warn({ err, roleId: options.roleId }, "failed to build memory section, proceeding without");
785
- }
786
- if (!options.mcpConfigPath) {
787
- try {
788
- options.mcpConfigPath = this.createMemoryMcpConfig(options.sessionId, options.roleId);
789
- log3.debug({ roleId: options.roleId, sessionId: options.sessionId }, "memory MCP config created");
790
- } catch (err) {
791
- log3.warn({ err, roleId: options.roleId }, "failed to create memory MCP config, proceeding without");
792
- }
735
+ log2.warn({ err, roleId: options.roleId }, "failed to build memory section, proceeding without");
793
736
  }
794
737
  }
795
738
  try {
@@ -832,7 +775,6 @@ ${options.systemPrompt}` : memorySection;
832
775
  const process2 = this.sessions.get(sessionId);
833
776
  if (!process2) return;
834
777
  this.conversationBuffer.delete(sessionId);
835
- this.cleanupMcpConfig(sessionId);
836
778
  this.sessions.delete(sessionId);
837
779
  await this.adapter.stop(process2);
838
780
  }
@@ -845,7 +787,6 @@ ${options.systemPrompt}` : memorySection;
845
787
  });
846
788
  }
847
789
  this.conversationBuffer.delete(sessionId);
848
- this.cleanupMcpConfig(sessionId);
849
790
  this.sessions.delete(sessionId);
850
791
  await this.adapter.stop(process2);
851
792
  }
@@ -858,7 +799,6 @@ ${options.systemPrompt}` : memorySection;
858
799
  });
859
800
  }
860
801
  this.conversationBuffer.delete(sessionId);
861
- this.cleanupMcpConfig(sessionId);
862
802
  this.sessions.delete(sessionId);
863
803
  await this.adapter.stop(process2).catch(() => {
864
804
  });
@@ -872,56 +812,107 @@ ${options.systemPrompt}` : memorySection;
872
812
  get activeSessionCount() {
873
813
  return this.sessions.size;
874
814
  }
875
- // ── Private helpers ─────────────────────────────────────────────────
876
- /** Generate a temporary MCP config file for the memory server. */
877
- createMemoryMcpConfig(sessionId, roleId) {
815
+ };
816
+
817
+ // src/start.ts
818
+ import { SwarmCoordinator, initRoles } from "@mclawnet/swarm";
819
+
820
+ // src/skill-loader.ts
821
+ import { existsSync as existsSync2, mkdirSync, copyFileSync, readdirSync as readdirSync2, writeFileSync } from "fs";
822
+ import { join as join2, dirname } from "path";
823
+ import { homedir as homedir2 } from "os";
824
+ import { createRequire } from "module";
825
+ import { fileURLToPath } from "url";
826
+ import { createLogger as createLogger3 } from "@mclawnet/logger";
827
+ var log3 = createLogger3({ module: "agent/skill-loader" });
828
+ var CLAWNET_DIR = join2(homedir2(), ".clawnet");
829
+ var SKILLS_DIR = join2(CLAWNET_DIR, ".claude", "skills");
830
+ var MCP_CONFIG_PATH = join2(CLAWNET_DIR, "mcp.json");
831
+ async function initSkills() {
832
+ ensureSkillsDir();
833
+ copyBuiltinSkills();
834
+ ensureMcpConfig();
835
+ }
836
+ function ensureSkillsDir() {
837
+ if (!existsSync2(SKILLS_DIR)) {
838
+ mkdirSync(SKILLS_DIR, { recursive: true });
839
+ log3.info({ dir: SKILLS_DIR }, "created skills directory");
840
+ }
841
+ }
842
+ function copyBuiltinSkills() {
843
+ const thisFile = fileURLToPath(import.meta.url);
844
+ const srcDir = join2(dirname(thisFile), "..", "skills");
845
+ if (!existsSync2(srcDir)) {
846
+ log3.debug({ srcDir }, "no built-in skills directory found, skipping");
847
+ return;
848
+ }
849
+ let entries;
850
+ try {
851
+ entries = readdirSync2(srcDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
852
+ } catch {
853
+ log3.warn({ srcDir }, "failed to read built-in skills directory");
854
+ return;
855
+ }
856
+ for (const skillName of entries) {
857
+ const srcSkillMd = join2(srcDir, skillName, "SKILL.md");
858
+ const destDir = join2(SKILLS_DIR, skillName);
859
+ const destSkillMd = join2(destDir, "SKILL.md");
860
+ if (!existsSync2(srcSkillMd)) continue;
861
+ if (existsSync2(destSkillMd)) {
862
+ log3.debug({ skill: skillName }, "skill already exists, skipping");
863
+ continue;
864
+ }
865
+ try {
866
+ mkdirSync(destDir, { recursive: true });
867
+ copyFileSync(srcSkillMd, destSkillMd);
868
+ log3.info({ skill: skillName }, "copied built-in skill");
869
+ } catch (err) {
870
+ log3.warn({ skill: skillName, err }, "failed to copy built-in skill");
871
+ }
872
+ }
873
+ }
874
+ function ensureMcpConfig() {
875
+ if (existsSync2(MCP_CONFIG_PATH)) {
876
+ log3.debug("mcp.json already exists, skipping");
877
+ return;
878
+ }
879
+ let mcpServerPath;
880
+ try {
878
881
  const req = createRequire(import.meta.url);
879
882
  const memoryPkgDir = dirname(req.resolve("@mclawnet/memory/package.json"));
880
- const mcpServerPath = join3(memoryPkgDir, "dist", "mcp", "server.js");
881
- const configPath = join3(tmpdir(), `clawnet-mcp-${sessionId.replace(/::/g, "-")}.json`);
882
- writeFileSync2(
883
- configPath,
884
- JSON.stringify({
885
- mcpServers: {
886
- "clawnet-memory": {
887
- command: "node",
888
- args: [mcpServerPath, "--role-id", roleId]
889
- }
890
- }
891
- })
892
- );
893
- this.mcpConfigPaths.set(sessionId, configPath);
894
- log3.debug({ sessionId, roleId, configPath, mcpServerPath }, "memory MCP config file written");
895
- return configPath;
883
+ mcpServerPath = join2(memoryPkgDir, "dist", "mcp", "server.js");
884
+ } catch {
885
+ log3.warn("could not resolve @mclawnet/memory package path, skipping mcp.json generation");
886
+ return;
896
887
  }
897
- /** Remove temporary MCP config file for a session. */
898
- cleanupMcpConfig(sessionId) {
899
- const configPath = this.mcpConfigPaths.get(sessionId);
900
- if (configPath) {
901
- try {
902
- unlinkSync(configPath);
903
- } catch {
888
+ const config = {
889
+ mcpServers: {
890
+ "clawnet-memory": {
891
+ command: "node",
892
+ args: [mcpServerPath]
904
893
  }
905
- this.mcpConfigPaths.delete(sessionId);
906
- log3.debug({ sessionId, configPath }, "memory MCP config file cleaned up");
907
894
  }
895
+ };
896
+ try {
897
+ mkdirSync(CLAWNET_DIR, { recursive: true });
898
+ writeFileSync(MCP_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n");
899
+ log3.info({ path: MCP_CONFIG_PATH }, "generated default mcp.json");
900
+ } catch (err) {
901
+ log3.warn({ err }, "failed to generate mcp.json");
908
902
  }
909
- };
910
-
911
- // src/start.ts
912
- import { SwarmCoordinator, initRoles } from "@mclawnet/swarm";
903
+ }
913
904
 
914
905
  // src/brain-bridge.ts
915
- import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync as readdirSync2 } from "fs";
916
- import { join as join4 } from "path";
906
+ import { existsSync as existsSync3, readFileSync as readFileSync2, readdirSync as readdirSync3 } from "fs";
907
+ import { join as join3 } from "path";
917
908
  import { createLogger as createLogger4 } from "@mclawnet/logger";
918
909
  var log4 = createLogger4({ module: "brain-bridge" });
919
910
  var BrainBridge = class {
920
911
  constructor(hub, options) {
921
912
  this.hub = hub;
922
913
  const home = process.env.HOME || process.env.USERPROFILE || "";
923
- this.brainHome = options?.brainHomePath || process.env.BRAIN_HOME || join4(home, "BrainData");
924
- this.brainCorePath = options?.brainCorePath || join4(home, ".brain", "BrainCore");
914
+ this.brainHome = options?.brainHomePath || process.env.BRAIN_HOME || join3(home, "BrainData");
915
+ this.brainCorePath = options?.brainCorePath || join3(home, ".brain", "BrainCore");
925
916
  this.hub.registerNamespace("brain", (msg) => this.handleRequest(msg));
926
917
  log4.info(
927
918
  { brainHome: this.brainHome, brainCorePath: this.brainCorePath },
@@ -952,7 +943,7 @@ var BrainBridge = class {
952
943
  return "not_installed";
953
944
  }
954
945
  const home = process.env.HOME || process.env.USERPROFILE || "";
955
- const installJson = join4(home, ".brain", "install.json");
946
+ const installJson = join3(home, ".brain", "install.json");
956
947
  if (!existsSync3(installJson)) {
957
948
  return "needs_config";
958
949
  }
@@ -964,18 +955,18 @@ var BrainBridge = class {
964
955
  /** Read the most recent daily briefing report */
965
956
  async getBriefing(date) {
966
957
  const targetDate = date || (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
967
- const reportsDir = join4(this.brainHome, "reports", "daily");
958
+ const reportsDir = join3(this.brainHome, "reports", "daily");
968
959
  if (!existsSync3(reportsDir)) {
969
960
  log4.info({ reportsDir }, "get_briefing: reports dir not found");
970
961
  return { briefing: null, actions: [], projects: [], meetings: [], feed: [] };
971
962
  }
972
- const files = readdirSync2(reportsDir).filter((f) => f.includes(targetDate) && f.endsWith(".md")).sort().reverse();
963
+ const files = readdirSync3(reportsDir).filter((f) => f.includes(targetDate) && f.endsWith(".md")).sort().reverse();
973
964
  if (files.length === 0) {
974
965
  log4.info({ targetDate }, "get_briefing: no report for date");
975
966
  return { briefing: null, actions: [], projects: [], meetings: [], feed: [] };
976
967
  }
977
968
  log4.info({ targetDate, file: files[0] }, "get_briefing: reading report");
978
- const content = readFileSync3(join4(reportsDir, files[0]), "utf-8");
969
+ const content = readFileSync2(join3(reportsDir, files[0]), "utf-8");
979
970
  const tldrMatch = content.match(/## TL;DR\n([\s\S]*?)(?=\n---|\n## )/);
980
971
  const tldr = tldrMatch ? tldrMatch[1].trim() : content.slice(0, 200);
981
972
  const actions = this.parseActions(content);
@@ -1174,12 +1165,12 @@ var BrainBridge = class {
1174
1165
  if (!recapPath) {
1175
1166
  return { error: "No recap path provided" };
1176
1167
  }
1177
- const fullPath = join4(this.brainHome, recapPath);
1168
+ const fullPath = join3(this.brainHome, recapPath);
1178
1169
  if (!existsSync3(fullPath)) {
1179
1170
  log4.warn({ fullPath }, "meeting recap file not found");
1180
1171
  return { error: "Recap file not found" };
1181
1172
  }
1182
- const content = readFileSync3(fullPath, "utf-8");
1173
+ const content = readFileSync2(fullPath, "utf-8");
1183
1174
  return { recap: this.parseRecapMarkdown(content) };
1184
1175
  }
1185
1176
  /** Parse meeting recap markdown into structured sections */
@@ -1212,7 +1203,7 @@ var BrainBridge = class {
1212
1203
  };
1213
1204
 
1214
1205
  // src/fs-bridge.ts
1215
- import { existsSync as existsSync4, readFileSync as readFileSync4, statSync as statSync2 } from "fs";
1206
+ import { existsSync as existsSync4, readFileSync as readFileSync3, statSync as statSync2 } from "fs";
1216
1207
  import { extname, isAbsolute } from "path";
1217
1208
  import { createLogger as createLogger5 } from "@mclawnet/logger";
1218
1209
  var log5 = createLogger5({ module: "fs-bridge" });
@@ -1280,12 +1271,12 @@ var FsBridge = class {
1280
1271
  const mimeType = getMimeType(filePath);
1281
1272
  const totalSize = stat2.size;
1282
1273
  if (isTextMime(mimeType)) {
1283
- const raw = readFileSync4(filePath, "utf-8");
1274
+ const raw = readFileSync3(filePath, "utf-8");
1284
1275
  const truncated2 = raw.length > MAX_TEXT_SIZE;
1285
1276
  const content = truncated2 ? raw.slice(0, MAX_TEXT_SIZE) : raw;
1286
1277
  return { content, encoding: "utf-8", size: content.length, totalSize, mimeType, truncated: truncated2 };
1287
1278
  }
1288
- const buf = readFileSync4(filePath);
1279
+ const buf = readFileSync3(filePath);
1289
1280
  const truncated = buf.length > MAX_TEXT_SIZE;
1290
1281
  const slice = truncated ? buf.subarray(0, MAX_TEXT_SIZE) : buf;
1291
1282
  return { content: slice.toString("base64"), encoding: "base64", size: slice.length, totalSize, mimeType, truncated };
@@ -1304,6 +1295,7 @@ async function startAgent(options) {
1304
1295
  log6.info({ backend: options.adapter.type }, "starting agent");
1305
1296
  log6.info({ hubUrl: config.hubUrl }, "connecting to hub");
1306
1297
  await initRoles();
1298
+ await initSkills();
1307
1299
  const hub = new HubConnection({
1308
1300
  hubUrl: config.hubUrl,
1309
1301
  token: config.token,
@@ -1367,10 +1359,8 @@ async function startAgent(options) {
1367
1359
  export {
1368
1360
  HubConnection,
1369
1361
  SessionManager,
1370
- loadConfig,
1371
- saveConfig,
1372
1362
  BrainBridge,
1373
1363
  FsBridge,
1374
1364
  startAgent
1375
1365
  };
1376
- //# sourceMappingURL=chunk-KHPEQTWF.js.map
1366
+ //# sourceMappingURL=chunk-GLO5OZAY.js.map