chore: 初始化仓库

This commit is contained in:
Lydanne
2026-02-15 22:02:21 +08:00
commit 08d011d63f
381 changed files with 87202 additions and 0 deletions

View File

@@ -0,0 +1,200 @@
# 核心模块
`@spaceflow/core` 提供的共享模块,所有 Extension 都可以导入使用。
## 模块总览
| 模块 | 导出 | 说明 |
|------|------|------|
| Git Provider | `GitProviderModule`, `GitProviderService` | Git 平台适配GitHub / Gitea |
| Git SDK | `GitSdkModule`, `GitSdkService` | Git 命令封装 |
| LLM Proxy | `LlmProxyModule`, `LlmProxyService` | 多 LLM 统一代理 |
| Feishu SDK | `FeishuSdkModule`, `FeishuSdkService` | 飞书 API |
| Storage | `StorageModule`, `StorageService` | 本地存储 |
| Parallel | `ParallelModule`, `ParallelService` | 并行执行工具 |
| Logger | `Logger` | 日志系统 |
| i18n | `t()`, `initI18n()`, `addLocaleResources()` | 国际化 |
| Config | `loadSpaceflowConfig()` | 配置加载 |
## Git Provider
Git 平台适配器,支持 GitHub 和 Gitea。
```typescript
import { GitProviderModule, GitProviderService } from "@spaceflow/core";
@Module({
imports: [GitProviderModule.forFeature()],
})
export class MyModule {}
@Injectable()
export class MyService {
constructor(private readonly gitProvider: GitProviderService) {}
async createComment(prNumber: number, body: string): Promise<void> {
await this.gitProvider.createPRComment(prNumber, body);
}
async listFiles(prNumber: number): Promise<string[]> {
const files = await this.gitProvider.getPRFiles(prNumber);
return files.map((f) => f.filename);
}
}
```
### 支持的平台
| 平台 | 适配器 | 检测方式 |
|------|--------|----------|
| GitHub | `GithubAdapter` | `GITHUB_TOKEN` 环境变量 |
| Gitea | `GiteaAdapter` | `GITEA_TOKEN` 环境变量 |
默认使用 GitHub。可通过 `GIT_PROVIDER_TYPE` 环境变量显式指定。
## Git SDK
封装常用 Git 命令操作。
```typescript
import { GitSdkModule, GitSdkService } from "@spaceflow/core";
@Injectable()
export class MyService {
constructor(private readonly gitSdk: GitSdkService) {}
async getChanges(): Promise<string> {
return this.gitSdk.diff("main", "HEAD");
}
async getCurrentBranch(): Promise<string> {
return this.gitSdk.currentBranch();
}
}
```
## LLM Proxy
多 LLM 统一代理,支持 OpenAI、Claude、Claude Code、OpenCode 等。
```typescript
import { LlmProxyModule, LlmProxyService } from "@spaceflow/core";
@Injectable()
export class MyService {
constructor(private readonly llm: LlmProxyService) {}
async analyze(code: string): Promise<string> {
const result = await this.llm.chat({
messages: [
{ role: "system", content: "你是一个代码分析助手" },
{ role: "user", content: `分析以下代码:\n${code}` },
],
});
return result.content;
}
}
```
## Logger
日志系统,支持 Plain 模式CI/管道)和 TUI 模式(终端交互)。
```typescript
import { Logger } from "@spaceflow/core";
const logger = new Logger("my-extension");
await logger.init(); // 加载 TUI 渲染器
// 基础日志
logger.info("信息");
logger.success("成功");
logger.warn("警告");
logger.error("错误");
// Spinner
const spinner = logger.spin("处理中...");
spinner.update("仍在处理...");
spinner.succeed("处理完成");
// 进度条
const progress = logger.progress({ total: 100, label: "下载" });
progress.update(50);
progress.finish();
// 子 Logger
const child = logger.child("compile");
child.info("编译中..."); // 输出: [my-extension:compile] 编译中...
```
### 日志级别
| 级别 | 说明 |
|------|------|
| `silent` | 不输出任何日志 |
| `info` | 默认级别 |
| `verbose` | 详细日志(`-v` |
| `debug` | 调试日志(`-vv` |
## Feishu SDK
飞书 API 封装,支持发送消息。
```typescript
import { FeishuSdkModule, FeishuSdkService } from "@spaceflow/core";
@Injectable()
export class MyService {
constructor(private readonly feishu: FeishuSdkService) {}
async notify(chatId: string, message: string): Promise<void> {
await this.feishu.sendMessage(chatId, message);
}
}
```
需要配置环境变量 `FEISHU_APP_ID``FEISHU_APP_SECRET`
## Storage
本地存储服务。
```typescript
import { StorageModule, StorageService } from "@spaceflow/core";
@Injectable()
export class MyService {
constructor(private readonly storage: StorageService) {}
async saveData(key: string, data: unknown): Promise<void> {
await this.storage.set(key, data);
}
async loadData<T>(key: string): Promise<T | undefined> {
return this.storage.get<T>(key);
}
}
```
## Parallel
并行执行工具,支持并发控制。
```typescript
import { ParallelModule, ParallelService } from "@spaceflow/core";
@Injectable()
export class MyService {
constructor(private readonly parallel: ParallelService) {}
async processFiles(files: string[]): Promise<void> {
await this.parallel.run(
files,
async (file) => {
// 处理单个文件
},
{ concurrency: 10 },
);
}
}
```

View File

@@ -0,0 +1,179 @@
# GitHub Actions
Spaceflow 提供预配置的 GitHub Actions可以在 CI 中自动执行各种命令。
## 官方 Action
### 基本用法
```yaml
- uses: nicepkg/spaceflow/actions@main
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
command: review
```
### 输入参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `github-token` | ✅ | GitHub Token |
| `github-server-url` | ❌ | GitHub Server URL默认自动检测 |
| `command` | ✅ | 要执行的 spaceflow 命令 |
## 预配置工作流
### 自动 PR 审查
在 PR 创建或更新时自动执行 AI 代码审查:
```yaml
# .github/workflows/pr-review.yml
name: PR Review
on:
pull_request:
types: [opened, synchronize]
jobs:
review:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: nicepkg/spaceflow/actions@main
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
command: review
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
```
### 手动触发 PR 审查
通过 PR 评论触发审查(如输入 `/review`
```yaml
# .github/workflows/pr-review-command.yml
name: PR Review Command
on:
issue_comment:
types: [created]
jobs:
review:
if: >
github.event.issue.pull_request &&
contains(github.event.comment.body, '/review')
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: nicepkg/spaceflow/actions@main
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
command: review
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
```
### 自动版本发布
```yaml
# .github/workflows/publish.yml
name: Publish
on:
push:
branches: [main]
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 20
registry-url: https://registry.npmjs.org
- run: pnpm install --frozen-lockfile
- uses: nicepkg/spaceflow/actions@main
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
command: publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
```
### 执行自定义命令
```yaml
# .github/workflows/test-command.yml
name: Run Command
on:
workflow_dispatch:
inputs:
command:
description: "要执行的 spaceflow 命令"
required: true
jobs:
run:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: nicepkg/spaceflow/actions@main
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
command: ${{ github.event.inputs.command }}
```
## 环境变量配置
在 GitHub 仓库的 Settings → Secrets and variables → Actions 中配置:
| Secret | 说明 |
|--------|------|
| `OPENAI_API_KEY` | OpenAI API Keyreview 命令需要) |
| `ANTHROPIC_API_KEY` | Anthropic API Key可选 |
| `NPM_TOKEN` | npm 发布 Tokenpublish 命令需要) |
| `FEISHU_APP_ID` | 飞书 App ID通知需要 |
| `FEISHU_APP_SECRET` | 飞书 App Secret通知需要 |
::: tip
`GITHUB_TOKEN` 由 GitHub Actions 自动注入,无需手动配置。
:::
## 权限配置
根据使用的命令,需要配置不同的权限:
| 命令 | 需要的权限 |
|------|-----------|
| `review` | `contents: read`, `pull-requests: write` |
| `publish` | `contents: write`, `packages: write` |
| `ci-scripts` | `contents: read` |
| `ci-shell` | `contents: read` |

156
docs/advanced/i18n.md Normal file
View File

@@ -0,0 +1,156 @@
# i18n 国际化
Spaceflow 基于 [i18next](https://www.i18next.com/) 实现国际化,采用纯函数式设计,不依赖 NestJS DI。
## 语言检测优先级
1. 环境变量 `SPACEFLOW_LANG`
2. `spaceflow.json` 中的 `lang` 字段
3. 系统 locale`process.env.LANG` / `process.env.LC_ALL`
4. 回退到 `zh-CN`
## 核心 API
### `initI18n(lang?: string)`
同步初始化 i18next。必须在 NestJS 模块加载前调用。
```typescript
import { initI18n } from "@spaceflow/core";
initI18n(); // 自动检测语言
initI18n("en"); // 手动指定
```
### `t(key: string, options?: Record<string, unknown>)`
全局翻译函数,装饰器和运行时均可使用。
```typescript
import { t } from "@spaceflow/core";
// 公共 key默认命名空间
t("common.executionFailed", { error: msg });
// Extension 命名空间(用 : 分隔)
t("build:description");
t("review:options.dryRun");
```
### `addLocaleResources(ns: string, resources: Record<string, Record<string, string>>)`
注册 Extension 的语言资源到指定命名空间。
```typescript
import { addLocaleResources } from "@spaceflow/core";
addLocaleResources("hello", {
"zh-CN": { description: "打招呼命令" },
en: { description: "Say hello" },
});
```
## 命名空间规则
| 类型 | 命名空间 | 示例 |
|------|----------|------|
| 公共 key | 默认(`translation` | `common.*`, `config.*`, `extensionLoader.*` |
| 内部 Extension | Extension 名称 | `build:`, `dev:`, `commit:`, `install:` 等 |
| 外部 Extension | Extension 名称 | `review:`, `publish:`, `ci-scripts:` 等 |
## 语言包结构
### core 公共语言包
```text
core/src/locales/
├── zh-cn/
│ └── translation.json # 公共 key
└── en/
└── translation.json
```
### Extension 语言包
```text
cli/src/commands/<name>/locales/
├── zh-cn/
│ └── <name>.json
├── en/
│ └── <name>.json
└── index.ts # 导入并注册资源
```
## Extension 中使用 i18n
### 1. 创建语言包文件
```json
// locales/zh-cn/hello.json
{
"description": "打招呼命令",
"options.name": "名字",
"greeting": "你好,{{name}}"
}
```
### 2. 注册语言资源
```typescript
// locales/index.ts
import zhCN from "./zh-cn/hello.json";
import en from "./en/hello.json";
import { addLocaleResources } from "@spaceflow/core";
export const helloLocales = { "zh-CN": zhCN, en };
// Side-effect: 立即注册
addLocaleResources("hello", helloLocales);
```
### 3. 在命令中使用
```typescript
import { t } from "@spaceflow/core";
// 必须在命令模块 import 之前导入 localesside-effect
import "./locales";
@Command({
name: "hello",
description: t("hello:description"),
})
export class HelloCommand extends CommandRunner {
// ...
}
```
### 4. 在 Extension 入口声明
```typescript
export class HelloExtension implements SpaceflowExtension {
getMetadata(): SpaceflowExtensionMetadata {
return {
name: "hello",
commands: ["hello"],
locales: helloLocales, // 加载时自动注册
};
}
}
```
## 插值语法
使用 i18next 默认的 `{{variable}}` 语法:
```typescript
t("hello:greeting", { name: "World" });
// → "你好World"
```
## 注意事项
- **同步初始化** — `initI18n()` 使用 `initSync`,确保装饰器执行时语言包已加载
- **装饰器时机** — `@Command` / `@Option` 在 import 时执行,`initI18n()` 必须在所有命令模块 import 之前调用
- **Side-effect import** — 每个 Extension 的 `locales/index.ts` 在导入时立即调用 `addLocaleResources`
- **key 不存在时** — i18next 默认返回 key 本身,不会报错

View File

@@ -0,0 +1,286 @@
# 插件开发指南
本指南介绍如何开发自定义 Spaceflow Extension。
## 前置知识
- [NestJS](https://nestjs.com/) 基础Module、Injectable、依赖注入
- [nest-commander](https://docs.nestjs.com/recipes/nest-commander) CLI 命令定义
- TypeScript
## 快速开始
### 使用模板创建
```bash
spaceflow create my-extension
```
这会在当前目录下创建一个标准的 Extension 模板。
### 手动创建
```bash
mkdir spaceflow-plugin-hello
cd spaceflow-plugin-hello
pnpm init
pnpm add @spaceflow/core
pnpm add -D typescript @types/node
```
## 目录结构
```text
spaceflow-plugin-hello/
├── src/
│ ├── hello.command.ts # 命令定义
│ ├── hello.service.ts # 业务逻辑
│ ├── hello.module.ts # NestJS 模块
│ ├── locales/ # i18n 资源
│ │ ├── zh-cn/
│ │ │ └── hello.json
│ │ ├── en/
│ │ │ └── hello.json
│ │ └── index.ts
│ └── index.ts # Extension 入口
├── package.json
├── tsconfig.json
└── README.md
```
## Extension 入口
每个 Extension 必须实现 `SpaceflowExtension` 接口:
```typescript
// src/index.ts
import type {
SpaceflowExtension,
SpaceflowExtensionMetadata,
} from "@spaceflow/core";
import { HelloModule } from "./hello.module";
import { helloLocales } from "./locales";
export class HelloExtension implements SpaceflowExtension {
getMetadata(): SpaceflowExtensionMetadata {
return {
name: "hello",
commands: ["hello"],
configKey: "hello",
description: "示例 Extension",
locales: helloLocales,
};
}
getModule() {
return HelloModule;
}
}
export default HelloExtension;
```
## 命令定义
使用 `@Command` 装饰器定义 CLI 命令:
```typescript
// src/hello.command.ts
import { Command, CommandRunner, Option } from "@spaceflow/core";
import { t } from "@spaceflow/core";
import { HelloService } from "./hello.service";
interface HelloOptions {
readonly name?: string;
}
@Command({
name: "hello",
description: t("hello:description"),
})
export class HelloCommand extends CommandRunner {
constructor(private readonly helloService: HelloService) {
super();
}
async run(_params: string[], options: HelloOptions): Promise<void> {
const name = options.name ?? "World";
console.log(this.helloService.greet(name));
}
@Option({
flags: "-n, --name <name>",
description: t("hello:options.name"),
})
parseName(val: string): string {
return val;
}
}
```
## 业务逻辑
```typescript
// src/hello.service.ts
import { Injectable } from "@spaceflow/core";
@Injectable()
export class HelloService {
greet(name: string): string {
return `Hello, ${name}!`;
}
}
```
## NestJS 模块
```typescript
// src/hello.module.ts
import { Module } from "@spaceflow/core";
import { HelloCommand } from "./hello.command";
import { HelloService } from "./hello.service";
@Module({
providers: [HelloCommand, HelloService],
})
export class HelloModule {}
```
## 使用核心能力
Extension 可以导入 `@spaceflow/core` 提供的共享模块:
```typescript
import { Module } from "@spaceflow/core";
import { GitSdkModule } from "@spaceflow/core";
import { LlmProxyModule } from "@spaceflow/core";
@Module({
imports: [
GitSdkModule, // Git 命令封装
LlmProxyModule, // 多 LLM 代理
],
providers: [MyCommand, MyService],
})
export class MyModule {}
```
在 Service 中注入使用:
```typescript
import { Injectable } from "@spaceflow/core";
import { GitSdkService } from "@spaceflow/core";
@Injectable()
export class MyService {
constructor(private readonly gitSdk: GitSdkService) {}
async getDiff(): Promise<string> {
return this.gitSdk.diff("HEAD~1", "HEAD");
}
}
```
### 可用的核心模块
| 模块 | 说明 |
|------|------|
| `GitProviderModule` | Git 平台适配器GitHub / Gitea |
| `GitSdkModule` | Git 命令封装 |
| `LlmProxyModule` | 多 LLM 统一代理 |
| `FeishuSdkModule` | 飞书 API |
| `StorageModule` | 本地存储 |
| `ParallelModule` | 并行执行工具 |
## package.json 规范
```json
{
"name": "@spaceflow/plugin-hello",
"version": "1.0.0",
"description": "Spaceflow Hello Extension",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"type": "module",
"exports": {
".": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
}
},
"peerDependencies": {
"@spaceflow/core": "^0.17.0"
},
"spaceflow": {
"commands": ["hello"],
"configKey": "hello"
}
}
```
## i18n 支持
`locales/` 目录下管理翻译资源:
```typescript
// src/locales/index.ts
import zhCN from "./zh-cn/hello.json";
import en from "./en/hello.json";
import { addLocaleResources } from "@spaceflow/core";
export const helloLocales: Record<string, Record<string, string>> = {
"zh-CN": zhCN,
en,
};
// Side-effect: 立即注册资源
addLocaleResources("hello", helloLocales);
```
```json
// src/locales/zh-cn/hello.json
{
"description": "打招呼命令",
"options.name": "名字"
}
```
```json
// src/locales/en/hello.json
{
"description": "Say hello",
"options.name": "Name"
}
```
## 构建与发布
### 构建
```bash
spaceflow build
```
### 本地测试
`spaceflow.json` 中使用 `link:` 引用本地 Extension
```json
{
"dependencies": {
"@spaceflow/plugin-hello": "link:./path/to/plugin-hello"
}
}
```
### 发布到 npm
```bash
npm publish
```
用户安装:
```bash
spaceflow install @spaceflow/plugin-hello
```