commit per task
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@mikefreno/ralpi",
|
"name": "@mikefreno/ralpi",
|
||||||
"version": "0.1.9",
|
"version": "0.2.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",
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ import {
|
|||||||
writeFileSafe,
|
writeFileSafe,
|
||||||
ensureDir,
|
ensureDir,
|
||||||
captureGitCommits,
|
captureGitCommits,
|
||||||
|
hasUncommittedChanges,
|
||||||
|
getGitStatusPorcelain,
|
||||||
|
getGitDiff,
|
||||||
formatDuration,
|
formatDuration,
|
||||||
} from "./utils";
|
} from "./utils";
|
||||||
import { updateTaskInFile } from "./parser";
|
import { updateTaskInFile } from "./parser";
|
||||||
@@ -673,6 +676,76 @@ async function executeTask(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
|
// ── Auto-Commit: Trigger follow-up agent session for uncommitted changes ──
|
||||||
|
let finalCommitMessages = result.commitMessages ?? [];
|
||||||
|
let finalCommitSummary = result.commitSummary ?? "";
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (hasUncommittedChanges(projectDir)) {
|
||||||
|
const status = getGitStatusPorcelain(projectDir);
|
||||||
|
const diff = getGitDiff(projectDir);
|
||||||
|
const commitPrompt = [
|
||||||
|
`## Auto-Commit for Task ${task.id}: ${task.title}`,
|
||||||
|
"",
|
||||||
|
"The previous task is complete. There are uncommitted changes in the repository.",
|
||||||
|
"",
|
||||||
|
"First stage all intended changes with `git add -A` (including untracked files), then create a meaningful git commit.",
|
||||||
|
"Use a descriptive commit message and follow conventional commits format.",
|
||||||
|
"",
|
||||||
|
"### Current Changes (git status --porcelain)",
|
||||||
|
"```text",
|
||||||
|
status || "(no status output)",
|
||||||
|
"```",
|
||||||
|
"",
|
||||||
|
"### Current Tracked Diff (git diff)",
|
||||||
|
"```diff",
|
||||||
|
diff || "(no tracked diff output)",
|
||||||
|
"```",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
// Use a short timeout for the commit session (60s should be enough)
|
||||||
|
const commitTimeout = Math.min(
|
||||||
|
60_000,
|
||||||
|
config.execution.timeoutMs,
|
||||||
|
);
|
||||||
|
const commitResult = await runAgentSession(
|
||||||
|
commitPrompt,
|
||||||
|
projectDir,
|
||||||
|
commitTimeout,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
currentModel,
|
||||||
|
config.thinkingLevel,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (commitResult.success) {
|
||||||
|
// Re-capture commits made during this follow-up session
|
||||||
|
const newCommits = captureGitCommits(projectDir);
|
||||||
|
if (newCommits.commitMessages.length > 0) {
|
||||||
|
finalCommitMessages = [
|
||||||
|
...finalCommitMessages,
|
||||||
|
...newCommits.commitMessages,
|
||||||
|
];
|
||||||
|
finalCommitSummary = finalCommitSummary
|
||||||
|
? `${finalCommitSummary}; ${newCommits.commitSummary}`
|
||||||
|
: newCommits.commitSummary;
|
||||||
|
}
|
||||||
|
sendChatMessage?.(`✓ commit for ${task.id} · ${task.title}`);
|
||||||
|
} else {
|
||||||
|
sendChatMessage?.(
|
||||||
|
`~ commit for ${task.id} · ${task.title} — follow-up commit session failed: ${commitResult.error}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Don't fail the task if auto-commit fails
|
||||||
|
sendChatMessage?.(
|
||||||
|
`~ commit for ${task.id} · ${task.title} — auto-commit error: ${
|
||||||
|
error instanceof Error ? error.message : String(error)
|
||||||
|
}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Save reflection
|
// Save reflection
|
||||||
if (result.reflection) {
|
if (result.reflection) {
|
||||||
saveReflectionToFile(projectDir, config, result.reflection);
|
saveReflectionToFile(projectDir, config, result.reflection);
|
||||||
@@ -685,8 +758,8 @@ async function executeTask(
|
|||||||
result.reflection,
|
result.reflection,
|
||||||
result.toolUsage,
|
result.toolUsage,
|
||||||
result.outputPreview,
|
result.outputPreview,
|
||||||
result.commitMessages,
|
finalCommitMessages,
|
||||||
result.commitSummary,
|
finalCommitSummary,
|
||||||
);
|
);
|
||||||
// Auto-update the PRD source file checkbox
|
// Auto-update the PRD source file checkbox
|
||||||
try {
|
try {
|
||||||
|
|||||||
47
src/utils.ts
47
src/utils.ts
@@ -579,6 +579,53 @@ function extractAssistantText(content: unknown): string {
|
|||||||
|
|
||||||
// ─── Git Commit Capture ──────────────────────────────────────────────────────
|
// ─── Git Commit Capture ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if there are any uncommitted changes in the git repository.
|
||||||
|
*/
|
||||||
|
export function hasUncommittedChanges(projectDir: string): boolean {
|
||||||
|
const { execSync } = require("node:child_process");
|
||||||
|
try {
|
||||||
|
const output = execSync("git status --porcelain", {
|
||||||
|
cwd: projectDir,
|
||||||
|
encoding: "utf-8",
|
||||||
|
}).trim();
|
||||||
|
return output.length > 0;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current git status in porcelain format.
|
||||||
|
* Includes untracked files, which `git diff` alone would miss.
|
||||||
|
*/
|
||||||
|
export function getGitStatusPorcelain(projectDir: string): string {
|
||||||
|
const { execSync } = require("node:child_process");
|
||||||
|
try {
|
||||||
|
return execSync("git status --porcelain", {
|
||||||
|
cwd: projectDir,
|
||||||
|
encoding: "utf-8",
|
||||||
|
}).trim();
|
||||||
|
} catch {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current git diff for tracked uncommitted changes.
|
||||||
|
*/
|
||||||
|
export function getGitDiff(projectDir: string): string {
|
||||||
|
const { execSync } = require("node:child_process");
|
||||||
|
try {
|
||||||
|
return execSync("git diff", {
|
||||||
|
cwd: projectDir,
|
||||||
|
encoding: "utf-8",
|
||||||
|
}).trim();
|
||||||
|
} catch {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Capture recent git commits made during task execution
|
* Capture recent git commits made during task execution
|
||||||
* Returns commit messages and a summary string
|
* Returns commit messages and a summary string
|
||||||
|
|||||||
Reference in New Issue
Block a user