Recovers lost files and commits work from the past 5-6 days. Holy shit that was a close call.
This commit is contained in:
@@ -5,61 +5,53 @@
|
||||
|
||||
import { z } from "zod";
|
||||
import {
|
||||
handleApiResult,
|
||||
createErrorResponse,
|
||||
createContentResponse,
|
||||
getProjectRootFromSession
|
||||
getProjectRootFromSession,
|
||||
executeTaskMasterCommand,
|
||||
handleApiResult
|
||||
} from "./utils.js";
|
||||
import { addTaskDirect } from "../core/task-master-core.js";
|
||||
|
||||
/**
|
||||
* Register the add-task tool with the MCP server
|
||||
* Register the addTask tool with the MCP server
|
||||
* @param {Object} server - FastMCP server instance
|
||||
* @param {AsyncOperationManager} asyncManager - The async operation manager instance.
|
||||
*/
|
||||
export function registerAddTaskTool(server, asyncManager) {
|
||||
export function registerAddTaskTool(server) {
|
||||
server.addTool({
|
||||
name: "add_task",
|
||||
description: "Starts adding a new task using AI in the background.",
|
||||
description: "Add a new task using AI",
|
||||
parameters: z.object({
|
||||
prompt: z.string().describe("Description of the task to add"),
|
||||
dependencies: z.string().optional().describe("Comma-separated list of task IDs this task depends on"),
|
||||
priority: z.string().optional().describe("Task priority (high, medium, low)"),
|
||||
file: z.string().optional().describe("Path to the tasks file"),
|
||||
projectRoot: z.string().optional().describe("Root directory of the project (default: current working directory)")
|
||||
projectRoot: z.string().optional().describe("Root directory of the project"),
|
||||
research: z.boolean().optional().describe("Whether to use research capabilities for task creation")
|
||||
}),
|
||||
execute: async (args, context) => {
|
||||
const { log, reportProgress, session } = context;
|
||||
execute: async (args, { log, reportProgress, session }) => {
|
||||
try {
|
||||
log.info(`MCP add_task request received with prompt: \"${args.prompt}\"`);
|
||||
log.info(`Starting add-task with args: ${JSON.stringify(args)}`);
|
||||
|
||||
if (!args.prompt) {
|
||||
return createErrorResponse("Prompt is required for add_task.", "VALIDATION_ERROR");
|
||||
}
|
||||
|
||||
// Get project root from session
|
||||
let rootFolder = getProjectRootFromSession(session, log);
|
||||
|
||||
if (!rootFolder && args.projectRoot) {
|
||||
rootFolder = args.projectRoot;
|
||||
log.info(`Using project root from args as fallback: ${rootFolder}`);
|
||||
}
|
||||
|
||||
const directArgs = {
|
||||
projectRoot: rootFolder,
|
||||
...args
|
||||
};
|
||||
|
||||
const operationId = asyncManager.addOperation(addTaskDirect, directArgs, context);
|
||||
|
||||
log.info(`Started background operation for add_task. Operation ID: ${operationId}`);
|
||||
|
||||
return createContentResponse({
|
||||
message: "Add task operation started successfully.",
|
||||
operationId: operationId
|
||||
});
|
||||
|
||||
// Call the direct function
|
||||
const result = await addTaskDirect({
|
||||
...args,
|
||||
projectRoot: rootFolder
|
||||
}, log, { reportProgress, session });
|
||||
|
||||
// Return the result
|
||||
return handleApiResult(result, log);
|
||||
} catch (error) {
|
||||
log.error(`Error initiating add_task operation: ${error.message}`, { stack: error.stack });
|
||||
return createErrorResponse(`Failed to start add task operation: ${error.message}`, "ADD_TASK_INIT_ERROR");
|
||||
log.error(`Error in add-task tool: ${error.message}`);
|
||||
return createErrorResponse(error.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -27,10 +27,9 @@ export function registerAnalyzeTool(server) {
|
||||
research: z.boolean().optional().describe("Use Perplexity AI for research-backed complexity analysis"),
|
||||
projectRoot: z.string().optional().describe("Root directory of the project (default: current working directory)")
|
||||
}),
|
||||
execute: async (args, { log, session, reportProgress }) => {
|
||||
execute: async (args, { log, session }) => {
|
||||
try {
|
||||
log.info(`Analyzing task complexity with args: ${JSON.stringify(args)}`);
|
||||
// await reportProgress({ progress: 0 });
|
||||
|
||||
let rootFolder = getProjectRootFromSession(session, log);
|
||||
|
||||
@@ -42,9 +41,7 @@ export function registerAnalyzeTool(server) {
|
||||
const result = await analyzeTaskComplexityDirect({
|
||||
projectRoot: rootFolder,
|
||||
...args
|
||||
}, log/*, { reportProgress, mcpLog: log, session}*/);
|
||||
|
||||
// await reportProgress({ progress: 100 });
|
||||
}, log, { session });
|
||||
|
||||
if (result.success) {
|
||||
log.info(`Task complexity analysis complete: ${result.data.message}`);
|
||||
|
||||
@@ -20,17 +20,16 @@ export function registerExpandAllTool(server) {
|
||||
name: "expand_all",
|
||||
description: "Expand all pending tasks into subtasks",
|
||||
parameters: z.object({
|
||||
num: z.union([z.number(), z.string()]).optional().describe("Number of subtasks to generate for each task"),
|
||||
num: z.string().optional().describe("Number of subtasks to generate for each task"),
|
||||
research: z.boolean().optional().describe("Enable Perplexity AI for research-backed subtask generation"),
|
||||
prompt: z.string().optional().describe("Additional context to guide subtask generation"),
|
||||
force: z.boolean().optional().describe("Force regeneration of subtasks for tasks that already have them"),
|
||||
file: z.string().optional().describe("Path to the tasks file (default: tasks/tasks.json)"),
|
||||
projectRoot: z.string().optional().describe("Root directory of the project (default: current working directory)")
|
||||
}),
|
||||
execute: async (args, { log, session, reportProgress }) => {
|
||||
execute: async (args, { log, session }) => {
|
||||
try {
|
||||
log.info(`Expanding all tasks with args: ${JSON.stringify(args)}`);
|
||||
// await reportProgress({ progress: 0 });
|
||||
|
||||
let rootFolder = getProjectRootFromSession(session, log);
|
||||
|
||||
@@ -42,9 +41,7 @@ export function registerExpandAllTool(server) {
|
||||
const result = await expandAllTasksDirect({
|
||||
projectRoot: rootFolder,
|
||||
...args
|
||||
}, log/*, { reportProgress, mcpLog: log, session}*/);
|
||||
|
||||
// await reportProgress({ progress: 100 });
|
||||
}, log, { session });
|
||||
|
||||
if (result.success) {
|
||||
log.info(`Successfully expanded all tasks: ${result.data.message}`);
|
||||
|
||||
@@ -10,6 +10,8 @@ import {
|
||||
getProjectRootFromSession
|
||||
} from "./utils.js";
|
||||
import { expandTaskDirect } from "../core/task-master-core.js";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
/**
|
||||
* Register the expand-task tool with the MCP server
|
||||
@@ -21,10 +23,9 @@ export function registerExpandTaskTool(server) {
|
||||
description: "Expand a task into subtasks for detailed implementation",
|
||||
parameters: z.object({
|
||||
id: z.string().describe("ID of task to expand"),
|
||||
num: z.union([z.number(), z.string()]).optional().describe("Number of subtasks to generate"),
|
||||
num: z.union([z.string(), z.number()]).optional().describe("Number of subtasks to generate"),
|
||||
research: z.boolean().optional().describe("Use Perplexity AI for research-backed generation"),
|
||||
prompt: z.string().optional().describe("Additional context for subtask generation"),
|
||||
force: z.boolean().optional().describe("Force regeneration even for tasks that already have subtasks"),
|
||||
file: z.string().optional().describe("Path to the tasks file"),
|
||||
projectRoot: z
|
||||
.string()
|
||||
@@ -33,11 +34,11 @@ export function registerExpandTaskTool(server) {
|
||||
"Root directory of the project (default: current working directory)"
|
||||
),
|
||||
}),
|
||||
execute: async (args, { log, session, reportProgress }) => {
|
||||
execute: async (args, { log, reportProgress, session }) => {
|
||||
try {
|
||||
log.info(`Expanding task with args: ${JSON.stringify(args)}`);
|
||||
// await reportProgress({ progress: 0 });
|
||||
log.info(`Starting expand-task with args: ${JSON.stringify(args)}`);
|
||||
|
||||
// Get project root from session
|
||||
let rootFolder = getProjectRootFromSession(session, log);
|
||||
|
||||
if (!rootFolder && args.projectRoot) {
|
||||
@@ -45,19 +46,27 @@ export function registerExpandTaskTool(server) {
|
||||
log.info(`Using project root from args as fallback: ${rootFolder}`);
|
||||
}
|
||||
|
||||
const result = await expandTaskDirect({
|
||||
projectRoot: rootFolder,
|
||||
...args
|
||||
}, log/*, { reportProgress, mcpLog: log, session}*/);
|
||||
log.info(`Project root resolved to: ${rootFolder}`);
|
||||
|
||||
// await reportProgress({ progress: 100 });
|
||||
// Check for tasks.json in the standard locations
|
||||
const tasksJsonPath = path.join(rootFolder, 'tasks', 'tasks.json');
|
||||
|
||||
if (result.success) {
|
||||
log.info(`Successfully expanded task with ID ${args.id}`);
|
||||
if (fs.existsSync(tasksJsonPath)) {
|
||||
log.info(`Found tasks.json at ${tasksJsonPath}`);
|
||||
// Add the file parameter directly to args
|
||||
args.file = tasksJsonPath;
|
||||
} else {
|
||||
log.error(`Failed to expand task: ${result.error?.message || 'Unknown error'}`);
|
||||
log.warn(`Could not find tasks.json at ${tasksJsonPath}`);
|
||||
}
|
||||
|
||||
// Call direct function with only session in the context, not reportProgress
|
||||
// Use the pattern recommended in the MCP guidelines
|
||||
const result = await expandTaskDirect({
|
||||
...args,
|
||||
projectRoot: rootFolder
|
||||
}, log, { session }); // Only pass session, NOT reportProgress
|
||||
|
||||
// Return the result
|
||||
return handleApiResult(result, log, 'Error expanding task');
|
||||
} catch (error) {
|
||||
log.error(`Error in expand task tool: ${error.message}`);
|
||||
|
||||
@@ -28,7 +28,6 @@ import { registerAddDependencyTool } from "./add-dependency.js";
|
||||
import { registerRemoveTaskTool } from './remove-task.js';
|
||||
import { registerInitializeProjectTool } from './initialize-project.js';
|
||||
import { asyncOperationManager } from '../core/utils/async-manager.js';
|
||||
import { registerGetOperationStatusTool } from './get-operation-status.js';
|
||||
|
||||
/**
|
||||
* Register all Task Master tools with the MCP server
|
||||
@@ -61,7 +60,6 @@ export function registerTaskMasterTools(server, asyncManager) {
|
||||
registerAddDependencyTool(server);
|
||||
registerRemoveTaskTool(server);
|
||||
registerInitializeProjectTool(server);
|
||||
registerGetOperationStatusTool(server, asyncManager);
|
||||
} catch (error) {
|
||||
logger.error(`Error registering Task Master tools: ${error.message}`);
|
||||
throw error;
|
||||
|
||||
@@ -31,7 +31,7 @@ export function registerParsePRDTool(server) {
|
||||
"Root directory of the project (default: automatically detected from session or CWD)"
|
||||
),
|
||||
}),
|
||||
execute: async (args, { log, session, reportProgress }) => {
|
||||
execute: async (args, { log, session }) => {
|
||||
try {
|
||||
log.info(`Parsing PRD with args: ${JSON.stringify(args)}`);
|
||||
|
||||
@@ -45,9 +45,7 @@ export function registerParsePRDTool(server) {
|
||||
const result = await parsePRDDirect({
|
||||
projectRoot: rootFolder,
|
||||
...args
|
||||
}, log/*, { reportProgress, mcpLog: log, session}*/);
|
||||
|
||||
// await reportProgress({ progress: 100 });
|
||||
}, log, { session });
|
||||
|
||||
if (result.success) {
|
||||
log.info(`Successfully parsed PRD: ${result.data.message}`);
|
||||
|
||||
@@ -34,11 +34,11 @@ export function registerSetTaskStatusTool(server) {
|
||||
"Root directory of the project (default: automatically detected)"
|
||||
),
|
||||
}),
|
||||
execute: async (args, { log, session, reportProgress }) => {
|
||||
execute: async (args, { log, session }) => {
|
||||
try {
|
||||
log.info(`Setting status of task(s) ${args.id} to: ${args.status}`);
|
||||
// await reportProgress({ progress: 0 });
|
||||
|
||||
// Get project root from session
|
||||
let rootFolder = getProjectRootFromSession(session, log);
|
||||
|
||||
if (!rootFolder && args.projectRoot) {
|
||||
@@ -46,19 +46,20 @@ export function registerSetTaskStatusTool(server) {
|
||||
log.info(`Using project root from args as fallback: ${rootFolder}`);
|
||||
}
|
||||
|
||||
// Call the direct function with the project root
|
||||
const result = await setTaskStatusDirect({
|
||||
projectRoot: rootFolder,
|
||||
...args
|
||||
}, log/*, { reportProgress, mcpLog: log, session}*/);
|
||||
|
||||
// await reportProgress({ progress: 100 });
|
||||
...args,
|
||||
projectRoot: rootFolder
|
||||
}, log);
|
||||
|
||||
// Log the result
|
||||
if (result.success) {
|
||||
log.info(`Successfully updated status for task(s) ${args.id} to "${args.status}": ${result.data.message}`);
|
||||
} else {
|
||||
log.error(`Failed to update task status: ${result.error?.message || 'Unknown error'}`);
|
||||
}
|
||||
|
||||
// Format and return the result
|
||||
return handleApiResult(result, log, 'Error setting task status');
|
||||
} catch (error) {
|
||||
log.error(`Error in setTaskStatus tool: ${error.message}`);
|
||||
|
||||
@@ -31,10 +31,9 @@ export function registerUpdateSubtaskTool(server) {
|
||||
"Root directory of the project (default: current working directory)"
|
||||
),
|
||||
}),
|
||||
execute: async (args, { log, session, reportProgress }) => {
|
||||
execute: async (args, { log, session }) => {
|
||||
try {
|
||||
log.info(`Updating subtask with args: ${JSON.stringify(args)}`);
|
||||
// await reportProgress({ progress: 0 });
|
||||
|
||||
let rootFolder = getProjectRootFromSession(session, log);
|
||||
|
||||
@@ -46,9 +45,7 @@ export function registerUpdateSubtaskTool(server) {
|
||||
const result = await updateSubtaskByIdDirect({
|
||||
projectRoot: rootFolder,
|
||||
...args
|
||||
}, log/*, { reportProgress, mcpLog: log, session}*/);
|
||||
|
||||
// await reportProgress({ progress: 100 });
|
||||
}, log, { session });
|
||||
|
||||
if (result.success) {
|
||||
log.info(`Successfully updated subtask with ID ${args.id}`);
|
||||
|
||||
@@ -20,7 +20,7 @@ export function registerUpdateTaskTool(server) {
|
||||
name: "update_task",
|
||||
description: "Updates a single task by ID with new information or context provided in the prompt.",
|
||||
parameters: z.object({
|
||||
id: z.union([z.number(), z.string()]).describe("ID of the task or subtask (e.g., '15', '15.2') to update"),
|
||||
id: z.string().describe("ID of the task or subtask (e.g., '15', '15.2') to update"),
|
||||
prompt: z.string().describe("New information or context to incorporate into the task"),
|
||||
research: z.boolean().optional().describe("Use Perplexity AI for research-backed updates"),
|
||||
file: z.string().optional().describe("Path to the tasks file"),
|
||||
@@ -31,10 +31,9 @@ export function registerUpdateTaskTool(server) {
|
||||
"Root directory of the project (default: current working directory)"
|
||||
),
|
||||
}),
|
||||
execute: async (args, { log, session, reportProgress }) => {
|
||||
execute: async (args, { log, session }) => {
|
||||
try {
|
||||
log.info(`Updating task with args: ${JSON.stringify(args)}`);
|
||||
// await reportProgress({ progress: 0 });
|
||||
|
||||
let rootFolder = getProjectRootFromSession(session, log);
|
||||
|
||||
@@ -46,9 +45,7 @@ export function registerUpdateTaskTool(server) {
|
||||
const result = await updateTaskByIdDirect({
|
||||
projectRoot: rootFolder,
|
||||
...args
|
||||
}, log/*, { reportProgress, mcpLog: log, session}*/);
|
||||
|
||||
// await reportProgress({ progress: 100 });
|
||||
}, log, { session });
|
||||
|
||||
if (result.success) {
|
||||
log.info(`Successfully updated task with ID ${args.id}`);
|
||||
|
||||
@@ -18,9 +18,9 @@ import { updateTasksDirect } from "../core/task-master-core.js";
|
||||
export function registerUpdateTool(server) {
|
||||
server.addTool({
|
||||
name: "update",
|
||||
description: "Update multiple upcoming tasks (with ID >= 'from' ID) based on new context or changes provided in the prompt.",
|
||||
description: "Update multiple upcoming tasks (with ID >= 'from' ID) based on new context or changes provided in the prompt. Use 'update_task' instead for a single specific task.",
|
||||
parameters: z.object({
|
||||
from: z.union([z.number(), z.string()]).describe("Task ID from which to start updating (inclusive)"),
|
||||
from: z.string().describe("Task ID from which to start updating (inclusive). IMPORTANT: This tool uses 'from', not 'id'"),
|
||||
prompt: z.string().describe("Explanation of changes or new context to apply"),
|
||||
research: z.boolean().optional().describe("Use Perplexity AI for research-backed updates"),
|
||||
file: z.string().optional().describe("Path to the tasks file"),
|
||||
@@ -31,10 +31,9 @@ export function registerUpdateTool(server) {
|
||||
"Root directory of the project (default: current working directory)"
|
||||
),
|
||||
}),
|
||||
execute: async (args, { log, session, reportProgress }) => {
|
||||
execute: async (args, { log, session }) => {
|
||||
try {
|
||||
log.info(`Updating tasks with args: ${JSON.stringify(args)}`);
|
||||
// await reportProgress({ progress: 0 });
|
||||
|
||||
let rootFolder = getProjectRootFromSession(session, log);
|
||||
|
||||
@@ -46,9 +45,7 @@ export function registerUpdateTool(server) {
|
||||
const result = await updateTasksDirect({
|
||||
projectRoot: rootFolder,
|
||||
...args
|
||||
}, log/*, { reportProgress, mcpLog: log, session}*/);
|
||||
|
||||
// await reportProgress({ progress: 100 });
|
||||
}, log, { session });
|
||||
|
||||
if (result.success) {
|
||||
log.info(`Successfully updated tasks from ID ${args.from}: ${result.data.message}`);
|
||||
|
||||
@@ -75,21 +75,43 @@ function getProjectRoot(projectRootRaw, log) {
|
||||
*/
|
||||
function getProjectRootFromSession(session, log) {
|
||||
try {
|
||||
// Add detailed logging of session structure
|
||||
log.info(`Session object: ${JSON.stringify({
|
||||
hasSession: !!session,
|
||||
hasRoots: !!session?.roots,
|
||||
rootsType: typeof session?.roots,
|
||||
isRootsArray: Array.isArray(session?.roots),
|
||||
rootsLength: session?.roots?.length,
|
||||
firstRoot: session?.roots?.[0],
|
||||
hasRootsRoots: !!session?.roots?.roots,
|
||||
rootsRootsType: typeof session?.roots?.roots,
|
||||
isRootsRootsArray: Array.isArray(session?.roots?.roots),
|
||||
rootsRootsLength: session?.roots?.roots?.length,
|
||||
firstRootsRoot: session?.roots?.roots?.[0]
|
||||
})}`);
|
||||
|
||||
// ALWAYS ensure we return a valid path for project root
|
||||
const cwd = process.cwd();
|
||||
|
||||
// If we have a session with roots array
|
||||
if (session?.roots?.[0]?.uri) {
|
||||
const rootUri = session.roots[0].uri;
|
||||
log.info(`Found rootUri in session.roots[0].uri: ${rootUri}`);
|
||||
const rootPath = rootUri.startsWith('file://')
|
||||
? decodeURIComponent(rootUri.slice(7))
|
||||
: rootUri;
|
||||
log.info(`Decoded rootPath: ${rootPath}`);
|
||||
return rootPath;
|
||||
}
|
||||
|
||||
// If we have a session with roots.roots array (different structure)
|
||||
if (session?.roots?.roots?.[0]?.uri) {
|
||||
const rootUri = session.roots.roots[0].uri;
|
||||
log.info(`Found rootUri in session.roots.roots[0].uri: ${rootUri}`);
|
||||
const rootPath = rootUri.startsWith('file://')
|
||||
? decodeURIComponent(rootUri.slice(7))
|
||||
: rootUri;
|
||||
log.info(`Decoded rootPath: ${rootPath}`);
|
||||
return rootPath;
|
||||
}
|
||||
|
||||
@@ -106,24 +128,15 @@ function getProjectRootFromSession(session, log) {
|
||||
if (fs.existsSync(path.join(projectRoot, '.cursor')) ||
|
||||
fs.existsSync(path.join(projectRoot, 'mcp-server')) ||
|
||||
fs.existsSync(path.join(projectRoot, 'package.json'))) {
|
||||
log.info(`Found project root from server path: ${projectRoot}`);
|
||||
return projectRoot;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we get here, we'll try process.cwd() but only if it's not "/"
|
||||
const cwd = process.cwd();
|
||||
if (cwd !== '/') {
|
||||
return cwd;
|
||||
}
|
||||
|
||||
// Last resort: try to derive from the server path we found earlier
|
||||
if (serverPath) {
|
||||
const mcpServerIndex = serverPath.indexOf('mcp-server');
|
||||
return mcpServerIndex !== -1 ? serverPath.substring(0, mcpServerIndex - 1) : cwd;
|
||||
}
|
||||
|
||||
throw new Error('Could not determine project root');
|
||||
// ALWAYS ensure we return a valid path as a last resort
|
||||
log.info(`Using current working directory as ultimate fallback: ${cwd}`);
|
||||
return cwd;
|
||||
} catch (e) {
|
||||
// If we have a server path, use it as a basis for project root
|
||||
const serverPath = process.argv[1];
|
||||
@@ -171,18 +184,20 @@ function handleApiResult(result, log, errorPrefix = 'API error', processFunction
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a Task Master CLI command using child_process
|
||||
* @param {string} command - The command to execute
|
||||
* @param {Object} log - The logger object from FastMCP
|
||||
* Executes a task-master CLI command synchronously.
|
||||
* @param {string} command - The command to execute (e.g., 'add-task')
|
||||
* @param {Object} log - Logger instance
|
||||
* @param {Array} args - Arguments for the command
|
||||
* @param {string|undefined} projectRootRaw - Optional raw project root path (will be normalized internally)
|
||||
* @param {Object|null} customEnv - Optional object containing environment variables to pass to the child process
|
||||
* @returns {Object} - The result of the command execution
|
||||
*/
|
||||
function executeTaskMasterCommand(
|
||||
command,
|
||||
log,
|
||||
args = [],
|
||||
projectRootRaw = null
|
||||
projectRootRaw = null,
|
||||
customEnv = null // Changed from session to customEnv
|
||||
) {
|
||||
try {
|
||||
// Normalize project root internally using the getProjectRoot utility
|
||||
@@ -201,8 +216,13 @@ function executeTaskMasterCommand(
|
||||
const spawnOptions = {
|
||||
encoding: "utf8",
|
||||
cwd: cwd,
|
||||
// Merge process.env with customEnv, giving precedence to customEnv
|
||||
env: { ...process.env, ...(customEnv || {}) }
|
||||
};
|
||||
|
||||
// Log the environment being passed (optional, for debugging)
|
||||
// log.info(`Spawn options env: ${JSON.stringify(spawnOptions.env)}`);
|
||||
|
||||
// Execute the command using the global task-master CLI or local script
|
||||
// Try the global CLI first
|
||||
let result = spawnSync("task-master", fullArgs, spawnOptions);
|
||||
@@ -210,6 +230,7 @@ function executeTaskMasterCommand(
|
||||
// If global CLI is not available, try fallback to the local script
|
||||
if (result.error && result.error.code === "ENOENT") {
|
||||
log.info("Global task-master not found, falling back to local script");
|
||||
// Pass the same spawnOptions (including env) to the fallback
|
||||
result = spawnSync("node", ["scripts/dev.js", ...fullArgs], spawnOptions);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user