Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions docs/architecture_report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Báo cáo Kiến trúc: Tích hợp Plannotator vào MiMo-Code

## 1. Tổng quan
Mục tiêu của dự án là tích hợp Plannotator vào MiMo-Code như một lớp đánh giá và lập kế hoạch có sự tham gia của con người (human-in-the-loop). Điều này cho phép người dùng xem xét, chú thích và phê duyệt các kế hoạch do MiMo-Code tạo ra trước khi thực thi, cũng như đánh giá mã nguồn sau khi triển khai.

## 2. Phân tích hiện trạng

### 2.1. MiMo-Code
MiMo-Code là một trợ lý lập trình AI chạy trên terminal, hỗ trợ nhiều agent (build, plan, compose).
- **Quy trình lập kế hoạch (Planning Pipeline):** MiMo-Code sử dụng agent `plan` và các skill trong `compose` (như `compose:plan`) để tạo ra các kế hoạch triển khai dưới dạng Markdown. Các kế hoạch này thường được lưu trong `docs/compose/plans/`.
- **Quy trình thực thi (Execution Pipeline):** Sau khi kế hoạch được tạo, agent `build` hoặc skill `compose:execute` / `compose:subagent` sẽ thực thi các bước trong kế hoạch.
- **Quy trình kiểm tra/xác minh (Verification Pipeline):** Các skill như `compose:review` và `compose:verify` được sử dụng để kiểm tra mã nguồn sau khi thực thi.

### 2.2. Plannotator
Plannotator là một công cụ đánh giá dựa trên trình duyệt dành cho các AI coding agents.
- **Quy trình đánh giá kế hoạch:** Nhận đầu vào là nội dung Markdown của kế hoạch, hiển thị giao diện người dùng để chú thích và trả về quyết định (approve/deny) cùng với phản hồi.
- **Quy trình đánh giá mã nguồn:** Nhận đầu vào là git diff hoặc URL của Pull Request, hiển thị giao diện đánh giá mã nguồn và trả về phản hồi.
- **Điểm tích hợp:** Plannotator cung cấp CLI (ví dụ: `plannotator opencode-plan`, `plannotator opencode-review`) có thể được gọi thông qua các lệnh shell.

## 3. Kiến trúc tích hợp đề xuất

Để đáp ứng yêu cầu giảm thiểu sửa đổi kiến trúc lõi của MiMo-Code, chúng tôi đề xuất xây dựng một module tích hợp (adapter/wrapper) tại `packages/opencode/src/integrations/plannotator/`.

### 3.1. Các thành phần chính

1. **`exporter.ts`**: Chuyển đổi đầu ra kế hoạch của MiMo-Code thành định dạng Markdown tương thích với Plannotator.
2. **`importer.ts`**: Phân tích các chú thích và phản hồi từ Plannotator, chuyển đổi thành cấu trúc dữ liệu mà MiMo-Code có thể hiểu được.
3. **`review_client.ts`**: Wrapper để gọi CLI của Plannotator (sử dụng `ChildProcess` hoặc `CrossSpawnSpawner` của MiMo-Code).
4. **`hooks.ts`**: Cung cấp các điểm móc (hooks) `before_execution(plan)` và `after_implementation(diff)` để chèn vào quy trình làm việc của MiMo-Code.

### 3.2. Quy trình làm việc (Workflow)

```text
User Task
MiMo Plan Generation (compose:plan)
Plannotator Plan Review (hooks.before_execution)
Approval / Revision (importer.ts xử lý phản hồi)
MiMo Execution (compose:execute / build agent)
Tests (compose:verify)
Git Diff Generation (Git.diff)
Plannotator Code Review (hooks.after_implementation)
MiMo Revision Loop (nếu có phản hồi từ review)
```

## 4. Đánh giá rủi ro và độ phức tạp

- **Rủi ro:** Việc gọi CLI của Plannotator có thể bị chặn (block) luồng thực thi của MiMo-Code nếu không được xử lý bất đồng bộ đúng cách.
- **Độ phức tạp:** Trung bình. Việc tạo module tích hợp khá độc lập, nhưng việc chèn hooks vào quy trình `compose` hiện tại đòi hỏi sự cẩn thận để không làm hỏng các luồng hiện có.
- **Giải pháp:** Sử dụng thư viện `effect` (đang được MiMo-Code sử dụng rộng rãi) để quản lý các tác vụ bất đồng bộ và lỗi khi gọi Plannotator CLI.

## 5. Kế hoạch triển khai

1. Tạo thư mục `packages/opencode/src/integrations/plannotator/`.
2. Triển khai các tệp `exporter.ts`, `importer.ts`, `review_client.ts`, và `hooks.ts`.
3. Cập nhật `packages/opencode/src/skill/compose/.bundle/execute/SKILL.md` hoặc tạo một workflow mới để tích hợp các hooks.
4. Xây dựng pipeline tạo bộ dữ liệu (Dataset Generation) lưu trữ tại `datasets/plannotator_feedback/`.
5. Viết tài liệu hướng dẫn và kiểm thử.
46 changes: 46 additions & 0 deletions docs/integration_architecture.d2
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
direction: right

User: "Người dùng"
MiMoCode: "MiMo-Code"
Plannotator: "Plannotator"
IntegrationModule: "Module tích hợp Plannotator"
Dataset: "Bộ dữ liệu phản hồi"

User -> MiMoCode: "Yêu cầu tác vụ"

subgraph MiMoCode {
MiMoPlanGeneration: "Sinh kế hoạch MiMo"
MiMoExecution: "Thực thi MiMo"
MiMoTests: "Kiểm thử MiMo"
MiMoGitDiff: "Sinh Git Diff"
MiMoRevisionLoop: "Vòng lặp sửa đổi MiMo"
}

subgraph IntegrationModule {
Exporter: "exporter.ts"
ReviewClient: "review_client.ts"
Importer: "importer.ts"
Hooks: "hooks.ts"
}

subgraph Plannotator {
PlannotatorPlanReview: "Đánh giá kế hoạch Plannotator"
PlannotatorCodeReview: "Đánh giá mã nguồn Plannotator"
}

MiMoPlanGeneration -> Exporter: "Kế hoạch MiMo"
Exporter -> ReviewClient: "Kế hoạch đã xuất"
ReviewClient -> PlannotatorPlanReview: "Gửi kế hoạch"
PlannotatorPlanReview -> Importer: "Phản hồi Plannotator"
Importer -> Hooks: "Phản hồi đã nhập"
Hooks -> MiMoExecution: "Phê duyệt / Từ chối"

MiMoExecution -> MiMoTests: "Mã đã thực thi"
MiMoTests -> MiMoGitDiff: "Kết quả kiểm thử"
MiMoGitDiff -> ReviewClient: "Git Diff"
ReviewClient -> PlannotatorCodeReview: "Gửi Git Diff"
PlannotatorCodeReview -> Importer: "Phản hồi đánh giá mã nguồn"
Importer -> MiMoRevisionLoop: "Phản hồi đã nhập"
MiMoRevisionLoop -> MiMoPlanGeneration: "Yêu cầu sửa đổi"

Hooks -> Dataset: "Lưu phản hồi"
Binary file added docs/integration_architecture.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
43 changes: 43 additions & 0 deletions docs/plannotator_integration_guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Hướng dẫn Tích hợp Plannotator vào MiMo-Code

Tài liệu này hướng dẫn cách sử dụng và cấu trúc của module tích hợp Plannotator trong MiMo-Code.

## 1. Cấu trúc thư mục
Module tích hợp nằm tại `packages/opencode/src/integrations/plannotator/`:
- `exporter.ts`: Chuyển đổi kế hoạch MiMo sang Markdown.
- `importer.ts`: Phân tích phản hồi từ Plannotator.
- `review_client.ts`: Giao tiếp với CLI Plannotator.
- `hooks.ts`: Các điểm móc cho quy trình lập kế hoạch và thực thi.
- `skill.ts`: Workflow cấp cao để sử dụng trong MiMo-Code.

## 2. Cách thức hoạt động

### 2.1. Đánh giá kế hoạch (Plan Review)
Khi một kế hoạch được tạo ra, nó sẽ được gửi đến Plannotator qua lệnh `opencode-plan`. Người dùng sẽ thấy một giao diện web để phê duyệt hoặc yêu cầu thay đổi.
- **Approve**: MiMo-Code tiếp tục thực thi.
- **Reject**: MiMo-Code nhận phản hồi và quay lại bước lập kế hoạch.

### 2.2. Đánh giá mã nguồn (Code Review)
Sau khi mã nguồn được viết, MiMo-Code có thể gửi `git diff` đến Plannotator qua lệnh `opencode-review`.

### 2.3. Lưu trữ dữ liệu (Dataset Generation)
Mọi tương tác đều được lưu lại tại `datasets/plannotator_feedback/` dưới dạng JSON để phục vụ việc huấn luyện mô hình trong tương lai.

## 3. Cài đặt
Đảm bảo `plannotator` đã được cài đặt trong hệ thống và có thể truy cập từ dòng lệnh.
```bash
curl -fsSL https://plannotator.ai/install.sh | bash
```

## 4. Sử dụng trong Skill Compose
Bạn có thể gọi `PlannotatorWorkflow.reviewPlan` trong các skill như `compose:plan` để kích hoạt việc đánh giá.

```typescript
import { PlannotatorWorkflow } from "../../integrations/plannotator/skill"

// Trong logic lập kế hoạch
const result = yield* PlannotatorWorkflow.reviewPlan(planContent, taskTitle)
if (result.status === "approved") {
// Tiếp tục
}
```
20 changes: 20 additions & 0 deletions packages/opencode/src/integrations/plannotator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Plannotator Integration for MiMo-Code

This module integrates [Plannotator](https://github.com/backnotprop/plannotator) into MiMo-Code as a human-in-the-loop review layer.

## Components

- `exporter.ts`: Converts MiMo plans to Plannotator-compatible Markdown.
- `importer.ts`: Parses Plannotator feedback and approval status.
- `review_client.ts`: Handles CLI communication with Plannotator.
- `hooks.ts`: Provides integration points for the planning and execution workflows.

## Workflow

1. **Plan Review**: Before executing a plan, it is sent to Plannotator for human approval.
2. **Code Review**: After implementation, the changes (git diff) are sent to Plannotator for review.
3. **Dataset Generation**: All reviews and feedback are stored in `datasets/plannotator_feedback/` for future model fine-tuning.

## Usage

The integration is designed to be used within MiMo-Code's `compose` skills or workflows.
16 changes: 16 additions & 0 deletions packages/opencode/src/integrations/plannotator/exporter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Instance } from "../../project/instance"
import path from "path"

/**
* Chuyển đổi kế hoạch của MiMo-Code sang định dạng Markdown tương thích với Plannotator.
*/
export function exportPlan(planContent: string, taskTitle: string): string {
const header = `# Task\n\n${taskTitle}\n\n# Plan\n\n`

// Kiểm tra nếu kế hoạch đã có tiêu đề hoặc định dạng Markdown phù hợp
if (planContent.trim().startsWith("#")) {
return `${header}${planContent}`
}

return `${header}${planContent}`
}
Comment on lines +1 to +16
52 changes: 52 additions & 0 deletions packages/opencode/src/integrations/plannotator/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Effect } from "effect"
import { makeReviewClient } from "./review_client"
import { exportPlan } from "./exporter"
import { importFeedback, PlannotatorFeedback } from "./importer"
import { AppFileSystem } from "../../file"
import path from "path"
Comment on lines +4 to +6

export const PlannotatorHooks = Effect.gen(function* () {
const client = yield* makeReviewClient
const fsys = yield* AppFileSystem.Service

const saveToDataset = (data: any) => Effect.gen(function* () {
const datasetDir = "datasets/plannotator_feedback"
yield* fsys.mkdir(datasetDir, { recursive: true })
const filename = `${Date.now()}.json`
yield* fsys.write(path.join(datasetDir, filename), JSON.stringify(data, null, 2))
})

return {
beforeExecution: (plan: string, taskTitle: string) =>
Effect.gen(function* () {
const exportedPlan = exportPlan(plan, taskTitle)
const output = yield* client.submitPlan(exportedPlan)
const feedback = importFeedback(output)

yield* saveToDataset({
type: "plan_review",
task: taskTitle,
original_plan: plan,
review_comments: feedback.comments.join("\n"),
approval: feedback.approval_status === "approved"
})

return feedback
}),

afterImplementation: (diff: string, description: string) =>
Effect.gen(function* () {
const output = yield* client.submitDiff(diff, description)
const feedback = importFeedback(output)

yield* saveToDataset({
type: "code_review",
code_diff: diff,
review_comments: feedback.comments.join("\n"),
approval: feedback.approval_status === "approved"
})

return feedback
})
}
})
33 changes: 33 additions & 0 deletions packages/opencode/src/integrations/plannotator/importer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* Phân tích phản hồi từ Plannotator.
*/
export interface PlannotatorFeedback {
comments: string[]
changes_requested: string[]
approval_status: "approved" | "rejected" | "dismissed"
}

export function importFeedback(plannotatorOutput: any): PlannotatorFeedback {
const decision = plannotatorOutput.decision
const feedback = plannotatorOutput.feedback || ""

const result: PlannotatorFeedback = {
comments: [],
changes_requested: [],
approval_status: "dismissed"
}

if (decision === "submitted") {
result.approval_status = plannotatorOutput.result?.approved ? "approved" : "rejected"
if (feedback) {
result.comments.push(feedback)
if (result.approval_status === "rejected") {
result.changes_requested.push(feedback)
}
}
} else if (decision === "dismissed") {
result.approval_status = "dismissed"
}

return result
}
78 changes: 78 additions & 0 deletions packages/opencode/src/integrations/plannotator/review_client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { Effect } from "effect"
import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
import { Stream } from "effect"
import { tmpdir } from "node:os"
import path from "node:path"
import { randomUUID } from "node:crypto"
import { AppFileSystem } from "../../file"

export interface ReviewClient {
submitPlan(plan: string): Effect.Effect<any, Error>
submitDiff(diff: string, description: string): Effect.Effect<any, Error>
}
Comment on lines +7 to +12

export const makeReviewClient = Effect.gen(function* () {
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner
const fsys = yield* AppFileSystem.Service

const runPlannotator = (mode: string, input: any) =>
Effect.gen(function* () {
const plannotatorBin = process.env.PLANNOTATOR_BIN || "plannotator"
const readyFile = path.join(
tmpdir(),
`plannotator-mimo-${process.pid}-${Date.now()}-${randomUUID()}.jsonl`
)

const env = {
...process.env,
PLANNOTATOR_ORIGIN: "mimo-code",
PLANNOTATOR_READY_FILE: readyFile,
}

const proc = ChildProcess.make(plannotatorBin, [mode], {
stdin: "pipe",
stdout: "pipe",
stderr: "pipe",
env,
})

const handle = yield* spawner.spawn(proc)

// Gửi input qua stdin
const inputStr = JSON.stringify(input)
yield* Stream.fromIterable([Buffer.from(inputStr)]).pipe(
Stream.run(handle.stdin)
)

const [stdout, stderr] = yield* Effect.all(
[
Stream.mkString(Stream.decodeText(handle.stdout)),
Stream.mkString(Stream.decodeText(handle.stderr))
],
{ concurrency: 2 }
)

const exitCode = yield* handle.exitCode

// Dọn dẹp ready file
if (yield* fsys.existsSafe(readyFile)) {
yield* fsys.remove(readyFile).pipe(Effect.ignore)
}

if (exitCode !== 0) {
return yield* Effect.fail(new Error(`Plannotator failed (exit ${exitCode}): ${stderr}`))
}

try {
return JSON.parse(stdout)
} catch (e) {
return yield* Effect.fail(new Error(`Failed to parse Plannotator output: ${stdout}`))
}
Comment on lines +66 to +70
}).pipe(Effect.scoped)

return {
submitPlan: (plan: string) => runPlannotator("opencode-plan", { plan }),
submitDiff: (diff: string, description: string) =>
runPlannotator("opencode-review", { arguments: "--git", description, diff }),
}
})
38 changes: 38 additions & 0 deletions packages/opencode/src/integrations/plannotator/skill.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Effect } from "effect"
import { PlannotatorHooks } from "./hooks"
import { Question } from "../../question"

/**
* Tích hợp Plannotator vào quy trình compose của MiMo-Code.
*/
export const PlannotatorWorkflow = Effect.gen(function* () {
const hooks = yield* PlannotatorHooks
const question = yield* Question.Service

Comment on lines +1 to +11
return {
/**
* Chèn bước review kế hoạch vào quy trình.
*/
reviewPlan: (plan: string, taskTitle: string) =>
Effect.gen(function* () {
const feedback = yield* hooks.beforeExecution(plan, taskTitle)

if (feedback.approval_status === "approved") {
return { status: "approved", feedback }
} else if (feedback.approval_status === "rejected") {
return { status: "rejected", feedback }
} else {
return { status: "dismissed", feedback }
}
}),

/**
* Chèn bước review mã nguồn vào quy trình.
*/
reviewCode: (diff: string, description: string) =>
Effect.gen(function* () {
const feedback = yield* hooks.afterImplementation(diff, description)
return feedback
})
}
})