@deepstorm/cli 0.9.2 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. package/dist/cli.js +508 -24
  2. package/dist/registry.json +168 -0
  3. package/dist/skills/reef-commit/SKILL.md +27 -100
  4. package/dist/skills/reef-commit/scripts/branch-check.mjs +109 -0
  5. package/dist/skills/reef-commit/scripts/check-openspec-status.mjs +92 -0
  6. package/dist/skills/reef-commit/scripts/collect-git-context.mjs +186 -0
  7. package/dist/skills/reef-commit/scripts/run-tests.sh +91 -0
  8. package/dist/skills/reef-commit/scripts/stash-and-switch.sh +44 -0
  9. package/dist/skills/reef-gen-backend/variants/nodejs/steps.md +53 -0
  10. package/dist/skills/reef-harden/SKILL.md +11 -19
  11. package/dist/skills/reef-harden/scripts/find-change-dir.mjs +157 -0
  12. package/dist/skills/reef-pr/SKILL.md +7 -19
  13. package/dist/skills/reef-pr/scripts/create-pr.mjs +217 -0
  14. package/dist/skills/reef-style-backend/fragments/nodejs/eslint-config.json +30 -0
  15. package/dist/skills/reef-style-backend/fragments/nodejs/nestjs-structure.md +58 -0
  16. package/dist/skills/reef-style-backend/fragments/nodejs/prettier-config.json +19 -0
  17. package/dist/skills/reef-style-backend/variants/nodejs/examples/module-example.md +166 -0
  18. package/dist/skills/reef-style-backend/variants/nodejs/examples/prisma-example.md +115 -0
  19. package/dist/skills/reef-style-backend/variants/nodejs/quick-reference.md +116 -0
  20. package/dist/skills/sweep-init/SKILL.md +12 -125
  21. package/dist/skills/sweep-init/scripts/init-project.mjs +166 -0
  22. package/dist/skills/sweep-run/SKILL.md +11 -35
  23. package/dist/skills/sweep-run/scripts/env-manager.mjs +71 -2
  24. package/dist/skills/sweep-run/scripts/generate-report.mjs +116 -0
  25. package/package.json +2 -2
@@ -0,0 +1,217 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * create-pr.mjs — PR 上下文收集与创建
5
+ *
6
+ * 用法:
7
+ * node create-pr.mjs --collect # 收集 PR 上下文
8
+ * node create-pr.mjs --create <args> # 创建 PR
9
+ * node create-pr.mjs --help
10
+ */
11
+
12
+ import { execSync } from 'node:child_process';
13
+ import { existsSync, readFileSync } from 'node:fs';
14
+ import { join } from 'node:path';
15
+
16
+ // ── Pure functions ────────────────────────────────────────────────
17
+
18
+ export function checkUncommitted(statusShort) {
19
+ return statusShort.trim().length > 0;
20
+ }
21
+
22
+ export function buildPrBody(ctx) {
23
+ const lines = [];
24
+
25
+ lines.push('## 概要');
26
+ if (ctx.proposalTitle) {
27
+ lines.push(ctx.proposalTitle);
28
+ } else if (ctx.commitLog && ctx.commitLog.length > 0) {
29
+ lines.push(ctx.commitLog[0].message);
30
+ } else {
31
+ lines.push(ctx.branch);
32
+ }
33
+
34
+ lines.push('');
35
+ lines.push('## 关联');
36
+ lines.push(`Branch: ${ctx.branch}`);
37
+
38
+ if (ctx.diffStat) {
39
+ const s = ctx.diffStat;
40
+ lines.push(`Changes: ${s.files} 文件, +${s.insertions}/-${s.deletions}`);
41
+ lines.push('');
42
+ lines.push('## 变更清单');
43
+ s.changedFiles.forEach(f => lines.push(`- \`${f}\``));
44
+ }
45
+
46
+ if (ctx.commitLog && ctx.commitLog.length > 0) {
47
+ lines.push('');
48
+ lines.push('## Commits');
49
+ ctx.commitLog.forEach(c => lines.push(`- ${c.hash} ${c.message}`));
50
+ }
51
+
52
+ return lines.join('\n');
53
+ }
54
+
55
+ export function buildGhCreateArgs(opts) {
56
+ const args = ['gh pr create'];
57
+ args.push(`--title "${opts.title}"`);
58
+ args.push(`--body "${opts.body}"`);
59
+ if (opts.reviewer) args.push(`--reviewer ${opts.reviewer}`);
60
+ if (opts.label) args.push(`--label ${opts.label}`);
61
+ if (opts.draft) args.push('--draft');
62
+ return args.join(' ');
63
+ }
64
+
65
+ // ── Git helpers ────────────────────────────────────────────────────
66
+
67
+ function exec(command) {
68
+ try {
69
+ return execSync(command, { encoding: 'utf8', timeout: 10000 }).trim();
70
+ } catch {
71
+ return '';
72
+ }
73
+ }
74
+
75
+ function collectContext() {
76
+ const branch = exec('git branch --show-current');
77
+ const forkPoint = exec(
78
+ 'git merge-base main HEAD 2>/dev/null || echo ""'
79
+ );
80
+ const diffStat = exec(
81
+ `git diff "${forkPoint || 'main'}..HEAD" --stat`
82
+ );
83
+ const commitLog = forkPoint
84
+ ? exec(`git log "${forkPoint}"..HEAD --oneline`)
85
+ : '';
86
+ const statusShort = exec('git status --short');
87
+
88
+ // 查找关联 proposal
89
+ let proposalTitle = '';
90
+ const root = exec(
91
+ 'git rev-parse --show-toplevel 2>/dev/null || echo ""'
92
+ );
93
+ if (root && branch) {
94
+ const p = join(root, 'openspec', 'changes', branch, 'proposal.md');
95
+ if (existsSync(p)) {
96
+ const firstLine = readFileSync(p, 'utf8')
97
+ .split('\n')[0] || '';
98
+ proposalTitle = firstLine.replace(/^#+\s*/, '').trim();
99
+ }
100
+ }
101
+
102
+ return {
103
+ branch,
104
+ hasUncommitted: checkUncommitted(statusShort),
105
+ proposalTitle,
106
+ commitLog: commitLog
107
+ ? commitLog.split('\n').filter(Boolean).map(l => {
108
+ const m = l.match(/^(\S+)\s+(.*)/);
109
+ return m ? { hash: m[1], message: m[2] } : null;
110
+ }).filter(Boolean)
111
+ : [],
112
+ diffStat: diffStat ? parseDiffStat(diffStat) : null,
113
+ };
114
+ }
115
+
116
+ function parseDiffStat(output) {
117
+ const lines = output.trim().split('\n').filter(Boolean);
118
+ const changedFiles = [];
119
+ let files = 0, insertions = 0, deletions = 0;
120
+
121
+ for (const line of lines) {
122
+ const sm = line.match(/(\d+)\s+files?\s+changed/);
123
+ if (sm) {
124
+ files = parseInt(sm[1], 10);
125
+ const im = line.match(/(\d+)\s+insertions?\(\+\)/);
126
+ if (im) insertions = parseInt(im[1], 10);
127
+ const dm = line.match(/(\d+)\s+deletions?\(-\)/);
128
+ if (dm) deletions = parseInt(dm[1], 10);
129
+ continue;
130
+ }
131
+ const fm = line.match(/^\s*(.+?)\s+\|/);
132
+ if (fm) changedFiles.push(fm[1].trim());
133
+ }
134
+
135
+ return { files, insertions, deletions, changedFiles };
136
+ }
137
+
138
+ function checkExistingPR() {
139
+ const out = exec('gh pr view --json url 2>/dev/null || true');
140
+ return out ? { exists: true, url: out } : { exists: false };
141
+ }
142
+
143
+ // ── CLI entry ─────────────────────────────────────────────────────
144
+
145
+ if (import.meta.filename === process.argv[1]) {
146
+ if (process.argv.includes('--help') || process.argv.includes('-h')) {
147
+ console.log(`
148
+ create-pr.mjs — PR 上下文收集与创建
149
+
150
+ 用法:
151
+ node create-pr.mjs --collect 收集 PR 上下文(JSON)
152
+ node create-pr.mjs --create 创建 PR
153
+ node create-pr.mjs --help 显示帮助
154
+
155
+ --create 选项:
156
+ --title <text> PR 标题
157
+ --body <text> PR 正文
158
+ --reviewer <user> Reviewer
159
+ --label <label> 标签
160
+ --draft 创建 Draft PR
161
+ `);
162
+ process.exit(0);
163
+ }
164
+
165
+ if (process.argv.includes('--collect')) {
166
+ const ctx = collectContext();
167
+ process.stdout.write(JSON.stringify(ctx, null, 2) + '\n');
168
+ process.exit(0);
169
+ }
170
+
171
+ if (process.argv.includes('--create')) {
172
+ const titleArg = process.argv.indexOf('--title');
173
+ const bodyArg = process.argv.indexOf('--body');
174
+ const reviewerArg = process.argv.indexOf('--reviewer');
175
+ const labelArg = process.argv.indexOf('--label');
176
+ const isDraft = process.argv.includes('--draft');
177
+
178
+ if (titleArg < 0 || bodyArg < 0) {
179
+ console.error('--title 和 --body 必填');
180
+ process.exit(1);
181
+ }
182
+
183
+ const existing = checkExistingPR();
184
+ if (existing.exists) {
185
+ process.stdout.write(JSON.stringify(existing, null, 2) + '\n');
186
+ process.exit(0);
187
+ }
188
+
189
+ const branch = exec('git branch --show-current');
190
+ const pushOut = exec(`git push -u origin ${branch} 2>&1`);
191
+ if (!pushOut) {
192
+ console.error('git push 失败');
193
+ process.exit(1);
194
+ }
195
+
196
+ const cmd = buildGhCreateArgs({
197
+ title: process.argv[titleArg + 1],
198
+ body: process.argv[bodyArg + 1],
199
+ branch,
200
+ reviewer: reviewerArg >= 0
201
+ ? process.argv[reviewerArg + 1] : null,
202
+ label: labelArg >= 0 ? process.argv[labelArg + 1] : null,
203
+ draft: isDraft,
204
+ });
205
+
206
+ const out = exec(cmd);
207
+ process.stdout.write(JSON.stringify({
208
+ created: true,
209
+ url: out,
210
+ }, null, 2) + '\n');
211
+ process.exit(0);
212
+ }
213
+
214
+ // 默认:--collect
215
+ const ctx = collectContext();
216
+ process.stdout.write(JSON.stringify(ctx, null, 2) + '\n');
217
+ }
@@ -0,0 +1,30 @@
1
+ {
2
+ "root": true,
3
+ "env": {
4
+ "node": true,
5
+ "jest": true
6
+ },
7
+ "parser": "@typescript-eslint/parser",
8
+ "parserOptions": {
9
+ "project": "tsconfig.json",
10
+ "sourceType": "module"
11
+ },
12
+ "plugins": ["@typescript-eslint/eslint-plugin"],
13
+ "extends": [
14
+ "plugin:@typescript-eslint/recommended",
15
+ "plugin:prettier/recommended"
16
+ ],
17
+ "rules": {
18
+ "@typescript-eslint/no-explicit-any": "error",
19
+ "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
20
+ "@typescript-eslint/explicit-function-return-type": "off",
21
+ "@typescript-eslint/explicit-module-boundary-types": "off",
22
+ "no-console": "warn",
23
+ "prefer-const": "error",
24
+ "no-var": "error",
25
+ "eqeqeq": ["error", "always"],
26
+ "curly": ["error", "all"],
27
+ "no-throw-literal": "error",
28
+ "prefer-promise-reject-errors": "error"
29
+ }
30
+ }
@@ -0,0 +1,58 @@
1
+ # NestJS 项目结构规范
2
+
3
+ ## 顶层目录
4
+
5
+ ```
6
+ src/
7
+ ├── main.ts # 入口(Bootstrap)
8
+ ├── app.module.ts # 根模块
9
+ ├── app.controller.ts # 健康检查
10
+ └── app.service.ts # 全局服务
11
+ ```
12
+
13
+ ## 模块目录规范
14
+
15
+ ```
16
+ src/modules/{module-name}/
17
+ ├── {module-name}.module.ts # Module 定义
18
+ ├── {module-name}.controller.ts # REST Controller
19
+ ├── {module-name}.service.ts # 业务逻辑
20
+ ├── dto/
21
+ │ ├── create-{entity}.dto.ts # 创建 DTO
22
+ │ └── update-{entity}.dto.ts # 更新 DTO(PartialType)
23
+ └── entities/
24
+ └── {entity}.entity.ts # Prisma 封装类型(可选)
25
+ ```
26
+
27
+ ## 公共目录
28
+
29
+ ```
30
+ src/
31
+ ├── prisma/
32
+ │ ├── prisma.module.ts
33
+ │ └── prisma.service.ts
34
+ ├── common/
35
+ │ ├── filters/
36
+ │ │ └── http-exception.filter.ts
37
+ │ ├── pipes/
38
+ │ │ └── validation.pipe.ts
39
+ │ ├── interceptors/
40
+ │ │ ├── transform.interceptor.ts
41
+ │ │ └── logging.interceptor.ts
42
+ │ └── guards/
43
+ │ └── auth.guard.ts
44
+ └── config/
45
+ └── configuration.ts
46
+ ```
47
+
48
+ ## 命名规范
49
+
50
+ | 类型 | 命名规则 | 示例 |
51
+ |------|---------|------|
52
+ | Module 类 | PascalCase + Module | `UsersModule` |
53
+ | Controller 类 | PascalCase + Controller | `UsersController` |
54
+ | Service 类 | PascalCase + Service | `UsersService` |
55
+ | DTO 类 | PascalCase + Dto | `CreateUserDto` |
56
+ | Module 目录 | kebab-case | `src/modules/user-roles/` |
57
+ | DTO 文件 | kebab-case | `create-user-role.dto.ts` |
58
+ | 配置键 | UPPER_SNAKE_CASE | `DATABASE_URL` |
@@ -0,0 +1,19 @@
1
+ {
2
+ "semi": true,
3
+ "singleQuote": true,
4
+ "tabWidth": 2,
5
+ "trailingComma": "all",
6
+ "printWidth": 100,
7
+ "bracketSpacing": true,
8
+ "arrowParens": "always",
9
+ "endOfLine": "lf",
10
+ "quoteProps": "as-needed",
11
+ "overrides": [
12
+ {
13
+ "files": "*.prisma",
14
+ "options": {
15
+ "tabWidth": 2
16
+ }
17
+ }
18
+ ]
19
+ }
@@ -0,0 +1,166 @@
1
+ # NestJS Module 示例 — Users 模块
2
+
3
+ ```typescript
4
+ // users.module.ts
5
+ import { Module } from '@nestjs/common';
6
+ import { UsersController } from './users.controller';
7
+ import { UsersService } from './users.service';
8
+ import { PrismaModule } from '../prisma/prisma.module';
9
+
10
+ /**
11
+ * 用户管理模块
12
+ * 提供用户 CRUD 操作和权限管理
13
+ */
14
+ @Module({
15
+ imports: [PrismaModule],
16
+ controllers: [UsersController],
17
+ providers: [UsersService],
18
+ exports: [UsersService],
19
+ })
20
+ export class UsersModule {}
21
+ ```
22
+
23
+ ```typescript
24
+ // users.controller.ts
25
+ import {
26
+ Controller, Get, Post, Body, Param, Put, Delete,
27
+ ParseIntPipe,
28
+ } from '@nestjs/common';
29
+ import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger';
30
+ import { UsersService } from './users.service';
31
+ import { CreateUserDto } from './dto/create-user.dto';
32
+ import { UpdateUserDto } from './dto/update-user.dto';
33
+
34
+ @ApiTags('Users')
35
+ @Controller('users')
36
+ @ApiBearerAuth()
37
+ export class UsersController {
38
+ constructor(private readonly usersService: UsersService) {}
39
+
40
+ @Post()
41
+ @ApiOperation({ summary: '创建用户', description: '创建一个新的系统用户' })
42
+ create(@Body() createUserDto: CreateUserDto) {
43
+ return this.usersService.create(createUserDto);
44
+ }
45
+
46
+ @Get()
47
+ @ApiOperation({ summary: '查询用户列表' })
48
+ findAll() {
49
+ return this.usersService.findAll();
50
+ }
51
+
52
+ @Get(':id')
53
+ @ApiOperation({ summary: '查询单个用户' })
54
+ findOne(@Param('id', ParseIntPipe) id: number) {
55
+ return this.usersService.findOne(id);
56
+ }
57
+
58
+ @Put(':id')
59
+ @ApiOperation({ summary: '更新用户信息' })
60
+ update(
61
+ @Param('id', ParseIntPipe) id: number,
62
+ @Body() updateUserDto: UpdateUserDto,
63
+ ) {
64
+ return this.usersService.update(id, updateUserDto);
65
+ }
66
+
67
+ @Delete(':id')
68
+ @ApiOperation({ summary: '删除用户' })
69
+ remove(@Param('id', ParseIntPipe) id: number) {
70
+ return this.usersService.remove(id);
71
+ }
72
+ }
73
+ ```
74
+
75
+ ```typescript
76
+ // users.service.ts
77
+ import { Injectable, Logger, NotFoundException } from '@nestjs/common';
78
+ import { PrismaService } from '../prisma/prisma.service';
79
+ import { CreateUserDto } from './dto/create-user.dto';
80
+ import { UpdateUserDto } from './dto/update-user.dto';
81
+
82
+ @Injectable()
83
+ export class UsersService {
84
+ private readonly logger = new Logger(UsersService.name);
85
+
86
+ constructor(private readonly prisma: PrismaService) {}
87
+
88
+ /**
89
+ * 创建用户
90
+ * @param createUserDto 用户创建参数
91
+ * @returns 创建的用户记录
92
+ */
93
+ async create(createUserDto: CreateUserDto) {
94
+ return this.prisma.user.create({ data: createUserDto });
95
+ }
96
+
97
+ /** 查询所有用户 */
98
+ async findAll() {
99
+ return this.prisma.user.findMany();
100
+ }
101
+
102
+ /**
103
+ * 根据 ID 查询用户
104
+ * @param id 用户 ID
105
+ * @throws NotFoundException 用户不存在
106
+ */
107
+ async findOne(id: number) {
108
+ const user = await this.prisma.user.findUnique({ where: { id } });
109
+ if (!user) throw new NotFoundException(`User #${id} not found`);
110
+ return user;
111
+ }
112
+
113
+ /**
114
+ * 更新用户信息
115
+ * @param id 用户 ID
116
+ * @param updateUserDto 更新参数
117
+ */
118
+ async update(id: number, updateUserDto: UpdateUserDto) {
119
+ await this.findOne(id);
120
+ return this.prisma.user.update({
121
+ where: { id },
122
+ data: updateUserDto,
123
+ });
124
+ }
125
+
126
+ /**
127
+ * 删除用户
128
+ * @param id 用户 ID
129
+ */
130
+ async remove(id: number) {
131
+ await this.findOne(id);
132
+ return this.prisma.user.delete({ where: { id } });
133
+ }
134
+ }
135
+ ```
136
+
137
+ ```typescript
138
+ // dto/create-user.dto.ts
139
+ import { ApiProperty } from '@nestjs/swagger';
140
+ import { IsString, IsEmail, IsOptional, IsBoolean } from 'class-validator';
141
+
142
+ /** 创建用户请求 DTO */
143
+ export class CreateUserDto {
144
+ @ApiProperty({ description: '用户名', example: 'john_doe' })
145
+ @IsString()
146
+ name!: string;
147
+
148
+ @ApiProperty({ description: '邮箱地址', example: 'john@example.com' })
149
+ @IsEmail()
150
+ email!: string;
151
+
152
+ @ApiProperty({ description: '是否激活', required: false, default: true })
153
+ @IsOptional()
154
+ @IsBoolean()
155
+ active?: boolean;
156
+ }
157
+ ```
158
+
159
+ ```typescript
160
+ // dto/update-user.dto.ts
161
+ import { PartialType } from '@nestjs/swagger';
162
+ import { CreateUserDto } from './create-user.dto';
163
+
164
+ /** 更新用户请求 DTO — 所有字段可选 */
165
+ export class UpdateUserDto extends PartialType(CreateUserDto) {}
166
+ ```
@@ -0,0 +1,115 @@
1
+ # Prisma Service 与 Schema 示例
2
+
3
+ ## Prisma Schema
4
+
5
+ ```prisma
6
+ // prisma/schema.prisma
7
+
8
+ generator client {
9
+ provider = "prisma-client-js"
10
+ }
11
+
12
+ datasource db {
13
+ provider = "postgresql"
14
+ url = env("DATABASE_URL")
15
+ }
16
+
17
+ /// 系统用户
18
+ model User {
19
+ id Int @id @default(autoincrement())
20
+ name String
21
+ email String @unique
22
+ active Boolean @default(true)
23
+ posts Post[]
24
+ createdAt DateTime @default(now())
25
+ updatedAt DateTime @updatedAt
26
+
27
+ @@map("users")
28
+ }
29
+
30
+ /// 文章
31
+ model Post {
32
+ id Int @id @default(autoincrement())
33
+ title String
34
+ content String
35
+ published Boolean @default(false)
36
+ author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
37
+ authorId Int
38
+ createdAt DateTime @default(now())
39
+ updatedAt DateTime @updatedAt
40
+
41
+ @@map("posts")
42
+ }
43
+ ```
44
+
45
+ ## Prisma Module & Service
46
+
47
+ ```typescript
48
+ // prisma/prisma.module.ts
49
+ import { Global, Module } from '@nestjs/common';
50
+ import { PrismaService } from './prisma.service';
51
+
52
+ /**
53
+ * Prisma 全局模块
54
+ * 作为 Global 模块,所有其他模块无需 imports 即可注入 PrismaService
55
+ */
56
+ @Global()
57
+ @Module({
58
+ providers: [PrismaService],
59
+ exports: [PrismaService],
60
+ })
61
+ export class PrismaModule {}
62
+ ```
63
+
64
+ ```typescript
65
+ // prisma/prisma.service.ts
66
+ import { Injectable, Logger, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
67
+ import { PrismaClient } from '@prisma/client';
68
+
69
+ /**
70
+ * Prisma 数据库服务
71
+ * 封装 PrismaClient,提供数据库连接生命周期管理
72
+ */
73
+ @Injectable()
74
+ export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
75
+ private readonly logger = new Logger(PrismaService.name);
76
+
77
+ async onModuleInit() {
78
+ await this.$connect();
79
+ this.logger.log('Database connected');
80
+ }
81
+
82
+ async onModuleDestroy() {
83
+ await this.$disconnect();
84
+ this.logger.log('Database disconnected');
85
+ }
86
+ }
87
+ ```
88
+
89
+ ## Prisma 查询最佳实践
90
+
91
+ ```typescript
92
+ // 使用事务:多个原子操作
93
+ const [user, post] = await this.prisma.$transaction([
94
+ this.prisma.user.create({ data: { name: 'Alice', email: 'alice@test.com' } }),
95
+ this.prisma.post.create({ data: { title: 'Hello', content: 'World', authorId: 1 } }),
96
+ ]);
97
+
98
+ // 使用 select 限制返回字段,避免 N+1
99
+ const users = await this.prisma.user.findMany({
100
+ select: {
101
+ id: true,
102
+ name: true,
103
+ email: true,
104
+ posts: {
105
+ select: { title: true, published: true },
106
+ where: { published: true },
107
+ },
108
+ },
109
+ });
110
+
111
+ // 使用 findFirstOrThrow 简化判空
112
+ const user = await this.prisma.user.findFirstOrThrow({
113
+ where: { email: 'alice@test.com' },
114
+ });
115
+ ```