凌晨3点47分,我看着一份20页的项目规划文档发呆。突然想起Manus——那个被$2B收购的AI项目管理工具——它的核心秘密不是AI多聪明,而是持久化的Markdown规划文件。
Manus式规划的核心思想:
| 问题 | 无持久化 | Manus式持久化 |
|---|---|---|
| Agent重启 | ❌ 丢失所有规划 | ✅ 从文件恢复 |
| 长周期项目 | ❌ 上下文超限 | ✅ 文件按需加载 |
| 多人协作 | ❌ 无法共享 | ✅ Git版本控制 |
| 进度追踪 | ❌ 动态调整困难 | ✅ 实时更新状态 |
| 审计回溯 | ❌ 无记录 | ✅ Git历史完整 |
skills/planning-with-files/
├── SKILL.md # Skill描述
├── index.js # 主逻辑
├── templates/ # 规划模板
│ ├── project.md # 项目规划模板
│ ├── sprint.md # 迭代规划模板
│ └── task.md # 任务模板
├── utils/
│ ├── parser.js # Markdown解析
│ ├── tracker.js # 进度追踪
│ └── validator.js # 规划校验
└── tests/
└── planning.test.js
# planning-with-files Manus-style persistent markdown planning for OpenClaw. ## Description Implements persistent project planning using markdown files, enabling long-running projects with task decomposition, progress tracking, and dynamic plan adjustments. ## Usage - "Create a project plan for [project name]" - "Update task [task-id] to completed" - "Show me the current project status" - "Add a new task to sprint 2" ## Features - ✅ Persistent markdown-based planning - ✅ Automatic task dependency tracking - ✅ Progress visualization - ✅ Git integration for version control - ✅ Context-efficient (load only what's needed)
import fs from 'fs';
import path from 'path';
export default {
name: "planning-with-files",
description: "Manus-style persistent markdown planning",
async execute(context, query) {
const planDir = context.config.planDir || './plans';
await fs.promises.mkdir(planDir, { recursive: true });
if (query.match(/create.*plan/i)) {
return await this.createProjectPlan(context, query, planDir);
} else if (query.match(/update.*task/i)) {
return await this.updateTask(context, query, planDir);
} else if (query.match(/status|progress/i)) {
return await this.showProgress(context, planDir);
}
},
async createProjectPlan(context, query, planDir) {
const projectName = query.match(/for\s+(.+?)(?:\s|$)/i)?.[1] || 'New Project';
const planFile = path.join(planDir, `${this.slugify(projectName)}.md`);
const planContent = `# ${projectName} - Project Plan
> Created: ${new Date().toISOString().split('T')[0]}
> Status: 🟡 In Progress
## 📊 Overview
- **Goal**: [Define your project goal]
- **Timeline**: [Start date] → [End date]
- **Owner**: [Team/Person]
## 🎯 Milestones
### Milestone 1: [Name] (Due: YYYY-MM-DD)
- [ ] Task 1.1: [Description]
- [ ] Task 1.2: [Description]
### Milestone 2: [Name] (Due: YYYY-MM-DD)
- [ ] Task 2.1: [Description]
## ✅ Completed Tasks
*(Automatically updated when tasks are marked complete)*
## 📝 Notes
- Add project-specific notes here
`;
await fs.promises.writeFile(planFile, planContent, 'utf8');
return `✅ Project plan created: ${planFile}\n\n${planContent}`;
},
async updateTask(context, query, planDir) {
const taskMatch = query.match(/task\s+([\w.-]+)/i);
if (!taskMatch) return "❌ Please specify a task ID (e.g., '1.1' or '2.3')";
const taskId = taskMatch[1];
// Find and update the task in the markdown file
const files = await fs.promises.readdir(planDir);
const planFile = files.find(f => f.endsWith('.md'));
if (!planFile) return "❌ No project plan found. Create one first.";
let content = await fs.promises.readFile(path.join(planDir, planFile), 'utf8');
// Toggle task status
const taskRegex = new RegExp(`(- \\[)([ x])(\\]\\s+Task\\s+${taskId.replace('.', '\\.')})`, 'i');
const match = content.match(taskRegex);
if (!match) return `❌ Task ${taskId} not found in the plan.`;
const newStatus = match[2] === ' ' ? 'x' : ' ';
content = content.replace(taskRegex, `$1${newStatus}$3`);
// Move completed tasks to the "Completed" section
if (newStatus === 'x') {
const completedSection = '## ✅ Completed Tasks\n';
const insertPos = content.indexOf(completedSection) + completedSection.length;
const taskLine = `- [x] Task ${taskId} (completed: ${new Date().toISOString().split('T')[0]})\n`;
content = content.slice(0, insertPos) + taskLine + content.slice(insertPos);
}
await fs.promises.writeFile(path.join(planDir, planFile), content, 'utf8');
return `✅ Task ${taskId} marked as ${newStatus === 'x' ? 'completed ✅' : 'pending ⏳'}`;
},
async showProgress(context, planDir) {
const files = await fs.promises.readdir(planDir);
const planFile = files.find(f => f.endsWith('.md'));
if (!planFile) return "❌ No project plan found.";
const content = await fs.promises.readFile(path.join(planDir, planFile), 'utf8');
const totalTasks = (content.match(/- \[[ x]\]/g) || []).length;
const completedTasks = (content.match(/- \[x\]/g) || []).length;
const progress = totalTasks > 0 ? Math.round((completedTasks / totalTasks) * 100) : 0;
const progressBar = '█'.repeat(Math.floor(progress / 5)) + '░'.repeat(20 - Math.floor(progress / 5));
return `📊 **Project Progress**
${progressBar} ${progress}%
- Total Tasks: ${totalTasks}
- Completed: ${completedTasks}
- Remaining: ${totalTasks - completedTasks}
📄 Plan file: ${planFile}`;
},
slugify(text) {
return text.toLowerCase().replace(/[^\w]+/g, '-').replace(/^-+|-+$/g, '');
}
};
一个标准的规划文件应该包含:
# [项目名称] - Project Plan > Created: 2026-05-24 > Status: 🟡 In Progress > Owner: AI Team ## 📊 Overview - **Goal**: 实现OpenClaw多Agent协作框架 - **Timeline**: 2026-05-24 → 2026-06-30 - **Priority**: P0 ## 🎯 Milestones ### M1: 基础架构 (Due: 2026-06-07) - [x] Task 1.1: 设计Agent通信协议 (completed: 2026-05-25) - [x] Task 1.2: 实现消息队列 - [ ] Task 1.3: 编写单元测试 ### M2: Skills集成 (Due: 2026-06-21) - [ ] Task 2.1: 开发Skills加载器 - [ ] Task 2.2: 实现热重载机制 ## ✅ Completed Tasks - [x] Task 1.1: 设计Agent通信协议 (completed: 2026-05-25) ## 📝 Notes - 使用Redis做消息队列 - Skills热重载需要文件监听
用户: "Create a project plan for OpenClaw Discord Bot" Agent (使用planning-with-files skill): ✅ Project plan created: ./plans/openclaw-discord-bot.md # OpenClaw Discord Bot - Project Plan > Created: 2026-05-24 > Status: 🟡 In Progress ## 📊 Overview - **Goal**: 开发Discord语音跟随功能 - **Timeline**: 2026-05-24 → 2026-06-15 ...
用户: "Update task 1.1 to completed" Agent: ✅ Task 1.1 marked as completed ✅ (规划文件自动更新,任务移到"Completed Tasks"区)
用户: "Show me the current project status" Agent: 📊 **Project Progress** ████████████░░░░░░░░░░░░ 50% - Total Tasks: 10 - Completed: 5 - Remaining: 5
# 方法1: 从ClawHub安装 clawhub install planning-with-files # 方法2: 手动安装 cp -r planning-with-files ~/.openclaw/skills/ # 验证安装 openclaw skills list | grep planning
// ~/.openclaw/config.json
{
"skills": {
"planning-with-files": {
"enabled": true,
"planDir": "./project-plans",
"autoGitCommit": true,
"gitMessage": "chore: update project plan"
}
}
}
自动将规划文件变更提交到Git:
{
"autoGitCommit": true,
"gitHook": {
"preCommit": "npm test",
"postCommit": "git push origin main"
}
}
答: 拆分为多个文件:
plans/
├── project-overview.md # 总览
├── milestones/
│ ├── m1-architecture.md
│ ├── m2-skills.md
│ └── m3-deployment.md
└── daily/
├── 2026-05-24.md
└── 2026-05-25.md
答: 将 plans/ 目录加入Git版本控制,团队成员克隆仓库即可同步。
答: 规划状态全部存储在Markdown文件中,Agent重启后只需重新读取文件即可恢复。无需额外的状态管理。