mstro-app 0.3.7 → 0.3.9
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/README.md +4 -8
- package/bin/mstro.js +54 -15
- package/dist/server/cli/headless/claude-invoker.d.ts.map +1 -1
- package/dist/server/cli/headless/claude-invoker.js +18 -9
- package/dist/server/cli/headless/claude-invoker.js.map +1 -1
- package/dist/server/cli/headless/headless-logger.d.ts +10 -0
- package/dist/server/cli/headless/headless-logger.d.ts.map +1 -0
- package/dist/server/cli/headless/headless-logger.js +66 -0
- package/dist/server/cli/headless/headless-logger.js.map +1 -0
- package/dist/server/cli/headless/mcp-config.d.ts.map +1 -1
- package/dist/server/cli/headless/mcp-config.js +6 -5
- package/dist/server/cli/headless/mcp-config.js.map +1 -1
- package/dist/server/cli/headless/runner.d.ts.map +1 -1
- package/dist/server/cli/headless/runner.js +4 -0
- package/dist/server/cli/headless/runner.js.map +1 -1
- package/dist/server/cli/headless/stall-assessor.d.ts +21 -0
- package/dist/server/cli/headless/stall-assessor.d.ts.map +1 -1
- package/dist/server/cli/headless/stall-assessor.js +74 -20
- package/dist/server/cli/headless/stall-assessor.js.map +1 -1
- package/dist/server/cli/headless/tool-watchdog.d.ts +0 -12
- package/dist/server/cli/headless/tool-watchdog.d.ts.map +1 -1
- package/dist/server/cli/headless/tool-watchdog.js +30 -9
- package/dist/server/cli/headless/tool-watchdog.js.map +1 -1
- package/dist/server/cli/headless/types.d.ts +8 -1
- package/dist/server/cli/headless/types.d.ts.map +1 -1
- package/dist/server/cli/improvisation-session-manager.d.ts +16 -0
- package/dist/server/cli/improvisation-session-manager.d.ts.map +1 -1
- package/dist/server/cli/improvisation-session-manager.js +94 -11
- package/dist/server/cli/improvisation-session-manager.js.map +1 -1
- package/dist/server/index.js +0 -4
- package/dist/server/index.js.map +1 -1
- package/dist/server/mcp/bouncer-cli.d.ts +3 -0
- package/dist/server/mcp/bouncer-cli.d.ts.map +1 -0
- package/dist/server/mcp/bouncer-cli.js +54 -0
- package/dist/server/mcp/bouncer-cli.js.map +1 -0
- package/dist/server/mcp/bouncer-integration.d.ts +2 -0
- package/dist/server/mcp/bouncer-integration.d.ts.map +1 -1
- package/dist/server/mcp/bouncer-integration.js +55 -39
- package/dist/server/mcp/bouncer-integration.js.map +1 -1
- package/dist/server/mcp/bouncer-sandbox.d.ts +60 -0
- package/dist/server/mcp/bouncer-sandbox.d.ts.map +1 -0
- package/dist/server/mcp/bouncer-sandbox.js +182 -0
- package/dist/server/mcp/bouncer-sandbox.js.map +1 -0
- package/dist/server/mcp/security-patterns.d.ts +6 -12
- package/dist/server/mcp/security-patterns.d.ts.map +1 -1
- package/dist/server/mcp/security-patterns.js +197 -10
- package/dist/server/mcp/security-patterns.js.map +1 -1
- package/dist/server/services/plan/composer.d.ts +4 -0
- package/dist/server/services/plan/composer.d.ts.map +1 -0
- package/dist/server/services/plan/composer.js +181 -0
- package/dist/server/services/plan/composer.js.map +1 -0
- package/dist/server/services/plan/dependency-resolver.d.ts +28 -0
- package/dist/server/services/plan/dependency-resolver.d.ts.map +1 -0
- package/dist/server/services/plan/dependency-resolver.js +152 -0
- package/dist/server/services/plan/dependency-resolver.js.map +1 -0
- package/dist/server/services/plan/executor.d.ts +91 -0
- package/dist/server/services/plan/executor.d.ts.map +1 -0
- package/dist/server/services/plan/executor.js +545 -0
- package/dist/server/services/plan/executor.js.map +1 -0
- package/dist/server/services/plan/parser.d.ts +11 -0
- package/dist/server/services/plan/parser.d.ts.map +1 -0
- package/dist/server/services/plan/parser.js +415 -0
- package/dist/server/services/plan/parser.js.map +1 -0
- package/dist/server/services/plan/state-reconciler.d.ts +2 -0
- package/dist/server/services/plan/state-reconciler.d.ts.map +1 -0
- package/dist/server/services/plan/state-reconciler.js +105 -0
- package/dist/server/services/plan/state-reconciler.js.map +1 -0
- package/dist/server/services/plan/types.d.ts +120 -0
- package/dist/server/services/plan/types.d.ts.map +1 -0
- package/dist/server/services/plan/types.js +4 -0
- package/dist/server/services/plan/types.js.map +1 -0
- package/dist/server/services/plan/watcher.d.ts +14 -0
- package/dist/server/services/plan/watcher.d.ts.map +1 -0
- package/dist/server/services/plan/watcher.js +69 -0
- package/dist/server/services/plan/watcher.js.map +1 -0
- package/dist/server/services/websocket/file-explorer-handlers.js +20 -0
- package/dist/server/services/websocket/file-explorer-handlers.js.map +1 -1
- package/dist/server/services/websocket/handler.d.ts +0 -1
- package/dist/server/services/websocket/handler.d.ts.map +1 -1
- package/dist/server/services/websocket/handler.js +28 -2
- package/dist/server/services/websocket/handler.js.map +1 -1
- package/dist/server/services/websocket/plan-handlers.d.ts +6 -0
- package/dist/server/services/websocket/plan-handlers.d.ts.map +1 -0
- package/dist/server/services/websocket/plan-handlers.js +494 -0
- package/dist/server/services/websocket/plan-handlers.js.map +1 -0
- package/dist/server/services/websocket/quality-handlers.d.ts +4 -0
- package/dist/server/services/websocket/quality-handlers.d.ts.map +1 -0
- package/dist/server/services/websocket/quality-handlers.js +470 -0
- package/dist/server/services/websocket/quality-handlers.js.map +1 -0
- package/dist/server/services/websocket/quality-persistence.d.ts +45 -0
- package/dist/server/services/websocket/quality-persistence.d.ts.map +1 -0
- package/dist/server/services/websocket/quality-persistence.js +187 -0
- package/dist/server/services/websocket/quality-persistence.js.map +1 -0
- package/dist/server/services/websocket/quality-service.d.ts +54 -0
- package/dist/server/services/websocket/quality-service.d.ts.map +1 -0
- package/dist/server/services/websocket/quality-service.js +816 -0
- package/dist/server/services/websocket/quality-service.js.map +1 -0
- package/dist/server/services/websocket/session-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/session-handlers.js +23 -0
- package/dist/server/services/websocket/session-handlers.js.map +1 -1
- package/dist/server/services/websocket/types.d.ts +2 -2
- package/dist/server/services/websocket/types.d.ts.map +1 -1
- package/package.json +3 -2
- package/server/cli/headless/claude-invoker.ts +21 -9
- package/server/cli/headless/headless-logger.ts +78 -0
- package/server/cli/headless/mcp-config.ts +6 -5
- package/server/cli/headless/runner.ts +4 -0
- package/server/cli/headless/stall-assessor.ts +101 -20
- package/server/cli/headless/tool-watchdog.ts +18 -9
- package/server/cli/headless/types.ts +10 -1
- package/server/cli/improvisation-session-manager.ts +118 -11
- package/server/index.ts +0 -4
- package/server/mcp/bouncer-cli.ts +73 -0
- package/server/mcp/bouncer-integration.ts +66 -44
- package/server/mcp/bouncer-sandbox.ts +214 -0
- package/server/mcp/security-patterns.ts +206 -10
- package/server/services/plan/composer.ts +199 -0
- package/server/services/plan/dependency-resolver.ts +179 -0
- package/server/services/plan/executor.ts +604 -0
- package/server/services/plan/parser.ts +459 -0
- package/server/services/plan/state-reconciler.ts +132 -0
- package/server/services/plan/types.ts +164 -0
- package/server/services/plan/watcher.ts +73 -0
- package/server/services/websocket/file-explorer-handlers.ts +20 -0
- package/server/services/websocket/handler.ts +28 -2
- package/server/services/websocket/plan-handlers.ts +592 -0
- package/server/services/websocket/quality-handlers.ts +570 -0
- package/server/services/websocket/quality-persistence.ts +250 -0
- package/server/services/websocket/quality-service.ts +975 -0
- package/server/services/websocket/session-handlers.ts +26 -0
- package/server/services/websocket/types.ts +62 -2
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
// Copyright (c) 2025-present Mstro, Inc. All rights reserved.
|
|
2
2
|
// Licensed under the MIT License. See LICENSE file for details.
|
|
3
|
+
/**
|
|
4
|
+
* Security Patterns - Single Source of Truth
|
|
5
|
+
*
|
|
6
|
+
* Consolidated pattern definitions for fast-path security checks.
|
|
7
|
+
* All pattern-based security decisions use this module to avoid duplication.
|
|
8
|
+
*
|
|
9
|
+
* PHILOSOPHY:
|
|
10
|
+
* - Most operations should be evaluated by CONTEXT, not by path or extension
|
|
11
|
+
* - Only truly catastrophic operations (rm -rf /, fork bombs) are auto-denied
|
|
12
|
+
* - Sensitive operations (system paths, credentials) get AI review with context
|
|
13
|
+
* - The question is: "Does this operation make sense given user intent?"
|
|
14
|
+
*/
|
|
15
|
+
import { resolve } from 'node:path';
|
|
3
16
|
/**
|
|
4
17
|
* Sensitive paths that require AI context review
|
|
5
18
|
* These aren't auto-denied - they need context analysis to determine intent
|
|
@@ -61,7 +74,16 @@ export const CRITICAL_THREATS = [
|
|
|
61
74
|
{
|
|
62
75
|
pattern: /chmod\s+000\s+\//i,
|
|
63
76
|
reason: 'Attempting to make system directories inaccessible'
|
|
64
|
-
}
|
|
77
|
+
},
|
|
78
|
+
// Reverse shells - never legitimate in a dev workflow
|
|
79
|
+
{
|
|
80
|
+
pattern: /\/dev\/tcp\//i,
|
|
81
|
+
reason: 'Reverse shell via /dev/tcp - classic backdoor technique'
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
pattern: /\bnc\b.*-[elp].*\b\d+\b/i,
|
|
85
|
+
reason: 'Netcat listener/reverse shell - common backdoor technique'
|
|
86
|
+
},
|
|
65
87
|
// NOTE: curl|bash is NOT here - it goes to Haiku for context review
|
|
66
88
|
// The question is "did a bad actor inject this?" not "is curl|bash dangerous?"
|
|
67
89
|
];
|
|
@@ -126,12 +148,96 @@ export const NEEDS_AI_REVIEW = [
|
|
|
126
148
|
pattern: /rm\s+-rf/i,
|
|
127
149
|
reason: 'Recursive deletion - verify target matches user intent'
|
|
128
150
|
},
|
|
151
|
+
// Data exfiltration patterns — piping data to network tools
|
|
152
|
+
{
|
|
153
|
+
pattern: /\|\s*(nc|netcat|ncat)\b/i,
|
|
154
|
+
reason: 'Pipe to netcat - potential data exfiltration'
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
pattern: /\bscp\b.*@/i,
|
|
158
|
+
reason: 'SCP to remote host - potential data exfiltration'
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
pattern: /\|\s*curl\b/i,
|
|
162
|
+
reason: 'Pipe to curl - potential data exfiltration'
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
pattern: /curl\b.*-d\s*@/i,
|
|
166
|
+
reason: 'Curl with file upload - potential data exfiltration'
|
|
167
|
+
},
|
|
129
168
|
// ALL Write/Edit operations that aren't to /tmp go through context review
|
|
130
169
|
// This is the key change: we review based on context, not blanket allow/deny
|
|
131
170
|
{
|
|
132
171
|
pattern: /^(Write|Edit):\s*(?!\/tmp\/|\/var\/tmp\/)/i,
|
|
133
172
|
reason: 'File modification - verify aligns with user request'
|
|
134
173
|
},
|
|
174
|
+
// Reverse shells and bind shells — network-connected interactive shells
|
|
175
|
+
{
|
|
176
|
+
pattern: /\/dev\/tcp\//i,
|
|
177
|
+
reason: 'Potential reverse shell via /dev/tcp'
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
pattern: /\b(nc|netcat|ncat)\b.*-e\s/i,
|
|
181
|
+
reason: 'Netcat with -e flag - potential reverse shell'
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
pattern: /\bsocket\b.*\bconnect\b.*\b(dup2|subprocess|exec)\b/i,
|
|
185
|
+
reason: 'Programmatic reverse shell pattern (socket+connect+exec)'
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
pattern: /\bperl\b.*\bsocket\b.*\bexec\b/i,
|
|
189
|
+
reason: 'Perl reverse shell pattern'
|
|
190
|
+
},
|
|
191
|
+
// Encoded/obfuscated payloads piped to shell or eval
|
|
192
|
+
{
|
|
193
|
+
pattern: /\b(base64|base32)\b.*-d.*\|\s*(bash|sh)\b/i,
|
|
194
|
+
reason: 'Decoded payload piped to shell - obfuscated command execution'
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
pattern: /\\x[0-9a-f]{2}.*\|\s*(bash|sh)\b/i,
|
|
198
|
+
reason: 'Hex-encoded payload piped to shell'
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
pattern: /\bexec\b.*\b(base64|b64decode)\b/i,
|
|
202
|
+
reason: 'Exec with base64 decoding - obfuscated code execution'
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
pattern: /\bprintf\b.*\\x[0-9a-f].*\|\s*(bash|sh)\b/i,
|
|
206
|
+
reason: 'Printf hex payload piped to shell'
|
|
207
|
+
},
|
|
208
|
+
// Cloud metadata / SSRF — accessing cloud instance credentials
|
|
209
|
+
{
|
|
210
|
+
pattern: /169\.254\.169\.254/i,
|
|
211
|
+
reason: 'AWS/Azure IMDS access - potential credential theft'
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
pattern: /metadata\.google\.internal/i,
|
|
215
|
+
reason: 'GCP metadata access - potential credential theft'
|
|
216
|
+
},
|
|
217
|
+
// Persistence — writing to shell profiles, cron, authorized_keys via echo/append
|
|
218
|
+
{
|
|
219
|
+
pattern: />>\s*~?\/?.*\/(authorized_keys|\.bashrc|\.bash_profile|\.zshrc|\.profile)/i,
|
|
220
|
+
reason: 'Appending to sensitive file - potential persistence mechanism'
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
pattern: /\bld\.so\.preload\b/i,
|
|
224
|
+
reason: 'LD_PRELOAD injection - shared library hijacking'
|
|
225
|
+
},
|
|
226
|
+
// wget with file upload
|
|
227
|
+
{
|
|
228
|
+
pattern: /wget\b.*--post-file/i,
|
|
229
|
+
reason: 'wget file upload - potential data exfiltration'
|
|
230
|
+
},
|
|
231
|
+
// pip install from custom index (supply chain attack)
|
|
232
|
+
{
|
|
233
|
+
pattern: /pip\b.*--index-url\s+https?:\/\/(?!pypi\.org)/i,
|
|
234
|
+
reason: 'pip install from non-PyPI index - potential supply chain attack'
|
|
235
|
+
},
|
|
236
|
+
// MCP server manipulation
|
|
237
|
+
{
|
|
238
|
+
pattern: /\bclaude\b.*\bmcp\b.*\badd\b/i,
|
|
239
|
+
reason: 'Adding MCP server - verify source is trusted'
|
|
240
|
+
},
|
|
135
241
|
];
|
|
136
242
|
/**
|
|
137
243
|
* Check if operation matches any pattern in array
|
|
@@ -144,11 +250,64 @@ export function matchesPattern(operation, patterns) {
|
|
|
144
250
|
}
|
|
145
251
|
return null;
|
|
146
252
|
}
|
|
253
|
+
/**
|
|
254
|
+
* Normalize file paths in Write/Edit/Read operations to resolve .. traversal.
|
|
255
|
+
* Prevents path traversal attacks like "Write: /home/user/../../etc/passwd"
|
|
256
|
+
* from matching safe home-directory patterns.
|
|
257
|
+
*/
|
|
258
|
+
export function normalizeOperation(operation) {
|
|
259
|
+
const match = operation.match(/^(Write|Edit|Read):\s*(\S+)/i);
|
|
260
|
+
if (match?.[2].includes('..')) {
|
|
261
|
+
const [, tool, rawPath] = match;
|
|
262
|
+
const normalizedPath = resolve(rawPath);
|
|
263
|
+
return `${tool}: ${normalizedPath}`;
|
|
264
|
+
}
|
|
265
|
+
return operation;
|
|
266
|
+
}
|
|
267
|
+
/** Check if a Bash command contains chain operators that could hide dangerous ops after a safe prefix. */
|
|
268
|
+
function containsChainOperators(operation) {
|
|
269
|
+
const commandPart = operation.replace(/^Bash:\s*/i, '');
|
|
270
|
+
return /;|&&|\|\||\n/.test(commandPart);
|
|
271
|
+
}
|
|
272
|
+
/** Check if a Bash command pipes output to known exfiltration/network tools or shells. */
|
|
273
|
+
function containsDangerousPipe(operation) {
|
|
274
|
+
const commandPart = operation.replace(/^Bash:\s*/i, '');
|
|
275
|
+
return /\|\s*(nc|netcat|ncat|curl|wget|scp|bash|sh)\b/i.test(commandPart);
|
|
276
|
+
}
|
|
277
|
+
/** Check if a Bash command redirects output to sensitive paths (append or overwrite). */
|
|
278
|
+
function containsSensitiveRedirect(operation) {
|
|
279
|
+
const commandPart = operation.replace(/^Bash:\s*/i, '');
|
|
280
|
+
return />>?\s*~?\/?.*\/(authorized_keys|\.bashrc|\.bash_profile|\.zshrc|\.profile|\.ssh\/|\.aws\/|\.gnupg\/|ld\.so\.preload|crontab|sudoers)/i.test(commandPart)
|
|
281
|
+
|| />>?\s*\/etc\//i.test(commandPart);
|
|
282
|
+
}
|
|
283
|
+
/** Check if a Bash command contains subshell or backtick expansion (not simple ${VAR}). */
|
|
284
|
+
function containsBashExpansion(operation) {
|
|
285
|
+
const commandPart = operation.replace(/^Bash:\s*/i, '');
|
|
286
|
+
return /`[^`]+`/.test(commandPart) || /\$\([^)]+\)/.test(commandPart);
|
|
287
|
+
}
|
|
288
|
+
/** Check if a Bash command contains any form of shell expansion: ${VAR}, $(...), or backticks. */
|
|
289
|
+
function containsAnyExpansion(operation) {
|
|
290
|
+
const cmd = operation.replace(/^Bash:\s*/i, '');
|
|
291
|
+
return /\$\{[^}]+\}/.test(cmd) || /\$\([^)]+\)/.test(cmd) || /`[^`]+`/.test(cmd);
|
|
292
|
+
}
|
|
293
|
+
/** Check if expansion is safely used as an argument to a known-safe command prefix.
|
|
294
|
+
* e.g., "echo ${HOME}" or "cat ${FILE}" — the expansion can't change the command itself. */
|
|
295
|
+
function isSafeExpansionUse(operation) {
|
|
296
|
+
const cmd = operation.replace(/^Bash:\s*/i, '').trim();
|
|
297
|
+
// If the expansion IS the command (first token), it's never safe
|
|
298
|
+
if (/^(\$\{|\$\(|`)/.test(cmd))
|
|
299
|
+
return false;
|
|
300
|
+
// Safe command prefixes where expansion as an argument is harmless
|
|
301
|
+
const safePrefix = /^(echo|printf|cat|ls|pwd|whoami|date|env|printenv|test|true|false)\s/i;
|
|
302
|
+
return safePrefix.test(cmd);
|
|
303
|
+
}
|
|
147
304
|
/**
|
|
148
305
|
* Determine if operation requires AI context review
|
|
149
306
|
*
|
|
150
307
|
* The philosophy here is:
|
|
151
|
-
* -
|
|
308
|
+
* - SENSITIVE_PATHS: Always require review (credentials, system configs)
|
|
309
|
+
* - SAFE_OPERATIONS: No review needed, UNLESS the bash command contains
|
|
310
|
+
* chain operators, dangerous pipes, or subshell/backtick expansion
|
|
152
311
|
* - CRITICAL_THREATS: Auto-deny, no review (catastrophic operations)
|
|
153
312
|
* - Everything else: AI reviews context to determine if it matches user intent
|
|
154
313
|
*/
|
|
@@ -162,18 +321,43 @@ const SAFE_RM_PATTERNS = [
|
|
|
162
321
|
/rm\s+-rf\s+(\.\/)?__pycache__($|\s)/i,
|
|
163
322
|
];
|
|
164
323
|
export function requiresAIReview(operation) {
|
|
165
|
-
|
|
324
|
+
// Normalize paths to prevent .. traversal bypass
|
|
325
|
+
const op = normalizeOperation(operation);
|
|
326
|
+
// Check sensitive paths BEFORE safe operations — prevents home-dir
|
|
327
|
+
// safe pattern from masking .ssh, .aws, .bashrc, etc.
|
|
328
|
+
if (matchesPattern(op, SENSITIVE_PATHS))
|
|
329
|
+
return true;
|
|
330
|
+
// Bash commands with any shell expansion (${VAR}, $(...), backticks) are
|
|
331
|
+
// opaque — the bouncer can't predict what they expand to at runtime.
|
|
332
|
+
// Route to AI review BEFORE checking CRITICAL_THREATS or SAFE_OPERATIONS,
|
|
333
|
+
// UNLESS the command is clearly safe (expansion is just an argument to a
|
|
334
|
+
// known-safe prefix like "echo ${HOME}").
|
|
335
|
+
if (/^Bash:/i.test(op) && containsAnyExpansion(op) && !isSafeExpansionUse(op)) {
|
|
336
|
+
return true;
|
|
337
|
+
}
|
|
338
|
+
if (matchesPattern(op, SAFE_OPERATIONS)) {
|
|
339
|
+
// Safe bash commands must not contain chain operators, dangerous pipes,
|
|
340
|
+
// or subshell/backtick expansion that could hide dangerous operations.
|
|
341
|
+
// A safe prefix (e.g., "git clone") with chain operators (&&, ;, ||)
|
|
342
|
+
// means the full command isn't necessarily safe — route to AI review.
|
|
343
|
+
if (/^Bash:/i.test(op) && (containsChainOperators(op) ||
|
|
344
|
+
containsDangerousPipe(op) ||
|
|
345
|
+
containsBashExpansion(op) ||
|
|
346
|
+
containsSensitiveRedirect(op))) {
|
|
347
|
+
return true;
|
|
348
|
+
}
|
|
166
349
|
return false;
|
|
167
|
-
|
|
350
|
+
}
|
|
351
|
+
if (matchesPattern(op, CRITICAL_THREATS))
|
|
168
352
|
return false;
|
|
169
|
-
if (matchesPattern(
|
|
170
|
-
return !SAFE_RM_PATTERNS.some(p => p.test(
|
|
353
|
+
if (matchesPattern(op, NEEDS_AI_REVIEW)) {
|
|
354
|
+
return !SAFE_RM_PATTERNS.some(p => p.test(op));
|
|
171
355
|
}
|
|
172
|
-
//
|
|
173
|
-
if (/^Bash:/.test(
|
|
174
|
-
if (
|
|
356
|
+
// Glob patterns and script execution are concerning in Bash commands
|
|
357
|
+
if (/^Bash:/.test(op)) {
|
|
358
|
+
if (/\*\*?/.test(op))
|
|
175
359
|
return true;
|
|
176
|
-
if (/^Bash:\s*\.\//.test(
|
|
360
|
+
if (/^Bash:\s*\.\//.test(op))
|
|
177
361
|
return true;
|
|
178
362
|
}
|
|
179
363
|
return false;
|
|
@@ -220,6 +404,9 @@ export function classifyRisk(operation) {
|
|
|
220
404
|
{ pattern: /chmod\s+777/i, reason: 'Dangerous permissions' },
|
|
221
405
|
{ pattern: /(curl|wget).*\|.*(bash|sh)/i, reason: 'Remote code execution' },
|
|
222
406
|
{ pattern: /pkill|killall/i, reason: 'Process termination' },
|
|
407
|
+
{ pattern: /\|\s*(nc|netcat|ncat)\b/i, reason: 'Data exfiltration via netcat' },
|
|
408
|
+
{ pattern: /\bscp\b.*@/i, reason: 'Data exfiltration via SCP' },
|
|
409
|
+
{ pattern: /curl\b.*-d\s*@/i, reason: 'Data exfiltration via curl file upload' },
|
|
223
410
|
];
|
|
224
411
|
for (const pattern of elevatedPatterns) {
|
|
225
412
|
if (pattern.pattern.test(operation)) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"security-patterns.js","sourceRoot":"","sources":["../../../server/mcp/security-patterns.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,gEAAgE;
|
|
1
|
+
{"version":3,"file":"security-patterns.js","sourceRoot":"","sources":["../../../server/mcp/security-patterns.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,gEAAgE;AAEhE;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC;;;GAGG;AACH,MAAM,CAAC,MAAM,eAAe,GAAsB;IAChD,qFAAqF;IACrF,EAAE,OAAO,EAAE,2BAA2B,EAAE,MAAM,EAAE,2CAA2C,EAAE;IAC7F,EAAE,OAAO,EAAE,qDAAqD,EAAE,MAAM,EAAE,sCAAsC,EAAE;IAClH,EAAE,OAAO,EAAE,4BAA4B,EAAE,MAAM,EAAE,qCAAqC,EAAE;IACxF,EAAE,OAAO,EAAE,4BAA4B,EAAE,MAAM,EAAE,gCAAgC,EAAE;IACnF,EAAE,OAAO,EAAE,8BAA8B,EAAE,MAAM,EAAE,mCAAmC,EAAE;IACxF,EAAE,OAAO,EAAE,6DAA6D,EAAE,MAAM,EAAE,4CAA4C,EAAE;IAEhI,uEAAuE;IACvE,EAAE,OAAO,EAAE,+BAA+B,EAAE,MAAM,EAAE,wCAAwC,EAAE;IAC9F,EAAE,OAAO,EAAE,iCAAiC,EAAE,MAAM,EAAE,+BAA+B,EAAE;IACvF,EAAE,OAAO,EAAE,mDAAmD,EAAE,MAAM,EAAE,sCAAsC,EAAE;IAChH,EAAE,OAAO,EAAE,+DAA+D,EAAE,MAAM,EAAE,0CAA0C,EAAE;IAEhI,kEAAkE;IAClE,EAAE,OAAO,EAAE,+EAA+E,EAAE,MAAM,EAAE,oCAAoC,EAAE;CAC3I,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAsB;IACjD,+DAA+D;IAC/D,qEAAqE;IACrE;QACE,OAAO,EAAE,0BAA0B;QACnC,MAAM,EAAE,wEAAwE;KACjF;IACD;QACE,OAAO,EAAE,4BAA4B;QACrC,MAAM,EAAE,6DAA6D;KACtE;IACD;QACE,OAAO,EAAE,qCAAqC;QAC9C,MAAM,EAAE,0DAA0D;KACnE;IACD;QACE,OAAO,EAAE,SAAS;QAClB,MAAM,EAAE,0DAA0D;KACnE;IACD;QACE,OAAO,EAAE,yBAAyB;QAClC,MAAM,EAAE,iEAAiE;KAC1E;IACD;QACE,OAAO,EAAE,qBAAqB;QAC9B,MAAM,EAAE,wDAAwD;KACjE;IACD;QACE,OAAO,EAAE,mBAAmB;QAC5B,MAAM,EAAE,oDAAoD;KAC7D;IACD,sDAAsD;IACtD;QACE,OAAO,EAAE,eAAe;QACxB,MAAM,EAAE,yDAAyD;KAClE;IACD;QACE,OAAO,EAAE,0BAA0B;QACnC,MAAM,EAAE,2DAA2D;KACpE;IACD,oEAAoE;IACpE,+EAA+E;CAChF,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,eAAe,GAAsB;IAChD,oDAAoD;IACpD,EAAE,OAAO,EAAE,SAAS,EAAE;IACtB,EAAE,OAAO,EAAE,SAAS,EAAE;IACtB,EAAE,OAAO,EAAE,SAAS,EAAE;IAEtB,iFAAiF;IACjF,gEAAgE;IAChE,EAAE,OAAO,EAAE,6BAA6B,EAAE,EAAG,0BAA0B;IACvE,EAAE,OAAO,EAAE,4BAA4B,EAAE,EAAI,yBAAyB;IACtE,EAAE,OAAO,EAAE,4BAA4B,EAAE,EAAI,0BAA0B;IACvE,EAAE,OAAO,EAAE,2BAA2B,EAAE,EAAK,yBAAyB;IAEtE,oDAAoD;IACpD,qEAAqE;IACrE,EAAE,OAAO,EAAE,yFAAyF,EAAE;IACtG,EAAE,OAAO,EAAE,yFAAyF,EAAE;IACtG,EAAE,OAAO,EAAE,6DAA6D,EAAE;IAC1E,EAAE,OAAO,EAAE,mFAAmF,EAAE;IAChG,EAAE,OAAO,EAAE,uFAAuF,EAAE;IAEpG,+DAA+D;IAC/D,EAAE,OAAO,EAAE,gDAAgD,EAAE;IAC7D,EAAE,OAAO,EAAE,wCAAwC,EAAE;IACrD,EAAE,OAAO,EAAE,yCAAyC,EAAE;IACtD,EAAE,OAAO,EAAE,2CAA2C,EAAE;IACxD,EAAE,OAAO,EAAE,0CAA0C,EAAE;IACvD,EAAE,OAAO,EAAE,0CAA0C,EAAE;IACvD,EAAE,OAAO,EAAE,+CAA+C,EAAE;IAE5D,uDAAuD;IACvD,EAAE,OAAO,EAAE,2BAA2B,EAAE;IACxC,EAAE,OAAO,EAAE,gCAAgC,EAAE;IAE7C,4DAA4D;IAC5D,EAAE,OAAO,EAAE,2DAA2D,EAAE;CACzE,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,eAAe,GAAsB;IAChD,iCAAiC;IACjC;QACE,OAAO,EAAE,+BAA+B;QACxC,MAAM,EAAE,iEAAiE;KAC1E;IAED,sBAAsB;IACtB;QACE,OAAO,EAAE,OAAO;QAChB,MAAM,EAAE,wDAAwD;KACjE;IAED,8DAA8D;IAC9D;QACE,OAAO,EAAE,WAAW;QACpB,MAAM,EAAE,wDAAwD;KACjE;IAED,4DAA4D;IAC5D;QACE,OAAO,EAAE,0BAA0B;QACnC,MAAM,EAAE,8CAA8C;KACvD;IACD;QACE,OAAO,EAAE,aAAa;QACtB,MAAM,EAAE,kDAAkD;KAC3D;IACD;QACE,OAAO,EAAE,cAAc;QACvB,MAAM,EAAE,4CAA4C;KACrD;IACD;QACE,OAAO,EAAE,iBAAiB;QAC1B,MAAM,EAAE,qDAAqD;KAC9D;IAED,0EAA0E;IAC1E,6EAA6E;IAC7E;QACE,OAAO,EAAE,4CAA4C;QACrD,MAAM,EAAE,qDAAqD;KAC9D;IAED,wEAAwE;IACxE;QACE,OAAO,EAAE,eAAe;QACxB,MAAM,EAAE,sCAAsC;KAC/C;IACD;QACE,OAAO,EAAE,6BAA6B;QACtC,MAAM,EAAE,+CAA+C;KACxD;IACD;QACE,OAAO,EAAE,sDAAsD;QAC/D,MAAM,EAAE,0DAA0D;KACnE;IACD;QACE,OAAO,EAAE,iCAAiC;QAC1C,MAAM,EAAE,4BAA4B;KACrC;IAED,qDAAqD;IACrD;QACE,OAAO,EAAE,4CAA4C;QACrD,MAAM,EAAE,+DAA+D;KACxE;IACD;QACE,OAAO,EAAE,mCAAmC;QAC5C,MAAM,EAAE,oCAAoC;KAC7C;IACD;QACE,OAAO,EAAE,mCAAmC;QAC5C,MAAM,EAAE,uDAAuD;KAChE;IACD;QACE,OAAO,EAAE,4CAA4C;QACrD,MAAM,EAAE,mCAAmC;KAC5C;IAED,+DAA+D;IAC/D;QACE,OAAO,EAAE,qBAAqB;QAC9B,MAAM,EAAE,oDAAoD;KAC7D;IACD;QACE,OAAO,EAAE,6BAA6B;QACtC,MAAM,EAAE,kDAAkD;KAC3D;IAED,iFAAiF;IACjF;QACE,OAAO,EAAE,4EAA4E;QACrF,MAAM,EAAE,+DAA+D;KACxE;IACD;QACE,OAAO,EAAE,sBAAsB;QAC/B,MAAM,EAAE,iDAAiD;KAC1D;IAED,wBAAwB;IACxB;QACE,OAAO,EAAE,sBAAsB;QAC/B,MAAM,EAAE,gDAAgD;KACzD;IAED,sDAAsD;IACtD;QACE,OAAO,EAAE,gDAAgD;QACzD,MAAM,EAAE,iEAAiE;KAC1E;IAED,0BAA0B;IAC1B;QACE,OAAO,EAAE,+BAA+B;QACxC,MAAM,EAAE,8CAA8C;KACvD;CACF,CAAC;AAEF;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,SAAiB,EAAE,QAA2B;IAC3E,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;YACpC,OAAO,OAAO,CAAC;QACjB,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,SAAiB;IAClD,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAC;IAC9D,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9B,MAAM,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,GAAG,KAAK,CAAC;QAChC,MAAM,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QACxC,OAAO,GAAG,IAAI,KAAK,cAAc,EAAE,CAAC;IACtC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,0GAA0G;AAC1G,SAAS,sBAAsB,CAAC,SAAiB;IAC/C,MAAM,WAAW,GAAG,SAAS,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;IACxD,OAAO,cAAc,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;AAC1C,CAAC;AAED,0FAA0F;AAC1F,SAAS,qBAAqB,CAAC,SAAiB;IAC9C,MAAM,WAAW,GAAG,SAAS,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;IACxD,OAAO,gDAAgD,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;AAC5E,CAAC;AAED,yFAAyF;AACzF,SAAS,yBAAyB,CAAC,SAAiB;IAClD,MAAM,WAAW,GAAG,SAAS,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;IACxD,OAAO,uIAAuI,CAAC,IAAI,CAAC,WAAW,CAAC;WAC3J,gBAAgB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;AAC1C,CAAC;AAED,2FAA2F;AAC3F,SAAS,qBAAqB,CAAC,SAAiB;IAC9C,MAAM,WAAW,GAAG,SAAS,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;IACxD,OAAO,SAAS,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,aAAa,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;AACxE,CAAC;AAED,kGAAkG;AAClG,SAAS,oBAAoB,CAAC,SAAiB;IAC7C,MAAM,GAAG,GAAG,SAAS,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;IAChD,OAAO,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACnF,CAAC;AAED;6FAC6F;AAC7F,SAAS,kBAAkB,CAAC,SAAiB;IAC3C,MAAM,GAAG,GAAG,SAAS,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACvD,iEAAiE;IACjE,IAAI,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IAC7C,mEAAmE;IACnE,MAAM,UAAU,GAAG,uEAAuE,CAAC;IAC3F,OAAO,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC9B,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,gBAAgB,GAAG;IACvB,uCAAuC;IACvC,+BAA+B;IAC/B,gCAAgC;IAChC,kCAAkC;IAClC,iCAAiC;IACjC,iCAAiC;IACjC,sCAAsC;CACvC,CAAC;AAEF,MAAM,UAAU,gBAAgB,CAAC,SAAiB;IAChD,iDAAiD;IACjD,MAAM,EAAE,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAC;IAEzC,mEAAmE;IACnE,sDAAsD;IACtD,IAAI,cAAc,CAAC,EAAE,EAAE,eAAe,CAAC;QAAE,OAAO,IAAI,CAAC;IAErD,yEAAyE;IACzE,qEAAqE;IACrE,0EAA0E;IAC1E,yEAAyE;IACzE,0CAA0C;IAC1C,IAAI,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,oBAAoB,CAAC,EAAE,CAAC,IAAI,CAAC,kBAAkB,CAAC,EAAE,CAAC,EAAE,CAAC;QAC9E,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,cAAc,CAAC,EAAE,EAAE,eAAe,CAAC,EAAE,CAAC;QACxC,wEAAwE;QACxE,uEAAuE;QACvE,qEAAqE;QACrE,sEAAsE;QACtE,IAAI,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CACxB,sBAAsB,CAAC,EAAE,CAAC;YAC1B,qBAAqB,CAAC,EAAE,CAAC;YACzB,qBAAqB,CAAC,EAAE,CAAC;YACzB,yBAAyB,CAAC,EAAE,CAAC,CAC9B,EAAE,CAAC;YACF,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,cAAc,CAAC,EAAE,EAAE,gBAAgB,CAAC;QAAE,OAAO,KAAK,CAAC;IAEvD,IAAI,cAAc,CAAC,EAAE,EAAE,eAAe,CAAC,EAAE,CAAC;QACxC,OAAO,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;IACjD,CAAC;IAED,qEAAqE;IACrE,IAAI,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;QACtB,IAAI,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YAAE,OAAO,IAAI,CAAC;QAClC,IAAI,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC;YAAE,OAAO,IAAI,CAAC;IAC5C,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,SAAiB;IAC/C,OAAO,cAAc,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;AACpD,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,YAAY,CAAC,SAAiB;IAK5C,mCAAmC;IACnC,MAAM,cAAc,GAAG,cAAc,CAAC,SAAS,EAAE,gBAAgB,CAAC,CAAC;IACnE,IAAI,cAAc,EAAE,CAAC;QACnB,OAAO;YACL,aAAa,EAAE,IAAI;YACnB,SAAS,EAAE,UAAU;YACrB,OAAO,EAAE,CAAC,cAAc,CAAC,MAAM,IAAI,0BAA0B,CAAC;SAC/D,CAAC;IACJ,CAAC;IAED,4DAA4D;IAC5D,MAAM,aAAa,GAAG,cAAc,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;IACjE,IAAI,aAAa,EAAE,CAAC;QAClB,OAAO;YACL,aAAa,EAAE,KAAK,EAAE,6CAA6C;YACnE,SAAS,EAAE,MAAM;YACjB,OAAO,EAAE,CAAC,aAAa,CAAC,MAAM,IAAI,6CAA6C,CAAC;SACjF,CAAC;IACJ,CAAC;IAED,2CAA2C;IAC3C,MAAM,gBAAgB,GAAsB;QAC1C,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,+BAA+B,EAAE;QAC7D,EAAE,OAAO,EAAE,0BAA0B,EAAE,MAAM,EAAE,mBAAmB,EAAE;QACpE,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,uBAAuB,EAAE;QAC5D,EAAE,OAAO,EAAE,6BAA6B,EAAE,MAAM,EAAE,uBAAuB,EAAE;QAC3E,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,qBAAqB,EAAE;QAC5D,EAAE,OAAO,EAAE,0BAA0B,EAAE,MAAM,EAAE,8BAA8B,EAAE;QAC/E,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,2BAA2B,EAAE;QAC/D,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,EAAE,wCAAwC,EAAE;KACjF,CAAC;IAEF,KAAK,MAAM,OAAO,IAAI,gBAAgB,EAAE,CAAC;QACvC,IAAI,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;YACpC,OAAO;gBACL,aAAa,EAAE,IAAI;gBACnB,SAAS,EAAE,MAAM;gBACjB,OAAO,EAAE,CAAC,OAAO,CAAC,MAAM,IAAI,yBAAyB,CAAC;aACvD,CAAC;QACJ,CAAC;IACH,CAAC;IAED,0DAA0D;IAC1D,4EAA4E;IAC5E,IAAI,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;QAChC,sDAAsD;QACtD,IAAI,cAAc,CAAC,SAAS,EAAE,eAAe,CAAC,EAAE,CAAC;YAC/C,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;QACjE,CAAC;QACD,OAAO;YACL,aAAa,EAAE,IAAI;YACnB,SAAS,EAAE,QAAQ;YACnB,OAAO,EAAE,CAAC,oBAAoB,CAAC;SAChC,CAAC;IACJ,CAAC;IAED,OAAO;QACL,aAAa,EAAE,KAAK;QACpB,SAAS,EAAE,KAAK;QAChB,OAAO,EAAE,EAAE;KACZ,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { HandlerContext } from '../websocket/handler-context.js';
|
|
2
|
+
import type { WSContext } from '../websocket/types.js';
|
|
3
|
+
export declare function handlePlanPrompt(ctx: HandlerContext, ws: WSContext, userPrompt: string, workingDir: string): Promise<void>;
|
|
4
|
+
//# sourceMappingURL=composer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"composer.d.ts","sourceRoot":"","sources":["../../../../server/services/plan/composer.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAkDvD,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,cAAc,EACnB,EAAE,EAAE,SAAS,EACb,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CA+Hf"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
// Copyright (c) 2025-present Mstro, Inc. All rights reserved.
|
|
2
|
+
// Licensed under the MIT License. See LICENSE file for details.
|
|
3
|
+
/**
|
|
4
|
+
* Plan Composer — Handles natural language prompts for PPS creation/editing.
|
|
5
|
+
*
|
|
6
|
+
* When a planPrompt message arrives, this builds a context-enriched prompt
|
|
7
|
+
* against the .pm/ (or legacy .plan/) directory and spawns a scoped
|
|
8
|
+
* HeadlessRunner session to execute it.
|
|
9
|
+
*/
|
|
10
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
import { runWithFileLogger } from '../../cli/headless/headless-logger.js';
|
|
13
|
+
import { HeadlessRunner } from '../../cli/headless/index.js';
|
|
14
|
+
import { getNextId, parsePlanDirectory, resolvePmDir } from './parser.js';
|
|
15
|
+
const PROMPT_TOOL_MESSAGES = {
|
|
16
|
+
Glob: 'Discovering project files...',
|
|
17
|
+
Read: 'Reading project structure...',
|
|
18
|
+
Grep: 'Searching codebase...',
|
|
19
|
+
Write: 'Creating project files...',
|
|
20
|
+
Edit: 'Updating project files...',
|
|
21
|
+
Bash: 'Running commands...',
|
|
22
|
+
};
|
|
23
|
+
function getPromptToolCompleteMessage(event) {
|
|
24
|
+
const input = event.completeInput;
|
|
25
|
+
if (!input)
|
|
26
|
+
return null;
|
|
27
|
+
if (event.toolName === 'Write' && input.file_path) {
|
|
28
|
+
const filename = String(input.file_path).split('/').pop() ?? '';
|
|
29
|
+
return `Created ${filename}`;
|
|
30
|
+
}
|
|
31
|
+
if (event.toolName === 'Edit' && input.file_path) {
|
|
32
|
+
const filename = String(input.file_path).split('/').pop() ?? '';
|
|
33
|
+
return `Updated ${filename}`;
|
|
34
|
+
}
|
|
35
|
+
if (event.toolName === 'Read' && input.file_path) {
|
|
36
|
+
return `Read ${String(input.file_path).split('/').slice(-2).join('/')}`;
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
function createPromptProgressTracker() {
|
|
41
|
+
const seenToolStarts = new Set();
|
|
42
|
+
return (event) => {
|
|
43
|
+
if (event.type === 'tool_start' && event.toolName) {
|
|
44
|
+
if (seenToolStarts.has(event.toolName))
|
|
45
|
+
return null;
|
|
46
|
+
seenToolStarts.add(event.toolName);
|
|
47
|
+
return PROMPT_TOOL_MESSAGES[event.toolName] ?? null;
|
|
48
|
+
}
|
|
49
|
+
if (event.type === 'tool_complete')
|
|
50
|
+
return getPromptToolCompleteMessage(event);
|
|
51
|
+
return null;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function readFileOrEmpty(path) {
|
|
55
|
+
try {
|
|
56
|
+
if (existsSync(path))
|
|
57
|
+
return readFileSync(path, 'utf-8');
|
|
58
|
+
}
|
|
59
|
+
catch { /* skip */ }
|
|
60
|
+
return '';
|
|
61
|
+
}
|
|
62
|
+
export async function handlePlanPrompt(ctx, ws, userPrompt, workingDir) {
|
|
63
|
+
const pmDir = resolvePmDir(workingDir) ?? join(workingDir, '.pm');
|
|
64
|
+
const stateContent = readFileOrEmpty(join(pmDir, 'STATE.md'));
|
|
65
|
+
const projectContent = readFileOrEmpty(join(pmDir, 'project.md'));
|
|
66
|
+
// Compute next available IDs
|
|
67
|
+
const fullState = parsePlanDirectory(workingDir);
|
|
68
|
+
let idInfo = '';
|
|
69
|
+
if (fullState) {
|
|
70
|
+
const nextIS = getNextId(fullState.issues, 'IS');
|
|
71
|
+
const nextBG = getNextId(fullState.issues, 'BG');
|
|
72
|
+
const nextEP = getNextId(fullState.issues, 'EP');
|
|
73
|
+
idInfo = `Next available IDs: ${nextIS}, ${nextBG}, ${nextEP}`;
|
|
74
|
+
}
|
|
75
|
+
// Read existing epic files to provide context
|
|
76
|
+
let epicContext = '';
|
|
77
|
+
if (fullState) {
|
|
78
|
+
const existingEpics = fullState.issues.filter((i) => i.type === 'epic');
|
|
79
|
+
if (existingEpics.length > 0) {
|
|
80
|
+
epicContext = `\nExisting epics:\n${existingEpics.map((e) => `- ${e.id}: ${e.title} (${e.path}, children: ${e.children.length})`).join('\n')}\n`;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const enrichedPrompt = `You are managing a project in the .pm/ directory format (Project Plan Spec).
|
|
84
|
+
The project's current state is:
|
|
85
|
+
|
|
86
|
+
<state>
|
|
87
|
+
${stateContent || 'No STATE.md exists yet'}
|
|
88
|
+
</state>
|
|
89
|
+
|
|
90
|
+
<project>
|
|
91
|
+
${projectContent || 'No project.md yet'}
|
|
92
|
+
</project>
|
|
93
|
+
|
|
94
|
+
${idInfo}
|
|
95
|
+
${epicContext}
|
|
96
|
+
|
|
97
|
+
Follow these rules:
|
|
98
|
+
- When creating .pm/ files, use YAML front matter + markdown body
|
|
99
|
+
- When modifying issues, preserve all existing YAML fields you don't change
|
|
100
|
+
- After any state change, update STATE.md to reflect the new status
|
|
101
|
+
- Use the next available ID for new entities
|
|
102
|
+
- Respond briefly describing what you did
|
|
103
|
+
|
|
104
|
+
Issue scoping rules (critical for execution quality):
|
|
105
|
+
- Each issue is executed by a single AI agent with its own context window
|
|
106
|
+
- Issues estimated at 1-3 story points execute well (focused, single concern)
|
|
107
|
+
- Issues at 5 story points are viable if scoped to one subsystem
|
|
108
|
+
- Issues at 8+ story points MUST be decomposed into smaller sub-issues
|
|
109
|
+
- Issues at 13+ story points MUST become an epic with child issues
|
|
110
|
+
- Each issue should touch one logical concern (one component, one service, one data flow)
|
|
111
|
+
- If an issue requires work across multiple subsystems, split it into one issue per subsystem with blocked_by edges between them
|
|
112
|
+
- Research/investigation issues should be separate from implementation issues
|
|
113
|
+
|
|
114
|
+
Epic creation rules (when user asks for a feature with sub-tasks or an epic):
|
|
115
|
+
- Create an EP-*.md file in .pm/backlog/ with type: epic and a children: [] field in front matter
|
|
116
|
+
- Create individual IS-*.md (or BG-*.md) files for each child issue
|
|
117
|
+
- Each child issue must have epic: backlog/EP-XXX.md in its front matter
|
|
118
|
+
- The epic's children field must list all child paths: [backlog/IS-001.md, backlog/IS-002.md, ...]
|
|
119
|
+
- Set blocked_by between child issues where there are natural dependencies
|
|
120
|
+
- Give each child issue clear acceptance criteria and files to modify when possible
|
|
121
|
+
- Set appropriate priorities (P0-P3) based on the issue's importance within the epic
|
|
122
|
+
|
|
123
|
+
User request: ${userPrompt}`;
|
|
124
|
+
try {
|
|
125
|
+
ctx.broadcastToAll({
|
|
126
|
+
type: 'planPromptProgress',
|
|
127
|
+
data: { message: 'Starting project planning...' },
|
|
128
|
+
});
|
|
129
|
+
const runner = new HeadlessRunner({
|
|
130
|
+
workingDir,
|
|
131
|
+
directPrompt: enrichedPrompt,
|
|
132
|
+
outputCallback: (text) => {
|
|
133
|
+
ctx.send(ws, {
|
|
134
|
+
type: 'planPromptStreaming',
|
|
135
|
+
data: { token: text },
|
|
136
|
+
});
|
|
137
|
+
},
|
|
138
|
+
toolUseCallback: (() => {
|
|
139
|
+
const getProgressMessage = createPromptProgressTracker();
|
|
140
|
+
return (event) => {
|
|
141
|
+
const message = getProgressMessage(event);
|
|
142
|
+
if (message) {
|
|
143
|
+
ctx.broadcastToAll({
|
|
144
|
+
type: 'planPromptProgress',
|
|
145
|
+
data: { message },
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
})(),
|
|
150
|
+
});
|
|
151
|
+
ctx.broadcastToAll({
|
|
152
|
+
type: 'planPromptProgress',
|
|
153
|
+
data: { message: 'Claude is planning your project...' },
|
|
154
|
+
});
|
|
155
|
+
const result = await runWithFileLogger('pm-compose', () => runner.run());
|
|
156
|
+
ctx.broadcastToAll({
|
|
157
|
+
type: 'planPromptProgress',
|
|
158
|
+
data: { message: 'Finalizing project plan...' },
|
|
159
|
+
});
|
|
160
|
+
ctx.send(ws, {
|
|
161
|
+
type: 'planPromptResponse',
|
|
162
|
+
data: {
|
|
163
|
+
response: result.completed ? 'Prompt executed successfully.' : (result.error || 'Unknown error'),
|
|
164
|
+
success: result.completed,
|
|
165
|
+
error: result.error || null,
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
// Re-parse and broadcast updated state
|
|
169
|
+
const updatedState = parsePlanDirectory(workingDir);
|
|
170
|
+
if (updatedState) {
|
|
171
|
+
ctx.broadcastToAll({ type: 'planStateUpdated', data: updatedState });
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
ctx.send(ws, {
|
|
176
|
+
type: 'planError',
|
|
177
|
+
data: { error: error instanceof Error ? error.message : String(error) },
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=composer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"composer.js","sourceRoot":"","sources":["../../../../server/services/plan/composer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency Resolver — Validates and computes the dependency DAG.
|
|
3
|
+
*
|
|
4
|
+
* Builds adjacency list from blocked_by/blocks fields, detects cycles,
|
|
5
|
+
* and computes the "ready to work" set.
|
|
6
|
+
*/
|
|
7
|
+
import type { Issue } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Detect cycles in the dependency graph.
|
|
10
|
+
* Returns the first cycle found as an array of issue IDs, or null if acyclic.
|
|
11
|
+
*/
|
|
12
|
+
export declare function detectCycles(issues: Issue[]): string[] | null;
|
|
13
|
+
/**
|
|
14
|
+
* Compute the set of issues that are ready to work on.
|
|
15
|
+
* An issue is ready if:
|
|
16
|
+
* - It's not an epic
|
|
17
|
+
* - Its status is backlog or todo (not started, done, or cancelled)
|
|
18
|
+
* - All its blocked_by items are done or cancelled
|
|
19
|
+
*
|
|
20
|
+
* If epicScope is provided, only returns issues belonging to that epic.
|
|
21
|
+
*/
|
|
22
|
+
export declare function resolveReadyToWork(issues: Issue[], epicScope?: string): Issue[];
|
|
23
|
+
/**
|
|
24
|
+
* Compute the critical path through incomplete issues.
|
|
25
|
+
* Returns the longest chain of dependent issues.
|
|
26
|
+
*/
|
|
27
|
+
export declare function computeCriticalPath(issues: Issue[]): Issue[];
|
|
28
|
+
//# sourceMappingURL=dependency-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dependency-resolver.d.ts","sourceRoot":"","sources":["../../../../server/services/plan/dependency-resolver.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,EAAE,GAAG,IAAI,CAsB7D;AAuCD;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,KAAK,EAAE,CA+C/E;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,CAuC5D"}
|