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.
- package/.claude/agents/generated/channel-intelligence-director.md +87 -0
- package/.claude/agents/generated/chief-growth-officer.md +88 -0
- package/.claude/agents/generated/content-seo-strategist.md +90 -0
- package/.claude/agents/generated/developer-community-strategist.md +91 -0
- package/.claude/agents/generated/outreach-partnership-strategist.md +90 -0
- package/.claude/agents/generated/social-media-strategist.md +91 -0
- package/.claude/agents/generated/video-visual-strategist.md +90 -0
- package/.claude/commands/mastermind/master.md +1 -1
- package/.claude/helpers/auto-memory-hook.mjs +13 -4
- package/.claude/helpers/control-start.cjs +5 -0
- package/.claude/helpers/event-logger.cjs +114 -0
- package/.claude/helpers/handlers/adr-draft-handler.cjs +19 -5
- package/.claude/helpers/handlers/agent-start-handler.cjs +13 -4
- package/.claude/helpers/handlers/compact-handler.cjs +2 -0
- package/.claude/helpers/handlers/edit-handler.cjs +1 -1
- package/.claude/helpers/handlers/gates-handler.cjs +3 -0
- package/.claude/helpers/handlers/graph-status-handler.cjs +14 -8
- package/.claude/helpers/handlers/loops-status-handler.cjs +5 -2
- package/.claude/helpers/handlers/route-handler.cjs +13 -6
- package/.claude/helpers/handlers/session-handler.cjs +11 -4
- package/.claude/helpers/handlers/session-restore-handler.cjs +21 -11
- package/.claude/helpers/handlers/task-handler.cjs +13 -5
- package/.claude/helpers/intelligence.cjs +7 -2
- package/.claude/helpers/loop-tracker.cjs +15 -3
- package/.claude/helpers/memory.cjs +6 -1
- package/.claude/helpers/router.cjs +5 -2
- package/.claude/helpers/session.cjs +2 -0
- package/.claude/helpers/statusline.cjs +10 -2
- package/.claude/helpers/utils/micro-agents.cjs +20 -4
- package/.claude/scheduled_tasks.lock +1 -1
- package/.claude/settings.json +92 -1
- package/.claude/skills/mastermind/_protocol.md +25 -15
- package/.claude/skills/mastermind/architect.md +3 -3
- package/.claude/skills/mastermind/autodev.md +4 -2
- package/.claude/skills/mastermind/idea.md +10 -0
- package/.claude/skills/mastermind/ops.md +3 -3
- package/.claude/skills/mastermind/runorg.md +153 -86
- package/package.json +20 -3
- package/packages/@monomind/cli/dist/src/agents/registry-builder.js +2 -0
- package/packages/@monomind/cli/dist/src/autopilot-state.js +10 -5
- package/packages/@monomind/cli/dist/src/benchmarks/benchmark-runner.js +13 -0
- package/packages/@monomind/cli/dist/src/benchmarks/metric-evaluators.js +20 -9
- package/packages/@monomind/cli/dist/src/browser/actions.js +10 -3
- package/packages/@monomind/cli/dist/src/browser/browser.js +12 -2
- package/packages/@monomind/cli/dist/src/browser/cdp.js +21 -3
- package/packages/@monomind/cli/dist/src/browser/har.js +27 -5
- package/packages/@monomind/cli/dist/src/commands/agent.js +11 -8
- package/packages/@monomind/cli/dist/src/commands/analyze.js +36 -21
- package/packages/@monomind/cli/dist/src/commands/autopilot.js +12 -4
- package/packages/@monomind/cli/dist/src/commands/benchmark.js +51 -8
- package/packages/@monomind/cli/dist/src/commands/browse.js +5 -2
- package/packages/@monomind/cli/dist/src/commands/claims.js +29 -11
- package/packages/@monomind/cli/dist/src/commands/cleanup.js +25 -5
- package/packages/@monomind/cli/dist/src/commands/config.js +15 -7
- package/packages/@monomind/cli/dist/src/commands/daemon.js +6 -0
- package/packages/@monomind/cli/dist/src/commands/deployment.js +34 -19
- package/packages/@monomind/cli/dist/src/commands/doctor.js +97 -20
- package/packages/@monomind/cli/dist/src/commands/guidance.js +15 -2
- package/packages/@monomind/cli/dist/src/commands/hive-mind.js +37 -14
- package/packages/@monomind/cli/dist/src/commands/hooks.js +42 -25
- package/packages/@monomind/cli/dist/src/commands/init.js +9 -4
- package/packages/@monomind/cli/dist/src/commands/issues.js +29 -26
- package/packages/@monomind/cli/dist/src/commands/mcp.js +11 -5
- package/packages/@monomind/cli/dist/src/commands/memory.js +10 -0
- package/packages/@monomind/cli/dist/src/commands/migrate.js +5 -5
- package/packages/@monomind/cli/dist/src/commands/monograph.js +18 -5
- package/packages/@monomind/cli/dist/src/commands/monovector/backup.js +8 -2
- package/packages/@monomind/cli/dist/src/commands/monovector/benchmark.js +20 -7
- package/packages/@monomind/cli/dist/src/commands/monovector/import.js +15 -0
- package/packages/@monomind/cli/dist/src/commands/monovector/migrate.js +4 -1
- package/packages/@monomind/cli/dist/src/commands/monovector/optimize.js +11 -0
- package/packages/@monomind/cli/dist/src/commands/monovector/setup.js +11 -1
- package/packages/@monomind/cli/dist/src/commands/neural.js +1 -1
- package/packages/@monomind/cli/dist/src/commands/performance.js +20 -7
- package/packages/@monomind/cli/dist/src/commands/platforms.js +90 -8
- package/packages/@monomind/cli/dist/src/commands/plugins.js +12 -5
- package/packages/@monomind/cli/dist/src/commands/process.js +33 -10
- package/packages/@monomind/cli/dist/src/commands/progress.js +5 -3
- package/packages/@monomind/cli/dist/src/commands/providers.js +5 -5
- package/packages/@monomind/cli/dist/src/commands/replay.js +8 -2
- package/packages/@monomind/cli/dist/src/commands/route.js +27 -7
- package/packages/@monomind/cli/dist/src/commands/security.js +4 -0
- package/packages/@monomind/cli/dist/src/commands/session.js +12 -1
- package/packages/@monomind/cli/dist/src/commands/start.js +11 -4
- package/packages/@monomind/cli/dist/src/commands/status.js +7 -4
- package/packages/@monomind/cli/dist/src/commands/swarm.js +27 -13
- package/packages/@monomind/cli/dist/src/commands/task.js +26 -11
- package/packages/@monomind/cli/dist/src/commands/tokens.js +7 -2
- package/packages/@monomind/cli/dist/src/commands/transfer-store.js +36 -22
- package/packages/@monomind/cli/dist/src/commands/update.js +15 -3
- package/packages/@monomind/cli/dist/src/commands/workflow.js +39 -6
- package/packages/@monomind/cli/dist/src/consensus/audit-writer.js +18 -7
- package/packages/@monomind/cli/dist/src/consensus/vote-signer.js +25 -8
- package/packages/@monomind/cli/dist/src/index.js +7 -3
- package/packages/@monomind/cli/dist/src/init/executor.js +14 -11
- package/packages/@monomind/cli/dist/src/init/shared-instructions-generator.js +20 -4
- package/packages/@monomind/cli/dist/src/init/statusline-generator.js +36 -15
- package/packages/@monomind/cli/dist/src/mcp-tools/a2a-tools.js +98 -13
- package/packages/@monomind/cli/dist/src/mcp-tools/agent-tools.js +16 -3
- package/packages/@monomind/cli/dist/src/mcp-tools/analyze-tools.js +80 -17
- package/packages/@monomind/cli/dist/src/mcp-tools/browser-tools.js +84 -22
- package/packages/@monomind/cli/dist/src/mcp-tools/claims-tools.js +35 -7
- package/packages/@monomind/cli/dist/src/mcp-tools/config-tools.js +82 -17
- package/packages/@monomind/cli/dist/src/mcp-tools/coordination-tools.js +37 -4
- package/packages/@monomind/cli/dist/src/mcp-tools/daa-tools.js +49 -7
- package/packages/@monomind/cli/dist/src/mcp-tools/embeddings-tools.js +45 -18
- package/packages/@monomind/cli/dist/src/mcp-tools/github-tools.js +75 -25
- package/packages/@monomind/cli/dist/src/mcp-tools/guidance-tools.js +32 -10
- package/packages/@monomind/cli/dist/src/mcp-tools/hive-mind-tools.js +91 -20
- package/packages/@monomind/cli/dist/src/mcp-tools/hooks-tools.js +188 -29
- package/packages/@monomind/cli/dist/src/mcp-tools/memory-tools.js +25 -7
- package/packages/@monomind/cli/dist/src/mcp-tools/monograph-compat.js +11 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/monograph-tools.js +148 -26
- package/packages/@monomind/cli/dist/src/mcp-tools/neural-tools.js +44 -9
- package/packages/@monomind/cli/dist/src/mcp-tools/performance-tools.js +45 -10
- package/packages/@monomind/cli/dist/src/mcp-tools/progress-tools.js +7 -4
- package/packages/@monomind/cli/dist/src/mcp-tools/request-tracker.js +15 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/security-tools.js +61 -9
- package/packages/@monomind/cli/dist/src/mcp-tools/session-tools.js +45 -14
- package/packages/@monomind/cli/dist/src/mcp-tools/swarm-tools.js +15 -3
- package/packages/@monomind/cli/dist/src/mcp-tools/system-tools.js +14 -7
- package/packages/@monomind/cli/dist/src/mcp-tools/task-tools.js +52 -10
- package/packages/@monomind/cli/dist/src/mcp-tools/terminal-tools.js +40 -6
- package/packages/@monomind/cli/dist/src/mcp-tools/transfer-tools.js +37 -4
- package/packages/@monomind/cli/dist/src/mcp-tools/workflow-tools.js +29 -6
- package/packages/@monomind/cli/dist/src/memory/ewc-consolidation.js +26 -10
- package/packages/@monomind/cli/dist/src/memory/intelligence.js +80 -19
- package/packages/@monomind/cli/dist/src/memory/memory-bridge.js +21 -2
- package/packages/@monomind/cli/dist/src/memory/memory-initializer.js +67 -3
- package/packages/@monomind/cli/dist/src/memory/sona-optimizer.js +14 -4
- package/packages/@monomind/cli/dist/src/monovector/command-outcomes.js +43 -7
- package/packages/@monomind/cli/dist/src/monovector/coverage-router.js +8 -4
- package/packages/@monomind/cli/dist/src/monovector/coverage-tools.js +6 -3
- package/packages/@monomind/cli/dist/src/monovector/diff-classifier.js +13 -0
- package/packages/@monomind/cli/dist/src/monovector/route-outcomes.d.ts +2 -1
- package/packages/@monomind/cli/dist/src/monovector/route-outcomes.js +46 -4
- package/packages/@monomind/cli/dist/src/plugins/manager.js +8 -3
- package/packages/@monomind/cli/dist/src/plugins/store/discovery.js +46 -2
- package/packages/@monomind/cli/dist/src/plugins/store/search.js +5 -4
- package/packages/@monomind/cli/dist/src/production/circuit-breaker.js +17 -3
- package/packages/@monomind/cli/dist/src/production/error-handler.js +3 -0
- package/packages/@monomind/cli/dist/src/production/monitoring.js +20 -3
- package/packages/@monomind/cli/dist/src/production/rate-limiter.js +13 -4
- package/packages/@monomind/cli/dist/src/production/retry.js +17 -9
- package/packages/@monomind/cli/dist/src/routing/embed-worker.js +6 -2
- package/packages/@monomind/cli/dist/src/routing/embedder.js +0 -0
- package/packages/@monomind/cli/dist/src/routing/llm-caller.js +13 -2
- package/packages/@monomind/cli/dist/src/routing/route-layer-factory.js +18 -3
- package/packages/@monomind/cli/dist/src/services/claim-service.d.ts +1 -0
- package/packages/@monomind/cli/dist/src/services/claim-service.js +8 -0
- package/packages/@monomind/cli/dist/src/services/config-file-manager.js +14 -2
- package/packages/@monomind/cli/dist/src/services/headless-worker-executor.js +18 -2
- package/packages/@monomind/cli/dist/src/services/worker-daemon.js +53 -12
- package/packages/@monomind/cli/dist/src/transfer/anonymization/index.d.ts +0 -3
- package/packages/@monomind/cli/dist/src/transfer/anonymization/index.js +16 -1
- package/packages/@monomind/cli/dist/src/transfer/export.js +8 -0
- package/packages/@monomind/cli/dist/src/transfer/ipfs/upload.js +33 -3
- package/packages/@monomind/cli/dist/src/transfer/serialization/cfp.js +9 -3
- package/packages/@monomind/cli/dist/src/transfer/storage/gcs.js +37 -3
- package/packages/@monomind/cli/dist/src/transfer/store/discovery.js +45 -3
- package/packages/@monomind/cli/dist/src/transfer/store/download.js +5 -0
- package/packages/@monomind/cli/dist/src/transfer/store/publish.js +13 -1
- package/packages/@monomind/cli/dist/src/transfer/store/registry.d.ts +8 -0
- package/packages/@monomind/cli/dist/src/transfer/store/registry.js +30 -5
- package/packages/@monomind/cli/dist/src/transfer/store/search.js +20 -5
- package/packages/@monomind/cli/dist/src/update/checker.js +59 -7
- package/packages/@monomind/cli/dist/src/update/executor.js +50 -3
- package/packages/@monomind/cli/dist/src/update/index.js +18 -1
- package/packages/@monomind/cli/dist/src/update/rate-limiter.d.ts +6 -0
- package/packages/@monomind/cli/dist/src/update/rate-limiter.js +79 -7
- package/packages/@monomind/cli/dist/src/update/validator.js +52 -1
- 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,
|
|
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
|
-
*
|
|
33
|
+
* Maximum directory recursion depth for size calculation.
|
|
34
|
+
* Prevents stack overflow on deeply-nested or circular-symlink trees.
|
|
34
35
|
*/
|
|
35
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
326
|
-
: 'npm update
|
|
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
|
-
|
|
392
|
-
join(
|
|
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
|
-
|
|
395
|
-
|
|
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
|
|
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
|
|
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}
|
|
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
|
}
|