monomind 1.11.14 → 1.12.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.
Files changed (172) hide show
  1. package/.claude/agents/generated/channel-intelligence-director.md +87 -0
  2. package/.claude/agents/generated/chief-growth-officer.md +88 -0
  3. package/.claude/agents/generated/content-seo-strategist.md +90 -0
  4. package/.claude/agents/generated/developer-community-strategist.md +91 -0
  5. package/.claude/agents/generated/outreach-partnership-strategist.md +90 -0
  6. package/.claude/agents/generated/social-media-strategist.md +91 -0
  7. package/.claude/agents/generated/video-visual-strategist.md +90 -0
  8. package/.claude/commands/mastermind/master.md +1 -1
  9. package/.claude/helpers/auto-memory-hook.mjs +13 -4
  10. package/.claude/helpers/control-start.cjs +5 -0
  11. package/.claude/helpers/event-logger.cjs +114 -0
  12. package/.claude/helpers/handlers/adr-draft-handler.cjs +19 -5
  13. package/.claude/helpers/handlers/agent-start-handler.cjs +13 -4
  14. package/.claude/helpers/handlers/compact-handler.cjs +2 -0
  15. package/.claude/helpers/handlers/edit-handler.cjs +1 -1
  16. package/.claude/helpers/handlers/gates-handler.cjs +3 -0
  17. package/.claude/helpers/handlers/graph-status-handler.cjs +14 -8
  18. package/.claude/helpers/handlers/loops-status-handler.cjs +5 -2
  19. package/.claude/helpers/handlers/route-handler.cjs +13 -6
  20. package/.claude/helpers/handlers/session-handler.cjs +11 -4
  21. package/.claude/helpers/handlers/session-restore-handler.cjs +21 -11
  22. package/.claude/helpers/handlers/task-handler.cjs +13 -5
  23. package/.claude/helpers/intelligence.cjs +7 -2
  24. package/.claude/helpers/loop-tracker.cjs +15 -3
  25. package/.claude/helpers/memory.cjs +6 -1
  26. package/.claude/helpers/router.cjs +5 -2
  27. package/.claude/helpers/session.cjs +2 -0
  28. package/.claude/helpers/statusline.cjs +10 -2
  29. package/.claude/helpers/utils/micro-agents.cjs +20 -4
  30. package/.claude/scheduled_tasks.lock +1 -1
  31. package/.claude/settings.json +92 -1
  32. package/.claude/skills/mastermind/_protocol.md +25 -15
  33. package/.claude/skills/mastermind/architect.md +3 -3
  34. package/.claude/skills/mastermind/autodev.md +4 -2
  35. package/.claude/skills/mastermind/idea.md +10 -0
  36. package/.claude/skills/mastermind/ops.md +3 -3
  37. package/.claude/skills/mastermind/runorg.md +153 -86
  38. package/package.json +20 -3
  39. package/packages/@monomind/cli/dist/src/agents/registry-builder.js +2 -0
  40. package/packages/@monomind/cli/dist/src/autopilot-state.js +10 -5
  41. package/packages/@monomind/cli/dist/src/benchmarks/benchmark-runner.js +13 -0
  42. package/packages/@monomind/cli/dist/src/benchmarks/metric-evaluators.js +20 -9
  43. package/packages/@monomind/cli/dist/src/browser/actions.js +10 -3
  44. package/packages/@monomind/cli/dist/src/browser/browser.js +12 -2
  45. package/packages/@monomind/cli/dist/src/browser/cdp.js +21 -3
  46. package/packages/@monomind/cli/dist/src/browser/har.js +27 -5
  47. package/packages/@monomind/cli/dist/src/commands/agent.js +11 -8
  48. package/packages/@monomind/cli/dist/src/commands/analyze.js +36 -21
  49. package/packages/@monomind/cli/dist/src/commands/autopilot.js +12 -4
  50. package/packages/@monomind/cli/dist/src/commands/benchmark.js +51 -8
  51. package/packages/@monomind/cli/dist/src/commands/browse.js +5 -2
  52. package/packages/@monomind/cli/dist/src/commands/claims.js +29 -11
  53. package/packages/@monomind/cli/dist/src/commands/cleanup.js +25 -5
  54. package/packages/@monomind/cli/dist/src/commands/config.js +15 -7
  55. package/packages/@monomind/cli/dist/src/commands/daemon.js +6 -0
  56. package/packages/@monomind/cli/dist/src/commands/deployment.js +34 -19
  57. package/packages/@monomind/cli/dist/src/commands/doctor.js +97 -20
  58. package/packages/@monomind/cli/dist/src/commands/guidance.js +15 -2
  59. package/packages/@monomind/cli/dist/src/commands/hive-mind.js +37 -14
  60. package/packages/@monomind/cli/dist/src/commands/hooks.js +42 -25
  61. package/packages/@monomind/cli/dist/src/commands/init.js +9 -4
  62. package/packages/@monomind/cli/dist/src/commands/issues.js +29 -26
  63. package/packages/@monomind/cli/dist/src/commands/mcp.js +11 -5
  64. package/packages/@monomind/cli/dist/src/commands/memory.js +10 -0
  65. package/packages/@monomind/cli/dist/src/commands/migrate.js +5 -5
  66. package/packages/@monomind/cli/dist/src/commands/monograph.js +18 -5
  67. package/packages/@monomind/cli/dist/src/commands/monovector/backup.js +8 -2
  68. package/packages/@monomind/cli/dist/src/commands/monovector/benchmark.js +20 -7
  69. package/packages/@monomind/cli/dist/src/commands/monovector/import.js +15 -0
  70. package/packages/@monomind/cli/dist/src/commands/monovector/migrate.js +4 -1
  71. package/packages/@monomind/cli/dist/src/commands/monovector/optimize.js +11 -0
  72. package/packages/@monomind/cli/dist/src/commands/monovector/setup.js +11 -1
  73. package/packages/@monomind/cli/dist/src/commands/neural.js +1 -1
  74. package/packages/@monomind/cli/dist/src/commands/performance.js +20 -7
  75. package/packages/@monomind/cli/dist/src/commands/platforms.js +90 -8
  76. package/packages/@monomind/cli/dist/src/commands/plugins.js +12 -5
  77. package/packages/@monomind/cli/dist/src/commands/process.js +33 -10
  78. package/packages/@monomind/cli/dist/src/commands/progress.js +5 -3
  79. package/packages/@monomind/cli/dist/src/commands/providers.js +5 -5
  80. package/packages/@monomind/cli/dist/src/commands/replay.js +8 -2
  81. package/packages/@monomind/cli/dist/src/commands/route.js +27 -7
  82. package/packages/@monomind/cli/dist/src/commands/security.js +4 -0
  83. package/packages/@monomind/cli/dist/src/commands/session.js +12 -1
  84. package/packages/@monomind/cli/dist/src/commands/start.js +11 -4
  85. package/packages/@monomind/cli/dist/src/commands/status.js +7 -4
  86. package/packages/@monomind/cli/dist/src/commands/swarm.js +27 -13
  87. package/packages/@monomind/cli/dist/src/commands/task.js +26 -11
  88. package/packages/@monomind/cli/dist/src/commands/tokens.js +7 -2
  89. package/packages/@monomind/cli/dist/src/commands/transfer-store.js +36 -22
  90. package/packages/@monomind/cli/dist/src/commands/update.js +15 -3
  91. package/packages/@monomind/cli/dist/src/commands/workflow.js +39 -6
  92. package/packages/@monomind/cli/dist/src/consensus/audit-writer.js +18 -7
  93. package/packages/@monomind/cli/dist/src/consensus/vote-signer.js +25 -8
  94. package/packages/@monomind/cli/dist/src/index.js +7 -3
  95. package/packages/@monomind/cli/dist/src/init/executor.js +14 -11
  96. package/packages/@monomind/cli/dist/src/init/shared-instructions-generator.js +20 -4
  97. package/packages/@monomind/cli/dist/src/init/statusline-generator.js +36 -15
  98. package/packages/@monomind/cli/dist/src/mcp-tools/a2a-tools.js +98 -13
  99. package/packages/@monomind/cli/dist/src/mcp-tools/agent-tools.js +16 -3
  100. package/packages/@monomind/cli/dist/src/mcp-tools/analyze-tools.js +80 -17
  101. package/packages/@monomind/cli/dist/src/mcp-tools/browser-tools.js +84 -22
  102. package/packages/@monomind/cli/dist/src/mcp-tools/claims-tools.js +35 -7
  103. package/packages/@monomind/cli/dist/src/mcp-tools/config-tools.js +82 -17
  104. package/packages/@monomind/cli/dist/src/mcp-tools/coordination-tools.js +37 -4
  105. package/packages/@monomind/cli/dist/src/mcp-tools/daa-tools.js +49 -7
  106. package/packages/@monomind/cli/dist/src/mcp-tools/embeddings-tools.js +45 -18
  107. package/packages/@monomind/cli/dist/src/mcp-tools/github-tools.js +75 -25
  108. package/packages/@monomind/cli/dist/src/mcp-tools/guidance-tools.js +32 -10
  109. package/packages/@monomind/cli/dist/src/mcp-tools/hive-mind-tools.js +91 -20
  110. package/packages/@monomind/cli/dist/src/mcp-tools/hooks-tools.js +188 -29
  111. package/packages/@monomind/cli/dist/src/mcp-tools/memory-tools.js +25 -7
  112. package/packages/@monomind/cli/dist/src/mcp-tools/monograph-compat.js +11 -2
  113. package/packages/@monomind/cli/dist/src/mcp-tools/monograph-tools.js +148 -26
  114. package/packages/@monomind/cli/dist/src/mcp-tools/neural-tools.js +44 -9
  115. package/packages/@monomind/cli/dist/src/mcp-tools/performance-tools.js +45 -10
  116. package/packages/@monomind/cli/dist/src/mcp-tools/progress-tools.js +7 -4
  117. package/packages/@monomind/cli/dist/src/mcp-tools/request-tracker.js +15 -1
  118. package/packages/@monomind/cli/dist/src/mcp-tools/security-tools.js +61 -9
  119. package/packages/@monomind/cli/dist/src/mcp-tools/session-tools.js +45 -14
  120. package/packages/@monomind/cli/dist/src/mcp-tools/swarm-tools.js +15 -3
  121. package/packages/@monomind/cli/dist/src/mcp-tools/system-tools.js +14 -7
  122. package/packages/@monomind/cli/dist/src/mcp-tools/task-tools.js +52 -10
  123. package/packages/@monomind/cli/dist/src/mcp-tools/terminal-tools.js +40 -6
  124. package/packages/@monomind/cli/dist/src/mcp-tools/transfer-tools.js +37 -4
  125. package/packages/@monomind/cli/dist/src/mcp-tools/workflow-tools.js +29 -6
  126. package/packages/@monomind/cli/dist/src/memory/ewc-consolidation.js +26 -10
  127. package/packages/@monomind/cli/dist/src/memory/intelligence.js +80 -19
  128. package/packages/@monomind/cli/dist/src/memory/memory-bridge.js +21 -2
  129. package/packages/@monomind/cli/dist/src/memory/memory-initializer.js +67 -3
  130. package/packages/@monomind/cli/dist/src/memory/sona-optimizer.js +14 -4
  131. package/packages/@monomind/cli/dist/src/monovector/command-outcomes.js +43 -7
  132. package/packages/@monomind/cli/dist/src/monovector/coverage-router.js +8 -4
  133. package/packages/@monomind/cli/dist/src/monovector/coverage-tools.js +6 -3
  134. package/packages/@monomind/cli/dist/src/monovector/diff-classifier.js +13 -0
  135. package/packages/@monomind/cli/dist/src/monovector/route-outcomes.d.ts +2 -1
  136. package/packages/@monomind/cli/dist/src/monovector/route-outcomes.js +46 -4
  137. package/packages/@monomind/cli/dist/src/plugins/manager.js +8 -3
  138. package/packages/@monomind/cli/dist/src/plugins/store/discovery.js +46 -2
  139. package/packages/@monomind/cli/dist/src/plugins/store/search.js +5 -4
  140. package/packages/@monomind/cli/dist/src/production/circuit-breaker.js +17 -3
  141. package/packages/@monomind/cli/dist/src/production/error-handler.js +3 -0
  142. package/packages/@monomind/cli/dist/src/production/monitoring.js +20 -3
  143. package/packages/@monomind/cli/dist/src/production/rate-limiter.js +13 -4
  144. package/packages/@monomind/cli/dist/src/production/retry.js +17 -9
  145. package/packages/@monomind/cli/dist/src/routing/embed-worker.js +6 -2
  146. package/packages/@monomind/cli/dist/src/routing/embedder.js +0 -0
  147. package/packages/@monomind/cli/dist/src/routing/llm-caller.js +13 -2
  148. package/packages/@monomind/cli/dist/src/routing/route-layer-factory.js +18 -3
  149. package/packages/@monomind/cli/dist/src/services/claim-service.d.ts +1 -0
  150. package/packages/@monomind/cli/dist/src/services/claim-service.js +8 -0
  151. package/packages/@monomind/cli/dist/src/services/config-file-manager.js +14 -2
  152. package/packages/@monomind/cli/dist/src/services/headless-worker-executor.js +18 -2
  153. package/packages/@monomind/cli/dist/src/services/worker-daemon.js +53 -12
  154. package/packages/@monomind/cli/dist/src/transfer/anonymization/index.d.ts +0 -3
  155. package/packages/@monomind/cli/dist/src/transfer/anonymization/index.js +16 -1
  156. package/packages/@monomind/cli/dist/src/transfer/export.js +8 -0
  157. package/packages/@monomind/cli/dist/src/transfer/ipfs/upload.js +33 -3
  158. package/packages/@monomind/cli/dist/src/transfer/serialization/cfp.js +9 -3
  159. package/packages/@monomind/cli/dist/src/transfer/storage/gcs.js +37 -3
  160. package/packages/@monomind/cli/dist/src/transfer/store/discovery.js +45 -3
  161. package/packages/@monomind/cli/dist/src/transfer/store/download.js +5 -0
  162. package/packages/@monomind/cli/dist/src/transfer/store/publish.js +13 -1
  163. package/packages/@monomind/cli/dist/src/transfer/store/registry.d.ts +8 -0
  164. package/packages/@monomind/cli/dist/src/transfer/store/registry.js +30 -5
  165. package/packages/@monomind/cli/dist/src/transfer/store/search.js +20 -5
  166. package/packages/@monomind/cli/dist/src/update/checker.js +59 -7
  167. package/packages/@monomind/cli/dist/src/update/executor.js +50 -3
  168. package/packages/@monomind/cli/dist/src/update/index.js +18 -1
  169. package/packages/@monomind/cli/dist/src/update/rate-limiter.d.ts +6 -0
  170. package/packages/@monomind/cli/dist/src/update/rate-limiter.js +79 -7
  171. package/packages/@monomind/cli/dist/src/update/validator.js +52 -1
  172. package/packages/@monomind/cli/package.json +2 -3
@@ -5,7 +5,7 @@
5
5
  * github.com/monoes/monomind
6
6
  */
7
7
  import { output } from '../output.js';
8
- import { existsSync, statSync, rmSync, readdirSync } from 'fs';
8
+ import { existsSync, lstatSync, rmSync, readdirSync } from 'fs';
9
9
  import { join } from 'path';
10
10
  /**
11
11
  * Artifact directories and files that monomind/monomind may create
@@ -30,11 +30,27 @@ const KEEP_CONFIG_PATHS = [
30
30
  join('.claude', 'settings.json'),
31
31
  ];
32
32
  /**
33
- * Calculate the total size of a path (file or directory) in bytes
33
+ * Maximum directory recursion depth for size calculation.
34
+ * Prevents stack overflow on deeply-nested or circular-symlink trees.
34
35
  */
35
- function getSize(fullPath) {
36
+ const MAX_SIZE_DEPTH = 20;
37
+ /**
38
+ * Calculate the total size of a path (file or directory) in bytes.
39
+ *
40
+ * Uses lstatSync (not statSync) so that symlinks are never followed:
41
+ * a symlink counts only the size of the link itself, not its target.
42
+ * This prevents a crafted symlink (e.g. .claude -> /) from causing
43
+ * the cleanup command to recursively traverse the entire filesystem.
44
+ */
45
+ function getSize(fullPath, depth = 0) {
46
+ if (depth > MAX_SIZE_DEPTH)
47
+ return 0;
36
48
  try {
37
- const stat = statSync(fullPath);
49
+ const stat = lstatSync(fullPath);
50
+ if (stat.isSymbolicLink()) {
51
+ // Count only the symlink entry itself; never traverse the target.
52
+ return stat.size;
53
+ }
38
54
  if (stat.isFile()) {
39
55
  return stat.size;
40
56
  }
@@ -42,7 +58,11 @@ function getSize(fullPath) {
42
58
  let total = 0;
43
59
  const entries = readdirSync(fullPath, { withFileTypes: true });
44
60
  for (const entry of entries) {
45
- total += getSize(join(fullPath, entry.name));
61
+ // Skip symlinks at the entry level too — lstatSync below will still
62
+ // catch them, but checking here avoids unnecessary path joins.
63
+ if (!entry.isSymbolicLink()) {
64
+ total += getSize(join(fullPath, entry.name), depth + 1);
65
+ }
46
66
  }
47
67
  return total;
48
68
  }
@@ -69,7 +69,7 @@ const getCommand = {
69
69
  { command: 'monomind config get -k memory.backend', description: 'Get memory backend' }
70
70
  ],
71
71
  action: async (ctx) => {
72
- const key = ctx.flags.key || ctx.args[0];
72
+ const key = (ctx.flags.key || ctx.args[0] || '').slice(0, 256);
73
73
  if (!key) {
74
74
  // Show all config from actual config file (fall back to defaults)
75
75
  const config = configManager.getConfig(ctx.cwd);
@@ -102,6 +102,14 @@ const getCommand = {
102
102
  });
103
103
  return { success: true, data: flatEntries };
104
104
  }
105
+ // Prototype pollution guard — mirrors the same check in setCommand.
106
+ const FORBIDDEN_KEY_SEGMENTS = new Set(['__proto__', 'constructor', 'prototype']);
107
+ for (const seg of key.split('.')) {
108
+ if (FORBIDDEN_KEY_SEGMENTS.has(seg)) {
109
+ output.printError(`Forbidden config key segment: "${seg}"`);
110
+ return { success: false, exitCode: 1 };
111
+ }
112
+ }
105
113
  const value = configManager.get(ctx.cwd, key);
106
114
  if (value === undefined) {
107
115
  output.printError(`Configuration key not found: ${key}`);
@@ -141,8 +149,8 @@ const setCommand = {
141
149
  { command: 'monomind config set -k memory.backend -v agentdb', description: 'Set memory backend' }
142
150
  ],
143
151
  action: async (ctx) => {
144
- const key = ctx.flags.key || ctx.args[0];
145
- const value = ctx.flags.value || ctx.args[1];
152
+ const key = (ctx.flags.key || ctx.args[0] || '').slice(0, 256);
153
+ const value = (ctx.flags.value ?? ctx.args[1] ?? '');
146
154
  if (!key || value === undefined) {
147
155
  output.printError('Both key and value are required');
148
156
  return { success: false, exitCode: 1 };
@@ -203,10 +211,10 @@ const providersCommand = {
203
211
  { name: 'gemini', model: 'gemini-2.0-flash', priority: 4, enabled: false, status: 'Disabled' }
204
212
  ];
205
213
  // Handle mutation flags
206
- const addProvider = ctx.flags.add;
207
- const removeProvider = ctx.flags.remove;
208
- const enableProvider = ctx.flags.enable;
209
- const disableProvider = ctx.flags.disable;
214
+ const addProvider = ctx.flags.add?.slice(0, 64);
215
+ const removeProvider = ctx.flags.remove?.slice(0, 64);
216
+ const enableProvider = ctx.flags.enable?.slice(0, 64);
217
+ const disableProvider = ctx.flags.disable?.slice(0, 64);
210
218
  if (addProvider || removeProvider || enableProvider || disableProvider) {
211
219
  // Read current providers from config
212
220
  let currentProviders = configManager.get(ctx.cwd, 'providers') || [];
@@ -327,6 +327,10 @@ async function killBackgroundDaemon(projectRoot) {
327
327
  return false;
328
328
  }
329
329
  try {
330
+ if (fs.statSync(pidFile).size > 32) {
331
+ fs.unlinkSync(pidFile);
332
+ return false;
333
+ }
330
334
  const pid = parseInt(fs.readFileSync(pidFile, 'utf-8').trim(), 10);
331
335
  if (isNaN(pid)) {
332
336
  fs.unlinkSync(pidFile);
@@ -376,6 +380,8 @@ function getBackgroundDaemonPid(projectRoot) {
376
380
  return null;
377
381
  }
378
382
  try {
383
+ if (fs.statSync(pidFile).size > 32)
384
+ return null;
379
385
  const pid = parseInt(fs.readFileSync(pidFile, 'utf-8').trim(), 10);
380
386
  return isNaN(pid) ? null : pid;
381
387
  }
@@ -19,11 +19,23 @@ function getStatePath(cwd) {
19
19
  function emptyState() {
20
20
  return { environments: {}, history: [], activeDeployment: undefined };
21
21
  }
22
+ const MAX_DEPLOYMENT_STATE_BYTES = 10 * 1024 * 1024; // 10 MB
23
+ // Input length caps to prevent DoS via unbounded strings stored to disk
24
+ const MAX_ENV_NAME_LEN = 128;
25
+ const MAX_VERSION_LEN = 64;
26
+ const MAX_DESCRIPTION_LEN = 1024;
27
+ const MAX_URL_LEN = 2048;
28
+ const MAX_ENV_TYPE_LEN = 64;
29
+ const MAX_HISTORY_LIMIT = 1000;
30
+ const MAX_LOGS_LIMIT = 1000;
22
31
  function loadDeploymentState(cwd) {
23
32
  const filePath = getStatePath(cwd);
24
33
  if (!fs.existsSync(filePath)) {
25
34
  return emptyState();
26
35
  }
36
+ if (fs.statSync(filePath).size > MAX_DEPLOYMENT_STATE_BYTES) {
37
+ return emptyState();
38
+ }
27
39
  try {
28
40
  const raw = fs.readFileSync(filePath, 'utf-8');
29
41
  const parsed = JSON.parse(raw);
@@ -66,6 +78,9 @@ function readProjectVersion(cwd) {
66
78
  if (!fs.existsSync(pkgPath)) {
67
79
  return null;
68
80
  }
81
+ if (fs.statSync(pkgPath).size > 1024 * 1024) {
82
+ return null;
83
+ }
69
84
  try {
70
85
  const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
71
86
  return pkg.version ?? null;
@@ -92,10 +107,10 @@ const deployCommand = {
92
107
  ],
93
108
  action: async (ctx) => {
94
109
  try {
95
- const envName = String(ctx.flags['env'] || 'staging');
110
+ const envName = String(ctx.flags['env'] || 'staging').slice(0, MAX_ENV_NAME_LEN);
96
111
  const dryRun = Boolean(ctx.flags['dry-run']);
97
- const description = ctx.flags['description'] ? String(ctx.flags['description']) : undefined;
98
- let version = ctx.flags['version'] ? String(ctx.flags['version']) : null;
112
+ const description = ctx.flags['description'] ? String(ctx.flags['description']).slice(0, MAX_DESCRIPTION_LEN) : undefined;
113
+ let version = ctx.flags['version'] ? String(ctx.flags['version']).slice(0, MAX_VERSION_LEN) : null;
99
114
  if (!version) {
100
115
  version = readProjectVersion(ctx.cwd) || '0.0.0';
101
116
  }
@@ -181,7 +196,7 @@ const statusCommand = {
181
196
  action: async (ctx) => {
182
197
  try {
183
198
  const state = loadDeploymentState(ctx.cwd);
184
- const filterEnv = ctx.flags['env'] ? String(ctx.flags['env']) : null;
199
+ const filterEnv = ctx.flags['env'] ? String(ctx.flags['env']).slice(0, MAX_ENV_NAME_LEN) : null;
185
200
  output.writeln();
186
201
  output.writeln(output.bold('Deployment Status'));
187
202
  output.writeln();
@@ -277,13 +292,13 @@ const rollbackCommand = {
277
292
  ],
278
293
  action: async (ctx) => {
279
294
  try {
280
- const envName = String(ctx.flags['env'] || '');
295
+ const envName = String(ctx.flags['env'] || '').slice(0, MAX_ENV_NAME_LEN);
281
296
  if (!envName) {
282
297
  output.printError('Environment is required', 'Use --env or -e to specify');
283
298
  return { success: false, exitCode: 1 };
284
299
  }
285
- const targetVersion = ctx.flags['version'] ? String(ctx.flags['version']) : null;
286
- const steps = parseInt(ctx.flags.steps || '1', 10);
300
+ const targetVersion = ctx.flags['version'] ? String(ctx.flags['version']).slice(0, MAX_VERSION_LEN) : null;
301
+ const steps = Math.min(Math.max(parseInt(ctx.flags.steps || '1', 10), 1), 100);
287
302
  if (steps > 1) {
288
303
  output.printWarning(`Multi-step rollback (--steps ${steps}) is not yet implemented. Rolling back 1 step only.`);
289
304
  }
@@ -370,8 +385,8 @@ const historyCommand = {
370
385
  action: async (ctx) => {
371
386
  try {
372
387
  const state = loadDeploymentState(ctx.cwd);
373
- const filterEnv = ctx.flags['env'] ? String(ctx.flags['env']) : null;
374
- const limit = Number(ctx.flags['limit']) || 10;
388
+ const filterEnv = ctx.flags['env'] ? String(ctx.flags['env']).slice(0, MAX_ENV_NAME_LEN) : null;
389
+ const limit = Math.min(Math.max(Number(ctx.flags['limit']) || 10, 1), MAX_HISTORY_LIMIT);
375
390
  let records = [...state.history].reverse();
376
391
  if (filterEnv) {
377
392
  records = records.filter(r => r.environment === filterEnv);
@@ -455,7 +470,7 @@ const environmentsCommand = {
455
470
  return { success: true };
456
471
  }
457
472
  if (action === 'add') {
458
- const name = ctx.flags['name'] ? String(ctx.flags['name']) : null;
473
+ const name = ctx.flags['name'] ? String(ctx.flags['name']).slice(0, MAX_ENV_NAME_LEN) : null;
459
474
  if (!name) {
460
475
  output.printError('Environment name is required', 'Use --name or -n to specify');
461
476
  return { success: false, exitCode: 1 };
@@ -464,8 +479,8 @@ const environmentsCommand = {
464
479
  output.printWarning(`Environment '${name}' already exists`);
465
480
  return { success: false, exitCode: 1 };
466
481
  }
467
- const envType = String(ctx.flags['type'] || 'local');
468
- const url = ctx.flags['url'] ? String(ctx.flags['url']) : undefined;
482
+ const envType = String(ctx.flags['type'] || 'local').slice(0, MAX_ENV_TYPE_LEN);
483
+ const url = ctx.flags['url'] ? String(ctx.flags['url']).slice(0, MAX_URL_LEN) : undefined;
469
484
  state.environments[name] = {
470
485
  name,
471
486
  type: envType,
@@ -481,7 +496,7 @@ const environmentsCommand = {
481
496
  return { success: true };
482
497
  }
483
498
  if (action === 'remove') {
484
- const name = ctx.flags['name'] ? String(ctx.flags['name']) : null;
499
+ const name = ctx.flags['name'] ? String(ctx.flags['name']).slice(0, MAX_ENV_NAME_LEN) : null;
485
500
  if (!name) {
486
501
  output.printError('Environment name is required', 'Use --name or -n to specify');
487
502
  return { success: false, exitCode: 1 };
@@ -524,9 +539,9 @@ const logsCommand = {
524
539
  action: async (ctx) => {
525
540
  try {
526
541
  const state = loadDeploymentState(ctx.cwd);
527
- const filterEnv = ctx.flags['env'] ? String(ctx.flags['env']) : null;
528
- const deploymentId = ctx.flags['deployment'] ? String(ctx.flags['deployment']) : null;
529
- const limit = Number(ctx.flags['lines']) || 50;
542
+ const filterEnv = ctx.flags['env'] ? String(ctx.flags['env']).slice(0, MAX_ENV_NAME_LEN) : null;
543
+ const deploymentId = ctx.flags['deployment'] ? String(ctx.flags['deployment']).slice(0, 64) : null;
544
+ const limit = Math.min(Math.max(Number(ctx.flags['lines']) || 50, 1), MAX_LOGS_LIMIT);
530
545
  output.writeln();
531
546
  output.writeln(output.bold('Deployment Logs'));
532
547
  output.writeln();
@@ -588,9 +603,9 @@ const releaseCommand = {
588
603
  ],
589
604
  action: async (ctx) => {
590
605
  try {
591
- const envName = String(ctx.flags['env'] || 'production');
592
- const description = ctx.flags['description'] ? String(ctx.flags['description']) : undefined;
593
- let version = ctx.flags['version'] ? String(ctx.flags['version']) : null;
606
+ const envName = String(ctx.flags['env'] || 'production').slice(0, MAX_ENV_NAME_LEN);
607
+ const description = ctx.flags['description'] ? String(ctx.flags['description']).slice(0, MAX_DESCRIPTION_LEN) : undefined;
608
+ let version = ctx.flags['version'] ? String(ctx.flags['version']).slice(0, MAX_VERSION_LEN) : null;
594
609
  if (!version) {
595
610
  const pkgVersion = readProjectVersion(ctx.cwd);
596
611
  if (!pkgVersion) {
@@ -7,6 +7,11 @@
7
7
  import { output } from '../output.js';
8
8
  import { existsSync, readFileSync, statSync } from 'fs';
9
9
  import { join, dirname } from 'path';
10
+ const MAX_DOCTOR_PKG_BYTES = 1024 * 1024; // 1 MB — package.json / settings.json
11
+ const MAX_DOCTOR_CONFIG_BYTES = 10 * 1024 * 1024; // 10 MB — monomind.config.json / MCP configs
12
+ const MAX_DOCTOR_GITIGNORE_BYTES = 512 * 1024; // 512 KB — .gitignore
13
+ const MAX_DOCTOR_PID_BYTES = 64; // 64 bytes — daemon PID file
14
+ const MAX_DOCTOR_HELPER_BYTES = 2 * 1024 * 1024; // 2 MB — hook helper .cjs bundles
10
15
  import { fileURLToPath } from 'url';
11
16
  import { execSync, exec } from 'child_process';
12
17
  import { homedir } from 'os';
@@ -67,7 +72,7 @@ async function checkConfigFile() {
67
72
  '.monomind.json'
68
73
  ];
69
74
  for (const configPath of jsonPaths) {
70
- if (existsSync(configPath)) {
75
+ if (existsSync(configPath) && statSync(configPath).size <= MAX_DOCTOR_CONFIG_BYTES) {
71
76
  try {
72
77
  const content = readFileSync(configPath, 'utf8');
73
78
  JSON.parse(content);
@@ -95,7 +100,7 @@ async function checkConfigFile() {
95
100
  async function checkDaemonStatus() {
96
101
  try {
97
102
  const pidFile = '.monomind/daemon.pid';
98
- if (existsSync(pidFile)) {
103
+ if (existsSync(pidFile) && statSync(pidFile).size <= MAX_DOCTOR_PID_BYTES) {
99
104
  const pid = readFileSync(pidFile, 'utf8').trim();
100
105
  try {
101
106
  process.kill(parseInt(pid, 10), 0); // Check if process exists
@@ -184,7 +189,7 @@ async function checkMcpServers() {
184
189
  '.mcp.json'
185
190
  ];
186
191
  for (const configPath of mcpConfigPaths) {
187
- if (existsSync(configPath)) {
192
+ if (existsSync(configPath) && statSync(configPath).size <= MAX_DOCTOR_CONFIG_BYTES) {
188
193
  try {
189
194
  const content = JSON.parse(readFileSync(configPath, 'utf8'));
190
195
  const servers = content.mcpServers || content.servers || {};
@@ -202,7 +207,7 @@ async function checkMcpServers() {
202
207
  }
203
208
  }
204
209
  }
205
- return { name: 'MCP Servers', status: 'warn', message: 'No MCP config found', fix: 'claude mcp add monomind npx @monomind/cli@v1alpha mcp start' };
210
+ return { name: 'MCP Servers', status: 'warn', message: 'No MCP config found', fix: 'claude mcp add monomind -- npx -y monomind@latest mcp start' };
206
211
  }
207
212
  // Check disk space (async with proper env inheritance)
208
213
  async function checkDiskSpace() {
@@ -260,11 +265,11 @@ async function checkVersionFreshness() {
260
265
  for (;;) {
261
266
  const candidate = join(dir, 'package.json');
262
267
  try {
263
- if (existsSync(candidate)) {
268
+ if (existsSync(candidate) && statSync(candidate).size <= MAX_DOCTOR_PKG_BYTES) {
264
269
  const pkg = JSON.parse(readFileSync(candidate, 'utf8'));
265
270
  if (pkg.version &&
266
271
  typeof pkg.name === 'string' &&
267
- (pkg.name === '@monomind/cli' || pkg.name === 'monomind')) {
272
+ (pkg.name === '@monomind/cli' || pkg.name === 'monomind' || pkg.name === '@monoes/monomindcli')) {
268
273
  currentVersion = pkg.version;
269
274
  break;
270
275
  }
@@ -287,10 +292,10 @@ async function checkVersionFreshness() {
287
292
  const isNpx = process.argv[1]?.includes('_npx') ||
288
293
  process.env.npm_execpath?.includes('npx') ||
289
294
  process.cwd().includes('_npx');
290
- // Query npm for latest version (using alpha tag since that's what we publish to)
295
+ // Query npm for latest version of the published umbrella package
291
296
  let latestVersion = currentVersion;
292
297
  try {
293
- const npmInfo = await runCommand('npm view @monomind/cli@alpha version', 5000);
298
+ const npmInfo = await runCommand('npm view monomind version', 5000);
294
299
  latestVersion = npmInfo.trim();
295
300
  }
296
301
  catch {
@@ -322,8 +327,8 @@ async function checkVersionFreshness() {
322
327
  (latest.major === current.major && latest.minor === current.minor && latest.patch === current.patch && latest.prerelease > current.prerelease));
323
328
  if (isOutdated) {
324
329
  const fix = isNpx
325
- ? 'rm -rf ~/.npm/_npx/* && npx -y @monomind/cli@latest'
326
- : 'npm update @monomind/cli';
330
+ ? 'rm -rf ~/.npm/_npx/* && npx -y monomind@latest doctor'
331
+ : 'npm update -g monomind';
327
332
  return {
328
333
  name: 'Version Freshness',
329
334
  status: 'warn',
@@ -387,18 +392,39 @@ async function installClaudeCode() {
387
392
  async function checkMonograph() {
388
393
  try {
389
394
  const __filename = fileURLToPath(import.meta.url);
395
+ const _base = dirname(__filename);
396
+ let _globalRoot = '';
397
+ try {
398
+ _globalRoot = execSync('npm root -g', { encoding: 'utf8', timeout: 3000 }).trim();
399
+ }
400
+ catch { /* no npm */ }
390
401
  const candidates = [
391
- join(dirname(__filename), '..', '..', 'node_modules', '@monomind', 'monograph', 'package.json'),
392
- join(dirname(__filename), '..', '..', '..', '..', 'node_modules', '@monomind', 'monograph', 'package.json'),
402
+ // local dev monorepo paths (both old @monomind and published @monoes scope)
403
+ join(_base, '..', '..', 'node_modules', '@monomind', 'monograph', 'package.json'),
404
+ join(_base, '..', '..', '..', '..', 'node_modules', '@monomind', 'monograph', 'package.json'),
405
+ join(_base, '..', '..', 'node_modules', '@monoes', 'monograph', 'package.json'),
406
+ join(_base, '..', '..', '..', '..', 'node_modules', '@monoes', 'monograph', 'package.json'),
407
+ // global install paths
408
+ ...(_globalRoot ? [
409
+ join(_globalRoot, '@monomind', 'monograph', 'package.json'),
410
+ join(_globalRoot, '@monoes', 'monograph', 'package.json'),
411
+ ] : []),
393
412
  ];
394
- if (candidates.some(p => existsSync(p))) {
395
- return { name: 'Monograph', status: 'pass', message: 'available (knowledge graph engine)' };
413
+ const found = candidates.find(p => existsSync(p) && statSync(p).size <= MAX_DOCTOR_PKG_BYTES);
414
+ if (found) {
415
+ try {
416
+ const pkg = JSON.parse(readFileSync(found, 'utf-8'));
417
+ return { name: 'Monograph', status: 'pass', message: `v${pkg.version || '?'} available (knowledge graph engine)` };
418
+ }
419
+ catch {
420
+ return { name: 'Monograph', status: 'pass', message: 'available (knowledge graph engine)' };
421
+ }
396
422
  }
397
423
  return {
398
424
  name: 'Monograph',
399
425
  status: 'warn',
400
426
  message: 'Package not found (knowledge graph disabled)',
401
- fix: 'npm install in project root'
427
+ fix: 'npm install -g monomind@latest # reinstall to get @monoes/monograph'
402
428
  };
403
429
  }
404
430
  catch {
@@ -406,10 +432,46 @@ async function checkMonograph() {
406
432
  name: 'Monograph',
407
433
  status: 'warn',
408
434
  message: 'Package check failed (knowledge graph may be unavailable)',
409
- fix: 'npm install in project root'
435
+ fix: 'npm install -g monomind@latest'
410
436
  };
411
437
  }
412
438
  }
439
+ // Check @monoes/memory (optional HNSW vector search package)
440
+ async function checkMonoesMemory() {
441
+ try {
442
+ const __filename = fileURLToPath(import.meta.url);
443
+ const _base = dirname(__filename);
444
+ let _globalRoot = '';
445
+ try {
446
+ _globalRoot = execSync('npm root -g', { encoding: 'utf8', timeout: 3000 }).trim();
447
+ }
448
+ catch { /* no npm */ }
449
+ const candidates = [
450
+ join(_base, '..', '..', 'node_modules', '@monoes', 'memory', 'package.json'),
451
+ join(_base, '..', '..', '..', '..', 'node_modules', '@monoes', 'memory', 'package.json'),
452
+ ...(_globalRoot ? [join(_globalRoot, '@monoes', 'memory', 'package.json')] : []),
453
+ ];
454
+ const found = candidates.find(p => existsSync(p) && statSync(p).size <= MAX_DOCTOR_PKG_BYTES);
455
+ if (found) {
456
+ try {
457
+ const pkg = JSON.parse(readFileSync(found, 'utf-8'));
458
+ return { name: 'Vector Memory', status: 'pass', message: `@monoes/memory v${pkg.version || '?'} (HNSW search enabled)` };
459
+ }
460
+ catch {
461
+ return { name: 'Vector Memory', status: 'pass', message: '@monoes/memory available (HNSW search enabled)' };
462
+ }
463
+ }
464
+ return {
465
+ name: 'Vector Memory',
466
+ status: 'warn',
467
+ message: '@monoes/memory not installed (vector search disabled — using fallback)',
468
+ fix: 'npm install @monoes/memory'
469
+ };
470
+ }
471
+ catch {
472
+ return { name: 'Vector Memory', status: 'warn', message: 'Vector memory check failed' };
473
+ }
474
+ }
413
475
  // Check agentic-flow v1 integration (filesystem-based to avoid slow WASM/DB init)
414
476
  // Resolve the path to the bundled (npm-installed) copy of a helper file.
415
477
  // Walks up from this module's location to find the package root, then joins
@@ -420,10 +482,10 @@ function _resolveBundledHelper(relativePath) {
420
482
  let dir = dirname(thisFile);
421
483
  for (;;) {
422
484
  const candidate = join(dir, 'package.json');
423
- if (existsSync(candidate)) {
485
+ if (existsSync(candidate) && statSync(candidate).size <= MAX_DOCTOR_PKG_BYTES) {
424
486
  try {
425
487
  const pkg = JSON.parse(readFileSync(candidate, 'utf8'));
426
- if (pkg.name === '@monomind/cli' || pkg.name === 'monomind') {
488
+ if (pkg.name === '@monomind/cli' || pkg.name === 'monomind' || pkg.name === '@monoes/monomindcli') {
427
489
  const helperPath = join(dir, relativePath);
428
490
  return existsSync(helperPath) ? helperPath : null;
429
491
  }
@@ -451,11 +513,15 @@ async function _detectStaleHelpers() {
451
513
  const local = join(process.cwd(), '.claude', 'helpers', name);
452
514
  if (!existsSync(local))
453
515
  continue;
516
+ if (statSync(local).size > MAX_DOCTOR_HELPER_BYTES)
517
+ continue; // skip oversized helper
454
518
  const bundled = _resolveBundledHelper(join('.claude', 'helpers', name));
455
519
  if (!bundled) {
456
520
  missing.push(name);
457
521
  continue;
458
522
  }
523
+ if (statSync(bundled).size > MAX_DOCTOR_HELPER_BYTES)
524
+ continue; // skip oversized bundled
459
525
  try {
460
526
  const hashLocal = crypto.createHash('sha256').update(readFileSync(local)).digest('hex');
461
527
  const hashBundled = crypto.createHash('sha256').update(readFileSync(bundled)).digest('hex');
@@ -513,6 +579,9 @@ async function checkAgenticFlow() {
513
579
  fix: 'npm install agentic-flow@latest'
514
580
  };
515
581
  }
582
+ if (statSync(pkgJsonPath).size > MAX_DOCTOR_PKG_BYTES) {
583
+ return { name: 'agentic-flow', status: 'warn', message: 'package.json too large to parse' };
584
+ }
516
585
  const pkg = JSON.parse(readFileSync(pkgJsonPath, 'utf-8'));
517
586
  const version = pkg.version || 'unknown';
518
587
  const exports = pkg.exports || {};
@@ -524,7 +593,7 @@ async function checkAgenticFlow() {
524
593
  return {
525
594
  name: 'agentic-flow',
526
595
  status: 'pass',
527
- message: `v${version} (${features.join(', ')})`
596
+ message: `v${version}${features.length ? ' (' + features.join(', ') + ')' : ' (installed)'}`
528
597
  };
529
598
  }
530
599
  catch {
@@ -609,6 +678,9 @@ async function checkGitignoreCoverage() {
609
678
  fix: 'echo ".monomind/\\n**/.monomind/" >> .gitignore',
610
679
  };
611
680
  }
681
+ if (statSync(gitignorePath).size > MAX_DOCTOR_GITIGNORE_BYTES) {
682
+ return { name: 'Gitignore Coverage', status: 'warn', message: '.gitignore too large to parse' };
683
+ }
612
684
  const content = readFileSync(gitignorePath, 'utf-8');
613
685
  const lines = content.split('\n').map(l => l.trim()).filter(l => l && !l.startsWith('#'));
614
686
  const missing = REQUIRED_GITIGNORE_PATTERNS.filter(({ pattern }) => {
@@ -651,6 +723,9 @@ async function checkGuidanceGates() {
651
723
  };
652
724
  }
653
725
  try {
726
+ if (statSync(settingsPath).size > MAX_DOCTOR_CONFIG_BYTES) {
727
+ return { name: 'Guidance Gates', status: 'warn', message: 'settings.json too large to parse' };
728
+ }
654
729
  const settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
655
730
  const preToolUse = settings?.hooks?.PreToolUse ?? [];
656
731
  const hasPreWrite = preToolUse.some(e => e.matcher === 'Write|Edit|MultiEdit' && e.hooks.some(h => h.command?.includes('pre-write')));
@@ -723,7 +798,7 @@ export const doctorCommand = {
723
798
  {
724
799
  name: 'component',
725
800
  short: 'c',
726
- description: 'Check specific component (version, node, npm, config, daemon, memory, api, git, mcp, claude, disk, typescript, monograph, helpers, monoes, gates, gitignore)',
801
+ description: 'Check specific component (version, node, npm, config, daemon, memory, api, git, mcp, claude, disk, typescript, monograph, memory-pkg, helpers, agentic-flow, monoes, gates, gitignore)',
727
802
  type: 'string'
728
803
  },
729
804
  {
@@ -766,6 +841,7 @@ export const doctorCommand = {
766
841
  checkDiskSpace,
767
842
  checkBuildTools,
768
843
  checkMonograph,
844
+ checkMonoesMemory,
769
845
  checkHelpersFresh,
770
846
  checkAgenticFlow,
771
847
  checkMonoesIntegration,
@@ -787,6 +863,7 @@ export const doctorCommand = {
787
863
  'disk': checkDiskSpace,
788
864
  'typescript': checkBuildTools,
789
865
  'monograph': checkMonograph,
866
+ 'memory-pkg': checkMonoesMemory,
790
867
  'helpers': checkHelpersFresh,
791
868
  'agentic-flow': checkAgenticFlow,
792
869
  'monoes': checkMonoesIntegration,
@@ -461,6 +461,19 @@ const abTestCommand = {
461
461
  output.writeln(output.error(`Tasks file not found: ${tasksPath}`));
462
462
  return { success: false, message: `File not found: ${tasksPath}` };
463
463
  }
464
+ // Size guard: a multi-MB tasks file would be buffered in full before
465
+ // JSON.parse runs. Cap at 10 MB — any legitimate task list is far smaller.
466
+ const { statSync, lstatSync } = await import('node:fs');
467
+ const tasksLstat = lstatSync(tasksPath);
468
+ if (tasksLstat.isSymbolicLink()) {
469
+ output.writeln(output.error(`Symlinks are not allowed as task files: ${tasksPath}`));
470
+ return { success: false, message: 'Symlink not allowed as tasks file' };
471
+ }
472
+ const MAX_TASKS_FILE_BYTES = 10 * 1024 * 1024; // 10 MB
473
+ if (tasksLstat.size > MAX_TASKS_FILE_BYTES) {
474
+ output.writeln(output.error(`Tasks file too large (max 10 MB): ${tasksPath}`));
475
+ return { success: false, message: 'Tasks file too large' };
476
+ }
464
477
  const tasksJson = await readFile(tasksPath, 'utf-8');
465
478
  customTasks = JSON.parse(tasksJson);
466
479
  output.writeln(` Custom tasks: ${output.bold(String(customTasks.length))} loaded from ${tasksPath}`);
@@ -542,7 +555,7 @@ const setupCommand = {
542
555
  output.writeln(output.bold('Guidance Gates Setup'));
543
556
  output.writeln(output.dim('─'.repeat(50)));
544
557
  output.writeln();
545
- const { readFileSync, writeFileSync, existsSync } = await import('node:fs');
558
+ const { readFileSync, writeFileSync, existsSync, statSync } = await import('node:fs');
546
559
  const { join } = await import('node:path');
547
560
  const settingsPath = join(projectDir, '.claude', 'settings.json');
548
561
  const gatesHandlerPath = join(projectDir, '.claude', 'helpers', 'handlers', 'gates-handler.cjs');
@@ -553,7 +566,7 @@ const setupCommand = {
553
566
  }
554
567
  // Load or create settings.json
555
568
  let settings = {};
556
- if (existsSync(settingsPath)) {
569
+ if (existsSync(settingsPath) && statSync(settingsPath).size <= 1024 * 1024) {
557
570
  try {
558
571
  settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
559
572
  }