mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-09 22:31:08 +00:00
refactor: rename prepare-for-commit to iterate
- Renamed prepare_logs to iterations - Updated task manager to use new terminology - Simplified CLI interface - Added better TypeScript types - Improved error handling Task ID: 4d3f7a2e-8c1b-4f9d-b5e2-9c1d8f3a6b4d
This commit is contained in:
parent
589b159687
commit
61c9480b5b
7 changed files with 203 additions and 246 deletions
137
.roo/iterate-cli.ts
Normal file
137
.roo/iterate-cli.ts
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
#!/usr/bin/env node
|
||||
import { program } from "commander"
|
||||
import { taskManager } from "./task-manager"
|
||||
import { execSync } from "child_process"
|
||||
|
||||
program.name("iterate").description("CLI to manage task iterations")
|
||||
|
||||
program
|
||||
.command("create <taskId>")
|
||||
.description("Create a new iteration")
|
||||
.requiredOption("-d, --description <description>", "Task description")
|
||||
.action(async (taskId: string, options: { description: string }) => {
|
||||
try {
|
||||
await taskManager.createIteration(taskId, options.description)
|
||||
console.log(`Created iteration: ${taskId}`)
|
||||
} catch (error) {
|
||||
console.error("Failed to create iteration:", error)
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
||||
|
||||
program
|
||||
.command("list")
|
||||
.description("List all iterations")
|
||||
.action(async () => {
|
||||
try {
|
||||
const iterations = await taskManager.listIterations()
|
||||
console.log("Available iterations:")
|
||||
for (const taskId of iterations) {
|
||||
const task = await taskManager.getIteration(taskId)
|
||||
if (task) {
|
||||
console.log(`- ${taskId}: ${task.description} (${task.current_state.status})`)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to list iterations:", error)
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
||||
|
||||
program
|
||||
.command("status <taskId>")
|
||||
.description("Show iteration status")
|
||||
.action(async (taskId: string) => {
|
||||
try {
|
||||
const task = await taskManager.getIteration(taskId)
|
||||
if (!task) {
|
||||
console.log("No such iteration")
|
||||
return
|
||||
}
|
||||
|
||||
console.log(`Iteration: ${task.task_id}`)
|
||||
console.log(`Description: ${task.description}`)
|
||||
console.log(`Status: ${task.current_state.status}`)
|
||||
|
||||
if (task.checkpoints.length > 0) {
|
||||
console.log("\nCheckpoints:")
|
||||
task.checkpoints.forEach((checkpoint, i) => {
|
||||
console.log(`${i + 1}. ${checkpoint.description}`)
|
||||
console.log(` Changes: ${checkpoint.changes.join(", ")}`)
|
||||
console.log(` Timestamp: ${checkpoint.timestamp}`)
|
||||
})
|
||||
}
|
||||
|
||||
if (task.test_results) {
|
||||
console.log("\nTest results:")
|
||||
console.log(
|
||||
`- Unit tests: ${task.test_results.unit_tests.passing} passing, ${task.test_results.unit_tests.failing} failing`,
|
||||
)
|
||||
console.log(`- Linting: ${task.test_results.linting}`)
|
||||
console.log(`- Manual testing: ${task.test_results.manual_testing}`)
|
||||
}
|
||||
|
||||
if (task.current_state.final_commit) {
|
||||
console.log("\nCommit info:")
|
||||
console.log(`- Hash: ${task.current_state.final_commit.hash}`)
|
||||
console.log(`- Message: ${task.current_state.final_commit.message}`)
|
||||
console.log("- Changes:")
|
||||
task.current_state.final_commit.changes.forEach((change) => {
|
||||
console.log(` * ${change}`)
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to get iteration status:", error)
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
||||
|
||||
program
|
||||
.command("checkpoint <taskId>")
|
||||
.description("Create a new checkpoint")
|
||||
.requiredOption("-d, --description <description>", "Checkpoint description")
|
||||
.requiredOption("-c, --component <component>", "Component being modified")
|
||||
.requiredOption("--changes <changes...>", "List of changes")
|
||||
.requiredOption("--risks <risks...>", "List of risks")
|
||||
.requiredOption("--feedback <feedback...>", "Expected user feedback")
|
||||
.action(async (taskId: string, options) => {
|
||||
try {
|
||||
const checkpoint = {
|
||||
id: `checkpoint_${Date.now()}`,
|
||||
description: options.description,
|
||||
component: options.component,
|
||||
changes: options.changes,
|
||||
risks: options.risks,
|
||||
expected_feedback: options.feedback,
|
||||
timestamp: new Date().toISOString(),
|
||||
}
|
||||
|
||||
await taskManager.addCheckpoint(taskId, checkpoint)
|
||||
console.log(`Created checkpoint: ${checkpoint.id}`)
|
||||
} catch (error) {
|
||||
console.error("Failed to create checkpoint:", error)
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
||||
|
||||
program
|
||||
.command("complete <taskId>")
|
||||
.description("Complete an iteration with commit info")
|
||||
.requiredOption("--message <message>", "Commit message")
|
||||
.requiredOption("--changes <changes...>", "List of changes")
|
||||
.action(async (taskId: string, options) => {
|
||||
try {
|
||||
const hash = execSync("git rev-parse HEAD").toString().trim()
|
||||
await taskManager.completeIteration(taskId, {
|
||||
hash,
|
||||
message: options.message,
|
||||
changes: options.changes,
|
||||
})
|
||||
console.log(`Completed iteration: ${taskId}`)
|
||||
} catch (error) {
|
||||
console.error("Failed to complete iteration:", error)
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
||||
|
||||
program.parse()
|
||||
|
|
@ -51,6 +51,14 @@
|
|||
],
|
||||
"current_state": {
|
||||
"status": "completed",
|
||||
"summary": "Successfully removed unused YamlParser.ts with no regressions"
|
||||
"summary": "Successfully removed unused YamlParser.ts with no regressions",
|
||||
"final_commit": {
|
||||
"hash": "589b1596",
|
||||
"message": "refactor: remove unused YamlParser implementation",
|
||||
"changes": [
|
||||
"Removed src/services/package-manager/YamlParser.ts",
|
||||
"All tests passing (1263 pass, 0 fail, 23 pending)"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +1,15 @@
|
|||
{
|
||||
"name": "roo-prepare",
|
||||
"name": "roo-iterate",
|
||||
"version": "1.0.0",
|
||||
"description": "Prepare for commit task management system",
|
||||
"description": "Iteration task management system",
|
||||
"private": true,
|
||||
"bin": {
|
||||
"prepare": "./dist/prepare-cli.js"
|
||||
"iterate": "./dist/iterate-cli.js"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"prepare": "npm run build",
|
||||
"start": "node ./dist/prepare-cli.js"
|
||||
"start": "node ./dist/iterate-cli.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"commander": "^11.1.0"
|
||||
|
|
|
|||
|
|
@ -1,148 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
import { program } from "commander"
|
||||
import { taskManager } from "./task-manager"
|
||||
import * as fs from "fs/promises"
|
||||
import * as path from "path"
|
||||
import { execSync } from "child_process"
|
||||
|
||||
program.name("prepare").description("CLI to manage prepare for commit tasks")
|
||||
|
||||
program
|
||||
.command("create <taskId>")
|
||||
.description("Create a new task")
|
||||
.requiredOption("-d, --description <description>", "Task description")
|
||||
.action(async (taskId: string, options: { description: string }) => {
|
||||
try {
|
||||
const initialCommit = execSync("git rev-parse HEAD").toString().trim()
|
||||
await taskManager.createTask(taskId, options.description, initialCommit)
|
||||
console.log(`Created task: ${taskId}`)
|
||||
} catch (error) {
|
||||
console.error("Failed to create task:", error)
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
||||
|
||||
program
|
||||
.command("switch <taskId>")
|
||||
.description("Switch to a different task")
|
||||
.action(async (taskId: string) => {
|
||||
try {
|
||||
await taskManager.switchTask(taskId)
|
||||
const task = taskManager.getCurrentTask()
|
||||
console.log(`Switched to task: ${taskId}`)
|
||||
console.log("Current checkpoint:", task?.active_checkpoint)
|
||||
if (task?.pending_decisions.length) {
|
||||
console.log("\nPending decisions:")
|
||||
task.pending_decisions.forEach((decision, i) => {
|
||||
console.log(`${i + 1}. ${decision}`)
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to switch task:", error)
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
||||
|
||||
program
|
||||
.command("list")
|
||||
.description("List all tasks")
|
||||
.action(async () => {
|
||||
try {
|
||||
const tasks = await taskManager.listTasks()
|
||||
console.log("Available tasks:")
|
||||
for (const taskId of tasks) {
|
||||
const content = await fs.readFile(path.join(".roo", "prepare_logs", `${taskId}.json`), "utf-8")
|
||||
const task = JSON.parse(content)
|
||||
console.log(`- ${taskId}: ${task.description} (${task.status})`)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to list tasks:", error)
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
||||
|
||||
program
|
||||
.command("status")
|
||||
.description("Show current task status")
|
||||
.action(() => {
|
||||
const task = taskManager.getCurrentTask()
|
||||
if (!task) {
|
||||
console.log("No active task")
|
||||
return
|
||||
}
|
||||
|
||||
console.log(`Current task: ${task.task_id}`)
|
||||
console.log(`Description: ${task.description}`)
|
||||
console.log(`Status: ${task.status}`)
|
||||
console.log(`Active checkpoint: ${task.active_checkpoint}`)
|
||||
|
||||
if (task.pending_decisions.length) {
|
||||
console.log("\nPending decisions:")
|
||||
task.pending_decisions.forEach((decision, i) => {
|
||||
console.log(`${i + 1}. ${decision}`)
|
||||
})
|
||||
}
|
||||
|
||||
console.log("\nTest results:")
|
||||
console.log(
|
||||
`- Unit tests: ${task.test_results.unit_tests.passing} passing, ${task.test_results.unit_tests.failing} failing`,
|
||||
)
|
||||
console.log(`- Linting: ${task.test_results.linting}`)
|
||||
console.log(`- Manual testing: ${task.test_results.manual_testing}`)
|
||||
|
||||
console.log("\nRollback info:")
|
||||
console.log(`Full rollback: ${task.rollback_info.full_rollback}`)
|
||||
console.log("Partial rollbacks:")
|
||||
Object.entries(task.rollback_info.partial_rollbacks).forEach(([name, command]) => {
|
||||
console.log(`- ${name}: ${command}`)
|
||||
})
|
||||
})
|
||||
|
||||
program
|
||||
.command("checkpoint")
|
||||
.description("Create a new checkpoint")
|
||||
.requiredOption("-d, --description <description>", "Checkpoint description")
|
||||
.requiredOption("-c, --component <component>", "Component being modified")
|
||||
.requiredOption("--changes <changes...>", "List of changes")
|
||||
.requiredOption("--risks <risks...>", "List of risks")
|
||||
.requiredOption("--feedback <feedback...>", "Expected user feedback")
|
||||
.action(async (options) => {
|
||||
try {
|
||||
const commitHash = execSync("git rev-parse HEAD").toString().trim()
|
||||
const task = taskManager.getCurrentTask()
|
||||
if (!task) throw new Error("No active task")
|
||||
|
||||
const checkpoint = {
|
||||
id: `${task.task_id}_${task.checkpoints.length + 1}`,
|
||||
commit_hash: commitHash,
|
||||
description: options.description,
|
||||
component: options.component,
|
||||
changes: options.changes,
|
||||
risks: options.risks,
|
||||
expected_feedback: options.feedback,
|
||||
timestamp: new Date().toISOString(),
|
||||
}
|
||||
|
||||
await taskManager.addCheckpoint(checkpoint)
|
||||
console.log(`Created checkpoint: ${checkpoint.id}`)
|
||||
} catch (error) {
|
||||
console.error("Failed to create checkpoint:", error)
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
||||
|
||||
program
|
||||
.command("decide <index>")
|
||||
.description("Resolve a pending decision")
|
||||
.action(async (index: string) => {
|
||||
try {
|
||||
const idx = parseInt(index, 10) - 1
|
||||
await taskManager.resolvePendingDecision(idx)
|
||||
console.log("Decision resolved")
|
||||
} catch (error) {
|
||||
console.error("Failed to resolve decision:", error)
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
||||
|
||||
program.parse()
|
||||
|
|
@ -3,7 +3,6 @@ import * as path from "path"
|
|||
|
||||
interface TaskCheckpoint {
|
||||
id: string
|
||||
commit_hash: string
|
||||
description: string
|
||||
component: string
|
||||
changes: string[]
|
||||
|
|
@ -14,14 +13,10 @@ interface TaskCheckpoint {
|
|||
|
||||
interface TaskContext {
|
||||
task_id: string
|
||||
active_checkpoint: string
|
||||
status: "in_progress" | "completed" | "failed"
|
||||
created_at: string
|
||||
last_accessed: string
|
||||
description: string
|
||||
initial_commit: string
|
||||
created_at: string
|
||||
checkpoints: TaskCheckpoint[]
|
||||
test_results: {
|
||||
test_results?: {
|
||||
unit_tests: {
|
||||
passing: number
|
||||
failing: number
|
||||
|
|
@ -30,130 +25,95 @@ interface TaskContext {
|
|||
linting: string
|
||||
manual_testing: string
|
||||
}
|
||||
pending_decisions: string[]
|
||||
rollback_info: {
|
||||
full_rollback: string
|
||||
partial_rollbacks: Record<string, string>
|
||||
current_state: {
|
||||
status: "in_progress" | "completed" | "failed"
|
||||
summary?: string
|
||||
final_commit?: {
|
||||
hash: string
|
||||
message: string
|
||||
changes: string[]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TaskManager {
|
||||
private currentTaskId: string | null = null
|
||||
private currentContext: TaskContext | null = null
|
||||
private readonly logsDir = path.join(".roo", "prepare_logs")
|
||||
private readonly iterationsDir = path.join(".roo", "iterations")
|
||||
|
||||
constructor() {
|
||||
this.ensureLogsDirectory()
|
||||
this.ensureIterationsDirectory()
|
||||
}
|
||||
|
||||
private async ensureLogsDirectory() {
|
||||
private async ensureIterationsDirectory() {
|
||||
try {
|
||||
await fs.mkdir(this.logsDir, { recursive: true })
|
||||
await fs.mkdir(this.iterationsDir, { recursive: true })
|
||||
} catch (error) {
|
||||
console.error("Failed to create logs directory:", error)
|
||||
console.error("Failed to create iterations directory:", error)
|
||||
}
|
||||
}
|
||||
|
||||
async switchTask(taskId: string): Promise<void> {
|
||||
if (this.currentTaskId) {
|
||||
await this.saveCurrentTask()
|
||||
}
|
||||
await this.loadTask(taskId)
|
||||
}
|
||||
|
||||
private async saveCurrentTask(): Promise<void> {
|
||||
if (!this.currentTaskId || !this.currentContext) return
|
||||
|
||||
this.currentContext.last_accessed = new Date().toISOString()
|
||||
const logPath = this.getLogPath(this.currentTaskId)
|
||||
|
||||
await fs.writeFile(logPath, JSON.stringify(this.currentContext, null, 2), "utf-8")
|
||||
}
|
||||
|
||||
private async loadTask(taskId: string): Promise<void> {
|
||||
const logPath = this.getLogPath(taskId)
|
||||
|
||||
try {
|
||||
const content = await fs.readFile(logPath, "utf-8")
|
||||
this.currentContext = JSON.parse(content)
|
||||
this.currentTaskId = taskId
|
||||
} catch (error) {
|
||||
console.error(`Failed to load task ${taskId}:`, error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async createTask(taskId: string, description: string, initialCommit: string): Promise<void> {
|
||||
async createIteration(taskId: string, description: string): Promise<void> {
|
||||
const newTask: TaskContext = {
|
||||
task_id: taskId,
|
||||
active_checkpoint: "",
|
||||
status: "in_progress",
|
||||
created_at: new Date().toISOString(),
|
||||
last_accessed: new Date().toISOString(),
|
||||
description,
|
||||
initial_commit: initialCommit,
|
||||
created_at: new Date().toISOString(),
|
||||
checkpoints: [],
|
||||
test_results: {
|
||||
unit_tests: { passing: 0, failing: 0, pending: 0 },
|
||||
linting: "",
|
||||
manual_testing: "",
|
||||
},
|
||||
pending_decisions: [],
|
||||
rollback_info: {
|
||||
full_rollback: `git reset --hard ${initialCommit}`,
|
||||
partial_rollbacks: {},
|
||||
current_state: {
|
||||
status: "in_progress",
|
||||
},
|
||||
}
|
||||
|
||||
const logPath = this.getLogPath(taskId)
|
||||
await fs.writeFile(logPath, JSON.stringify(newTask, null, 2), "utf-8")
|
||||
|
||||
this.currentTaskId = taskId
|
||||
this.currentContext = newTask
|
||||
}
|
||||
|
||||
async addCheckpoint(checkpoint: TaskCheckpoint): Promise<void> {
|
||||
if (!this.currentContext) throw new Error("No active task")
|
||||
async addCheckpoint(taskId: string, checkpoint: TaskCheckpoint): Promise<void> {
|
||||
const logPath = this.getLogPath(taskId)
|
||||
const content = await fs.readFile(logPath, "utf-8")
|
||||
const task = JSON.parse(content) as TaskContext
|
||||
|
||||
this.currentContext.checkpoints.push(checkpoint)
|
||||
this.currentContext.active_checkpoint = checkpoint.id
|
||||
this.currentContext.rollback_info.partial_rollbacks[checkpoint.description.toLowerCase().replace(/\s+/g, "_")] =
|
||||
`git checkout ${checkpoint.commit_hash}`
|
||||
|
||||
await this.saveCurrentTask()
|
||||
task.checkpoints.push(checkpoint)
|
||||
await fs.writeFile(logPath, JSON.stringify(task, null, 2), "utf-8")
|
||||
}
|
||||
|
||||
async updateTestResults(results: TaskContext["test_results"]): Promise<void> {
|
||||
if (!this.currentContext) throw new Error("No active task")
|
||||
async updateTestResults(taskId: string, results: TaskContext["test_results"]): Promise<void> {
|
||||
const logPath = this.getLogPath(taskId)
|
||||
const content = await fs.readFile(logPath, "utf-8")
|
||||
const task = JSON.parse(content) as TaskContext
|
||||
|
||||
this.currentContext.test_results = results
|
||||
await this.saveCurrentTask()
|
||||
task.test_results = results
|
||||
await fs.writeFile(logPath, JSON.stringify(task, null, 2), "utf-8")
|
||||
}
|
||||
|
||||
async addPendingDecision(decision: string): Promise<void> {
|
||||
if (!this.currentContext) throw new Error("No active task")
|
||||
async completeIteration(taskId: string, commitInfo: TaskContext["current_state"]["final_commit"]): Promise<void> {
|
||||
const logPath = this.getLogPath(taskId)
|
||||
const content = await fs.readFile(logPath, "utf-8")
|
||||
const task = JSON.parse(content) as TaskContext
|
||||
|
||||
this.currentContext.pending_decisions.push(decision)
|
||||
await this.saveCurrentTask()
|
||||
task.current_state = {
|
||||
status: "completed",
|
||||
summary: `Successfully completed task with commit ${commitInfo.hash}`,
|
||||
final_commit: commitInfo,
|
||||
}
|
||||
|
||||
await fs.writeFile(logPath, JSON.stringify(task, null, 2), "utf-8")
|
||||
}
|
||||
|
||||
async resolvePendingDecision(index: number): Promise<void> {
|
||||
if (!this.currentContext) throw new Error("No active task")
|
||||
|
||||
this.currentContext.pending_decisions.splice(index, 1)
|
||||
await this.saveCurrentTask()
|
||||
}
|
||||
|
||||
getCurrentTask(): TaskContext | null {
|
||||
return this.currentContext
|
||||
async getIteration(taskId: string): Promise<TaskContext | null> {
|
||||
try {
|
||||
const logPath = this.getLogPath(taskId)
|
||||
const content = await fs.readFile(logPath, "utf-8")
|
||||
return JSON.parse(content) as TaskContext
|
||||
} catch (error) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
private getLogPath(taskId: string): string {
|
||||
return path.join(this.logsDir, `${taskId}.json`)
|
||||
return path.join(this.iterationsDir, `${taskId}.json`)
|
||||
}
|
||||
|
||||
async listTasks(): Promise<string[]> {
|
||||
const files = await fs.readdir(this.logsDir)
|
||||
async listIterations(): Promise<string[]> {
|
||||
const files = await fs.readdir(this.iterationsDir)
|
||||
return files.filter((file) => file.endsWith(".json")).map((file) => file.replace(".json", ""))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue