pqm-cli 1.0.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.
- package/README.md +254 -0
- package/bin/pqm.js +6 -0
- package/package.json +31 -0
- package/src/ai/analyzer/collector.js +191 -0
- package/src/ai/analyzer/dependency.js +269 -0
- package/src/ai/analyzer/index.js +234 -0
- package/src/ai/analyzer/quality.js +241 -0
- package/src/ai/analyzer/security.js +302 -0
- package/src/ai/index.js +16 -0
- package/src/ai/providers/bailian.js +121 -0
- package/src/ai/providers/base.js +177 -0
- package/src/ai/providers/deepseek.js +100 -0
- package/src/ai/providers/index.js +100 -0
- package/src/ai/providers/openai.js +100 -0
- package/src/builders/base.js +35 -0
- package/src/builders/rollup.js +47 -0
- package/src/builders/vite.js +47 -0
- package/src/cli.js +41 -0
- package/src/commands/ai.js +317 -0
- package/src/commands/build.js +24 -0
- package/src/commands/commit.js +68 -0
- package/src/commands/config.js +113 -0
- package/src/commands/doctor.js +146 -0
- package/src/commands/init.js +61 -0
- package/src/commands/login.js +37 -0
- package/src/commands/publish.js +250 -0
- package/src/commands/release.js +107 -0
- package/src/commands/scan.js +239 -0
- package/src/commands/status.js +129 -0
- package/src/commands/watch.js +170 -0
- package/src/commands/webhook.js +240 -0
- package/src/config/detector.js +82 -0
- package/src/config/global.js +136 -0
- package/src/config/loader.js +49 -0
- package/src/core/builder.js +88 -0
- package/src/index.js +5 -0
- package/src/logs/build.js +47 -0
- package/src/logs/manager.js +60 -0
- package/src/report/formatter.js +282 -0
- package/src/utils/http.js +130 -0
- package/src/utils/logger.js +24 -0
- package/src/utils/prompt.js +132 -0
- package/src/utils/spinner.js +134 -0
package/README.md
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
# PQM CLI
|
|
2
|
+
|
|
3
|
+
Package Quality Monitor CLI - 文件监控、构建、发布一体化工具。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
cd pqm-cli
|
|
9
|
+
npm install
|
|
10
|
+
npm link
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## 流程闭环
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
17
|
+
│ PQM 完整工作流 │
|
|
18
|
+
├─────────────────────────────────────────────────────────────┤
|
|
19
|
+
│ │
|
|
20
|
+
│ 开发阶段 发布阶段 │
|
|
21
|
+
│ ┌───────┐ ┌─────────┐ ┌─────────┐ │
|
|
22
|
+
│ │ watch │ ──────▶ │ commit │ ─────▶ │ release │ │
|
|
23
|
+
│ └───────┘ └─────────┘ └─────────┘ │
|
|
24
|
+
│ │ │ │ │
|
|
25
|
+
│ ▼ ▼ ▼ │
|
|
26
|
+
│ ┌───────┐ ┌─────────┐ ┌─────────┐ │
|
|
27
|
+
│ │ build │ │ status │ │ publish │ │
|
|
28
|
+
│ └───────┘ └─────────┘ └─────────┘ │
|
|
29
|
+
│ │ │
|
|
30
|
+
│ ▼ │
|
|
31
|
+
│ ┌─────────┐ │
|
|
32
|
+
│ │ webhook │ ◀── Git Tag │
|
|
33
|
+
│ └─────────┘ │
|
|
34
|
+
│ │ │
|
|
35
|
+
│ ▼ │
|
|
36
|
+
│ npm 发布 │
|
|
37
|
+
└─────────────────────────────────────────────────────────────┘
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## 命令总览
|
|
41
|
+
|
|
42
|
+
### 开发流程
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# 1. 初始化项目
|
|
46
|
+
pqm init
|
|
47
|
+
|
|
48
|
+
# 2. 启动文件监控(自动构建)
|
|
49
|
+
pqm watch
|
|
50
|
+
|
|
51
|
+
# 3. 查看项目状态
|
|
52
|
+
pqm status
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 提交发布
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# 4. 提交代码
|
|
59
|
+
pqm commit "feat: 新功能"
|
|
60
|
+
|
|
61
|
+
# 5. 创建发布版本
|
|
62
|
+
pqm release patch # 1.0.0 → 1.0.1
|
|
63
|
+
pqm release minor # 1.0.0 → 1.1.0
|
|
64
|
+
pqm release major # 1.0.0 → 2.0.0
|
|
65
|
+
|
|
66
|
+
# 6. 发布到 npm
|
|
67
|
+
pqm login # 登录 npm
|
|
68
|
+
pqm publish # 发布包
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Webhook 自动发布
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
# 启动 webhook 服务器
|
|
75
|
+
pqm webhook start
|
|
76
|
+
|
|
77
|
+
# 在另一个终端测试
|
|
78
|
+
pqm webhook test
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## 命令详情
|
|
82
|
+
|
|
83
|
+
### `pqm watch`
|
|
84
|
+
|
|
85
|
+
启动文件监控,自动触发构建。
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
pqm watch # 监控 ./src
|
|
89
|
+
pqm watch -p ./lib # 监控 ./lib
|
|
90
|
+
pqm watch -t vite # 指定构建工具
|
|
91
|
+
pqm watch --webhook # 同时启动 webhook
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### `pqm commit`
|
|
95
|
+
|
|
96
|
+
提交并推送代码。
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
pqm commit "feat: 添加新功能"
|
|
100
|
+
pqm commit "fix: 修复bug" -m "详细说明"
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### `pqm release`
|
|
104
|
+
|
|
105
|
+
创建版本发布。
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
pqm release # 默认 patch
|
|
109
|
+
pqm release patch # 补丁版本 1.0.0 → 1.0.1
|
|
110
|
+
pqm release minor # 次要版本 1.0.0 → 1.1.0
|
|
111
|
+
pqm release major # 主要版本 1.0.0 → 2.0.0
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### `pqm publish`
|
|
115
|
+
|
|
116
|
+
发布到 npm。
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
pqm publish # 交互式发布
|
|
120
|
+
pqm publish --access public # 公开包
|
|
121
|
+
pqm publish --skip-test # 跳过测试
|
|
122
|
+
pqm publish --skip-build # 跳过构建
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### `pqm webhook`
|
|
126
|
+
|
|
127
|
+
自动发布 webhook 服务器。
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
# 设置 NPM_TOKEN 环境变量
|
|
131
|
+
export NPM_TOKEN=npm_xxxxx
|
|
132
|
+
|
|
133
|
+
# 启动服务器
|
|
134
|
+
pqm webhook start
|
|
135
|
+
|
|
136
|
+
# 指定端口
|
|
137
|
+
pqm webhook start -p 3200
|
|
138
|
+
|
|
139
|
+
# 启用签名验证
|
|
140
|
+
pqm webhook start -s your-secret
|
|
141
|
+
|
|
142
|
+
# 测试 webhook
|
|
143
|
+
pqm webhook test
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### `pqm status`
|
|
147
|
+
|
|
148
|
+
查看项目状态。
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
pqm status
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
输出示例:
|
|
155
|
+
```
|
|
156
|
+
📦 包信息
|
|
157
|
+
────────────────────────────────
|
|
158
|
+
名称: my-package
|
|
159
|
+
版本: v1.0.0
|
|
160
|
+
|
|
161
|
+
🌿 Git 状态
|
|
162
|
+
────────────────────────────────
|
|
163
|
+
分支: main
|
|
164
|
+
变更: 工作区干净
|
|
165
|
+
|
|
166
|
+
📝 最近提交
|
|
167
|
+
────────────────────────────────
|
|
168
|
+
abc1234 feat: 新功能
|
|
169
|
+
def5678 fix: 修复bug
|
|
170
|
+
|
|
171
|
+
🔐 NPM 状态
|
|
172
|
+
────────────────────────────────
|
|
173
|
+
用户: your-username
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### 其他命令
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
pqm init # 初始化配置
|
|
180
|
+
pqm config list # 查看配置
|
|
181
|
+
pqm config set <k> <v> # 设置配置
|
|
182
|
+
pqm doctor # 环境诊断
|
|
183
|
+
pqm build # 手动构建
|
|
184
|
+
pqm build -t vite # 指定工具
|
|
185
|
+
pqm login # NPM 登录
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## 配置文件 (.pqmrc)
|
|
189
|
+
|
|
190
|
+
```json
|
|
191
|
+
{
|
|
192
|
+
"root": "./src",
|
|
193
|
+
"exclude": ["node_modules", "dist", ".git"],
|
|
194
|
+
"buildTool": "auto",
|
|
195
|
+
"buildMode": "incremental",
|
|
196
|
+
"buildOnStart": true,
|
|
197
|
+
"webhook": {
|
|
198
|
+
"enabled": false,
|
|
199
|
+
"port": 3200
|
|
200
|
+
},
|
|
201
|
+
"log": {
|
|
202
|
+
"level": "info",
|
|
203
|
+
"file": ".pqm/pqm.log"
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## 典型工作流
|
|
209
|
+
|
|
210
|
+
### 场景 1:日常开发
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
# 1. 开始开发
|
|
214
|
+
pqm watch
|
|
215
|
+
|
|
216
|
+
# 2. 修改文件...(自动构建)
|
|
217
|
+
|
|
218
|
+
# 3. 提交
|
|
219
|
+
pqm commit "feat: 添加新功能"
|
|
220
|
+
|
|
221
|
+
# 4. 发布版本
|
|
222
|
+
pqm release patch
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### 场景 2:CI/CD 自动发布
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
# 服务器端运行
|
|
229
|
+
export NPM_TOKEN=npm_xxxxx
|
|
230
|
+
pqm webhook start -p 3000
|
|
231
|
+
|
|
232
|
+
# 配置 Git 平台 Webhook:
|
|
233
|
+
# URL: http://your-server:3000/webhook/npm-publish
|
|
234
|
+
# 触发: Tag push
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### 场景 3:手动发布
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
pqm status # 检查状态
|
|
241
|
+
pqm login # 登录
|
|
242
|
+
pqm release minor # 创建版本
|
|
243
|
+
pqm publish # 发布
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## 依赖
|
|
247
|
+
|
|
248
|
+
- Node.js >= 18
|
|
249
|
+
- Git
|
|
250
|
+
- npm
|
|
251
|
+
|
|
252
|
+
## License
|
|
253
|
+
|
|
254
|
+
MIT
|
package/bin/pqm.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pqm-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Package Quality Monitor CLI - 文件监控、构建、发布一体化工具",
|
|
6
|
+
"bin": {
|
|
7
|
+
"pqm": "bin/pqm.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin",
|
|
11
|
+
"src"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"test": "echo \"No tests yet\""
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"commander": "^12.0.0",
|
|
18
|
+
"chalk": "^5.0.0",
|
|
19
|
+
"chokidar": "^5.0.0"
|
|
20
|
+
},
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=18.0.0"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"cli",
|
|
26
|
+
"watch",
|
|
27
|
+
"build",
|
|
28
|
+
"monitor"
|
|
29
|
+
],
|
|
30
|
+
"license": "MIT"
|
|
31
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { loadGlobalConfig } from '../../config/global.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Default exclude patterns
|
|
7
|
+
*/
|
|
8
|
+
const DEFAULT_EXCLUDES = [
|
|
9
|
+
'node_modules',
|
|
10
|
+
'dist',
|
|
11
|
+
'build',
|
|
12
|
+
'.git',
|
|
13
|
+
'.svn',
|
|
14
|
+
'.hg',
|
|
15
|
+
'coverage',
|
|
16
|
+
'.nyc_output',
|
|
17
|
+
'**/*.min.js',
|
|
18
|
+
'**/*.min.css',
|
|
19
|
+
'**/*.map',
|
|
20
|
+
'**/*.lock',
|
|
21
|
+
'package-lock.json',
|
|
22
|
+
'yarn.lock',
|
|
23
|
+
'pnpm-lock.yaml'
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Supported file extensions for analysis
|
|
28
|
+
*/
|
|
29
|
+
const SUPPORTED_EXTENSIONS = [
|
|
30
|
+
'.js', '.jsx', '.ts', '.tsx',
|
|
31
|
+
'.vue', '.svelte',
|
|
32
|
+
'.py', '.rb', '.go',
|
|
33
|
+
'.java', '.kt', '.rs',
|
|
34
|
+
'.c', '.cpp', '.h', '.hpp',
|
|
35
|
+
'.php', '.cs',
|
|
36
|
+
'.swift', '.m', '.mm',
|
|
37
|
+
'.sh', '.bash',
|
|
38
|
+
'.sql',
|
|
39
|
+
'.html', '.css', '.scss', '.less'
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Check if path should be excluded
|
|
44
|
+
* @param {string} filePath - File path
|
|
45
|
+
* @param {Array<string>} excludes - Exclude patterns
|
|
46
|
+
* @returns {boolean} True if excluded
|
|
47
|
+
*/
|
|
48
|
+
function isExcluded(filePath, excludes) {
|
|
49
|
+
const normalized = filePath.replace(/\\/g, '/');
|
|
50
|
+
|
|
51
|
+
for (const pattern of excludes) {
|
|
52
|
+
// Simple pattern matching
|
|
53
|
+
if (pattern.startsWith('**/*.')) {
|
|
54
|
+
const ext = pattern.slice(2);
|
|
55
|
+
if (normalized.endsWith(ext)) {
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
} else if (pattern.startsWith('**/')) {
|
|
59
|
+
const name = pattern.slice(3);
|
|
60
|
+
if (normalized.includes('/' + name + '/') || normalized.endsWith('/' + name)) {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
// Simple directory/name match
|
|
65
|
+
if (normalized.includes('/' + pattern + '/') || normalized.endsWith('/' + pattern)) {
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Check if file extension is supported
|
|
76
|
+
* @param {string} filePath - File path
|
|
77
|
+
* @returns {boolean} True if supported
|
|
78
|
+
*/
|
|
79
|
+
function isSupportedFile(filePath) {
|
|
80
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
81
|
+
return SUPPORTED_EXTENSIONS.includes(ext);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Collect files from a directory
|
|
86
|
+
* @param {string} dir - Directory path
|
|
87
|
+
* @param {Object} options - Options
|
|
88
|
+
* @returns {Promise<Array<Object>>} File list with content
|
|
89
|
+
*/
|
|
90
|
+
export async function collectFiles(dir, options = {}) {
|
|
91
|
+
const config = loadGlobalConfig();
|
|
92
|
+
const exclude = options.exclude || config.scan?.exclude || DEFAULT_EXCLUDES;
|
|
93
|
+
const maxFileSize = options.maxFileSize || config.scan?.maxFileSize || 1048576;
|
|
94
|
+
|
|
95
|
+
const files = [];
|
|
96
|
+
|
|
97
|
+
async function walk(currentPath) {
|
|
98
|
+
try {
|
|
99
|
+
const entries = await fs.promises.readdir(currentPath, { withFileTypes: true });
|
|
100
|
+
|
|
101
|
+
for (const entry of entries) {
|
|
102
|
+
const fullPath = path.join(currentPath, entry.name);
|
|
103
|
+
|
|
104
|
+
if (isExcluded(fullPath, exclude)) {
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (entry.isDirectory()) {
|
|
109
|
+
await walk(fullPath);
|
|
110
|
+
} else if (entry.isFile() && isSupportedFile(fullPath)) {
|
|
111
|
+
try {
|
|
112
|
+
const stat = await fs.promises.stat(fullPath);
|
|
113
|
+
|
|
114
|
+
if (stat.size > maxFileSize) {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const content = await fs.promises.readFile(fullPath, 'utf-8');
|
|
119
|
+
|
|
120
|
+
// Skip empty files
|
|
121
|
+
if (content.trim().length === 0) {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
files.push({
|
|
126
|
+
path: fullPath,
|
|
127
|
+
relativePath: path.relative(dir, fullPath),
|
|
128
|
+
content,
|
|
129
|
+
size: stat.size
|
|
130
|
+
});
|
|
131
|
+
} catch (error) {
|
|
132
|
+
// Skip files that can't be read
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
} catch (error) {
|
|
138
|
+
// Handle permission errors etc.
|
|
139
|
+
console.error(`Warning: Could not read directory ${currentPath}: ${error.message}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
await walk(dir);
|
|
144
|
+
|
|
145
|
+
return files;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Collect a single file
|
|
150
|
+
* @param {string} filePath - File path
|
|
151
|
+
* @returns {Promise<Object|null>} File object or null
|
|
152
|
+
*/
|
|
153
|
+
export async function collectSingleFile(filePath) {
|
|
154
|
+
if (!fs.existsSync(filePath)) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const stat = await fs.promises.stat(filePath);
|
|
159
|
+
|
|
160
|
+
if (!stat.isFile()) {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const content = await fs.promises.readFile(filePath, 'utf-8');
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
path: filePath,
|
|
168
|
+
relativePath: path.basename(filePath),
|
|
169
|
+
content,
|
|
170
|
+
size: stat.size
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Get file count in directory
|
|
176
|
+
* @param {string} dir - Directory path
|
|
177
|
+
* @param {Object} options - Options
|
|
178
|
+
* @returns {Promise<number>} File count
|
|
179
|
+
*/
|
|
180
|
+
export async function getFileCount(dir, options = {}) {
|
|
181
|
+
const files = await collectFiles(dir, options);
|
|
182
|
+
return files.length;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export default {
|
|
186
|
+
collectFiles,
|
|
187
|
+
collectSingleFile,
|
|
188
|
+
getFileCount,
|
|
189
|
+
DEFAULT_EXCLUDES,
|
|
190
|
+
SUPPORTED_EXTENSIONS
|
|
191
|
+
};
|