dependency parsing broken
This commit is contained in:
249
index.ts
249
index.ts
@@ -1,3 +1,4 @@
|
|||||||
|
import * as fs from "node:fs";
|
||||||
import * as path from "node:path";
|
import * as path from "node:path";
|
||||||
import type {
|
import type {
|
||||||
ExtensionAPI,
|
ExtensionAPI,
|
||||||
@@ -14,7 +15,7 @@ import {
|
|||||||
import { ProgressTracker } from "./src/progress";
|
import { ProgressTracker } from "./src/progress";
|
||||||
import { buildPlanPrompt } from "./src/prompts";
|
import { buildPlanPrompt } from "./src/prompts";
|
||||||
import { formatReflections } from "./src/reflection";
|
import { formatReflections } from "./src/reflection";
|
||||||
import { executeBatch } from "./src/executor";
|
import { executeBatch, type SendChatMessage } from "./src/executor";
|
||||||
import {
|
import {
|
||||||
loadConfig,
|
loadConfig,
|
||||||
resolveTaskArg,
|
resolveTaskArg,
|
||||||
@@ -25,6 +26,10 @@ import {
|
|||||||
|
|
||||||
const COMMANDS = ["status", "resume", "next", "reset"] as const;
|
const COMMANDS = ["status", "resume", "next", "reset"] as const;
|
||||||
|
|
||||||
|
type ExecutionMode = "parallel" | "sequential";
|
||||||
|
|
||||||
|
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Detect if a token looks like a file path rather than a subcommand.
|
* Detect if a token looks like a file path rather than a subcommand.
|
||||||
* Matches: @path, /path, ./path, ../path, path/to/file, path.md, path.yaml
|
* Matches: @path, /path, ./path, ../path, path/to/file, path.md, path.yaml
|
||||||
@@ -42,6 +47,116 @@ function looksLikePath(token: string): boolean {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Build the set of completed tasks from progress tracker and PRD checkboxes. */
|
||||||
|
function buildCompletedSet(
|
||||||
|
progress: ProgressTracker,
|
||||||
|
project: import("./src/types").Project,
|
||||||
|
): Set<string> {
|
||||||
|
const completed = new Set(progress.getCompletedTaskIds());
|
||||||
|
for (const task of project.tasks) {
|
||||||
|
if (task.status === "completed") {
|
||||||
|
completed.add(task.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return completed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Prompt user to select an execution mode with dependency validation. */
|
||||||
|
async function selectExecutionMode(
|
||||||
|
ctx: ExtensionContext,
|
||||||
|
project: import("./src/types").Project,
|
||||||
|
taskFile: string,
|
||||||
|
): Promise<ExecutionMode> {
|
||||||
|
const mode = await ctx.ui.select("Execution mode for this run?", [
|
||||||
|
"Parallel (where dependencies allow)",
|
||||||
|
"Sequential (one at a time)",
|
||||||
|
]);
|
||||||
|
const isParallel = mode?.startsWith("Parallel") ?? false;
|
||||||
|
|
||||||
|
if (!isParallel) return "sequential";
|
||||||
|
|
||||||
|
// Validate dependency graph for parallel mode
|
||||||
|
if (Object.keys(project.dependencies).length === 0) {
|
||||||
|
const hasDepsSection = await fs.promises
|
||||||
|
.readFile(taskFile, "utf-8")
|
||||||
|
.then((content) => /^##\s+Dependencies\s*$/m.test(content))
|
||||||
|
.catch(() => false);
|
||||||
|
|
||||||
|
if (hasDepsSection) {
|
||||||
|
const choice = await ctx.ui.select(
|
||||||
|
"Found ## Dependencies section but no valid dependencies were parsed.\n\n" +
|
||||||
|
"This may be due to unsupported format. Parallel mode requires explicit dependencies.\n\n" +
|
||||||
|
"See README.md for supported dependency formats:\n" +
|
||||||
|
"- Arrow notation: `1 -> 2,3,4`\n" +
|
||||||
|
"- Natural language: `13 depends on 17, 18, 19, 20`\n\n" +
|
||||||
|
"Fall back to sequential mode?",
|
||||||
|
["Yes, use sequential", "No, continue with parallel"],
|
||||||
|
);
|
||||||
|
if (choice?.startsWith("Yes")) {
|
||||||
|
return "sequential";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "parallel";
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Build an execution plan based on the selected mode. */
|
||||||
|
function buildPlanByMode(
|
||||||
|
mode: ExecutionMode,
|
||||||
|
project: Parameters<typeof buildExecutionPlan>[0],
|
||||||
|
completed: Set<string>,
|
||||||
|
) {
|
||||||
|
return mode === "parallel"
|
||||||
|
? buildExecutionPlan(project, completed)
|
||||||
|
: buildSequentialPlan(project, completed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Run all batches in a plan, updating the task file after each batch. */
|
||||||
|
async function executePlanBatches(
|
||||||
|
plan: ReturnType<typeof buildPlanByMode>,
|
||||||
|
project: Parameters<typeof buildExecutionPlan>[0],
|
||||||
|
taskFile: string,
|
||||||
|
config: import("./src/types").RalphConfig,
|
||||||
|
progress: ProgressTracker,
|
||||||
|
ctx: ExtensionContext,
|
||||||
|
mode: ExecutionMode,
|
||||||
|
sendChatMessage?: SendChatMessage,
|
||||||
|
projectDir?: string,
|
||||||
|
): Promise<void> {
|
||||||
|
for (const batch of plan.batches) {
|
||||||
|
if (progress.getState().paused) {
|
||||||
|
ctx.ui.notify(
|
||||||
|
"Execution paused. Use /ralph resume to continue.",
|
||||||
|
"warning",
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Array.isArray(batch.tasks)) {
|
||||||
|
throw new Error(
|
||||||
|
`Batch ${batch.batchIndex} has invalid tasks: expected array, got ${typeof batch.tasks}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await executeBatch(
|
||||||
|
batch.tasks,
|
||||||
|
project,
|
||||||
|
config,
|
||||||
|
progress,
|
||||||
|
ctx,
|
||||||
|
{ parallel: mode === "parallel" },
|
||||||
|
sendChatMessage,
|
||||||
|
projectDir,
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const task of batch.tasks) {
|
||||||
|
const status = progress.getTaskStatus(task.id);
|
||||||
|
updateTaskInFile(taskFile, task.id, status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ─── Extension Entry ────────────────────────────────────────────────────────
|
// ─── Extension Entry ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
export default function ralphLoopExtension(pi: ExtensionAPI): void {
|
export default function ralphLoopExtension(pi: ExtensionAPI): void {
|
||||||
@@ -113,7 +228,7 @@ export default function ralphLoopExtension(pi: ExtensionAPI): void {
|
|||||||
// Uses "ralph-progress" customType with a "progress" phase so the
|
// Uses "ralph-progress" customType with a "progress" phase so the
|
||||||
// renderer omits the label prefix entirely (no [INFO] etc.).
|
// renderer omits the label prefix entirely (no [INFO] etc.).
|
||||||
// Accepts an optional meta object with toolCalls for the expandable view.
|
// Accepts an optional meta object with toolCalls for the expandable view.
|
||||||
const sendProgress = (
|
const sendProgress: SendChatMessage = (
|
||||||
content: string,
|
content: string,
|
||||||
meta?: { toolCalls?: Array<{ name: string; label: string }> },
|
meta?: { toolCalls?: Array<{ name: string; label: string }> },
|
||||||
) => {
|
) => {
|
||||||
@@ -180,6 +295,11 @@ async function handlePlan(
|
|||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const taskFile = resolveTaskArg(args[0] || "README.md", process.cwd());
|
const taskFile = resolveTaskArg(args[0] || "README.md", process.cwd());
|
||||||
const project = parseTaskFile(taskFile);
|
const project = parseTaskFile(taskFile);
|
||||||
|
if (!Array.isArray(project.tasks)) {
|
||||||
|
throw new Error(
|
||||||
|
`Parsed project from ${taskFile} has invalid tasks: expected array, got ${typeof project.tasks}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const planPrompt = buildPlanPrompt(project);
|
const planPrompt = buildPlanPrompt(project);
|
||||||
const plan = buildExecutionPlan(project, new Set());
|
const plan = buildExecutionPlan(project, new Set());
|
||||||
@@ -193,7 +313,7 @@ async function handlePlan(
|
|||||||
async function handleRun(
|
async function handleRun(
|
||||||
ctx: ExtensionContext,
|
ctx: ExtensionContext,
|
||||||
args: string[],
|
args: string[],
|
||||||
sendChatMessage?: (content: string) => void,
|
sendChatMessage?: SendChatMessage,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const taskFile = resolveTaskArg(args[0] || "README.md", process.cwd());
|
const taskFile = resolveTaskArg(args[0] || "README.md", process.cwd());
|
||||||
|
|
||||||
@@ -201,7 +321,7 @@ async function handleRun(
|
|||||||
// auto-resume instead of starting fresh
|
// auto-resume instead of starting fresh
|
||||||
const existingProgress = findProgressFile(process.cwd(), taskFile);
|
const existingProgress = findProgressFile(process.cwd(), taskFile);
|
||||||
if (existingProgress) {
|
if (existingProgress) {
|
||||||
return handleResume(ctx, [args[0]!], sendChatMessage);
|
return handleResume(ctx, args.slice(0, 1), sendChatMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
// No existing progress for this task — check for any progress at all
|
// No existing progress for this task — check for any progress at all
|
||||||
@@ -218,13 +338,11 @@ async function handleRun(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const project = parseTaskFile(taskFile);
|
|
||||||
|
|
||||||
// Determine projectDir: prefer existing .ralph/ location, otherwise use cwd
|
|
||||||
const projectDir = found
|
const projectDir = found
|
||||||
? path.dirname(path.dirname(found.path))
|
? path.dirname(path.dirname(found.path))
|
||||||
: process.cwd();
|
: process.cwd();
|
||||||
|
|
||||||
|
const project = parseTaskFile(taskFile);
|
||||||
const config = loadConfig(projectDir);
|
const config = loadConfig(projectDir);
|
||||||
const progress = new ProgressTracker(projectDir, taskFile);
|
const progress = new ProgressTracker(projectDir, taskFile);
|
||||||
|
|
||||||
@@ -234,47 +352,22 @@ async function handleRun(
|
|||||||
`Starting ${project.tasks.length} tasks from ${path.basename(taskFile)}`,
|
`Starting ${project.tasks.length} tasks from ${path.basename(taskFile)}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
const completed = new Set(progress.getCompletedTaskIds());
|
const completed = buildCompletedSet(progress, project);
|
||||||
|
const mode = await selectExecutionMode(ctx, project, taskFile);
|
||||||
|
const plan = buildPlanByMode(mode, project, completed);
|
||||||
|
|
||||||
// Ask user for execution mode
|
await executePlanBatches(
|
||||||
const mode = await ctx.ui.select("Execution mode for this run?", [
|
plan,
|
||||||
"Parallel (DAG-optimized)",
|
|
||||||
"Sequential (one at a time)",
|
|
||||||
]);
|
|
||||||
const useParallel = mode?.startsWith("Parallel");
|
|
||||||
|
|
||||||
// Sequential mode: use buildSequentialPlan to avoid 29-task mega-batches
|
|
||||||
const plan = useParallel
|
|
||||||
? buildExecutionPlan(project, completed)
|
|
||||||
: buildSequentialPlan(project, completed);
|
|
||||||
|
|
||||||
for (const batch of plan.batches) {
|
|
||||||
if (progress.getState().paused) {
|
|
||||||
ctx.ui.notify(
|
|
||||||
"Execution paused. Use /ralph resume to continue.",
|
|
||||||
"warning",
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await executeBatch(
|
|
||||||
batch.batchIndex,
|
|
||||||
batch.tasks,
|
|
||||||
project,
|
project,
|
||||||
|
taskFile,
|
||||||
config,
|
config,
|
||||||
progress,
|
progress,
|
||||||
ctx as any,
|
ctx,
|
||||||
{ parallel: useParallel },
|
mode,
|
||||||
sendChatMessage,
|
sendChatMessage,
|
||||||
projectDir,
|
projectDir,
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const task of batch.tasks) {
|
|
||||||
const status = progress.getTaskStatus(task.id);
|
|
||||||
updateTaskInFile(taskFile, task.id, status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const state = progress.getState();
|
const state = progress.getState();
|
||||||
const output = formatProgressStatus(state);
|
const output = formatProgressStatus(state);
|
||||||
|
|
||||||
@@ -334,9 +427,8 @@ async function handleStatus(
|
|||||||
async function handleResume(
|
async function handleResume(
|
||||||
ctx: ExtensionContext,
|
ctx: ExtensionContext,
|
||||||
args: string[],
|
args: string[],
|
||||||
sendChatMessage?: (content: string) => void,
|
sendChatMessage?: SendChatMessage,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
// If a task file arg is provided, find progress for that specific PRD
|
|
||||||
let taskFile: string;
|
let taskFile: string;
|
||||||
let projectDir: string;
|
let projectDir: string;
|
||||||
let found: ReturnType<typeof findProgressFile>;
|
let found: ReturnType<typeof findProgressFile>;
|
||||||
@@ -369,6 +461,11 @@ async function handleResume(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const project = parseTaskFile(taskFile);
|
const project = parseTaskFile(taskFile);
|
||||||
|
if (!Array.isArray(project.tasks)) {
|
||||||
|
throw new Error(
|
||||||
|
`Parsed project from ${taskFile} has invalid tasks: expected array, got ${typeof project.tasks}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
const config = loadConfig(projectDir);
|
const config = loadConfig(projectDir);
|
||||||
const progress = new ProgressTracker(projectDir, taskFile, found.prdKey);
|
const progress = new ProgressTracker(projectDir, taskFile, found.prdKey);
|
||||||
|
|
||||||
@@ -377,39 +474,22 @@ async function handleResume(
|
|||||||
// Set resume status
|
// Set resume status
|
||||||
ctx.ui.setStatus("ralph", `Resuming from ${path.basename(taskFile)}`);
|
ctx.ui.setStatus("ralph", `Resuming from ${path.basename(taskFile)}`);
|
||||||
|
|
||||||
const completed = new Set(progress.getCompletedTaskIds());
|
const completed = buildCompletedSet(progress, project);
|
||||||
|
const mode = await selectExecutionMode(ctx, project, taskFile);
|
||||||
|
const plan = buildPlanByMode(mode, project, completed);
|
||||||
|
|
||||||
// Ask user for execution mode
|
await executePlanBatches(
|
||||||
const mode = await ctx.ui.select("Execution mode for this run?", [
|
plan,
|
||||||
"Parallel (DAG-optimized)",
|
|
||||||
"Sequential (one at a time)",
|
|
||||||
]);
|
|
||||||
const useParallel = mode?.startsWith("Parallel");
|
|
||||||
|
|
||||||
// Sequential mode: use buildSequentialPlan to avoid 29-task mega-batches
|
|
||||||
const plan = useParallel
|
|
||||||
? buildExecutionPlan(project, completed)
|
|
||||||
: buildSequentialPlan(project, completed);
|
|
||||||
|
|
||||||
for (const batch of plan.batches) {
|
|
||||||
await executeBatch(
|
|
||||||
batch.batchIndex,
|
|
||||||
batch.tasks,
|
|
||||||
project,
|
project,
|
||||||
|
taskFile,
|
||||||
config,
|
config,
|
||||||
progress,
|
progress,
|
||||||
ctx as any,
|
ctx,
|
||||||
{ parallel: useParallel },
|
mode,
|
||||||
sendChatMessage,
|
sendChatMessage,
|
||||||
projectDir,
|
projectDir,
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const task of batch.tasks) {
|
|
||||||
const status = progress.getTaskStatus(task.id);
|
|
||||||
updateTaskInFile(taskFile, task.id, status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.ui.notify(formatProgressStatus(progress.getState()), "info");
|
ctx.ui.notify(formatProgressStatus(progress.getState()), "info");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -418,7 +498,7 @@ async function handleResume(
|
|||||||
async function handleNext(
|
async function handleNext(
|
||||||
ctx: ExtensionContext,
|
ctx: ExtensionContext,
|
||||||
args: string[],
|
args: string[],
|
||||||
sendChatMessage?: (content: string) => void,
|
sendChatMessage?: SendChatMessage,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
let taskFile: string;
|
let taskFile: string;
|
||||||
let projectDir: string;
|
let projectDir: string;
|
||||||
@@ -448,10 +528,15 @@ async function handleNext(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const project = parseTaskFile(taskFile);
|
const project = parseTaskFile(taskFile);
|
||||||
|
if (!Array.isArray(project.tasks)) {
|
||||||
|
throw new Error(
|
||||||
|
`Parsed project from ${taskFile} has invalid tasks: expected array, got ${typeof project.tasks}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
const config = loadConfig(projectDir);
|
const config = loadConfig(projectDir);
|
||||||
const progress = new ProgressTracker(projectDir, taskFile, found?.prdKey);
|
const progress = new ProgressTracker(projectDir, taskFile, found?.prdKey);
|
||||||
|
|
||||||
const completed = new Set(progress.getCompletedTaskIds());
|
const completed = buildCompletedSet(progress, project);
|
||||||
const ready = getReadyTasks(project, completed);
|
const ready = getReadyTasks(project, completed);
|
||||||
|
|
||||||
if (ready.length === 0) {
|
if (ready.length === 0) {
|
||||||
@@ -469,13 +554,12 @@ async function handleNext(
|
|||||||
|
|
||||||
for (const task of nextBatch) {
|
for (const task of nextBatch) {
|
||||||
await executeBatch(
|
await executeBatch(
|
||||||
0,
|
|
||||||
[task],
|
[task],
|
||||||
project,
|
project,
|
||||||
config,
|
config,
|
||||||
progress,
|
progress,
|
||||||
ctx as any,
|
ctx,
|
||||||
undefined,
|
{ parallel: false },
|
||||||
sendChatMessage,
|
sendChatMessage,
|
||||||
projectDir,
|
projectDir,
|
||||||
);
|
);
|
||||||
@@ -496,11 +580,12 @@ async function handleReset(
|
|||||||
ctx: ExtensionContext,
|
ctx: ExtensionContext,
|
||||||
args: string[],
|
args: string[],
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
let projectDir: string;
|
|
||||||
if (args[0]) {
|
if (args[0]) {
|
||||||
const taskFile = resolveTaskArg(args[0], process.cwd());
|
const taskFile = resolveTaskArg(args[0], process.cwd());
|
||||||
const found = findProgressFile(process.cwd(), taskFile);
|
const found = findProgressFile(process.cwd(), taskFile);
|
||||||
projectDir = found ? path.dirname(path.dirname(found.path)) : process.cwd();
|
const projectDir = found
|
||||||
|
? path.dirname(path.dirname(found.path))
|
||||||
|
: process.cwd();
|
||||||
const progress = new ProgressTracker(projectDir, taskFile, found?.prdKey);
|
const progress = new ProgressTracker(projectDir, taskFile, found?.prdKey);
|
||||||
progress.reset();
|
progress.reset();
|
||||||
} else {
|
} else {
|
||||||
@@ -524,19 +609,3 @@ async function handleReset(
|
|||||||
|
|
||||||
ctx.ui.notify("Progress reset. All task statuses cleared.", "info");
|
ctx.ui.notify("Progress reset. All task statuses cleared.", "info");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
function formatDuration(ms: number): string {
|
|
||||||
const seconds = Math.floor(ms / 1000);
|
|
||||||
const minutes = Math.floor(seconds / 60);
|
|
||||||
const hours = Math.floor(minutes / 60);
|
|
||||||
|
|
||||||
if (hours > 0) {
|
|
||||||
return `${hours}h ${minutes % 60}m`;
|
|
||||||
}
|
|
||||||
if (minutes > 0) {
|
|
||||||
return `${minutes}m ${seconds % 60}s`;
|
|
||||||
}
|
|
||||||
return `${seconds}s`;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import * as path from "node:path";
|
|||||||
import type { Task, Project, Reflection, ToolUsage } from "./types";
|
import type { Task, Project, Reflection, ToolUsage } from "./types";
|
||||||
import type { RalphConfig } from "./types";
|
import type { RalphConfig } from "./types";
|
||||||
import type { ProgressTracker } from "./progress";
|
import type { ProgressTracker } from "./progress";
|
||||||
import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
||||||
import { buildTaskPrompt } from "./prompts";
|
import { buildTaskPrompt } from "./prompts";
|
||||||
import { extractReflection } from "./reflection";
|
import { extractReflection } from "./reflection";
|
||||||
import {
|
import {
|
||||||
@@ -36,7 +36,7 @@ export async function runTask(
|
|||||||
project: Project,
|
project: Project,
|
||||||
config: RalphConfig,
|
config: RalphConfig,
|
||||||
depReflections: Reflection[],
|
depReflections: Reflection[],
|
||||||
ctx: ExtensionCommandContext,
|
ctx: ExtensionContext,
|
||||||
sendChatMessage?: SendChatMessage,
|
sendChatMessage?: SendChatMessage,
|
||||||
projectDir: string = project.sourceDir,
|
projectDir: string = project.sourceDir,
|
||||||
): Promise<{
|
): Promise<{
|
||||||
@@ -210,16 +210,22 @@ function saveSessionOutput(
|
|||||||
* Execute a batch of tasks (sequentially or in parallel)
|
* Execute a batch of tasks (sequentially or in parallel)
|
||||||
*/
|
*/
|
||||||
export async function executeBatch(
|
export async function executeBatch(
|
||||||
_batchIndex: number,
|
|
||||||
tasks: Task[],
|
tasks: Task[],
|
||||||
project: Project,
|
project: Project,
|
||||||
config: RalphConfig,
|
config: RalphConfig,
|
||||||
progress: ProgressTracker,
|
progress: ProgressTracker,
|
||||||
ctx: ExtensionCommandContext,
|
ctx: ExtensionContext,
|
||||||
options?: { parallel?: boolean },
|
options?: { parallel?: boolean },
|
||||||
sendChatMessage?: SendChatMessage,
|
sendChatMessage?: SendChatMessage,
|
||||||
projectDir?: string,
|
projectDir?: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
// Defensive: ensure tasks is an iterable array
|
||||||
|
if (!Array.isArray(tasks)) {
|
||||||
|
throw new Error(
|
||||||
|
`executeBatch received invalid tasks: expected array, got ${typeof tasks}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Check if we should run parallel
|
// Check if we should run parallel
|
||||||
const shouldParallel =
|
const shouldParallel =
|
||||||
options?.parallel && tasks.length > 1 && config.execution.maxParallel > 0;
|
options?.parallel && tasks.length > 1 && config.execution.maxParallel > 0;
|
||||||
@@ -259,7 +265,7 @@ async function executeBatchParallel(
|
|||||||
project: Project,
|
project: Project,
|
||||||
config: RalphConfig,
|
config: RalphConfig,
|
||||||
progress: ProgressTracker,
|
progress: ProgressTracker,
|
||||||
ctx: ExtensionCommandContext,
|
ctx: ExtensionContext,
|
||||||
sendChatMessage?: SendChatMessage,
|
sendChatMessage?: SendChatMessage,
|
||||||
projectDir?: string,
|
projectDir?: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
@@ -300,7 +306,7 @@ async function executeTask(
|
|||||||
project: Project,
|
project: Project,
|
||||||
config: RalphConfig,
|
config: RalphConfig,
|
||||||
progress: ProgressTracker,
|
progress: ProgressTracker,
|
||||||
ctx: ExtensionCommandContext,
|
ctx: ExtensionContext,
|
||||||
sendChatMessage?: SendChatMessage,
|
sendChatMessage?: SendChatMessage,
|
||||||
projectDir: string = project.sourceDir,
|
projectDir: string = project.sourceDir,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
|||||||
Reference in New Issue
Block a user