Compare commits

..

3 Commits

4 changed files with 694 additions and 644 deletions

View File

@@ -201,13 +201,14 @@ async function selectLoopOptions(
/** /**
* When multiple PRD loops have progress, prompt the user to select which one * When multiple PRD loops have progress, prompt the user to select which one
* to resume. Returns the selected PRD key and sourcePath. * to act on. Returns the selected PRD key and sourcePath.
* If only one PRD exists, returns it without prompting. * If only one PRD exists, returns it without prompting.
* Returns null if no PRDs exist. * Returns null if no PRDs exist.
*/ */
async function selectPRDToResume( async function selectPRD(
ctx: ExtensionContext, ctx: ExtensionContext,
found: NonNullable<ReturnType<typeof findProgressFile>>, found: NonNullable<ReturnType<typeof findProgressFile>>,
prompt: string,
): Promise<{ prdKey: string; sourcePath: string } | null> { ): Promise<{ prdKey: string; sourcePath: string } | null> {
const prds = listPRDsSorted(found.state); const prds = listPRDsSorted(found.state);
if (prds.length === 0) return null; if (prds.length === 0) return null;
@@ -230,10 +231,7 @@ async function selectPRDToResume(
return `${relPath}${completed}/${total} done${failed ? `, ${failed} failed` : ""} · ${updated}`; return `${relPath}${completed}/${total} done${failed ? `, ${failed} failed` : ""} · ${updated}`;
}); });
const selected = await ctx.ui.select( const selected = await ctx.ui.select(prompt, options);
"Multiple loops found. Which to resume?",
options,
);
if (!selected) return null; if (!selected) return null;
const idx = options.indexOf(selected); const idx = options.indexOf(selected);
@@ -1231,7 +1229,11 @@ async function handleResume(
// When no specific task file is given, let the user select which loop // When no specific task file is given, let the user select which loop
// to resume from multiple PRDs (sorted by most recent first). // to resume from multiple PRDs (sorted by most recent first).
const selected = await selectPRDToResume(ctx, found); const selected = await selectPRD(
ctx,
found,
"Multiple loops found. Which to resume?",
);
if (!selected) { if (!selected) {
ctx.ui.notify("Resume cancelled.", "info"); ctx.ui.notify("Resume cancelled.", "info");
return; return;
@@ -1285,12 +1287,17 @@ async function handleReset(
ctx: ExtensionContext, ctx: ExtensionContext,
args: string[], args: string[],
): Promise<void> { ): Promise<void> {
let sourcePath: string;
let prdKey: string | undefined;
let progress: ProgressTracker;
if (args[0]) { if (args[0]) {
const taskFile = resolveTaskArg(args[0], ctx.cwd); const taskFile = resolveTaskArg(args[0], ctx.cwd);
const found = findProgressFile(ctx.cwd, taskFile); const found = findProgressFile(ctx.cwd, taskFile);
const projectDir = found ? path.dirname(path.dirname(found.path)) : ctx.cwd; const projectDir = found ? path.dirname(path.dirname(found.path)) : ctx.cwd;
const progress = new ProgressTracker(projectDir, taskFile, found?.prdKey); sourcePath = taskFile;
progress.reset(); prdKey = found?.prdKey;
progress = new ProgressTracker(projectDir, taskFile, prdKey);
} else { } else {
const found = findProgressFile(ctx.cwd); const found = findProgressFile(ctx.cwd);
if (!found) { if (!found) {
@@ -1301,13 +1308,53 @@ async function handleReset(
return; return;
} }
const projectDir = path.dirname(path.dirname(found.path)); const projectDir = path.dirname(path.dirname(found.path));
// Use the most recently updated PRD (first in sorted order)
const prds = listPRDsSorted(found.state); // Multiple loops may have progress — let the user select which one to
const sourcePath = // reset (sorted by most recent first), same as resume.
prds.length > 0 ? prds[0].prd.sourcePath : found.state.sourcePath; const selected = await selectPRD(
const progress = new ProgressTracker(projectDir, sourcePath); ctx,
progress.reset(); found,
"Multiple loops found. Which to reset?",
);
if (!selected) {
ctx.ui.notify("Reset cancelled.", "info");
return;
}
sourcePath = selected.sourcePath;
prdKey = selected.prdKey;
progress = new ProgressTracker(projectDir, sourcePath, prdKey);
} }
// Ask whether to also clear the progress markers (checkboxes/status) in the
// source PRD/README file itself, not just .ralpi/progress.json.
const taskIds = Object.keys(progress.getState().tasks);
if (taskIds.length > 0) {
const choice = await ctx.ui.select(
`Also reset progress markers in the source file (${path.basename(
sourcePath,
)})?`,
[
"Yes — clear checkboxes/status markers in the source PRD/README",
"No — only reset .ralpi/progress.json",
],
);
if (choice === undefined) {
ctx.ui.notify("Reset cancelled.", "info");
return;
}
if (choice.startsWith("Yes")) {
progress.reset();
for (const id of taskIds) {
updateTaskInFile(sourcePath, id, "pending");
}
ctx.ui.notify(
`Progress reset — cleared ${taskIds.length} task marker(s) in ${path.basename(sourcePath)}.`,
"info",
);
return;
}
}
progress.reset();
ctx.ui.notify("Progress reset. All task statuses cleared.", "info"); ctx.ui.notify("Progress reset. All task statuses cleared.", "info");
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mikefreno/ralpi", "name": "@mikefreno/ralpi",
"version": "0.4.3", "version": "0.5.0",
"description": "Execute tasks from task files/PRD's using DAG-based dependency resolution with persistent progress tracking", "description": "Execute tasks from task files/PRD's using DAG-based dependency resolution with persistent progress tracking",
"keywords": [ "keywords": [
"pi-package", "pi-package",

View File

@@ -1434,7 +1434,7 @@ async function runFollowUpSession(
undefined, undefined,
model, model,
config.thinkingLevel, config.thinkingLevel,
true, // noSkills — follow-up sessions don't need the skills catalog false, // noSkills=false — follow-up sessions load skills too
(ctx.modelRegistry as any).runtime as ModelRuntime, (ctx.modelRegistry as any).runtime as ModelRuntime,
); );

View File

@@ -596,14 +596,17 @@ export async function runAgentSession(
} = {}; } = {};
try { try {
// Loop sessions load the full normal pi context: extensions (so all
// extension-provided tools register), skills, and project context
// (AGENTS.md / CLAUDE.md)
const loader = new DefaultResourceLoader({ const loader = new DefaultResourceLoader({
cwd, cwd,
agentDir: getAgentDir(), agentDir: getAgentDir(),
noExtensions: true,
noSkills, noSkills,
noPromptTemplates: true, noPromptTemplates: true,
noThemes: true, noThemes: true,
noContextFiles: true, noExtensions: false,
noContextFiles: false,
}); });
await loader.reload(); await loader.reload();
@@ -613,7 +616,7 @@ export async function runAgentSession(
resourceLoader: loader, resourceLoader: loader,
settingsManager: SettingsManager.create(cwd, getAgentDir()), settingsManager: SettingsManager.create(cwd, getAgentDir()),
modelRuntime, modelRuntime,
tools: ["read", "bash", "edit", "write", "grep", "find", "ls"], // No `tools` allowlist: matches a normal pi session's tool set.
model: model as any, model: model as any,
thinkingLevel: thinkingLevel as any, thinkingLevel: thinkingLevel as any,
}); });