Skip to content
Merged
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
5 changes: 5 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pnpm agent readme
```

`pnpm agent readme` 是白皮书导览入口,白皮书具体路径与阅读建议以该命令输出为准。
最佳实践文件位于 `docs/white-book/00-必读/best-practices.md`,由 `pnpm agent practice` 维护。

常用命令:

Expand All @@ -35,6 +36,10 @@ pnpm agent --help # 查看帮助
pnpm agent roadmap current # 当前 Roadmap
pnpm agent toc # 白皮书目录
pnpm agent chapter <路径> # 读取白皮书章节
pnpm agent practice list # 最佳实践列表
pnpm agent practice add "<内容>"
pnpm agent practice remove <序号|内容>
pnpm agent practice update <序号> "<内容>"
pnpm agent claim <issue#> # 领取任务
pnpm agent worktree create <name> --branch <branch> --base main
pnpm agent worktree list
Expand Down
13 changes: 13 additions & 0 deletions docs/white-book/00-必读/best-practices.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# 最佳实践

> 由 `pnpm agent practice` 维护。

## 列表

- ❌ Radix Dialog / position:fixed → ✅ Stackflow BottomSheet/Modal
- ❌ React Router → ✅ Stackflow push/pop/replace
- ❌ 复制 mpay 代码 → ✅ 理解后用 React/TS 重写
- ❌ 随意创建 store → ✅ 遵循 stores/ 现有模式
- ❌ 明文选择器 → ✅ data-testid
- ❌ 安装新 UI 库 → ✅ shadcn/ui(已集成)
- ❌ 新建 CSS → ✅ Tailwind CSS
12 changes: 4 additions & 8 deletions docs/white-book/00-必读/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,10 @@ AI 模型在开发移动端 Web 应用时,往往会基于通用 Web 开发经

### 🚫 常见错误直觉

| 场景 | ❌ 错误直觉 | ✅ 正确做法 |
|------|------------|------------|
| 弹出层/Sheet | 使用 Radix Dialog、自定义 `position: fixed` | 使用 Stackflow 的 `BottomSheet` 或 `Modal` 组件 |
| 居中对话框 | 使用 Radix AlertDialog | 使用 Stackflow 的 `Modal` Activity |
| 页面导航 | 使用 React Router | 使用 Stackflow 的 `push()`、`pop()` |
| 状态管理 | 使用 Redux、Zustand 随意创建 store | 遵循 `stores/` 目录下的现有模式 |
| 样式 | 随意使用 inline style 或新建 CSS 文件 | 使用 Tailwind CSS 类名 |
| 组件库 | 随意安装 Ant Design、Material UI | 使用 shadcn/ui(已集成) |
最佳实践已独立维护在:

- `00-必读/best-practices.md`
- `pnpm agent practice list`

### 📚 为什么会出错?

Expand Down
14 changes: 14 additions & 0 deletions openspec/changes/update-agent-practices/proposal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Change: Extract best practices and add practice subcommand

## Why
最佳实践需要独立文件并可由子命令管理(读取/添加/删除/修改),当前实践内容散落在 `readme` 输出和 00-必读中,无法结构化维护。

## What Changes
- 新增独立文件 `docs/white-book/00-必读/best-practices.md`。
- 新增 `pnpm agent practice` 子命令,支持 list/add/remove/update。
- `pnpm agent readme` 的最佳实践从文件读取。
- 更新白皮书索引与文档提示,引用新的最佳实践文件与命令。

## Impact
- Affected specs: agent-cli
- Affected code: scripts/agent/cli.ts, scripts/agent/readme.ts, scripts/agent/practice.ts (new), scripts/agent/utils.ts, docs/white-book/00-必读/index.md, docs/white-book/00-必读/best-practices.md, AGENTS.md
20 changes: 20 additions & 0 deletions openspec/changes/update-agent-practices/specs/agent-cli/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
## ADDED Requirements

### Requirement: Practice subcommand
`pnpm agent practice` SHALL manage a standalone best-practices file and support list/add/remove/update.

#### Scenario: List practices
- **WHEN** the user runs `pnpm agent practice list`
- **THEN** the CLI prints the current best-practices items

#### Scenario: Add practice
- **WHEN** the user runs `pnpm agent practice add "..."`
- **THEN** the CLI appends the new practice item to the file

#### Scenario: Remove practice
- **WHEN** the user runs `pnpm agent practice remove 3`
- **THEN** the CLI removes the third practice item

#### Scenario: Update practice
- **WHEN** the user runs `pnpm agent practice update 2 "..."`
- **THEN** the CLI replaces the second practice item with the new content
6 changes: 6 additions & 0 deletions openspec/changes/update-agent-practices/tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
## 1. Implementation
- [x] 1.1 新增最佳实践独立文件并迁移内容
- [x] 1.2 新增 `pnpm agent practice` 子命令(list/add/remove/update)
- [x] 1.3 readme 输出改为读取最佳实践文件
- [x] 1.4 更新 AGENTS/白皮书索引提示
- [x] 1.5 验证:运行 `pnpm agent practice list` 与 `pnpm agent readme`
53 changes: 53 additions & 0 deletions scripts/agent/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
* pnpm agent worktree create <name> --branch <branch> [--base main]
* pnpm agent worktree delete <name> [--force]
* pnpm agent worktree list
* pnpm agent practice list|add|remove|update
*/

import { log } from './utils'
Expand All @@ -39,6 +40,7 @@ import {
} from './epic'
import { printWhiteBookToc, printChapterContent } from './whitebook'
import { createWorktree, deleteWorktree, listWorktrees } from './worktree'
import { addPractice, listPractices, removePractice, updatePractice } from './practice'

function printHelp(): void {
console.log(`
Expand Down Expand Up @@ -70,6 +72,12 @@ Worktree 管理:
pnpm agent worktree list
分支前缀仅允许: feat/, fix/, docs/, test/, refactor/, chore/, ci/, openspec/, release/

最佳实践管理:
pnpm agent practice list
pnpm agent practice add "<内容>"
pnpm agent practice remove <序号|内容>
pnpm agent practice update <序号> "<内容>"

Aliases: CURRENT -> V1, NEXT -> V2

Examples:
Expand All @@ -79,6 +87,7 @@ Examples:
pnpm agent create "修复某个问题" --category bug --roadmap v1
pnpm agent epic create "大功能" --roadmap v1 --issues 44,45,46
pnpm agent worktree create issue-28 --branch feat/issue-28
pnpm agent practice list
`)
}

Expand Down Expand Up @@ -255,6 +264,46 @@ function handleWorktree(args: string[]): void {
}
}

function handlePractice(args: string[]): void {
const subCommand = args[0]

if (!subCommand) {
listPractices()
return
}

if (subCommand === '--help' || subCommand === '-h') {
console.log('用法: pnpm agent practice list|add|remove|update')
return
}

switch (subCommand) {
case 'list':
listPractices()
break

case 'add':
addPractice(args.slice(1).join(' '))
break

case 'remove':
removePractice(args.slice(1).join(' '))
break

case 'update': {
const index = args[1]
const text = args.slice(2).join(' ')
updatePractice(index, text)
break
}

default:
log.error(`未知的 practice 子命令: ${subCommand}`)
console.log('可用命令: list, add, remove, update')
process.exit(1)
}
}

function main(): void {
const args = process.argv.slice(2)

Expand Down Expand Up @@ -344,6 +393,10 @@ function main(): void {
handleWorktree(rest)
break

case 'practice':
handlePractice(rest)
break

case 'help':
log.error('请使用 --help 查看帮助')
printHelp()
Expand Down
162 changes: 162 additions & 0 deletions scripts/agent/practice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/**
* 最佳实践管理
*/

import { readFileSync, writeFileSync, existsSync } from 'node:fs'
import { BEST_PRACTICES_FILE, log } from './utils'

interface PracticeDocument {
headerLines: string[]
items: string[]
footerLines: string[]
}

function readDocument(): PracticeDocument {
if (!existsSync(BEST_PRACTICES_FILE)) {
log.error(`最佳实践文件不存在: ${BEST_PRACTICES_FILE}`)
process.exit(1)
}

const raw = readFileSync(BEST_PRACTICES_FILE, 'utf-8')
const lines = raw.split('\n')
const listIndex = lines.findIndex(line => line.trim() === '## 列表')

if (listIndex === -1) {
log.error('最佳实践文件缺少 "## 列表" 标记')
process.exit(1)
}

const headerLines = lines.slice(0, listIndex + 1)
const itemLines: string[] = []
const footerLines: string[] = []

let inItems = true
for (let i = listIndex + 1; i < lines.length; i += 1) {
const line = lines[i]
if (inItems) {
if (line.startsWith('## ') && line.trim() !== '## 列表') {
inItems = false
footerLines.push(line)
continue
}
if (line.trim().startsWith('- ')) {
itemLines.push(line.trim().slice(2))
continue
}
if (line.trim().length === 0) {
continue
}
itemLines.push(line.trim())
continue
}
footerLines.push(line)
}

return {
headerLines,
items: itemLines,
footerLines,
}
}

function writeDocument(doc: PracticeDocument): void {
const lines = [...doc.headerLines]
lines.push('')
for (const item of doc.items) {
lines.push(`- ${item}`)
}
if (doc.footerLines.length > 0) {
lines.push('')
lines.push(...doc.footerLines)
}
const content = lines.join('\n').trimEnd() + '\n'
writeFileSync(BEST_PRACTICES_FILE, content)
}

export function listPractices(): void {
const doc = readDocument()
if (doc.items.length === 0) {
console.log('暂无最佳实践')
return
}
console.log('# 最佳实践\n')
doc.items.forEach((item, index) => {
console.log(`${index + 1}. ${item}`)
})
console.log()
}

export function addPractice(text?: string): void {
const value = text?.trim()
if (!value) {
log.error('请提供要添加的最佳实践文本')
process.exit(1)
}
const doc = readDocument()
doc.items.push(value)
writeDocument(doc)
log.success('已添加最佳实践')
}

export function removePractice(target?: string): void {
const value = target?.trim()
if (!value) {
log.error('请提供要删除的序号或文本')
process.exit(1)
}

const doc = readDocument()
const index = Number.parseInt(value, 10)
if (!Number.isNaN(index)) {
if (index < 1 || index > doc.items.length) {
log.error(`序号超出范围: ${index}`)
process.exit(1)
}
doc.items.splice(index - 1, 1)
writeDocument(doc)
log.success('已删除最佳实践')
return
}

const matchIndex = doc.items.findIndex(item => item === value)
if (matchIndex === -1) {
log.error('未找到匹配的最佳实践')
process.exit(1)
}
doc.items.splice(matchIndex, 1)
writeDocument(doc)
log.success('已删除最佳实践')
}

export function updatePractice(indexText?: string, text?: string): void {
const indexValue = indexText?.trim()
const value = text?.trim()
if (!indexValue || !value) {
log.error('请提供序号与新的最佳实践文本')
process.exit(1)
}

const index = Number.parseInt(indexValue, 10)
if (Number.isNaN(index)) {
log.error('序号必须是数字')
process.exit(1)
}

const doc = readDocument()
if (index < 1 || index > doc.items.length) {
log.error(`序号超出范围: ${index}`)
process.exit(1)
}

doc.items[index - 1] = value
writeDocument(doc)
log.success('已更新最佳实践')
}

export function printBestPracticesContent(): void {
if (!existsSync(BEST_PRACTICES_FILE)) {
log.warn(`最佳实践文件缺失: ${BEST_PRACTICES_FILE}`)
return
}
console.log(readFileSync(BEST_PRACTICES_FILE, 'utf-8'))
}
17 changes: 4 additions & 13 deletions scripts/agent/readme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,11 @@

import { fetchRoadmap, printStats } from './roadmap'
import { resolveRelease } from './utils'
import { printBestPracticesContent } from './practice'

export function printBestPractices(): void {
console.log(`
# 最佳实践

❌ Radix Dialog / position:fixed → ✅ Stackflow BottomSheet/Modal
❌ React Router → ✅ Stackflow push/pop/replace
❌ 复制 mpay 代码 → ✅ 理解后用 React/TS 重写
❌ 随意创建 store → ✅ 遵循 stores/ 现有模式
❌ 明文选择器 → ✅ data-testid
❌ 安装新 UI 库 → ✅ shadcn/ui(已集成)
❌ 新建 CSS → ✅ Tailwind CSS

详见: pnpm agent chapter 00-必读
`)
printBestPracticesContent()
console.log('详见: pnpm agent chapter 00-必读\n')
}

export function printKnowledgeMap(): void {
Expand Down Expand Up @@ -89,6 +79,7 @@ export function printWorkflow(): void {
# 工作流

pnpm agent readme 启动入口(索引 + 知识地图 + 最佳实践)
pnpm agent practice list 最佳实践列表
pnpm agent claim <#> 领取任务 (自动分配+worktree指引)
pnpm agent create "x" 创建任务 (--category bug --roadmap v1)
pnpm agent epic create "x" 创建 Epic (--roadmap v1)
Expand Down
1 change: 1 addition & 0 deletions scripts/agent/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const WORKTREE_DIR = (() => {
}
return resolve(ROOT, '.git-worktree')
})()
export const BEST_PRACTICES_FILE = resolve(WHITE_BOOK_DIR, '00-必读', 'best-practices.md')

export const PROJECT_NUMBER = 5
export const PROJECT_OWNER = 'BioforestChain'
Expand Down