dependfix 0.5.0 → 0.7.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.en-US.md +4 -3
- package/README.md +4 -3
- package/dist/bin.mjs +1 -2
- package/dist/index.d.mts +56 -8
- package/dist/index.mjs +1 -2
- package/dist/runner.mjs +66 -22
- package/dist/runner.mjs.map +1 -1
- package/package.json +12 -12
package/README.en-US.md
CHANGED
|
@@ -31,7 +31,7 @@ dependfix report-only --repo owner/repo --github-token $GITHUB_TOKEN
|
|
|
31
31
|
|
|
32
32
|
### `fix` — Fix alerts
|
|
33
33
|
|
|
34
|
-
Execute dependency upgrade, lockfile fix and verification (lint / build), modifications limited to local files, no branch or PR creation.
|
|
34
|
+
Execute dependency upgrade, lockfile fix and verification (lint / build / typecheck / test), modifications limited to local files, no branch or PR creation.
|
|
35
35
|
|
|
36
36
|
```bash
|
|
37
37
|
dependfix fix --repo owner/repo --github-token $GITHUB_TOKEN --severity-threshold high
|
|
@@ -60,7 +60,8 @@ dependfix fix-and-pr --repo owner/repo --github-token $GITHUB_TOKEN
|
|
|
60
60
|
| `--create-pr` | — | Create Pull Request (auto-enabled in `fix-and-pr` mode) | `false` |
|
|
61
61
|
| `--commit` | — | After fix, commit on local current branch (`fix` mode only; no push, no PR) | `false` |
|
|
62
62
|
| `--max-alerts-per-repository` | — | Max alerts processed per repo | `20` |
|
|
63
|
-
| `--commands` | — | Custom verification commands (comma-separated)
|
|
63
|
+
| `--commands` | — | Custom verification commands (comma-separated); **replaces the whole default chain** `install/lint/build/typecheck/test` (no merging; default-chain additions do not carry over — keep the entries you need) | — |
|
|
64
|
+
| `--override-protect` | — | Overrides protection list (per-repository, `repo-glob:pkg1,pkg2;...`); on a hit the override is not written | — |
|
|
64
65
|
| `--verbose` | — | Verbose logging | `false` |
|
|
65
66
|
|
|
66
67
|
### Environment variables
|
|
@@ -88,7 +89,7 @@ const { result, exitCode } = await app.run()
|
|
|
88
89
|
2. **Filter and sort** — filter by severity, sort by priority, limit count
|
|
89
90
|
3. **Upgrade dependencies** — `pnpm update <package>` to recommended version
|
|
90
91
|
4. **Lockfile fix** — detect and fix `pnpm frozen-lockfile` issues
|
|
91
|
-
5. **Verify** — execute `pnpm install --frozen-lockfile` → `pnpm lint` → `pnpm build`
|
|
92
|
+
5. **Verify** — execute `pnpm install --frozen-lockfile` → `pnpm lint` → `pnpm build` → `pnpm typecheck` → `pnpm test`
|
|
92
93
|
6. **Branch and PR** — in `fix-and-pr` mode, create branch, commit, push, PR
|
|
93
94
|
|
|
94
95
|
## Related packages
|
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ dependfix report-only --repo owner/repo --github-token $GITHUB_TOKEN
|
|
|
31
31
|
|
|
32
32
|
### `fix` — 修复告警
|
|
33
33
|
|
|
34
|
-
执行依赖升级、lockfile 修复和验证(lint / build),修改仅限本地文件,不创建分支或 PR。
|
|
34
|
+
执行依赖升级、lockfile 修复和验证(lint / build / typecheck / test),修改仅限本地文件,不创建分支或 PR。
|
|
35
35
|
|
|
36
36
|
```bash
|
|
37
37
|
dependfix fix --repo owner/repo --github-token $GITHUB_TOKEN --severity-threshold high
|
|
@@ -60,7 +60,8 @@ dependfix fix-and-pr --repo owner/repo --github-token $GITHUB_TOKEN
|
|
|
60
60
|
| `--create-pr` | — | 创建 Pull Request(`fix-and-pr` 模式自动启用) | `false` |
|
|
61
61
|
| `--commit` | — | 修复完成后在本地当前分支直接提交(仅 `fix` 模式;不推送、不创建 PR) | `false` |
|
|
62
62
|
| `--max-alerts-per-repository` | — | 每仓库最大告警处理数 | `20` |
|
|
63
|
-
| `--commands` | — |
|
|
63
|
+
| `--commands` | — | 自定义验证命令(逗号分隔),**整链替换**默认的 `install/lint/build/typecheck/test`(不做合并;默认链扩展不会自动跟随,须显式保留) | — |
|
|
64
|
+
| `--override-protect` | — | overrides 保护名单(按仓库粒度,`repo-glob:pkg1,pkg2;...`);命中则不写入该包 override | — |
|
|
64
65
|
| `--verbose` | — | 详细日志输出 | `false` |
|
|
65
66
|
|
|
66
67
|
### 环境变量
|
|
@@ -88,7 +89,7 @@ const { result, exitCode } = await app.run()
|
|
|
88
89
|
2. **过滤排序** — 按严重级别过滤、优先级排序、数量限制
|
|
89
90
|
3. **依赖升级** — `pnpm update <package>` 升级到推荐版本
|
|
90
91
|
4. **lockfile 修复** — 检测并修复 `pnpm frozen-lockfile` 问题
|
|
91
|
-
5. **验证** — 执行 `pnpm install --frozen-lockfile` → `pnpm lint` → `pnpm build`
|
|
92
|
+
5. **验证** — 执行 `pnpm install --frozen-lockfile` → `pnpm lint` → `pnpm build` → `pnpm typecheck` → `pnpm test`
|
|
92
93
|
6. **分支与 PR** — `fix-and-pr` 模式下,创建分支、commit、push、PR
|
|
93
94
|
|
|
94
95
|
## 相关包
|
package/dist/bin.mjs
CHANGED
|
@@ -2,13 +2,12 @@
|
|
|
2
2
|
import { r as runDependfixMain } from "./runner.mjs";
|
|
3
3
|
import { toAppError } from "@dependfix/core";
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
// src/bin.ts
|
|
6
6
|
runDependfixMain().catch((error) => {
|
|
7
7
|
const appError = toAppError(error, "CLI_EXECUTION_FAILED");
|
|
8
8
|
console.error(appError.message);
|
|
9
9
|
process.exitCode = 1;
|
|
10
10
|
});
|
|
11
11
|
|
|
12
|
-
//#endregion
|
|
13
12
|
export { };
|
|
14
13
|
//# sourceMappingURL=bin.mjs.map
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { AlertSourceKind, CliConfigOverrides, RuntimeConfig, RuntimeMode, SeverityThreshold, resolveRuntimeConfig } from "@dependfix/engine";
|
|
2
2
|
export * from "@dependfix/core";
|
|
3
3
|
export * from "@dependfix/engine";
|
|
4
|
-
//#region src/cli/index.d.ts
|
|
5
4
|
export interface CliInvocation {
|
|
6
5
|
mode?: RuntimeMode;
|
|
7
6
|
configOverrides: CliConfigOverrides;
|
|
@@ -39,6 +38,10 @@ export declare const argsDef: {
|
|
|
39
38
|
type: "string";
|
|
40
39
|
description: string;
|
|
41
40
|
};
|
|
41
|
+
'override-protect': {
|
|
42
|
+
type: "string";
|
|
43
|
+
description: string;
|
|
44
|
+
};
|
|
42
45
|
'repo-topics-exclude': {
|
|
43
46
|
type: "string";
|
|
44
47
|
description: string;
|
|
@@ -100,6 +103,10 @@ export declare const argsDef: {
|
|
|
100
103
|
description: string;
|
|
101
104
|
negativeDescription: string;
|
|
102
105
|
};
|
|
106
|
+
'rollback-granularity': {
|
|
107
|
+
type: "string";
|
|
108
|
+
description: string;
|
|
109
|
+
};
|
|
103
110
|
'max-alerts-per-repository': {
|
|
104
111
|
type: "string";
|
|
105
112
|
description: string;
|
|
@@ -141,6 +148,26 @@ export declare const argsDef: {
|
|
|
141
148
|
type: "string";
|
|
142
149
|
description: string;
|
|
143
150
|
};
|
|
151
|
+
'mirror-ci-scripts': {
|
|
152
|
+
type: "boolean";
|
|
153
|
+
description: string;
|
|
154
|
+
negativeDescription: string;
|
|
155
|
+
};
|
|
156
|
+
'retry-failed': {
|
|
157
|
+
type: "boolean";
|
|
158
|
+
description: string;
|
|
159
|
+
negativeDescription: string;
|
|
160
|
+
};
|
|
161
|
+
'wait-pr-checks': {
|
|
162
|
+
type: "boolean";
|
|
163
|
+
description: string;
|
|
164
|
+
negativeDescription: string;
|
|
165
|
+
};
|
|
166
|
+
'close-failed-pr': {
|
|
167
|
+
type: "boolean";
|
|
168
|
+
description: string;
|
|
169
|
+
negativeDescription: string;
|
|
170
|
+
};
|
|
144
171
|
verbose: {
|
|
145
172
|
type: "boolean";
|
|
146
173
|
description: string;
|
|
@@ -198,8 +225,6 @@ export declare function normalizeFlagList(value: string | string[] | undefined):
|
|
|
198
225
|
*/
|
|
199
226
|
export declare function parseUpgradeGroupsFlag(value: string): Record<string, string[]>;
|
|
200
227
|
export declare function parseCliArgs(rawArgs: string[]): CliInvocation;
|
|
201
|
-
//#endregion
|
|
202
|
-
//#region src/cli/runner.d.ts
|
|
203
228
|
export interface CliRunResult {
|
|
204
229
|
ok: true;
|
|
205
230
|
invocation: CliInvocation;
|
|
@@ -238,6 +263,10 @@ export declare const dependfixCommand: import("citty").CommandDef<{
|
|
|
238
263
|
type: "string";
|
|
239
264
|
description: string;
|
|
240
265
|
};
|
|
266
|
+
'override-protect': {
|
|
267
|
+
type: "string";
|
|
268
|
+
description: string;
|
|
269
|
+
};
|
|
241
270
|
'repo-topics-exclude': {
|
|
242
271
|
type: "string";
|
|
243
272
|
description: string;
|
|
@@ -299,6 +328,10 @@ export declare const dependfixCommand: import("citty").CommandDef<{
|
|
|
299
328
|
description: string;
|
|
300
329
|
negativeDescription: string;
|
|
301
330
|
};
|
|
331
|
+
'rollback-granularity': {
|
|
332
|
+
type: "string";
|
|
333
|
+
description: string;
|
|
334
|
+
};
|
|
302
335
|
'max-alerts-per-repository': {
|
|
303
336
|
type: "string";
|
|
304
337
|
description: string;
|
|
@@ -340,6 +373,26 @@ export declare const dependfixCommand: import("citty").CommandDef<{
|
|
|
340
373
|
type: "string";
|
|
341
374
|
description: string;
|
|
342
375
|
};
|
|
376
|
+
'mirror-ci-scripts': {
|
|
377
|
+
type: "boolean";
|
|
378
|
+
description: string;
|
|
379
|
+
negativeDescription: string;
|
|
380
|
+
};
|
|
381
|
+
'retry-failed': {
|
|
382
|
+
type: "boolean";
|
|
383
|
+
description: string;
|
|
384
|
+
negativeDescription: string;
|
|
385
|
+
};
|
|
386
|
+
'wait-pr-checks': {
|
|
387
|
+
type: "boolean";
|
|
388
|
+
description: string;
|
|
389
|
+
negativeDescription: string;
|
|
390
|
+
};
|
|
391
|
+
'close-failed-pr': {
|
|
392
|
+
type: "boolean";
|
|
393
|
+
description: string;
|
|
394
|
+
negativeDescription: string;
|
|
395
|
+
};
|
|
343
396
|
verbose: {
|
|
344
397
|
type: "boolean";
|
|
345
398
|
description: string;
|
|
@@ -375,8 +428,6 @@ export declare const dependfixCommand: import("citty").CommandDef<{
|
|
|
375
428
|
};
|
|
376
429
|
}>;
|
|
377
430
|
export declare function runDependfixMain(rawArgs?: string[]): Promise<void>;
|
|
378
|
-
//#endregion
|
|
379
|
-
//#region src/app/pipeline.d.ts
|
|
380
431
|
interface PipelineLogger {
|
|
381
432
|
info(msg: string): void;
|
|
382
433
|
error(msg: string): void;
|
|
@@ -409,12 +460,9 @@ interface Pipeline {
|
|
|
409
460
|
run(rawArgs: string[]): Promise<number>;
|
|
410
461
|
}
|
|
411
462
|
export declare function createPipeline(deps?: PipelineDeps): Pipeline;
|
|
412
|
-
//#endregion
|
|
413
|
-
//#region src/index.d.ts
|
|
414
463
|
/**
|
|
415
464
|
* 简化调用的入口(内部数据处理,非 CLI 使用)。
|
|
416
465
|
*/
|
|
417
466
|
export declare function main(args?: string[]): CliRunResult;
|
|
418
|
-
//#endregion
|
|
419
467
|
export type { Pipeline, PipelineDeps, PipelineLogger, PipelineParseResult };
|
|
420
468
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
CHANGED
|
@@ -6,7 +6,7 @@ export * from "@dependfix/core"
|
|
|
6
6
|
|
|
7
7
|
export * from "@dependfix/engine"
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
// src/index.ts
|
|
10
10
|
/**
|
|
11
11
|
* 简化调用的入口(内部数据处理,非 CLI 使用)。
|
|
12
12
|
*/
|
|
@@ -24,6 +24,5 @@ if (isExecutedAsEntryPoint()) runDependfixMain().catch((error) => {
|
|
|
24
24
|
process.exitCode = 1;
|
|
25
25
|
});
|
|
26
26
|
|
|
27
|
-
//#endregion
|
|
28
27
|
export { appendRepositories, argsDef, createPipeline, dependfixCommand, isAlertSource, isRuntimeMode, isSeverityThreshold, main, normalizeFlagList, parseCliArgs, parseCommandsFlag, parseIntegerFlag, parseUpgradeGroupsFlag, runCli, runDependfixMain };
|
|
29
28
|
//# sourceMappingURL=index.mjs.map
|
package/dist/runner.mjs
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { AppError, isValidRepoIdentifier, toAppError } from "@dependfix/core";
|
|
3
3
|
import { defineCommand, parseArgs, renderUsage, runCommand, runMain, showUsage } from "citty";
|
|
4
|
-
import { ALERT_SOURCES, DependfixApp, RUNTIME_MODES, SEVERITY_THRESHOLDS, formatHistory, queryRepoHistory, resolveRuntimeConfig } from "@dependfix/engine";
|
|
4
|
+
import { ALERT_SOURCES, DependfixApp, RUNTIME_MODES, SEVERITY_THRESHOLDS, formatHistory, parseOverrideProtectEntries, queryRepoHistory, resolveRuntimeConfig } from "@dependfix/engine";
|
|
5
5
|
import { homedir } from "node:os";
|
|
6
6
|
import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync } from "node:fs";
|
|
7
7
|
import { dirname, join } from "node:path";
|
|
8
8
|
import { createHash } from "node:crypto";
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
// src/cli/index.ts
|
|
11
11
|
const argsDef = {
|
|
12
12
|
mode: {
|
|
13
13
|
type: "positional",
|
|
@@ -44,6 +44,10 @@ const argsDef = {
|
|
|
44
44
|
type: "string",
|
|
45
45
|
description: "仓库黑名单 glob(逗号分隔多个或多次传入;显式列表与发现结果均受约束,与 include 冲突时胜出)"
|
|
46
46
|
},
|
|
47
|
+
"override-protect": {
|
|
48
|
+
type: "string",
|
|
49
|
+
description: "overrides 保护名单(防历史被人工移除的破坏性 override 复发),格式 \"repo-glob:pkg1,pkg2;repo-glob2:pkg3\""
|
|
50
|
+
},
|
|
47
51
|
"repo-topics-exclude": {
|
|
48
52
|
type: "string",
|
|
49
53
|
description: "发现结果 topic 黑名单(逗号分隔,排除含任一指定 topic 的仓库)"
|
|
@@ -102,9 +106,13 @@ const argsDef = {
|
|
|
102
106
|
},
|
|
103
107
|
"allow-major-upgrade": {
|
|
104
108
|
type: "boolean",
|
|
105
|
-
description: "跨线告警(推荐版本跨大版本,当前线内无修复版本)显式授权自动升级:仅根 package.json 直接依赖(workspace 成员独占声明维持人工)且 lockfile 单版本的告警自动跨线升级,升级后复核脆弱实例、强制完整验证(install+lint+build),失败自动回滚;间接依赖 / 多版本共存跨线告警维持人工处理。仅 CLI 可用,Action 不支持",
|
|
109
|
+
description: "跨线告警(推荐版本跨大版本,当前线内无修复版本)显式授权自动升级:仅根 package.json 直接依赖(workspace 成员独占声明维持人工)且 lockfile 单版本的告警自动跨线升级,升级后复核脆弱实例、强制完整验证(install+lint+build+typecheck+test),失败自动回滚;间接依赖 / 多版本共存跨线告警维持人工处理。仅 CLI 可用,Action 不支持",
|
|
106
110
|
negativeDescription: "不自动升级跨线告警(默认,维持人工处理)"
|
|
107
111
|
},
|
|
112
|
+
"rollback-granularity": {
|
|
113
|
+
type: "string",
|
|
114
|
+
description: "验证失败回滚粒度:unit(默认,回退最近一个已通过 probe 的变更单元并重验一次)/ repo(保持整仓 git reset --hard)"
|
|
115
|
+
},
|
|
108
116
|
"max-alerts-per-repository": {
|
|
109
117
|
type: "string",
|
|
110
118
|
description: "每个仓库最多处理的告警数",
|
|
@@ -127,7 +135,7 @@ const argsDef = {
|
|
|
127
135
|
},
|
|
128
136
|
"max-repos": {
|
|
129
137
|
type: "string",
|
|
130
|
-
description: "发现规模上限:最多保留的仓库数(默认 100;0
|
|
138
|
+
description: "发现规模上限:最多保留的仓库数(默认 100;0 表示不限制,负数会被拒绝;大 org 场景下防止一次性全量发现)",
|
|
131
139
|
default: "100"
|
|
132
140
|
},
|
|
133
141
|
history: {
|
|
@@ -144,7 +152,27 @@ const argsDef = {
|
|
|
144
152
|
},
|
|
145
153
|
commands: {
|
|
146
154
|
type: "string",
|
|
147
|
-
description: "
|
|
155
|
+
description: "自定义验证命令(逗号分隔),整链替换默认的 install/lint/build/typecheck/test(不做合并;默认链扩展不会自动跟随,默认链内的条目须显式保留)"
|
|
156
|
+
},
|
|
157
|
+
"mirror-ci-scripts": {
|
|
158
|
+
type: "boolean",
|
|
159
|
+
description: "把目标仓库 CI 独有的 pnpm/npm/yarn 脚本追加到验证链(默认关闭;最多 3 条、首失败即停;docker / gh / 任意 shell 只披露不执行;与 --commands 不叠加)",
|
|
160
|
+
negativeDescription: "不镜像 CI 独有脚本(默认)"
|
|
161
|
+
},
|
|
162
|
+
"retry-failed": {
|
|
163
|
+
type: "boolean",
|
|
164
|
+
description: "忽略同告警尝试上限并强制再试(仅 fix-and-pr;第 3 次起默认跳过并在报告标注「人工强制」)",
|
|
165
|
+
negativeDescription: "遵守同告警尝试上限(默认)"
|
|
166
|
+
},
|
|
167
|
+
"wait-pr-checks": {
|
|
168
|
+
type: "boolean",
|
|
169
|
+
description: "创建 PR 后等待 check 终态(仅 fix-and-pr;失败时默认评论失败摘要,受 per-PR / per-run 双预算约束,不改变 exit code)",
|
|
170
|
+
negativeDescription: "不等待 PR check(默认)"
|
|
171
|
+
},
|
|
172
|
+
"close-failed-pr": {
|
|
173
|
+
type: "boolean",
|
|
174
|
+
description: "check 失败且归因 self_caused 时关闭 PR 并删除分支(需同时开启 --wait-pr-checks;unknown / external_noise / pre_existing 只评论)",
|
|
175
|
+
negativeDescription: "不关闭失败 PR(默认)"
|
|
148
176
|
},
|
|
149
177
|
verbose: {
|
|
150
178
|
type: "boolean",
|
|
@@ -173,7 +201,7 @@ const argsDef = {
|
|
|
173
201
|
},
|
|
174
202
|
"ai-api-key": {
|
|
175
203
|
type: "string",
|
|
176
|
-
description: "AI API Key
|
|
204
|
+
description: "AI API Key(命令行参数优先,DEPENDFIX_AI_API_KEY 环境变量兜底;注意命令行参数会出现在进程列表/shell history,敏感环境请用 env)"
|
|
177
205
|
},
|
|
178
206
|
"ai-trigger": {
|
|
179
207
|
type: "string",
|
|
@@ -258,6 +286,11 @@ function parsedArgsToCliOverrides(parsed) {
|
|
|
258
286
|
if (parsed["code-scanning"] !== void 0) overrides.codeScanningEnabled = parsed["code-scanning"];
|
|
259
287
|
if (parsed["code-quality"] !== void 0) overrides.codeQualityEnabled = parsed["code-quality"];
|
|
260
288
|
if (parsed["allow-major-upgrade"] !== void 0) overrides.allowMajorUpgrade = parsed["allow-major-upgrade"];
|
|
289
|
+
const rollbackGranularity = parsed["rollback-granularity"]?.trim().toLowerCase();
|
|
290
|
+
if (rollbackGranularity) {
|
|
291
|
+
if (rollbackGranularity !== "unit" && rollbackGranularity !== "repo") throw new AppError("ARGUMENT_PARSE_ERROR", "--rollback-granularity must be one of: unit, repo");
|
|
292
|
+
overrides.rollbackGranularity = rollbackGranularity;
|
|
293
|
+
}
|
|
261
294
|
const maxAlerts = parsed["max-alerts-per-repository"];
|
|
262
295
|
if (maxAlerts) overrides.maxAlertsPerRepository = parseIntegerFlag(maxAlerts, "--max-alerts-per-repository", "Expected a positive integer.");
|
|
263
296
|
const maxConcurrency = parsed["max-concurrency"];
|
|
@@ -273,8 +306,14 @@ function parsedArgsToCliOverrides(parsed) {
|
|
|
273
306
|
if (parsed.verbose !== void 0) overrides.verbose = parsed.verbose;
|
|
274
307
|
const commandsValue = parsed.commands;
|
|
275
308
|
if (commandsValue) overrides.commands = parseCommandsFlag(commandsValue);
|
|
309
|
+
if (parsed["mirror-ci-scripts"] !== void 0) overrides.mirrorCiScripts = parsed["mirror-ci-scripts"];
|
|
310
|
+
if (parsed["retry-failed"] !== void 0) overrides.retryFailed = parsed["retry-failed"];
|
|
311
|
+
if (parsed["wait-pr-checks"] !== void 0) overrides.waitPrChecks = parsed["wait-pr-checks"];
|
|
312
|
+
if (parsed["close-failed-pr"] !== void 0) overrides.closeFailedPr = parsed["close-failed-pr"];
|
|
276
313
|
const upgradeGroups = parsed["upgrade-groups"];
|
|
277
314
|
if (upgradeGroups) overrides.upgradeGroups = parseUpgradeGroupsFlag(upgradeGroups);
|
|
315
|
+
const overrideProtect = parsed["override-protect"];
|
|
316
|
+
if (overrideProtect) overrides.overrideProtect = parseOverrideProtectEntries(overrideProtect, (message) => new AppError("ARGUMENT_PARSE_ERROR", message), "--override-protect");
|
|
278
317
|
const toolchainPnpmVersion = parsed["toolchain-pnpm-version"];
|
|
279
318
|
if (toolchainPnpmVersion) overrides.toolchainPnpmVersion = toolchainPnpmVersion;
|
|
280
319
|
if (parsed.ai !== void 0) overrides.aiEnabled = parsed.ai;
|
|
@@ -327,8 +366,7 @@ function parseCliArgs(rawArgs) {
|
|
|
327
366
|
};
|
|
328
367
|
}
|
|
329
368
|
|
|
330
|
-
|
|
331
|
-
//#region src/app/pipeline.ts
|
|
369
|
+
// src/app/pipeline.ts
|
|
332
370
|
function createPipeline(deps = {}) {
|
|
333
371
|
const env = deps.env ?? process.env;
|
|
334
372
|
const logger = deps.logger ?? console;
|
|
@@ -370,8 +408,7 @@ function createPipeline(deps = {}) {
|
|
|
370
408
|
};
|
|
371
409
|
}
|
|
372
410
|
|
|
373
|
-
|
|
374
|
-
//#region src/skills/agents.ts
|
|
411
|
+
// src/skills/agents.ts
|
|
375
412
|
const AGENTS = [
|
|
376
413
|
{
|
|
377
414
|
id: "claude-code",
|
|
@@ -403,8 +440,7 @@ const AGENTS = [
|
|
|
403
440
|
}
|
|
404
441
|
];
|
|
405
442
|
|
|
406
|
-
|
|
407
|
-
//#region src/skills/source.ts
|
|
443
|
+
// src/skills/source.ts
|
|
408
444
|
const PRODUCT_SKILL_NAME = "dependfix-remediator";
|
|
409
445
|
/**
|
|
410
446
|
* 解析产品 skill 内容目录(含 SKILL.md 的目录)。
|
|
@@ -415,8 +451,7 @@ function resolveProductSkillSourceDir() {
|
|
|
415
451
|
return join(dirname(pkgJsonPath), PRODUCT_SKILL_NAME);
|
|
416
452
|
}
|
|
417
453
|
|
|
418
|
-
|
|
419
|
-
//#region src/skills/installer.ts
|
|
454
|
+
// src/skills/installer.ts
|
|
420
455
|
/** 收集目录内文件相对路径 -> sha256(递归) */
|
|
421
456
|
function collectFileHashes(dir) {
|
|
422
457
|
const result = /* @__PURE__ */ new Map();
|
|
@@ -548,8 +583,7 @@ function errorMessage(error) {
|
|
|
548
583
|
return error instanceof Error ? error.message : String(error);
|
|
549
584
|
}
|
|
550
585
|
|
|
551
|
-
|
|
552
|
-
//#region src/skills/doctor.ts
|
|
586
|
+
// src/skills/doctor.ts
|
|
553
587
|
function runDoctor(options) {
|
|
554
588
|
const { homeDir, projectRoot = process.cwd() } = options;
|
|
555
589
|
const findings = [];
|
|
@@ -619,8 +653,7 @@ function safeResolveSourceDir() {
|
|
|
619
653
|
}
|
|
620
654
|
}
|
|
621
655
|
|
|
622
|
-
|
|
623
|
-
//#region src/skills/index.ts
|
|
656
|
+
// src/skills/index.ts
|
|
624
657
|
async function runInstall(args) {
|
|
625
658
|
const home = homedir();
|
|
626
659
|
const projectRoot = process.cwd();
|
|
@@ -758,8 +791,7 @@ const skillsCommand = defineCommand({
|
|
|
758
791
|
}
|
|
759
792
|
});
|
|
760
793
|
|
|
761
|
-
|
|
762
|
-
//#region src/cli/runner.ts
|
|
794
|
+
// src/cli/runner.ts
|
|
763
795
|
/** 默认 CLI 日志(console);platform 场景经 createPipeline 注入自定义 logger */
|
|
764
796
|
const cliLogger = console;
|
|
765
797
|
function runCli(rawArgs) {
|
|
@@ -768,10 +800,23 @@ function runCli(rawArgs) {
|
|
|
768
800
|
async function runApp(rawArgs) {
|
|
769
801
|
return createPipeline({ logger: cliLogger }).run(rawArgs);
|
|
770
802
|
}
|
|
803
|
+
/**
|
|
804
|
+
* 解析 CLI 版本:发布产物(dist/index.mjs)读 `../package.json`,源码运行
|
|
805
|
+
* (src/cli/runner.ts)读 `../../package.json`;只接受包名匹配的 JSON,避免误读。
|
|
806
|
+
* 不允许硬编码版本——否则 release 后 `--version` 会与 npm 包版本脱节。
|
|
807
|
+
*/
|
|
808
|
+
function resolveCliVersion() {
|
|
809
|
+
const require = createRequire(import.meta.url);
|
|
810
|
+
for (const specifier of ["../package.json", "../../package.json"]) try {
|
|
811
|
+
const pkg = require(specifier);
|
|
812
|
+
if (pkg?.name === "dependfix" && pkg.version) return pkg.version;
|
|
813
|
+
} catch {}
|
|
814
|
+
return "0.0.0-unknown";
|
|
815
|
+
}
|
|
771
816
|
const dependfixCommand = defineCommand({
|
|
772
817
|
meta: {
|
|
773
818
|
name: "dependfix",
|
|
774
|
-
version:
|
|
819
|
+
version: resolveCliVersion(),
|
|
775
820
|
description: "自动化处理 Dependabot / Code Scanning 安全告警的修复工具"
|
|
776
821
|
},
|
|
777
822
|
args: argsDef,
|
|
@@ -800,6 +845,5 @@ async function runDependfixMain(rawArgs = process.argv.slice(2)) {
|
|
|
800
845
|
await runMain(dependfixCommand, { showUsage });
|
|
801
846
|
}
|
|
802
847
|
|
|
803
|
-
//#endregion
|
|
804
848
|
export { appendRepositories as a, isRuntimeMode as c, parseCliArgs as d, parseCommandsFlag as f, createPipeline as i, isSeverityThreshold as l, parseUpgradeGroupsFlag as m, runCli as n, argsDef as o, parseIntegerFlag as p, runDependfixMain as r, isAlertSource as s, dependfixCommand as t, normalizeFlagList as u };
|
|
805
849
|
//# sourceMappingURL=runner.mjs.map
|
package/dist/runner.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.mjs","names":[],"sources":["../src/cli/index.ts","../src/app/pipeline.ts","../src/skills/agents.ts","../src/skills/source.ts","../src/skills/installer.ts","../src/skills/doctor.ts","../src/skills/index.ts","../src/cli/runner.ts"],"sourcesContent":["import {\n parseArgs,\n type ArgsDef,\n type ParsedArgs,\n} from 'citty'\nimport { AppError, isValidRepoIdentifier } from '@dependfix/core'\nimport {\n type CliConfigOverrides,\n type RuntimeMode,\n type SeverityThreshold,\n type AlertSourceKind,\n RUNTIME_MODES,\n SEVERITY_THRESHOLDS,\n ALERT_SOURCES,\n} from '@dependfix/engine'\n// ---------------------------------------------------------------------------\n// Public interfaces\n// ---------------------------------------------------------------------------\n\nexport interface CliInvocation {\n mode?: RuntimeMode\n configOverrides: CliConfigOverrides\n rawArgs: string[]\n}\n\n// ---------------------------------------------------------------------------\n// Args definition for citty\n// ---------------------------------------------------------------------------\n\nexport const argsDef = {\n mode: {\n type: 'positional' as const,\n description: '运行模式:report-only, fix, fix-and-pr, cleanup-branches',\n required: false,\n default: 'report-only' as const,\n },\n repo: {\n type: 'string' as const,\n description: '目标仓库 (owner/repo),逗号分隔多个',\n alias: ['r', 'repository', 'repositories'],\n },\n 'repos-file': {\n type: 'string' as const,\n description: '从文件读取仓库列表(每行一个 owner/repo)',\n },\n owner: {\n type: 'string' as const,\n description: 'owner / org 自动发现(逗号分隔多个或多次传入),与 --repo 合并去重(显式优先)',\n },\n 'repo-topics': {\n type: 'string' as const,\n description: '发现结果 topic 白名单(逗号分隔,AND 语义;仅影响 --owner 发现结果)',\n },\n 'repo-include': {\n type: 'string' as const,\n description: '仓库白名单 glob(逗号分隔多个或多次传入,如 owner/*、owner/pkg-*;仅作用于发现结果)',\n },\n 'repo-exclude': {\n type: 'string' as const,\n description: '仓库黑名单 glob(逗号分隔多个或多次传入;显式列表与发现结果均受约束,与 include 冲突时胜出)',\n },\n 'repo-topics-exclude': {\n type: 'string' as const,\n description: '发现结果 topic 黑名单(逗号分隔,排除含任一指定 topic 的仓库)',\n },\n 'severity-threshold': {\n type: 'string' as const,\n description: '严重级别阈值:critical, high, medium, all',\n default: 'high' as const,\n },\n 'dry-run': {\n type: 'boolean' as const,\n description: '试运行模式,不实际写入文件',\n negativeDescription: '关闭试运行模式',\n },\n 'create-pr': {\n type: 'boolean' as const,\n description: '创建 Pull Request',\n negativeDescription: '不创建 Pull Request',\n },\n commit: {\n type: 'boolean' as const,\n description: '修复完成后在本地当前分支直接提交(不推送、不创建 PR)',\n negativeDescription: '不自动提交(默认)',\n },\n 'cleanup-branches': {\n type: 'boolean' as const,\n description: '(fix-and-pr 模式)结束后列出已合并的 dependfix 分支到报告,不自动删除',\n negativeDescription: '不执行分支清理检查',\n },\n 'cleanup-branches-auto': {\n type: 'boolean' as const,\n description: '(fix-and-pr 模式)结束后自动删除已合并/已关闭的 dependfix 分支(非交互,不删有 open PR 的分支)',\n negativeDescription: '不自动删除分支',\n },\n 'github-token': {\n type: 'string' as const,\n description: 'GitHub Personal Access Token',\n },\n 'alerts-token': {\n type: 'string' as const,\n description: 'Dependabot alerts 专用 token(可选,最小权限 PAT,仅 Dependabot alerts: read;缺省回退 --github-token。GITHUB_TOKEN 无法读取 Dependabot alerts)',\n },\n 'alerts-source': {\n type: 'string' as const,\n description: '告警数据源:github-dependabot(默认,GitHub Dependabot alerts API)或 pnpm-audit(本地无 token 回退,扫描当前工作区 lockfile;repository 优先 --repo → git remote → local 兜底)',\n },\n 'code-scanning': {\n type: 'boolean' as const,\n description: '同时拉取 Code Scanning alerts(与 Dependabot 并行,默认关闭;需要 token 具备 security-events: read,GITHUB_TOKEN 默认具备)',\n negativeDescription: '不拉取 Code Scanning alerts(默认)',\n },\n 'code-quality': {\n type: 'boolean' as const,\n description: '同时拉取 Code Quality findings(与 Dependabot 并行,默认关闭;需要 fine-grained PAT 具备 Code quality: read,或 classic PAT 具备 repo/public_repo scope)',\n negativeDescription: '不拉取 Code Quality findings(默认)',\n },\n 'allow-major-upgrade': {\n type: 'boolean' as const,\n description: '跨线告警(推荐版本跨大版本,当前线内无修复版本)显式授权自动升级:仅根 package.json 直接依赖(workspace 成员独占声明维持人工)且 lockfile 单版本的告警自动跨线升级,升级后复核脆弱实例、强制完整验证(install+lint+build),失败自动回滚;间接依赖 / 多版本共存跨线告警维持人工处理。仅 CLI 可用,Action 不支持',\n negativeDescription: '不自动升级跨线告警(默认,维持人工处理)',\n },\n 'max-alerts-per-repository': {\n type: 'string' as const,\n description: '每个仓库最多处理的告警数',\n default: '20' as const,\n },\n 'max-concurrency': {\n type: 'string' as const,\n description: '多仓库并发窗口(1-16,默认 1 保守串行;>1 可能触发 GitHub 限流)',\n default: '1' as const,\n },\n 'max-retries': {\n type: 'string' as const,\n description: 'GitHub API 限流重试次数(0-10,默认 3;429/rate limit 指数退避重试)',\n default: '3' as const,\n },\n 'max-backoff-ms': {\n type: 'string' as const,\n description: '限流退避单次等待上限毫秒(100-120000,默认 30000;Retry-After / reset / 指数退避均受此约束)',\n default: '30000' as const,\n },\n 'max-repos': {\n type: 'string' as const,\n description: '发现规模上限:最多保留的仓库数(默认 100;0 或负数表示不限制;大 org 场景下防止一次性全量发现)',\n default: '100' as const,\n },\n history: {\n type: 'string' as const,\n description: '查询仓库历史运行摘要(读 dependfix-reports/index.json,倒序时间;不执行扫描)',\n },\n 'upgrade-groups': {\n type: 'string' as const,\n description: '用户显式分组(覆盖自动分组),格式 \"name1:pkg1,pkg2;name2:pkg3\"',\n },\n 'toolchain-pnpm-version': {\n type: 'string' as const,\n description: 'lockfile 修复用的 pnpm 版本(工具链固定;缺省从 package.json packageManager 解析;PIN_TOOLCHAIN 策略用 corepack pnpm@<version> 执行)',\n },\n commands: {\n type: 'string' as const,\n description: '自定义验证命令(逗号分隔),覆盖默认的 install/lint/build',\n },\n verbose: {\n type: 'boolean' as const,\n description: '输出详细日志',\n },\n ai: {\n type: 'boolean' as const,\n description: '开启 AI breaking change 研判(默认关闭;需配置 AI API Key。触发范围见 --ai-trigger;dry-run 不触发、不产生费用)',\n negativeDescription: '关闭 AI 研判(默认)',\n },\n 'ai-provider': {\n type: 'string' as const,\n description: 'AI 提供商:openai-compatible(默认,DeepSeek 等指定 --ai-base-url 兼容)或 anthropic',\n },\n 'ai-model': {\n type: 'string' as const,\n description: 'AI 模型名(默认 deepseek-v4-flash)',\n },\n 'ai-base-url': {\n type: 'string' as const,\n description: 'OpenAI 兼容端点基地址(默认 https://api.deepseek.com,与默认模型 deepseek-v4-flash 配套;使用 OpenAI 官方模型时指定 https://api.openai.com/v1 + --ai-model)',\n },\n 'ai-api-url': {\n type: 'string' as const,\n description: 'Anthropic 兼容端点(仅 --ai-provider anthropic 生效;默认 https://api.anthropic.com/v1/messages,自托管/网关可显式指定)',\n },\n 'ai-api-key': {\n type: 'string' as const,\n description: 'AI API Key(优先 DEPENDFIX_AI_API_KEY env;注意命令行参数会出现在进程列表/shell history,敏感环境请用 env)',\n },\n 'ai-trigger': {\n type: 'string' as const,\n description: 'AI 研判触发范围:failure(升级验证失败)/ major(major 升级)/ both(默认)',\n },\n} satisfies ArgsDef\n\n// ---------------------------------------------------------------------------\n// Conversion helpers\n// ---------------------------------------------------------------------------\n\nexport function isRuntimeMode(value: string): value is RuntimeMode {\n return RUNTIME_MODES.includes(value as RuntimeMode)\n}\n\nexport function isSeverityThreshold(value: string): value is SeverityThreshold {\n return SEVERITY_THRESHOLDS.includes(value as SeverityThreshold)\n}\n\nexport function isAlertSource(value: string): value is AlertSourceKind {\n return ALERT_SOURCES.includes(value as AlertSourceKind)\n}\n\nexport function appendRepositories(target: string[], value: string): void {\n for (const repository of value.split(',')) {\n const trimmed = repository.trim()\n\n if (!trimmed) {\n continue\n }\n\n if (!isValidRepoIdentifier(trimmed)) {\n throw new AppError(\n 'ARGUMENT_PARSE_ERROR',\n `Invalid repository identifier: \"${trimmed}\". Expected format: owner/repo`,\n )\n }\n\n target.push(trimmed)\n }\n}\n\nexport function parseCommandsFlag(value: string): string[] {\n return value\n .split(',')\n .map((cmd) => cmd.trim())\n .filter(Boolean)\n}\n\n/**\n * 严格整数字面量解析(修复:拒绝 `2.5` 被 parseInt 静默截断为 2)。\n * 仅接受 `^\\d+$`;范围语义由调用方在 expected 描述中声明(config 校验兜底)。\n */\nexport function parseIntegerFlag(value: string, flagName: string, expected: string): number {\n const trimmed = value.trim()\n if (!/^\\d+$/.test(trimmed)) {\n throw new AppError(\n 'ARGUMENT_PARSE_ERROR',\n `Invalid ${flagName} value: \"${value}\". ${expected}`,\n )\n }\n return Number.parseInt(trimmed, 10)\n}\n\n/**\n * 归一化可重复 flag 值(citty/mri 对重复传入返回数组,单次传入返回字符串):\n * 按逗号拆分 + 去空白 + 去空项。`--owner a,b --owner c` → ['a', 'b', 'c']。\n */\nexport function normalizeFlagList(value: string | string[] | undefined): string[] {\n if (value === undefined) {\n return []\n }\n const parts = Array.isArray(value) ? value : [value]\n return parts\n .flatMap((v) => v.split(','))\n .map((s) => s.trim())\n .filter(Boolean)\n}\n\nfunction parsedArgsToCliOverrides(parsed: ParsedArgs<typeof argsDef>): CliConfigOverrides {\n const overrides: CliConfigOverrides = {}\n\n // mode\n const rawMode = parsed.mode\n if (isRuntimeMode(rawMode)) {\n overrides.mode = rawMode\n }\n\n // repositories (from --repo / --repository)\n const repoValue = parsed.repo\n if (repoValue) {\n const repos: string[] = []\n appendRepositories(repos, repoValue)\n if (repos.length > 0) {\n overrides.repositories = repos\n }\n }\n\n // repos-file\n const reposFile = parsed['repos-file']\n if (reposFile) {\n overrides.reposFilePath = reposFile\n }\n\n // owner(自动发现;逗号分隔多个或多次传入)\n const ownerValue = normalizeFlagList(parsed.owner)\n if (ownerValue.length > 0) {\n overrides.owner = ownerValue\n }\n\n // repo-topics(发现结果 topic 白名单,AND 语义)\n const repoTopicsValue = normalizeFlagList(parsed['repo-topics'])\n if (repoTopicsValue.length > 0) {\n overrides.repoTopics = repoTopicsValue\n }\n\n // repo-include / repo-exclude / repo-topics-exclude(名单策略)\n const repoIncludeValue = normalizeFlagList(parsed['repo-include'])\n if (repoIncludeValue.length > 0) {\n overrides.repoInclude = repoIncludeValue\n }\n const repoExcludeValue = normalizeFlagList(parsed['repo-exclude'])\n if (repoExcludeValue.length > 0) {\n overrides.repoExclude = repoExcludeValue\n }\n const repoTopicsExcludeValue = normalizeFlagList(parsed['repo-topics-exclude'])\n if (repoTopicsExcludeValue.length > 0) {\n overrides.repoTopicsExclude = repoTopicsExcludeValue\n }\n\n // severity-threshold\n const severityThreshold = parsed['severity-threshold']\n if (isSeverityThreshold(severityThreshold)) {\n overrides.severityThreshold = severityThreshold\n }\n\n // dry-run (three-state: true / false / undefined)\n if (parsed['dry-run'] !== undefined) {\n overrides.dryRun = parsed['dry-run']\n }\n\n // create-pr (three-state: true / false / undefined)\n if (parsed['create-pr'] !== undefined) {\n overrides.createPullRequest = parsed['create-pr']\n }\n\n // commit (three-state: true / false / undefined)\n if (parsed.commit !== undefined) {\n overrides.commit = parsed.commit\n }\n\n // cleanup-branches (three-state: true / false / undefined)\n if (parsed['cleanup-branches'] !== undefined) {\n overrides.cleanupBranches = parsed['cleanup-branches']\n }\n\n // cleanup-branches-auto (three-state: true / false / undefined)\n if (parsed['cleanup-branches-auto'] !== undefined) {\n overrides.cleanupBranchesAuto = parsed['cleanup-branches-auto']\n }\n\n // github-token\n const githubToken = parsed['github-token']\n if (githubToken) {\n overrides.githubToken = githubToken\n }\n\n // alerts-token\n const alertsToken = parsed['alerts-token']\n if (alertsToken) {\n overrides.alertsToken = alertsToken\n }\n\n // alerts-source\n const alertsSource = parsed['alerts-source']\n if (alertsSource) {\n if (!isAlertSource(alertsSource)) {\n throw new AppError(\n 'ARGUMENT_PARSE_ERROR',\n `Invalid --alerts-source value: \"${alertsSource}\". Expected one of: ${ALERT_SOURCES.join(', ')}.`,\n )\n }\n overrides.alertSource = alertsSource\n }\n\n // code-scanning (three-state: true / false / undefined)\n if (parsed['code-scanning'] !== undefined) {\n overrides.codeScanningEnabled = parsed['code-scanning']\n }\n\n // code-quality (three-state: true / false / undefined)\n if (parsed['code-quality'] !== undefined) {\n overrides.codeQualityEnabled = parsed['code-quality']\n }\n\n // allow-major-upgrade (three-state: true / false / undefined; 无 env 通道,仅 CLI)\n if (parsed['allow-major-upgrade'] !== undefined) {\n overrides.allowMajorUpgrade = parsed['allow-major-upgrade']\n }\n\n // max-alerts-per-repository\n const maxAlerts = parsed['max-alerts-per-repository']\n if (maxAlerts) {\n overrides.maxAlertsPerRepository = parseIntegerFlag(maxAlerts, '--max-alerts-per-repository', 'Expected a positive integer.')\n }\n\n // max-concurrency(1-16,config 校验兜底)\n const maxConcurrency = parsed['max-concurrency']\n if (maxConcurrency) {\n overrides.maxConcurrency = parseIntegerFlag(maxConcurrency, '--max-concurrency', 'Expected an integer between 1 and 16.')\n }\n\n // max-retries(0-10,config 校验兜底)\n const maxRetries = parsed['max-retries']\n if (maxRetries) {\n overrides.maxRetries = parseIntegerFlag(maxRetries, '--max-retries', 'Expected an integer between 0 and 10.')\n }\n\n // max-backoff-ms(100-120000,config 校验兜底)\n const maxBackoffMs = parsed['max-backoff-ms']\n if (maxBackoffMs) {\n overrides.maxBackoffMs = parseIntegerFlag(maxBackoffMs, '--max-backoff-ms', 'Expected an integer between 100 and 120000.')\n }\n\n // max-repos(发现规模上限,config 校验兜底)\n const maxRepos = parsed['max-repos']\n if (maxRepos) {\n overrides.maxRepos = parseIntegerFlag(maxRepos, '--max-repos', 'Expected a non-negative integer (0 = unlimited).')\n }\n\n // history(独立查询命令,不进入运行配置)\n const history = parsed.history\n if (history) {\n overrides.history = history\n }\n\n // verbose (three-state: true / false / undefined)\n if (parsed.verbose !== undefined) {\n overrides.verbose = parsed.verbose\n }\n\n // commands\n const commandsValue = parsed.commands\n if (commandsValue) {\n overrides.commands = parseCommandsFlag(commandsValue)\n }\n\n // upgrade-groups\n const upgradeGroups = parsed['upgrade-groups']\n if (upgradeGroups) {\n overrides.upgradeGroups = parseUpgradeGroupsFlag(upgradeGroups)\n }\n\n // toolchain-pnpm-version\n const toolchainPnpmVersion = parsed['toolchain-pnpm-version']\n if (toolchainPnpmVersion) {\n overrides.toolchainPnpmVersion = toolchainPnpmVersion\n }\n\n // AI 研判(--ai 系列;--ai-api-key 为敏感参数,env 优先)\n if (parsed.ai !== undefined) {\n overrides.aiEnabled = parsed.ai\n }\n const aiProvider = parsed['ai-provider']\n if (aiProvider) {\n if (aiProvider !== 'openai-compatible' && aiProvider !== 'anthropic') {\n throw new AppError(\n 'ARGUMENT_PARSE_ERROR',\n `Invalid --ai-provider value: \"${aiProvider}\". Expected \"openai-compatible\" or \"anthropic\".`,\n )\n }\n overrides.aiProvider = aiProvider\n }\n const aiModel = parsed['ai-model']\n if (aiModel) {\n overrides.aiModel = aiModel\n }\n const aiBaseUrl = parsed['ai-base-url']\n if (aiBaseUrl) {\n overrides.aiBaseUrl = aiBaseUrl\n }\n const aiApiUrl = parsed['ai-api-url']\n if (aiApiUrl) {\n overrides.aiApiUrl = aiApiUrl\n }\n const aiApiKey = parsed['ai-api-key']\n if (aiApiKey) {\n overrides.aiApiKey = aiApiKey\n }\n const aiTrigger = parsed['ai-trigger']\n if (aiTrigger) {\n if (aiTrigger !== 'failure' && aiTrigger !== 'major' && aiTrigger !== 'both') {\n throw new AppError(\n 'ARGUMENT_PARSE_ERROR',\n `Invalid --ai-trigger value: \"${aiTrigger}\". Expected \"failure\", \"major\" or \"both\".`,\n )\n }\n overrides.aiTrigger = aiTrigger\n }\n\n return overrides\n}\n\n/**\n * 解析 `--upgrade-groups \"name1:pkg1,pkg2;name2:pkg3\"`。\n * 与 config 的 normalizeUpgradeGroups 保持一致(CLI 与 env 同格式):\n * 空 entry 忽略;非空但缺冒号/组名或包列表为空 → 抛 ARGUMENT_PARSE_ERROR;\n * 原型链风险键名(__proto__ / constructor / prototype)忽略。\n */\nexport function parseUpgradeGroupsFlag(value: string): Record<string, string[]> {\n const result: Record<string, string[]> = {}\n for (const entry of value.split(';')) {\n if (!entry.trim()) {\n continue\n }\n const idx = entry.indexOf(':')\n if (idx <= 0) {\n throw new AppError(\n 'ARGUMENT_PARSE_ERROR',\n `Invalid --upgrade-groups entry: \"${entry}\". Expected format: \"name:pkg1,pkg2\"`,\n )\n }\n const name = entry.slice(0, idx).trim()\n const pkgs = entry\n .slice(idx + 1)\n .split(',')\n .map((p) => p.trim())\n .filter(Boolean)\n if (name === '__proto__' || name === 'constructor' || name === 'prototype') {\n continue\n }\n if (!name || pkgs.length === 0) {\n throw new AppError(\n 'ARGUMENT_PARSE_ERROR',\n `Invalid --upgrade-groups entry: \"${entry}\". Expected format: \"name:pkg1,pkg2\"`,\n )\n }\n result[name] = pkgs\n }\n return result\n}\n\n// ---------------------------------------------------------------------------\n// Parsing API\n// ---------------------------------------------------------------------------\n\nexport function parseCliArgs(rawArgs: string[]): CliInvocation {\n const parsed = parseArgs<typeof argsDef>(rawArgs, argsDef)\n const configOverrides = parsedArgsToCliOverrides(parsed)\n\n return {\n mode: configOverrides.mode,\n configOverrides,\n rawArgs: [...rawArgs],\n }\n}\n","// 平台化管线抽象(独立平台前置):\n// local(CLI)与 platform 模式共用同一编排核心——参数解析 → 配置解析 →\n// DependfixApp.run → 退出码。process.env / console / process.exitCode\n// 全部经 PipelineDeps 注入,platform 可替换 env / logger / config resolver / exit。\n\nimport { resolveRuntimeConfig, queryRepoHistory, formatHistory, DependfixApp, type CliConfigOverrides } from '@dependfix/engine'\nimport { parseCliArgs, type CliInvocation } from '../cli'\n\nexport interface PipelineLogger {\n info(msg: string): void\n error(msg: string): void\n}\n\nexport interface PipelineDeps {\n /** 环境变量来源(默认 process.env)——platform 模式注入自定义 env */\n env?: NodeJS.ProcessEnv\n /** 日志输出(默认 console)——platform 模式注入自定义 logger */\n logger?: PipelineLogger\n /**\n * 配置解析(默认 resolveRuntimeConfig)。\n * platform 模式可注入自定义解析(如从数据库读取配置再合并 env)。\n */\n resolveConfig?: (opts: { env: NodeJS.ProcessEnv, cliOverrides: CliConfigOverrides }) => ReturnType<typeof resolveRuntimeConfig>\n /** 退出码设置(默认 process.exitCode)——platform 模式注入 */\n exit?: (code: number) => void\n}\n\nexport interface PipelineParseResult {\n ok: true\n invocation: CliInvocation\n config: ReturnType<typeof resolveRuntimeConfig>\n}\n\nexport interface Pipeline {\n /** 解析参数并解析配置(不执行扫描) */\n parse(rawArgs: string[]): PipelineParseResult\n /** 执行一次完整运行(含 history 查询分支),返回退出码 */\n run(rawArgs: string[]): Promise<number>\n}\n\nexport function createPipeline(deps: PipelineDeps = {}): Pipeline {\n const env = deps.env ?? process.env\n const logger = deps.logger ?? console\n const resolveConfig = deps.resolveConfig ?? ((opts: { env: NodeJS.ProcessEnv, cliOverrides: CliConfigOverrides }) => resolveRuntimeConfig(opts))\n const exit = deps.exit ?? ((code: number) => {\n process.exitCode = code\n })\n\n return {\n parse(rawArgs: string[]): PipelineParseResult {\n const invocation = parseCliArgs(rawArgs)\n const config = resolveConfig({ env, cliOverrides: invocation.configOverrides })\n return { ok: true, invocation, config }\n },\n\n async run(rawArgs: string[]): Promise<number> {\n const invocation = parseCliArgs(rawArgs)\n\n // --history:独立查询命令(读归档索引,不执行扫描、不要求 token/仓库配置)\n if (invocation.configOverrides.history) {\n const entries = queryRepoHistory(invocation.configOverrides.history)\n logger.info(formatHistory(entries, invocation.configOverrides.history))\n return 0\n }\n\n const config = resolveConfig({ env, cliOverrides: invocation.configOverrides })\n const app = new DependfixApp({\n config,\n verbose: invocation.configOverrides.verbose,\n commands: invocation.configOverrides.commands,\n })\n\n const { exitCode } = await app.run()\n exit(exitCode)\n return exitCode\n },\n }\n}\n","// ---------------------------------------------------------------------------\n// Agent 目录约定与检测\n// ---------------------------------------------------------------------------\n// 目录约定与 npx skills 生态对齐(vercel-labs/skills agents 定义):\n// - claude-code: 项目 .claude/skills,全局 ~/.claude/skills\n// - opencode: 项目 .agents/skills,全局 ~/.config/opencode/skills\n// - cursor: 项目 .cursor/skills,全局 ~/.cursor/skills\n// - copilot: 全局 ~/.copilot/skills(GitHub Copilot 无通用项目级目录约定)\n// 检测 = 对应主目录存在(存在即视为已安装该工具)。\n// homedir / projectRoot 均由调用方注入,便于测试。\n\nimport { existsSync } from 'node:fs'\nimport { join } from 'node:path'\n\nexport interface AgentDefinition {\n id: string\n displayName: string\n /** 项目级 skills 目录(相对项目根);不支持返回 undefined */\n projectSkillDir(projectRoot: string): string | undefined\n /** 用户级 skills 目录(绝对路径) */\n globalSkillDir(homeDir: string): string\n /** 检测是否已安装(对应主目录是否存在) */\n detectInstalled(homeDir: string): boolean\n}\n\nexport const AGENTS: AgentDefinition[] = [\n {\n id: 'claude-code',\n displayName: 'Claude Code',\n projectSkillDir: (root) => join(root, '.claude', 'skills'),\n globalSkillDir: (home) => join(home, '.claude', 'skills'),\n detectInstalled: (home) => existsSync(join(home, '.claude')),\n },\n {\n id: 'opencode',\n displayName: 'OpenCode',\n projectSkillDir: (root) => join(root, '.agents', 'skills'),\n globalSkillDir: (home) => join(home, '.config', 'opencode', 'skills'),\n detectInstalled: (home) => existsSync(join(home, '.config', 'opencode')),\n },\n {\n id: 'cursor',\n displayName: 'Cursor',\n projectSkillDir: (root) => join(root, '.cursor', 'skills'),\n globalSkillDir: (home) => join(home, '.cursor', 'skills'),\n detectInstalled: (home) => existsSync(join(home, '.cursor')),\n },\n {\n id: 'copilot',\n displayName: 'GitHub Copilot',\n projectSkillDir: () => undefined,\n globalSkillDir: (home) => join(home, '.copilot', 'skills'),\n detectInstalled: (home) => existsSync(join(home, '.copilot')),\n },\n]\n","// ---------------------------------------------------------------------------\n// 产品 skill 内容来源定位\n// ---------------------------------------------------------------------------\n// dependfix-remediator 权威源随 npm 包 @dependfix/skills 发布(files: dependfix-remediator)。\n// CLI 依赖该包,运行时通过 require.resolve 定位包内 skill 目录:\n// - 开发(tsx):workspace 链接解析到 packages/skills/dependfix-remediator\n// - 发布(npx dependfix):node_modules 中解析 @dependfix/skills/dependfix-remediator\n\nimport { createRequire } from 'node:module'\nimport { dirname, join } from 'node:path'\n\nexport const PRODUCT_SKILL_NAME = 'dependfix-remediator'\n\n/**\n * 解析产品 skill 内容目录(含 SKILL.md 的目录)。\n * 找不到包或内容目录时抛错(install / doctor 的必要前置)。\n */\nexport function resolveProductSkillSourceDir(): string {\n const require = createRequire(import.meta.url)\n const pkgJsonPath = require.resolve('@dependfix/skills/package.json')\n return join(dirname(pkgJsonPath), PRODUCT_SKILL_NAME)\n}\n","// ---------------------------------------------------------------------------\n// 产品 skill 安装器(dependfix skills install 核心)\n// ---------------------------------------------------------------------------\n// 语义:\n// - 目标不存在 → 创建并复制(installed)\n// - 目标已存在且内容一致 → 跳过(up-to-date,幂等)\n// - 目标已存在但内容不一致 → 覆盖需确认(force 或交互确认;拒绝则 skipped-conflict)\n// - dry-run 只输出将要执行的操作,不写文件\n// 覆盖采用镜像语义:删除旧内容后全量复制,保证与权威源严格一致。\n\nimport { createHash } from 'node:crypto'\nimport { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { PRODUCT_SKILL_NAME } from './source'\n\nexport type InstallStatus = 'installed' | 'up-to-date' | 'skipped-conflict' | 'failed'\n\nexport interface InstallResult {\n status: InstallStatus\n /** 安装目标目录(依赖 fix-and-pr 模式外,本为 dependfix-remediator 目录) */\n targetDir: string\n detail?: string\n}\n\nexport interface InstallOptions {\n /** 权威源内容目录(含 SKILL.md) */\n sourceDir: string\n /** 目标 agent skills 目录(dependfix-remediator 将位于其下) */\n targetDir: string\n /** 强制覆盖(跳过确认) */\n force?: boolean\n /** 试运行:只输出将要执行的操作 */\n dryRun?: boolean\n /** 覆盖确认回调;缺省返回 false(非交互默认拒绝,不静默覆盖) */\n confirmOverwrite?: (existingDir: string) => Promise<boolean> | boolean\n}\n\n/** 收集目录内文件相对路径 -> sha256(递归) */\nexport function collectFileHashes(dir: string): Map<string, string> {\n const result = new Map<string, string>()\n if (!existsSync(dir)) {\n return result\n }\n const walk = (current: string, relPrefix: string) => {\n for (const entry of readdirSync(current, { withFileTypes: true })) {\n const full = join(current, entry.name)\n const rel = relPrefix ? `${relPrefix}/${entry.name}` : entry.name\n if (entry.isDirectory()) {\n walk(full, rel)\n } else if (entry.isFile()) {\n result.set(rel, hashFile(full))\n }\n }\n }\n walk(dir, '')\n return result\n}\n\nfunction hashFile(filePath: string): string {\n return createHash('sha256').update(readFileSync(filePath)).digest('hex')\n}\n\n/** 两个目录内容是否完全一致(文件集合 + 内容 hash) */\nexport function isContentSame(dirA: string, dirB: string): boolean {\n const mapA = collectFileHashes(dirA)\n const mapB = collectFileHashes(dirB)\n if (mapA.size !== mapB.size) {\n return false\n }\n for (const [rel, hash] of mapA) {\n if (mapB.get(rel) !== hash) {\n return false\n }\n }\n return true\n}\n\n/** 复制目录(镜像语义:先清空目标内同名子目录再复制) */\nfunction mirrorCopy(sourceDir: string, targetSkillDir: string): void {\n rmSync(targetSkillDir, { recursive: true, force: true })\n mkdirSync(targetSkillDir, { recursive: true })\n for (const entry of readdirSync(sourceDir)) {\n const src = join(sourceDir, entry)\n const dst = join(targetSkillDir, entry)\n if (statSync(src).isDirectory()) {\n cpSync(src, dst, { recursive: true })\n } else {\n cpSync(src, dst)\n }\n }\n}\n\n/**\n * 安装产品 skill 到单个 agent skills 目录。\n * 返回 InstallResult;失败时 status='failed' 且 detail 含错误信息(不抛错,便于批量收尾)。\n */\nexport async function installSkillToDir(options: InstallOptions): Promise<InstallResult> {\n const { sourceDir, targetDir, force, dryRun, confirmOverwrite } = options\n const skillTarget = join(targetDir, PRODUCT_SKILL_NAME)\n\n if (!existsSync(sourceDir)) {\n return { status: 'failed', targetDir, detail: `权威源不存在: ${sourceDir}` }\n }\n\n if (dryRun) {\n try {\n const same = existsSync(skillTarget) && isContentSame(sourceDir, skillTarget)\n return same\n ? { status: 'up-to-date', targetDir, detail: '[dry-run] 已一致' }\n : { status: 'installed', targetDir, detail: '[dry-run] 将安装' }\n } catch (error: unknown) {\n return { status: 'failed', targetDir, detail: errorMessage(error) }\n }\n }\n\n if (!existsSync(skillTarget)) {\n try {\n mirrorCopy(sourceDir, skillTarget)\n return { status: 'installed', targetDir }\n } catch (error: unknown) {\n cleanupPartial(skillTarget)\n return { status: 'failed', targetDir, detail: errorMessage(error) }\n }\n }\n\n // 已存在:内容一致则幂等跳过\n let same: boolean\n try {\n same = isContentSame(sourceDir, skillTarget)\n } catch (error: unknown) {\n return { status: 'failed', targetDir, detail: errorMessage(error) }\n }\n if (same) {\n return { status: 'up-to-date', targetDir }\n }\n\n // 内容不一致:需要确认覆盖(非交互默认拒绝)\n const allowed = force ?? (confirmOverwrite ? await confirmOverwrite(skillTarget) : false)\n if (!allowed) {\n return {\n status: 'skipped-conflict',\n targetDir,\n detail: '目标存在内容不一致的同名 skill,已跳过(使用 --force 或交互确认覆盖)',\n }\n }\n\n try {\n mirrorCopy(sourceDir, skillTarget)\n return { status: 'installed', targetDir, detail: '已覆盖旧版本' }\n } catch (error: unknown) {\n cleanupPartial(skillTarget)\n return { status: 'failed', targetDir, detail: errorMessage(error) }\n }\n}\n\n/** mirrorCopy 失败后清理半写目录(避免下次安装误判为内容不一致需 force) */\nfunction cleanupPartial(skillTarget: string): void {\n try {\n rmSync(skillTarget, { recursive: true, force: true })\n } catch {\n // 清理失败不掩盖原始错误\n }\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n","// ---------------------------------------------------------------------------\n// dependfix skills doctor 核心逻辑\n// ---------------------------------------------------------------------------\n// 检查项:\n// 1. 已检测 agent 的 skills 目录存在性(目录约定漂移提示:主目录在但 skills 目录缺失)\n// 2. 产品 skill 安装状态(已检测 agent 的 skills 目录中 dependfix-remediator 是否存在、\n// 内容与当前 CLI 携带版本是否一致)\n// 3. 内部开发 skill internal 标记完整性(在 dependfix 仓库内运行时,扫描 .github/skills)\n// 全部为只读检查,输出分级问题清单。\n\nimport { existsSync, readFileSync, readdirSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { AGENTS } from './agents'\nimport { isContentSame } from './installer'\nimport { PRODUCT_SKILL_NAME, resolveProductSkillSourceDir } from './source'\n\nexport type DoctorLevel = 'ok' | 'warn' | 'error'\n\nexport interface DoctorFinding {\n level: DoctorLevel\n message: string\n}\n\nexport interface DoctorOptions {\n homeDir: string\n /** 项目根(用于项目级目录检查与内部 skill 扫描);缺省为当前工作目录 */\n projectRoot?: string\n /** 产品 skill 权威源目录(缺省从 @dependfix/skills 包解析;测试可注入) */\n productSourceDir?: string\n}\n\nexport function runDoctor(options: DoctorOptions): DoctorFinding[] {\n const { homeDir, projectRoot = process.cwd() } = options\n const findings: DoctorFinding[] = []\n\n // 1. agent 目录约定检查\n for (const agent of AGENTS) {\n const installed = agent.detectInstalled(homeDir)\n if (!installed) {\n continue\n }\n const globalDir = agent.globalSkillDir(homeDir)\n if (!existsSync(globalDir)) {\n findings.push({\n level: 'warn',\n message: `${agent.displayName} 已检测到,但全局 skills 目录缺失(${globalDir})——可能官方目录约定已变更,或尚未初始化`,\n })\n }\n const projectDir = agent.projectSkillDir(projectRoot)\n if (projectDir && !existsSync(projectDir)) {\n findings.push({\n level: 'ok',\n message: `${agent.displayName} 项目级 skills 目录不存在(${projectDir})——非问题,按需创建`,\n })\n }\n }\n\n // 2. 产品 skill 安装状态\n const sourceDir = options.productSourceDir ?? safeResolveSourceDir()\n if (sourceDir) {\n for (const agent of AGENTS) {\n if (!agent.detectInstalled(homeDir)) {\n continue\n }\n const installedDir = join(agent.globalSkillDir(homeDir), PRODUCT_SKILL_NAME)\n if (!existsSync(installedDir)) {\n findings.push({\n level: 'ok',\n message: `${agent.displayName}: ${PRODUCT_SKILL_NAME} 未安装(运行 dependfix skills install)`,\n })\n continue\n }\n if (isContentSame(sourceDir, installedDir)) {\n findings.push({\n level: 'ok',\n message: `${agent.displayName}: ${PRODUCT_SKILL_NAME} 已安装且与当前版本一致`,\n })\n } else {\n findings.push({\n level: 'warn',\n message: `${agent.displayName}: ${PRODUCT_SKILL_NAME} 已安装但内容与当前版本不一致(建议重新 install)`,\n })\n }\n }\n }\n\n // 3. 内部开发 skill internal 标记完整性(仅 dependfix 仓库内)\n const internalSkillsRoot = join(projectRoot, '.github', 'skills')\n if (existsSync(internalSkillsRoot)) {\n const missing: string[] = []\n for (const entry of readdirSync(internalSkillsRoot, { withFileTypes: true })) {\n if (!entry.isDirectory()) {\n continue\n }\n const skillFile = join(internalSkillsRoot, entry.name, 'SKILL.md')\n if (!existsSync(skillFile)) {\n missing.push(`${entry.name}(缺 SKILL.md)`)\n continue\n }\n const content = readFileSync(skillFile, 'utf8')\n if (!/metadata:\\s*\\r?\\n\\s*internal:\\s*true/.test(content)) {\n missing.push(entry.name)\n }\n }\n if (missing.length > 0) {\n findings.push({\n level: 'error',\n message: `内部 skill internal 标记缺失(生态可见泄漏风险): ${missing.join(', ')}`,\n })\n } else {\n findings.push({\n level: 'ok',\n message: `内部 skill internal 标记完整性检查通过(.github/skills)`,\n })\n }\n }\n\n return findings\n}\n\n/** 解析产品 skill 源目录;失败返回 undefined(如 CLI 运行在无 @dependfix/skills 的调试环境) */\nfunction safeResolveSourceDir(): string | undefined {\n try {\n return resolveProductSkillSourceDir()\n } catch {\n return undefined\n }\n}\n","// ---------------------------------------------------------------------------\n// dependfix skills 子命令(install / doctor)\n// ---------------------------------------------------------------------------\n// 定位:npx skills 生态的离线兜底安装器。\n// 不依赖 npx skills;不复刻 add/list/update/remove 完整命令矩阵。\n// 主通道(npx skills add dependfix/dependfix -s dependfix-remediator)见 README。\n\nimport { homedir } from 'node:os'\nimport { defineCommand, renderUsage } from 'citty'\nimport { AGENTS } from './agents'\nimport { runDoctor, type DoctorFinding } from './doctor'\nimport { installSkillToDir, type InstallResult } from './installer'\nimport { resolveProductSkillSourceDir } from './source'\n\n// ---------------------------------------------------------------------------\n// install\n// ---------------------------------------------------------------------------\n\ninterface InstallCliArgs {\n project?: boolean\n force?: boolean\n 'dry-run'?: boolean\n}\n\nasync function runInstall(args: InstallCliArgs): Promise<void> {\n const home = homedir()\n const projectRoot = process.cwd()\n\n let sourceDir: string\n try {\n sourceDir = resolveProductSkillSourceDir()\n } catch (error: unknown) {\n console.error(`无法定位产品 skill 内容(@dependfix/skills 未安装?): ${error instanceof Error ? error.message : String(error)}`)\n process.exitCode = 1\n return\n }\n\n const detected = AGENTS.filter((agent) => agent.detectInstalled(home))\n if (detected.length === 0) {\n console.info('未检测到已安装的 agent 工具(Claude Code / OpenCode / Cursor / Copilot)。')\n console.info('主通道安装(推荐): npx skills add dependfix/dependfix -s dependfix-remediator -g')\n return\n }\n\n console.info(`检测到 ${detected.length} 个 agent 工具,目标模式: ${args.project ? '项目级' : '全局'}\\n`)\n\n const results: { agent: string, result: InstallResult }[] = []\n for (const agent of detected) {\n const agentDir = args.project ? agent.projectSkillDir(projectRoot) : agent.globalSkillDir(home)\n if (!agentDir) {\n console.info(`- ${agent.displayName}: 不支持 ${args.project ? '项目级' : ''}安装,跳过`)\n continue\n }\n const result = await installSkillToDir({\n sourceDir,\n targetDir: agentDir,\n force: args.force,\n dryRun: args['dry-run'],\n confirmOverwrite: (existingDir) => confirmOverwritePrompt(agent.displayName, existingDir),\n })\n results.push({ agent: agent.displayName, result })\n }\n\n console.info('\\n安装清单:')\n const installIcons: Record<InstallResult['status'], string> = {\n installed: '✓',\n 'up-to-date': '=',\n 'skipped-conflict': '!',\n failed: '✗',\n }\n for (const { agent, result } of results) {\n const icon = installIcons[result.status]\n console.info(` ${icon} ${agent}: ${result.status}${result.detail ? ` — ${result.detail}` : ''}`)\n }\n\n const failed = results.filter((r) => r.result.status === 'failed')\n if (failed.length > 0) {\n process.exitCode = 1\n }\n}\n\n/** 交互式覆盖确认;非 TTY 返回 false(不静默覆盖) */\nasync function confirmOverwritePrompt(agentName: string, existingDir: string): Promise<boolean> {\n if (!process.stdin.isTTY) {\n console.warn(` ${agentName}: 目标已存在内容不一致的 dependfix-remediator,非交互环境跳过(使用 --force 覆盖)`)\n return false\n }\n const readline = (await import('node:readline')).createInterface({\n input: process.stdin,\n output: process.stdout,\n })\n return new Promise<boolean>((resolve) => {\n readline.question(` 覆盖 ${existingDir} 中的旧版本?[y/N] `, (answer) => {\n readline.close()\n resolve(answer.trim().toLowerCase() === 'y' || answer.trim().toLowerCase() === 'yes')\n })\n })\n}\n\n// ---------------------------------------------------------------------------\n// doctor\n// ---------------------------------------------------------------------------\n\nasync function runDoctorCli(): Promise<void> {\n const findings = runDoctor({ homeDir: homedir(), projectRoot: process.cwd() })\n const doctorIcons: Record<DoctorFinding['level'], string> = { ok: '✓', warn: '!', error: '✗' }\n let warnCount = 0\n let errorCount = 0\n for (const finding of findings) {\n const icon = doctorIcons[finding.level]\n console.info(` ${icon} [${finding.level}] ${finding.message}`)\n if (finding.level === 'warn') {\n warnCount++\n } else if (finding.level === 'error') {\n errorCount++\n }\n }\n console.info(`\\ndoctor 完成: ${findings.length} 项检查,${warnCount} warning, ${errorCount} error`)\n if (errorCount > 0) {\n process.exitCode = 1\n }\n}\n\n// ---------------------------------------------------------------------------\n// citty command\n// ---------------------------------------------------------------------------\n\nexport const skillsCommand = defineCommand({\n meta: {\n name: 'skills',\n description: '产品 skill 管理(npx skills 生态的离线兜底安装器)',\n },\n subCommands: {\n install: defineCommand({\n meta: {\n name: 'install',\n description: '检测本机已装 agent 工具并安装 dependfix-remediator 产品 skill',\n },\n args: {\n project: {\n type: 'boolean',\n description: '安装到当前项目目录(默认用户全局)',\n negativeDescription: '安装到用户全局(默认)',\n },\n force: {\n type: 'boolean',\n description: '覆盖已存在的内容不一致同名 skill(跳过确认)',\n negativeDescription: '内容不一致时需确认(默认)',\n },\n 'dry-run': {\n type: 'boolean',\n description: '只输出将要执行的操作,不写文件',\n negativeDescription: '实际执行安装',\n },\n },\n async run({ args }) {\n await runInstall(args as InstallCliArgs)\n },\n }),\n doctor: defineCommand({\n meta: {\n name: 'doctor',\n description: '检查 agent 目录约定、产品 skill 安装状态与内部 skill internal 标记完整性',\n },\n async run() {\n await runDoctorCli()\n },\n }),\n },\n async run({ cmd, rawArgs }) {\n // citty 0.2.2 在路由子命令后仍会执行父 run:子命令已执行时直接返回,避免重复输出 usage\n const sub = rawArgs[0]\n if (sub === 'install' || sub === 'doctor') {\n return\n }\n console.info(await renderUsage(cmd))\n },\n})\n","// CLI 执行层(解析与执行分离):\n// - cli/index.ts:argsDef + parseCliArgs(纯解析,无执行依赖)\n// - cli/runner.ts:runCli / runApp / dependfixCommand(经 createPipeline 执行)\n// - app/pipeline.ts:createPipeline(local 与 platform 共用编排核心)\n//\n// 依赖方向:runner → pipeline → cli/index(纯解析),无循环。\n\nimport { defineCommand, renderUsage, runCommand, runMain, showUsage } from 'citty'\nimport { toAppError } from '@dependfix/core'\nimport type { RuntimeConfig } from '@dependfix/engine'\nimport { createPipeline, type PipelineLogger } from '../app/pipeline'\nimport { skillsCommand } from '../skills'\nimport { argsDef, type CliInvocation } from './index'\n\nexport interface CliRunResult {\n ok: true\n invocation: CliInvocation\n config: RuntimeConfig\n}\n\n/** 默认 CLI 日志(console);platform 场景经 createPipeline 注入自定义 logger */\nconst cliLogger: PipelineLogger = console\n\n// ---------------------------------------------------------------------------\n// Public API(签名与行为保持兼容)\n// ---------------------------------------------------------------------------\n\nexport function runCli(rawArgs: string[]): CliRunResult {\n return createPipeline({ logger: cliLogger }).parse(rawArgs)\n}\n\n// ---------------------------------------------------------------------------\n// App execution\n// ---------------------------------------------------------------------------\n\nasync function runApp(rawArgs: string[]): Promise<number> {\n return createPipeline({ logger: cliLogger }).run(rawArgs)\n}\n\n// ---------------------------------------------------------------------------\n// citty command\n// ---------------------------------------------------------------------------\n// 注意:不使用 citty subCommands 字段——citty 0.2.2 在 subCommands 模式下会\n// ① 子命令执行后继续执行父 run;② 不匹配子命令的 positional(如 fix)抛\n// Unknown command。因此 skills 子命令由 run 首部编程路由(runCommand),\n// 主流程 positional 命令面不受影响。\n\nexport const dependfixCommand = defineCommand({\n meta: {\n name: 'dependfix',\n version: '0.1.0',\n description: '自动化处理 Dependabot / Code Scanning 安全告警的修复工具',\n },\n args: argsDef,\n async run({ rawArgs }) {\n // skills 子命令路由(install / doctor)\n if (rawArgs[0] === 'skills') {\n await runCommand(skillsCommand, { rawArgs: rawArgs.slice(1) })\n return\n }\n\n let exitCode = 1\n try {\n exitCode = await runApp(rawArgs)\n } catch (error: unknown) {\n const appError = toAppError(error, 'CLI_EXECUTION_FAILED')\n console.error(`Error: ${appError.message}`)\n exitCode = 1\n }\n process.exitCode = exitCode\n },\n})\n\n// ---------------------------------------------------------------------------\n// 入口封装:补 skills 子命令的 --help 路由\n// ---------------------------------------------------------------------------\n// citty runMain 在 runCommand 前拦截内建 --help,且 resolveSubCommand 依赖\n// subCommands 字段下钻;dependfixCommand 无该字段(见上),故 skills 相关\n// --help 会被截获为主命令 usage。此处显式路由到 skills usage。\n\nexport async function runDependfixMain(rawArgs: string[] = process.argv.slice(2)): Promise<void> {\n if (rawArgs[0] === 'skills' && (rawArgs.includes('--help') || rawArgs.includes('-h'))) {\n console.info(await renderUsage(skillsCommand))\n process.exitCode = 0\n return\n }\n await runMain(dependfixCommand, { showUsage })\n}\n"],"mappings":";;;;;;;;;;AA6BA,MAAa,UAAU;CACnB,MAAM;EACF,MAAM;EACN,aAAa;EACb,UAAU;EACV,SAAS;CACb;CACA,MAAM;EACF,MAAM;EACN,aAAa;EACb,OAAO;GAAC;GAAK;GAAc;EAAc;CAC7C;CACA,cAAc;EACV,MAAM;EACN,aAAa;CACjB;CACA,OAAO;EACH,MAAM;EACN,aAAa;CACjB;CACA,eAAe;EACX,MAAM;EACN,aAAa;CACjB;CACA,gBAAgB;EACZ,MAAM;EACN,aAAa;CACjB;CACA,gBAAgB;EACZ,MAAM;EACN,aAAa;CACjB;CACA,uBAAuB;EACnB,MAAM;EACN,aAAa;CACjB;CACA,sBAAsB;EAClB,MAAM;EACN,aAAa;EACb,SAAS;CACb;CACA,WAAW;EACP,MAAM;EACN,aAAa;EACb,qBAAqB;CACzB;CACA,aAAa;EACT,MAAM;EACN,aAAa;EACb,qBAAqB;CACzB;CACA,QAAQ;EACJ,MAAM;EACN,aAAa;EACb,qBAAqB;CACzB;CACA,oBAAoB;EAChB,MAAM;EACN,aAAa;EACb,qBAAqB;CACzB;CACA,yBAAyB;EACrB,MAAM;EACN,aAAa;EACb,qBAAqB;CACzB;CACA,gBAAgB;EACZ,MAAM;EACN,aAAa;CACjB;CACA,gBAAgB;EACZ,MAAM;EACN,aAAa;CACjB;CACA,iBAAiB;EACb,MAAM;EACN,aAAa;CACjB;CACA,iBAAiB;EACb,MAAM;EACN,aAAa;EACb,qBAAqB;CACzB;CACA,gBAAgB;EACZ,MAAM;EACN,aAAa;EACb,qBAAqB;CACzB;CACA,uBAAuB;EACnB,MAAM;EACN,aAAa;EACb,qBAAqB;CACzB;CACA,6BAA6B;EACzB,MAAM;EACN,aAAa;EACb,SAAS;CACb;CACA,mBAAmB;EACf,MAAM;EACN,aAAa;EACb,SAAS;CACb;CACA,eAAe;EACX,MAAM;EACN,aAAa;EACb,SAAS;CACb;CACA,kBAAkB;EACd,MAAM;EACN,aAAa;EACb,SAAS;CACb;CACA,aAAa;EACT,MAAM;EACN,aAAa;EACb,SAAS;CACb;CACA,SAAS;EACL,MAAM;EACN,aAAa;CACjB;CA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"}
|
|
1
|
+
{"version":3,"file":"runner.mjs","names":[],"sources":["../src/cli/index.ts","../src/app/pipeline.ts","../src/skills/agents.ts","../src/skills/source.ts","../src/skills/installer.ts","../src/skills/doctor.ts","../src/skills/index.ts","../src/cli/runner.ts"],"sourcesContent":["import {\n parseArgs,\n type ArgsDef,\n type ParsedArgs,\n} from 'citty'\nimport { AppError, isValidRepoIdentifier } from '@dependfix/core'\nimport {\n type CliConfigOverrides,\n type RuntimeMode,\n type SeverityThreshold,\n type AlertSourceKind,\n RUNTIME_MODES,\n SEVERITY_THRESHOLDS,\n ALERT_SOURCES,\n parseOverrideProtectEntries,\n} from '@dependfix/engine'\n// ---------------------------------------------------------------------------\n// Public interfaces\n// ---------------------------------------------------------------------------\n\nexport interface CliInvocation {\n mode?: RuntimeMode\n configOverrides: CliConfigOverrides\n rawArgs: string[]\n}\n\n// ---------------------------------------------------------------------------\n// Args definition for citty\n// ---------------------------------------------------------------------------\n\nexport const argsDef = {\n mode: {\n type: 'positional' as const,\n description: '运行模式:report-only, fix, fix-and-pr, cleanup-branches',\n required: false,\n default: 'report-only' as const,\n },\n repo: {\n type: 'string' as const,\n description: '目标仓库 (owner/repo),逗号分隔多个',\n alias: ['r', 'repository', 'repositories'],\n },\n 'repos-file': {\n type: 'string' as const,\n description: '从文件读取仓库列表(每行一个 owner/repo)',\n },\n owner: {\n type: 'string' as const,\n description: 'owner / org 自动发现(逗号分隔多个或多次传入),与 --repo 合并去重(显式优先)',\n },\n 'repo-topics': {\n type: 'string' as const,\n description: '发现结果 topic 白名单(逗号分隔,AND 语义;仅影响 --owner 发现结果)',\n },\n 'repo-include': {\n type: 'string' as const,\n description: '仓库白名单 glob(逗号分隔多个或多次传入,如 owner/*、owner/pkg-*;仅作用于发现结果)',\n },\n 'repo-exclude': {\n type: 'string' as const,\n description: '仓库黑名单 glob(逗号分隔多个或多次传入;显式列表与发现结果均受约束,与 include 冲突时胜出)',\n },\n 'override-protect': {\n type: 'string' as const,\n description: 'overrides 保护名单(防历史被人工移除的破坏性 override 复发),格式 \"repo-glob:pkg1,pkg2;repo-glob2:pkg3\"',\n },\n 'repo-topics-exclude': {\n type: 'string' as const,\n description: '发现结果 topic 黑名单(逗号分隔,排除含任一指定 topic 的仓库)',\n },\n 'severity-threshold': {\n type: 'string' as const,\n description: '严重级别阈值:critical, high, medium, all',\n default: 'high' as const,\n },\n 'dry-run': {\n type: 'boolean' as const,\n description: '试运行模式,不实际写入文件',\n negativeDescription: '关闭试运行模式',\n },\n 'create-pr': {\n type: 'boolean' as const,\n description: '创建 Pull Request',\n negativeDescription: '不创建 Pull Request',\n },\n commit: {\n type: 'boolean' as const,\n description: '修复完成后在本地当前分支直接提交(不推送、不创建 PR)',\n negativeDescription: '不自动提交(默认)',\n },\n 'cleanup-branches': {\n type: 'boolean' as const,\n description: '(fix-and-pr 模式)结束后列出已合并的 dependfix 分支到报告,不自动删除',\n negativeDescription: '不执行分支清理检查',\n },\n 'cleanup-branches-auto': {\n type: 'boolean' as const,\n description: '(fix-and-pr 模式)结束后自动删除已合并/已关闭的 dependfix 分支(非交互,不删有 open PR 的分支)',\n negativeDescription: '不自动删除分支',\n },\n 'github-token': {\n type: 'string' as const,\n description: 'GitHub Personal Access Token',\n },\n 'alerts-token': {\n type: 'string' as const,\n description: 'Dependabot alerts 专用 token(可选,最小权限 PAT,仅 Dependabot alerts: read;缺省回退 --github-token。GITHUB_TOKEN 无法读取 Dependabot alerts)',\n },\n 'alerts-source': {\n type: 'string' as const,\n description: '告警数据源:github-dependabot(默认,GitHub Dependabot alerts API)或 pnpm-audit(本地无 token 回退,扫描当前工作区 lockfile;repository 优先 --repo → git remote → local 兜底)',\n },\n 'code-scanning': {\n type: 'boolean' as const,\n description: '同时拉取 Code Scanning alerts(与 Dependabot 并行,默认关闭;需要 token 具备 security-events: read,GITHUB_TOKEN 默认具备)',\n negativeDescription: '不拉取 Code Scanning alerts(默认)',\n },\n 'code-quality': {\n type: 'boolean' as const,\n description: '同时拉取 Code Quality findings(与 Dependabot 并行,默认关闭;需要 fine-grained PAT 具备 Code quality: read,或 classic PAT 具备 repo/public_repo scope)',\n negativeDescription: '不拉取 Code Quality findings(默认)',\n },\n 'allow-major-upgrade': {\n type: 'boolean' as const,\n description: '跨线告警(推荐版本跨大版本,当前线内无修复版本)显式授权自动升级:仅根 package.json 直接依赖(workspace 成员独占声明维持人工)且 lockfile 单版本的告警自动跨线升级,升级后复核脆弱实例、强制完整验证(install+lint+build+typecheck+test),失败自动回滚;间接依赖 / 多版本共存跨线告警维持人工处理。仅 CLI 可用,Action 不支持',\n negativeDescription: '不自动升级跨线告警(默认,维持人工处理)',\n },\n 'rollback-granularity': {\n type: 'string' as const,\n description: '验证失败回滚粒度:unit(默认,回退最近一个已通过 probe 的变更单元并重验一次)/ repo(保持整仓 git reset --hard)',\n },\n 'max-alerts-per-repository': {\n type: 'string' as const,\n description: '每个仓库最多处理的告警数',\n default: '20' as const,\n },\n 'max-concurrency': {\n type: 'string' as const,\n description: '多仓库并发窗口(1-16,默认 1 保守串行;>1 可能触发 GitHub 限流)',\n default: '1' as const,\n },\n 'max-retries': {\n type: 'string' as const,\n description: 'GitHub API 限流重试次数(0-10,默认 3;429/rate limit 指数退避重试)',\n default: '3' as const,\n },\n 'max-backoff-ms': {\n type: 'string' as const,\n description: '限流退避单次等待上限毫秒(100-120000,默认 30000;Retry-After / reset / 指数退避均受此约束)',\n default: '30000' as const,\n },\n 'max-repos': {\n type: 'string' as const,\n description: '发现规模上限:最多保留的仓库数(默认 100;0 表示不限制,负数会被拒绝;大 org 场景下防止一次性全量发现)',\n default: '100' as const,\n },\n history: {\n type: 'string' as const,\n description: '查询仓库历史运行摘要(读 dependfix-reports/index.json,倒序时间;不执行扫描)',\n },\n 'upgrade-groups': {\n type: 'string' as const,\n description: '用户显式分组(覆盖自动分组),格式 \"name1:pkg1,pkg2;name2:pkg3\"',\n },\n 'toolchain-pnpm-version': {\n type: 'string' as const,\n description: 'lockfile 修复用的 pnpm 版本(工具链固定;缺省从 package.json packageManager 解析;PIN_TOOLCHAIN 策略用 corepack pnpm@<version> 执行)',\n },\n commands: {\n type: 'string' as const,\n description: '自定义验证命令(逗号分隔),整链替换默认的 install/lint/build/typecheck/test(不做合并;默认链扩展不会自动跟随,默认链内的条目须显式保留)',\n },\n 'mirror-ci-scripts': {\n type: 'boolean' as const,\n description: '把目标仓库 CI 独有的 pnpm/npm/yarn 脚本追加到验证链(默认关闭;最多 3 条、首失败即停;docker / gh / 任意 shell 只披露不执行;与 --commands 不叠加)',\n negativeDescription: '不镜像 CI 独有脚本(默认)',\n },\n 'retry-failed': {\n type: 'boolean' as const,\n description: '忽略同告警尝试上限并强制再试(仅 fix-and-pr;第 3 次起默认跳过并在报告标注「人工强制」)',\n negativeDescription: '遵守同告警尝试上限(默认)',\n },\n 'wait-pr-checks': {\n type: 'boolean' as const,\n description: '创建 PR 后等待 check 终态(仅 fix-and-pr;失败时默认评论失败摘要,受 per-PR / per-run 双预算约束,不改变 exit code)',\n negativeDescription: '不等待 PR check(默认)',\n },\n 'close-failed-pr': {\n type: 'boolean' as const,\n description: 'check 失败且归因 self_caused 时关闭 PR 并删除分支(需同时开启 --wait-pr-checks;unknown / external_noise / pre_existing 只评论)',\n negativeDescription: '不关闭失败 PR(默认)',\n },\n verbose: {\n type: 'boolean' as const,\n description: '输出详细日志',\n },\n ai: {\n type: 'boolean' as const,\n description: '开启 AI breaking change 研判(默认关闭;需配置 AI API Key。触发范围见 --ai-trigger;dry-run 不触发、不产生费用)',\n negativeDescription: '关闭 AI 研判(默认)',\n },\n 'ai-provider': {\n type: 'string' as const,\n description: 'AI 提供商:openai-compatible(默认,DeepSeek 等指定 --ai-base-url 兼容)或 anthropic',\n },\n 'ai-model': {\n type: 'string' as const,\n description: 'AI 模型名(默认 deepseek-v4-flash)',\n },\n 'ai-base-url': {\n type: 'string' as const,\n description: 'OpenAI 兼容端点基地址(默认 https://api.deepseek.com,与默认模型 deepseek-v4-flash 配套;使用 OpenAI 官方模型时指定 https://api.openai.com/v1 + --ai-model)',\n },\n 'ai-api-url': {\n type: 'string' as const,\n description: 'Anthropic 兼容端点(仅 --ai-provider anthropic 生效;默认 https://api.anthropic.com/v1/messages,自托管/网关可显式指定)',\n },\n 'ai-api-key': {\n type: 'string' as const,\n description: 'AI API Key(命令行参数优先,DEPENDFIX_AI_API_KEY 环境变量兜底;注意命令行参数会出现在进程列表/shell history,敏感环境请用 env)',\n },\n 'ai-trigger': {\n type: 'string' as const,\n description: 'AI 研判触发范围:failure(升级验证失败)/ major(major 升级)/ both(默认)',\n },\n} satisfies ArgsDef\n\n// ---------------------------------------------------------------------------\n// Conversion helpers\n// ---------------------------------------------------------------------------\n\nexport function isRuntimeMode(value: string): value is RuntimeMode {\n return RUNTIME_MODES.includes(value as RuntimeMode)\n}\n\nexport function isSeverityThreshold(value: string): value is SeverityThreshold {\n return SEVERITY_THRESHOLDS.includes(value as SeverityThreshold)\n}\n\nexport function isAlertSource(value: string): value is AlertSourceKind {\n return ALERT_SOURCES.includes(value as AlertSourceKind)\n}\n\nexport function appendRepositories(target: string[], value: string): void {\n for (const repository of value.split(',')) {\n const trimmed = repository.trim()\n\n if (!trimmed) {\n continue\n }\n\n if (!isValidRepoIdentifier(trimmed)) {\n throw new AppError(\n 'ARGUMENT_PARSE_ERROR',\n `Invalid repository identifier: \"${trimmed}\". Expected format: owner/repo`,\n )\n }\n\n target.push(trimmed)\n }\n}\n\nexport function parseCommandsFlag(value: string): string[] {\n return value\n .split(',')\n .map((cmd) => cmd.trim())\n .filter(Boolean)\n}\n\n/**\n * 严格整数字面量解析(修复:拒绝 `2.5` 被 parseInt 静默截断为 2)。\n * 仅接受 `^\\d+$`;范围语义由调用方在 expected 描述中声明(config 校验兜底)。\n */\nexport function parseIntegerFlag(value: string, flagName: string, expected: string): number {\n const trimmed = value.trim()\n if (!/^\\d+$/.test(trimmed)) {\n throw new AppError(\n 'ARGUMENT_PARSE_ERROR',\n `Invalid ${flagName} value: \"${value}\". ${expected}`,\n )\n }\n return Number.parseInt(trimmed, 10)\n}\n\n/**\n * 归一化可重复 flag 值(citty/mri 对重复传入返回数组,单次传入返回字符串):\n * 按逗号拆分 + 去空白 + 去空项。`--owner a,b --owner c` → ['a', 'b', 'c']。\n */\nexport function normalizeFlagList(value: string | string[] | undefined): string[] {\n if (value === undefined) {\n return []\n }\n const parts = Array.isArray(value) ? value : [value]\n return parts\n .flatMap((v) => v.split(','))\n .map((s) => s.trim())\n .filter(Boolean)\n}\n\nfunction parsedArgsToCliOverrides(parsed: ParsedArgs<typeof argsDef>): CliConfigOverrides {\n const overrides: CliConfigOverrides = {}\n\n // mode\n const rawMode = parsed.mode\n if (isRuntimeMode(rawMode)) {\n overrides.mode = rawMode\n }\n\n // repositories (from --repo / --repository)\n const repoValue = parsed.repo\n if (repoValue) {\n const repos: string[] = []\n appendRepositories(repos, repoValue)\n if (repos.length > 0) {\n overrides.repositories = repos\n }\n }\n\n // repos-file\n const reposFile = parsed['repos-file']\n if (reposFile) {\n overrides.reposFilePath = reposFile\n }\n\n // owner(自动发现;逗号分隔多个或多次传入)\n const ownerValue = normalizeFlagList(parsed.owner)\n if (ownerValue.length > 0) {\n overrides.owner = ownerValue\n }\n\n // repo-topics(发现结果 topic 白名单,AND 语义)\n const repoTopicsValue = normalizeFlagList(parsed['repo-topics'])\n if (repoTopicsValue.length > 0) {\n overrides.repoTopics = repoTopicsValue\n }\n\n // repo-include / repo-exclude / repo-topics-exclude(名单策略)\n const repoIncludeValue = normalizeFlagList(parsed['repo-include'])\n if (repoIncludeValue.length > 0) {\n overrides.repoInclude = repoIncludeValue\n }\n const repoExcludeValue = normalizeFlagList(parsed['repo-exclude'])\n if (repoExcludeValue.length > 0) {\n overrides.repoExclude = repoExcludeValue\n }\n const repoTopicsExcludeValue = normalizeFlagList(parsed['repo-topics-exclude'])\n if (repoTopicsExcludeValue.length > 0) {\n overrides.repoTopicsExclude = repoTopicsExcludeValue\n }\n\n // severity-threshold\n const severityThreshold = parsed['severity-threshold']\n if (isSeverityThreshold(severityThreshold)) {\n overrides.severityThreshold = severityThreshold\n }\n\n // dry-run (three-state: true / false / undefined)\n if (parsed['dry-run'] !== undefined) {\n overrides.dryRun = parsed['dry-run']\n }\n\n // create-pr (three-state: true / false / undefined)\n if (parsed['create-pr'] !== undefined) {\n overrides.createPullRequest = parsed['create-pr']\n }\n\n // commit (three-state: true / false / undefined)\n if (parsed.commit !== undefined) {\n overrides.commit = parsed.commit\n }\n\n // cleanup-branches (three-state: true / false / undefined)\n if (parsed['cleanup-branches'] !== undefined) {\n overrides.cleanupBranches = parsed['cleanup-branches']\n }\n\n // cleanup-branches-auto (three-state: true / false / undefined)\n if (parsed['cleanup-branches-auto'] !== undefined) {\n overrides.cleanupBranchesAuto = parsed['cleanup-branches-auto']\n }\n\n // github-token\n const githubToken = parsed['github-token']\n if (githubToken) {\n overrides.githubToken = githubToken\n }\n\n // alerts-token\n const alertsToken = parsed['alerts-token']\n if (alertsToken) {\n overrides.alertsToken = alertsToken\n }\n\n // alerts-source\n const alertsSource = parsed['alerts-source']\n if (alertsSource) {\n if (!isAlertSource(alertsSource)) {\n throw new AppError(\n 'ARGUMENT_PARSE_ERROR',\n `Invalid --alerts-source value: \"${alertsSource}\". Expected one of: ${ALERT_SOURCES.join(', ')}.`,\n )\n }\n overrides.alertSource = alertsSource\n }\n\n // code-scanning (three-state: true / false / undefined)\n if (parsed['code-scanning'] !== undefined) {\n overrides.codeScanningEnabled = parsed['code-scanning']\n }\n\n // code-quality (three-state: true / false / undefined)\n if (parsed['code-quality'] !== undefined) {\n overrides.codeQualityEnabled = parsed['code-quality']\n }\n\n // allow-major-upgrade (three-state: true / false / undefined; 无 env 通道,仅 CLI)\n if (parsed['allow-major-upgrade'] !== undefined) {\n overrides.allowMajorUpgrade = parsed['allow-major-upgrade']\n }\n\n // rollback-granularity(unit / repo;config 校验兜底)\n const rollbackGranularity = parsed['rollback-granularity']?.trim().toLowerCase()\n if (rollbackGranularity) {\n if (rollbackGranularity !== 'unit' && rollbackGranularity !== 'repo') {\n throw new AppError('ARGUMENT_PARSE_ERROR', '--rollback-granularity must be one of: unit, repo')\n }\n overrides.rollbackGranularity = rollbackGranularity\n }\n\n // max-alerts-per-repository\n const maxAlerts = parsed['max-alerts-per-repository']\n if (maxAlerts) {\n overrides.maxAlertsPerRepository = parseIntegerFlag(maxAlerts, '--max-alerts-per-repository', 'Expected a positive integer.')\n }\n\n // max-concurrency(1-16,config 校验兜底)\n const maxConcurrency = parsed['max-concurrency']\n if (maxConcurrency) {\n overrides.maxConcurrency = parseIntegerFlag(maxConcurrency, '--max-concurrency', 'Expected an integer between 1 and 16.')\n }\n\n // max-retries(0-10,config 校验兜底)\n const maxRetries = parsed['max-retries']\n if (maxRetries) {\n overrides.maxRetries = parseIntegerFlag(maxRetries, '--max-retries', 'Expected an integer between 0 and 10.')\n }\n\n // max-backoff-ms(100-120000,config 校验兜底)\n const maxBackoffMs = parsed['max-backoff-ms']\n if (maxBackoffMs) {\n overrides.maxBackoffMs = parseIntegerFlag(maxBackoffMs, '--max-backoff-ms', 'Expected an integer between 100 and 120000.')\n }\n\n // max-repos(发现规模上限,config 校验兜底)\n const maxRepos = parsed['max-repos']\n if (maxRepos) {\n overrides.maxRepos = parseIntegerFlag(maxRepos, '--max-repos', 'Expected a non-negative integer (0 = unlimited).')\n }\n\n // history(独立查询命令,不进入运行配置)\n const history = parsed.history\n if (history) {\n overrides.history = history\n }\n\n // verbose (three-state: true / false / undefined)\n if (parsed.verbose !== undefined) {\n overrides.verbose = parsed.verbose\n }\n\n // commands\n const commandsValue = parsed.commands\n if (commandsValue) {\n overrides.commands = parseCommandsFlag(commandsValue)\n }\n\n // mirror-ci-scripts (three-state: true / false / undefined)\n if (parsed['mirror-ci-scripts'] !== undefined) {\n overrides.mirrorCiScripts = parsed['mirror-ci-scripts']\n }\n\n // retry-failed (three-state: true / false / undefined)\n if (parsed['retry-failed'] !== undefined) {\n overrides.retryFailed = parsed['retry-failed']\n }\n\n // wait-pr-checks / close-failed-pr (three-state)\n if (parsed['wait-pr-checks'] !== undefined) {\n overrides.waitPrChecks = parsed['wait-pr-checks']\n }\n if (parsed['close-failed-pr'] !== undefined) {\n overrides.closeFailedPr = parsed['close-failed-pr']\n }\n\n // upgrade-groups\n const upgradeGroups = parsed['upgrade-groups']\n if (upgradeGroups) {\n overrides.upgradeGroups = parseUpgradeGroupsFlag(upgradeGroups)\n }\n\n // override-protect(与 env DEPENDFIX_OVERRIDE_PROTECT 同格式;解析器与 config 同源)\n const overrideProtect = parsed['override-protect']\n if (overrideProtect) {\n overrides.overrideProtect = parseOverrideProtectEntries(\n overrideProtect,\n (message) => new AppError('ARGUMENT_PARSE_ERROR', message),\n '--override-protect',\n )\n }\n\n // toolchain-pnpm-version\n const toolchainPnpmVersion = parsed['toolchain-pnpm-version']\n if (toolchainPnpmVersion) {\n overrides.toolchainPnpmVersion = toolchainPnpmVersion\n }\n\n // AI 研判(--ai 系列;--ai-api-key 为敏感参数:CLI 参数优先、env 兜底,敏感环境建议用 env)\n if (parsed.ai !== undefined) {\n overrides.aiEnabled = parsed.ai\n }\n const aiProvider = parsed['ai-provider']\n if (aiProvider) {\n if (aiProvider !== 'openai-compatible' && aiProvider !== 'anthropic') {\n throw new AppError(\n 'ARGUMENT_PARSE_ERROR',\n `Invalid --ai-provider value: \"${aiProvider}\". Expected \"openai-compatible\" or \"anthropic\".`,\n )\n }\n overrides.aiProvider = aiProvider\n }\n const aiModel = parsed['ai-model']\n if (aiModel) {\n overrides.aiModel = aiModel\n }\n const aiBaseUrl = parsed['ai-base-url']\n if (aiBaseUrl) {\n overrides.aiBaseUrl = aiBaseUrl\n }\n const aiApiUrl = parsed['ai-api-url']\n if (aiApiUrl) {\n overrides.aiApiUrl = aiApiUrl\n }\n const aiApiKey = parsed['ai-api-key']\n if (aiApiKey) {\n overrides.aiApiKey = aiApiKey\n }\n const aiTrigger = parsed['ai-trigger']\n if (aiTrigger) {\n if (aiTrigger !== 'failure' && aiTrigger !== 'major' && aiTrigger !== 'both') {\n throw new AppError(\n 'ARGUMENT_PARSE_ERROR',\n `Invalid --ai-trigger value: \"${aiTrigger}\". Expected \"failure\", \"major\" or \"both\".`,\n )\n }\n overrides.aiTrigger = aiTrigger\n }\n\n return overrides\n}\n\n/**\n * 解析 `--upgrade-groups \"name1:pkg1,pkg2;name2:pkg3\"`。\n * 与 config 的 normalizeUpgradeGroups 保持一致(CLI 与 env 同格式):\n * 空 entry 忽略;非空但缺冒号/组名或包列表为空 → 抛 ARGUMENT_PARSE_ERROR;\n * 原型链风险键名(__proto__ / constructor / prototype)忽略。\n */\nexport function parseUpgradeGroupsFlag(value: string): Record<string, string[]> {\n const result: Record<string, string[]> = {}\n for (const entry of value.split(';')) {\n if (!entry.trim()) {\n continue\n }\n const idx = entry.indexOf(':')\n if (idx <= 0) {\n throw new AppError(\n 'ARGUMENT_PARSE_ERROR',\n `Invalid --upgrade-groups entry: \"${entry}\". Expected format: \"name:pkg1,pkg2\"`,\n )\n }\n const name = entry.slice(0, idx).trim()\n const pkgs = entry\n .slice(idx + 1)\n .split(',')\n .map((p) => p.trim())\n .filter(Boolean)\n if (name === '__proto__' || name === 'constructor' || name === 'prototype') {\n continue\n }\n if (!name || pkgs.length === 0) {\n throw new AppError(\n 'ARGUMENT_PARSE_ERROR',\n `Invalid --upgrade-groups entry: \"${entry}\". Expected format: \"name:pkg1,pkg2\"`,\n )\n }\n result[name] = pkgs\n }\n return result\n}\n\n// ---------------------------------------------------------------------------\n// Parsing API\n// ---------------------------------------------------------------------------\n\nexport function parseCliArgs(rawArgs: string[]): CliInvocation {\n const parsed = parseArgs<typeof argsDef>(rawArgs, argsDef)\n const configOverrides = parsedArgsToCliOverrides(parsed)\n\n return {\n mode: configOverrides.mode,\n configOverrides,\n rawArgs: [...rawArgs],\n }\n}\n","// 平台化管线抽象(独立平台前置):\n// local(CLI)与 platform 模式共用同一编排核心——参数解析 → 配置解析 →\n// DependfixApp.run → 退出码。process.env / console / process.exitCode\n// 全部经 PipelineDeps 注入,platform 可替换 env / logger / config resolver / exit。\n\nimport { resolveRuntimeConfig, queryRepoHistory, formatHistory, DependfixApp, type CliConfigOverrides } from '@dependfix/engine'\nimport { parseCliArgs, type CliInvocation } from '../cli'\n\nexport interface PipelineLogger {\n info(msg: string): void\n error(msg: string): void\n}\n\nexport interface PipelineDeps {\n /** 环境变量来源(默认 process.env)——platform 模式注入自定义 env */\n env?: NodeJS.ProcessEnv\n /** 日志输出(默认 console)——platform 模式注入自定义 logger */\n logger?: PipelineLogger\n /**\n * 配置解析(默认 resolveRuntimeConfig)。\n * platform 模式可注入自定义解析(如从数据库读取配置再合并 env)。\n */\n resolveConfig?: (opts: { env: NodeJS.ProcessEnv, cliOverrides: CliConfigOverrides }) => ReturnType<typeof resolveRuntimeConfig>\n /** 退出码设置(默认 process.exitCode)——platform 模式注入 */\n exit?: (code: number) => void\n}\n\nexport interface PipelineParseResult {\n ok: true\n invocation: CliInvocation\n config: ReturnType<typeof resolveRuntimeConfig>\n}\n\nexport interface Pipeline {\n /** 解析参数并解析配置(不执行扫描) */\n parse(rawArgs: string[]): PipelineParseResult\n /** 执行一次完整运行(含 history 查询分支),返回退出码 */\n run(rawArgs: string[]): Promise<number>\n}\n\nexport function createPipeline(deps: PipelineDeps = {}): Pipeline {\n const env = deps.env ?? process.env\n const logger = deps.logger ?? console\n const resolveConfig = deps.resolveConfig ?? ((opts: { env: NodeJS.ProcessEnv, cliOverrides: CliConfigOverrides }) => resolveRuntimeConfig(opts))\n const exit = deps.exit ?? ((code: number) => {\n process.exitCode = code\n })\n\n return {\n parse(rawArgs: string[]): PipelineParseResult {\n const invocation = parseCliArgs(rawArgs)\n const config = resolveConfig({ env, cliOverrides: invocation.configOverrides })\n return { ok: true, invocation, config }\n },\n\n async run(rawArgs: string[]): Promise<number> {\n const invocation = parseCliArgs(rawArgs)\n\n // --history:独立查询命令(读归档索引,不执行扫描、不要求 token/仓库配置)\n if (invocation.configOverrides.history) {\n const entries = queryRepoHistory(invocation.configOverrides.history)\n logger.info(formatHistory(entries, invocation.configOverrides.history))\n return 0\n }\n\n const config = resolveConfig({ env, cliOverrides: invocation.configOverrides })\n const app = new DependfixApp({\n config,\n verbose: invocation.configOverrides.verbose,\n commands: invocation.configOverrides.commands,\n })\n\n const { exitCode } = await app.run()\n exit(exitCode)\n return exitCode\n },\n }\n}\n","// ---------------------------------------------------------------------------\n// Agent 目录约定与检测\n// ---------------------------------------------------------------------------\n// 目录约定与 npx skills 生态对齐(vercel-labs/skills agents 定义):\n// - claude-code: 项目 .claude/skills,全局 ~/.claude/skills\n// - opencode: 项目 .agents/skills,全局 ~/.config/opencode/skills\n// - cursor: 项目 .cursor/skills,全局 ~/.cursor/skills\n// - copilot: 全局 ~/.copilot/skills(GitHub Copilot 无通用项目级目录约定)\n// 检测 = 对应主目录存在(存在即视为已安装该工具)。\n// homedir / projectRoot 均由调用方注入,便于测试。\n\nimport { existsSync } from 'node:fs'\nimport { join } from 'node:path'\n\nexport interface AgentDefinition {\n id: string\n displayName: string\n /** 项目级 skills 目录(相对项目根);不支持返回 undefined */\n projectSkillDir(projectRoot: string): string | undefined\n /** 用户级 skills 目录(绝对路径) */\n globalSkillDir(homeDir: string): string\n /** 检测是否已安装(对应主目录是否存在) */\n detectInstalled(homeDir: string): boolean\n}\n\nexport const AGENTS: AgentDefinition[] = [\n {\n id: 'claude-code',\n displayName: 'Claude Code',\n projectSkillDir: (root) => join(root, '.claude', 'skills'),\n globalSkillDir: (home) => join(home, '.claude', 'skills'),\n detectInstalled: (home) => existsSync(join(home, '.claude')),\n },\n {\n id: 'opencode',\n displayName: 'OpenCode',\n projectSkillDir: (root) => join(root, '.agents', 'skills'),\n globalSkillDir: (home) => join(home, '.config', 'opencode', 'skills'),\n detectInstalled: (home) => existsSync(join(home, '.config', 'opencode')),\n },\n {\n id: 'cursor',\n displayName: 'Cursor',\n projectSkillDir: (root) => join(root, '.cursor', 'skills'),\n globalSkillDir: (home) => join(home, '.cursor', 'skills'),\n detectInstalled: (home) => existsSync(join(home, '.cursor')),\n },\n {\n id: 'copilot',\n displayName: 'GitHub Copilot',\n projectSkillDir: () => undefined,\n globalSkillDir: (home) => join(home, '.copilot', 'skills'),\n detectInstalled: (home) => existsSync(join(home, '.copilot')),\n },\n]\n","// ---------------------------------------------------------------------------\n// 产品 skill 内容来源定位\n// ---------------------------------------------------------------------------\n// dependfix-remediator 权威源随 npm 包 @dependfix/skills 发布(files: dependfix-remediator)。\n// CLI 依赖该包,运行时通过 require.resolve 定位包内 skill 目录:\n// - 开发(tsx):workspace 链接解析到 packages/skills/dependfix-remediator\n// - 发布(npx dependfix):node_modules 中解析 @dependfix/skills/dependfix-remediator\n\nimport { createRequire } from 'node:module'\nimport { dirname, join } from 'node:path'\n\nexport const PRODUCT_SKILL_NAME = 'dependfix-remediator'\n\n/**\n * 解析产品 skill 内容目录(含 SKILL.md 的目录)。\n * 找不到包或内容目录时抛错(install / doctor 的必要前置)。\n */\nexport function resolveProductSkillSourceDir(): string {\n const require = createRequire(import.meta.url)\n const pkgJsonPath = require.resolve('@dependfix/skills/package.json')\n return join(dirname(pkgJsonPath), PRODUCT_SKILL_NAME)\n}\n","// ---------------------------------------------------------------------------\n// 产品 skill 安装器(dependfix skills install 核心)\n// ---------------------------------------------------------------------------\n// 语义:\n// - 目标不存在 → 创建并复制(installed)\n// - 目标已存在且内容一致 → 跳过(up-to-date,幂等)\n// - 目标已存在但内容不一致 → 覆盖需确认(force 或交互确认;拒绝则 skipped-conflict)\n// - dry-run 只输出将要执行的操作,不写文件\n// 覆盖采用镜像语义:删除旧内容后全量复制,保证与权威源严格一致。\n\nimport { createHash } from 'node:crypto'\nimport { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { PRODUCT_SKILL_NAME } from './source'\n\nexport type InstallStatus = 'installed' | 'up-to-date' | 'skipped-conflict' | 'failed'\n\nexport interface InstallResult {\n status: InstallStatus\n /** 安装目标目录(依赖 fix-and-pr 模式外,本为 dependfix-remediator 目录) */\n targetDir: string\n detail?: string\n}\n\nexport interface InstallOptions {\n /** 权威源内容目录(含 SKILL.md) */\n sourceDir: string\n /** 目标 agent skills 目录(dependfix-remediator 将位于其下) */\n targetDir: string\n /** 强制覆盖(跳过确认) */\n force?: boolean\n /** 试运行:只输出将要执行的操作 */\n dryRun?: boolean\n /** 覆盖确认回调;缺省返回 false(非交互默认拒绝,不静默覆盖) */\n confirmOverwrite?: (existingDir: string) => Promise<boolean> | boolean\n}\n\n/** 收集目录内文件相对路径 -> sha256(递归) */\nexport function collectFileHashes(dir: string): Map<string, string> {\n const result = new Map<string, string>()\n if (!existsSync(dir)) {\n return result\n }\n const walk = (current: string, relPrefix: string) => {\n for (const entry of readdirSync(current, { withFileTypes: true })) {\n const full = join(current, entry.name)\n const rel = relPrefix ? `${relPrefix}/${entry.name}` : entry.name\n if (entry.isDirectory()) {\n walk(full, rel)\n } else if (entry.isFile()) {\n result.set(rel, hashFile(full))\n }\n }\n }\n walk(dir, '')\n return result\n}\n\nfunction hashFile(filePath: string): string {\n return createHash('sha256').update(readFileSync(filePath)).digest('hex')\n}\n\n/** 两个目录内容是否完全一致(文件集合 + 内容 hash) */\nexport function isContentSame(dirA: string, dirB: string): boolean {\n const mapA = collectFileHashes(dirA)\n const mapB = collectFileHashes(dirB)\n if (mapA.size !== mapB.size) {\n return false\n }\n for (const [rel, hash] of mapA) {\n if (mapB.get(rel) !== hash) {\n return false\n }\n }\n return true\n}\n\n/** 复制目录(镜像语义:先清空目标内同名子目录再复制) */\nfunction mirrorCopy(sourceDir: string, targetSkillDir: string): void {\n rmSync(targetSkillDir, { recursive: true, force: true })\n mkdirSync(targetSkillDir, { recursive: true })\n for (const entry of readdirSync(sourceDir)) {\n const src = join(sourceDir, entry)\n const dst = join(targetSkillDir, entry)\n if (statSync(src).isDirectory()) {\n cpSync(src, dst, { recursive: true })\n } else {\n cpSync(src, dst)\n }\n }\n}\n\n/**\n * 安装产品 skill 到单个 agent skills 目录。\n * 返回 InstallResult;失败时 status='failed' 且 detail 含错误信息(不抛错,便于批量收尾)。\n */\nexport async function installSkillToDir(options: InstallOptions): Promise<InstallResult> {\n const { sourceDir, targetDir, force, dryRun, confirmOverwrite } = options\n const skillTarget = join(targetDir, PRODUCT_SKILL_NAME)\n\n if (!existsSync(sourceDir)) {\n return { status: 'failed', targetDir, detail: `权威源不存在: ${sourceDir}` }\n }\n\n if (dryRun) {\n try {\n const same = existsSync(skillTarget) && isContentSame(sourceDir, skillTarget)\n return same\n ? { status: 'up-to-date', targetDir, detail: '[dry-run] 已一致' }\n : { status: 'installed', targetDir, detail: '[dry-run] 将安装' }\n } catch (error: unknown) {\n return { status: 'failed', targetDir, detail: errorMessage(error) }\n }\n }\n\n if (!existsSync(skillTarget)) {\n try {\n mirrorCopy(sourceDir, skillTarget)\n return { status: 'installed', targetDir }\n } catch (error: unknown) {\n cleanupPartial(skillTarget)\n return { status: 'failed', targetDir, detail: errorMessage(error) }\n }\n }\n\n // 已存在:内容一致则幂等跳过\n let same: boolean\n try {\n same = isContentSame(sourceDir, skillTarget)\n } catch (error: unknown) {\n return { status: 'failed', targetDir, detail: errorMessage(error) }\n }\n if (same) {\n return { status: 'up-to-date', targetDir }\n }\n\n // 内容不一致:需要确认覆盖(非交互默认拒绝)\n const allowed = force ?? (confirmOverwrite ? await confirmOverwrite(skillTarget) : false)\n if (!allowed) {\n return {\n status: 'skipped-conflict',\n targetDir,\n detail: '目标存在内容不一致的同名 skill,已跳过(使用 --force 或交互确认覆盖)',\n }\n }\n\n try {\n mirrorCopy(sourceDir, skillTarget)\n return { status: 'installed', targetDir, detail: '已覆盖旧版本' }\n } catch (error: unknown) {\n cleanupPartial(skillTarget)\n return { status: 'failed', targetDir, detail: errorMessage(error) }\n }\n}\n\n/** mirrorCopy 失败后清理半写目录(避免下次安装误判为内容不一致需 force) */\nfunction cleanupPartial(skillTarget: string): void {\n try {\n rmSync(skillTarget, { recursive: true, force: true })\n } catch {\n // 清理失败不掩盖原始错误\n }\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n","// ---------------------------------------------------------------------------\n// dependfix skills doctor 核心逻辑\n// ---------------------------------------------------------------------------\n// 检查项:\n// 1. 已检测 agent 的 skills 目录存在性(目录约定漂移提示:主目录在但 skills 目录缺失)\n// 2. 产品 skill 安装状态(已检测 agent 的 skills 目录中 dependfix-remediator 是否存在、\n// 内容与当前 CLI 携带版本是否一致)\n// 3. 内部开发 skill internal 标记完整性(在 dependfix 仓库内运行时,扫描 .github/skills)\n// 全部为只读检查,输出分级问题清单。\n\nimport { existsSync, readFileSync, readdirSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { AGENTS } from './agents'\nimport { isContentSame } from './installer'\nimport { PRODUCT_SKILL_NAME, resolveProductSkillSourceDir } from './source'\n\nexport type DoctorLevel = 'ok' | 'warn' | 'error'\n\nexport interface DoctorFinding {\n level: DoctorLevel\n message: string\n}\n\nexport interface DoctorOptions {\n homeDir: string\n /** 项目根(用于项目级目录检查与内部 skill 扫描);缺省为当前工作目录 */\n projectRoot?: string\n /** 产品 skill 权威源目录(缺省从 @dependfix/skills 包解析;测试可注入) */\n productSourceDir?: string\n}\n\nexport function runDoctor(options: DoctorOptions): DoctorFinding[] {\n const { homeDir, projectRoot = process.cwd() } = options\n const findings: DoctorFinding[] = []\n\n // 1. agent 目录约定检查\n for (const agent of AGENTS) {\n const installed = agent.detectInstalled(homeDir)\n if (!installed) {\n continue\n }\n const globalDir = agent.globalSkillDir(homeDir)\n if (!existsSync(globalDir)) {\n findings.push({\n level: 'warn',\n message: `${agent.displayName} 已检测到,但全局 skills 目录缺失(${globalDir})——可能官方目录约定已变更,或尚未初始化`,\n })\n }\n const projectDir = agent.projectSkillDir(projectRoot)\n if (projectDir && !existsSync(projectDir)) {\n findings.push({\n level: 'ok',\n message: `${agent.displayName} 项目级 skills 目录不存在(${projectDir})——非问题,按需创建`,\n })\n }\n }\n\n // 2. 产品 skill 安装状态\n const sourceDir = options.productSourceDir ?? safeResolveSourceDir()\n if (sourceDir) {\n for (const agent of AGENTS) {\n if (!agent.detectInstalled(homeDir)) {\n continue\n }\n const installedDir = join(agent.globalSkillDir(homeDir), PRODUCT_SKILL_NAME)\n if (!existsSync(installedDir)) {\n findings.push({\n level: 'ok',\n message: `${agent.displayName}: ${PRODUCT_SKILL_NAME} 未安装(运行 dependfix skills install)`,\n })\n continue\n }\n if (isContentSame(sourceDir, installedDir)) {\n findings.push({\n level: 'ok',\n message: `${agent.displayName}: ${PRODUCT_SKILL_NAME} 已安装且与当前版本一致`,\n })\n } else {\n findings.push({\n level: 'warn',\n message: `${agent.displayName}: ${PRODUCT_SKILL_NAME} 已安装但内容与当前版本不一致(建议重新 install)`,\n })\n }\n }\n }\n\n // 3. 内部开发 skill internal 标记完整性(仅 dependfix 仓库内)\n const internalSkillsRoot = join(projectRoot, '.github', 'skills')\n if (existsSync(internalSkillsRoot)) {\n const missing: string[] = []\n for (const entry of readdirSync(internalSkillsRoot, { withFileTypes: true })) {\n if (!entry.isDirectory()) {\n continue\n }\n const skillFile = join(internalSkillsRoot, entry.name, 'SKILL.md')\n if (!existsSync(skillFile)) {\n missing.push(`${entry.name}(缺 SKILL.md)`)\n continue\n }\n const content = readFileSync(skillFile, 'utf8')\n if (!/metadata:\\s*\\r?\\n\\s*internal:\\s*true/.test(content)) {\n missing.push(entry.name)\n }\n }\n if (missing.length > 0) {\n findings.push({\n level: 'error',\n message: `内部 skill internal 标记缺失(生态可见泄漏风险): ${missing.join(', ')}`,\n })\n } else {\n findings.push({\n level: 'ok',\n message: `内部 skill internal 标记完整性检查通过(.github/skills)`,\n })\n }\n }\n\n return findings\n}\n\n/** 解析产品 skill 源目录;失败返回 undefined(如 CLI 运行在无 @dependfix/skills 的调试环境) */\nfunction safeResolveSourceDir(): string | undefined {\n try {\n return resolveProductSkillSourceDir()\n } catch {\n return undefined\n }\n}\n","// ---------------------------------------------------------------------------\n// dependfix skills 子命令(install / doctor)\n// ---------------------------------------------------------------------------\n// 定位:npx skills 生态的离线兜底安装器。\n// 不依赖 npx skills;不复刻 add/list/update/remove 完整命令矩阵。\n// 主通道(npx skills add dependfix/dependfix -s dependfix-remediator)见 README。\n\nimport { homedir } from 'node:os'\nimport { defineCommand, renderUsage } from 'citty'\nimport { AGENTS } from './agents'\nimport { runDoctor, type DoctorFinding } from './doctor'\nimport { installSkillToDir, type InstallResult } from './installer'\nimport { resolveProductSkillSourceDir } from './source'\n\n// ---------------------------------------------------------------------------\n// install\n// ---------------------------------------------------------------------------\n\ninterface InstallCliArgs {\n project?: boolean\n force?: boolean\n 'dry-run'?: boolean\n}\n\nasync function runInstall(args: InstallCliArgs): Promise<void> {\n const home = homedir()\n const projectRoot = process.cwd()\n\n let sourceDir: string\n try {\n sourceDir = resolveProductSkillSourceDir()\n } catch (error: unknown) {\n console.error(`无法定位产品 skill 内容(@dependfix/skills 未安装?): ${error instanceof Error ? error.message : String(error)}`)\n process.exitCode = 1\n return\n }\n\n const detected = AGENTS.filter((agent) => agent.detectInstalled(home))\n if (detected.length === 0) {\n console.info('未检测到已安装的 agent 工具(Claude Code / OpenCode / Cursor / Copilot)。')\n console.info('主通道安装(推荐): npx skills add dependfix/dependfix -s dependfix-remediator -g')\n return\n }\n\n console.info(`检测到 ${detected.length} 个 agent 工具,目标模式: ${args.project ? '项目级' : '全局'}\\n`)\n\n const results: { agent: string, result: InstallResult }[] = []\n for (const agent of detected) {\n const agentDir = args.project ? agent.projectSkillDir(projectRoot) : agent.globalSkillDir(home)\n if (!agentDir) {\n console.info(`- ${agent.displayName}: 不支持 ${args.project ? '项目级' : ''}安装,跳过`)\n continue\n }\n const result = await installSkillToDir({\n sourceDir,\n targetDir: agentDir,\n force: args.force,\n dryRun: args['dry-run'],\n confirmOverwrite: (existingDir) => confirmOverwritePrompt(agent.displayName, existingDir),\n })\n results.push({ agent: agent.displayName, result })\n }\n\n console.info('\\n安装清单:')\n const installIcons: Record<InstallResult['status'], string> = {\n installed: '✓',\n 'up-to-date': '=',\n 'skipped-conflict': '!',\n failed: '✗',\n }\n for (const { agent, result } of results) {\n const icon = installIcons[result.status]\n console.info(` ${icon} ${agent}: ${result.status}${result.detail ? ` — ${result.detail}` : ''}`)\n }\n\n const failed = results.filter((r) => r.result.status === 'failed')\n if (failed.length > 0) {\n process.exitCode = 1\n }\n}\n\n/** 交互式覆盖确认;非 TTY 返回 false(不静默覆盖) */\nasync function confirmOverwritePrompt(agentName: string, existingDir: string): Promise<boolean> {\n if (!process.stdin.isTTY) {\n console.warn(` ${agentName}: 目标已存在内容不一致的 dependfix-remediator,非交互环境跳过(使用 --force 覆盖)`)\n return false\n }\n const readline = (await import('node:readline')).createInterface({\n input: process.stdin,\n output: process.stdout,\n })\n return new Promise<boolean>((resolve) => {\n readline.question(` 覆盖 ${existingDir} 中的旧版本?[y/N] `, (answer) => {\n readline.close()\n resolve(answer.trim().toLowerCase() === 'y' || answer.trim().toLowerCase() === 'yes')\n })\n })\n}\n\n// ---------------------------------------------------------------------------\n// doctor\n// ---------------------------------------------------------------------------\n\nasync function runDoctorCli(): Promise<void> {\n const findings = runDoctor({ homeDir: homedir(), projectRoot: process.cwd() })\n const doctorIcons: Record<DoctorFinding['level'], string> = { ok: '✓', warn: '!', error: '✗' }\n let warnCount = 0\n let errorCount = 0\n for (const finding of findings) {\n const icon = doctorIcons[finding.level]\n console.info(` ${icon} [${finding.level}] ${finding.message}`)\n if (finding.level === 'warn') {\n warnCount++\n } else if (finding.level === 'error') {\n errorCount++\n }\n }\n console.info(`\\ndoctor 完成: ${findings.length} 项检查,${warnCount} warning, ${errorCount} error`)\n if (errorCount > 0) {\n process.exitCode = 1\n }\n}\n\n// ---------------------------------------------------------------------------\n// citty command\n// ---------------------------------------------------------------------------\n\nexport const skillsCommand = defineCommand({\n meta: {\n name: 'skills',\n description: '产品 skill 管理(npx skills 生态的离线兜底安装器)',\n },\n subCommands: {\n install: defineCommand({\n meta: {\n name: 'install',\n description: '检测本机已装 agent 工具并安装 dependfix-remediator 产品 skill',\n },\n args: {\n project: {\n type: 'boolean',\n description: '安装到当前项目目录(默认用户全局)',\n negativeDescription: '安装到用户全局(默认)',\n },\n force: {\n type: 'boolean',\n description: '覆盖已存在的内容不一致同名 skill(跳过确认)',\n negativeDescription: '内容不一致时需确认(默认)',\n },\n 'dry-run': {\n type: 'boolean',\n description: '只输出将要执行的操作,不写文件',\n negativeDescription: '实际执行安装',\n },\n },\n async run({ args }) {\n await runInstall(args as InstallCliArgs)\n },\n }),\n doctor: defineCommand({\n meta: {\n name: 'doctor',\n description: '检查 agent 目录约定、产品 skill 安装状态与内部 skill internal 标记完整性',\n },\n async run() {\n await runDoctorCli()\n },\n }),\n },\n async run({ cmd, rawArgs }) {\n // citty 0.2.2 在路由子命令后仍会执行父 run:子命令已执行时直接返回,避免重复输出 usage\n const sub = rawArgs[0]\n if (sub === 'install' || sub === 'doctor') {\n return\n }\n console.info(await renderUsage(cmd))\n },\n})\n","// CLI 执行层(解析与执行分离):\n// - cli/index.ts:argsDef + parseCliArgs(纯解析,无执行依赖)\n// - cli/runner.ts:runCli / runApp / dependfixCommand(经 createPipeline 执行)\n// - app/pipeline.ts:createPipeline(local 与 platform 共用编排核心)\n//\n// 依赖方向:runner → pipeline → cli/index(纯解析),无循环。\n\nimport { createRequire } from 'node:module'\nimport { defineCommand, renderUsage, runCommand, runMain, showUsage } from 'citty'\nimport { toAppError } from '@dependfix/core'\nimport type { RuntimeConfig } from '@dependfix/engine'\nimport { createPipeline, type PipelineLogger } from '../app/pipeline'\nimport { skillsCommand } from '../skills'\nimport { argsDef, type CliInvocation } from './index'\n\nexport interface CliRunResult {\n ok: true\n invocation: CliInvocation\n config: RuntimeConfig\n}\n\n/** 默认 CLI 日志(console);platform 场景经 createPipeline 注入自定义 logger */\nconst cliLogger: PipelineLogger = console\n\n// ---------------------------------------------------------------------------\n// Public API(签名与行为保持兼容)\n// ---------------------------------------------------------------------------\n\nexport function runCli(rawArgs: string[]): CliRunResult {\n return createPipeline({ logger: cliLogger }).parse(rawArgs)\n}\n\n// ---------------------------------------------------------------------------\n// App execution\n// ---------------------------------------------------------------------------\n\nasync function runApp(rawArgs: string[]): Promise<number> {\n return createPipeline({ logger: cliLogger }).run(rawArgs)\n}\n\n// ---------------------------------------------------------------------------\n// citty command\n// ---------------------------------------------------------------------------\n// 注意:不使用 citty subCommands 字段——citty 0.2.2 在 subCommands 模式下会\n// ① 子命令执行后继续执行父 run;② 不匹配子命令的 positional(如 fix)抛\n// Unknown command。因此 skills 子命令由 run 首部编程路由(runCommand),\n// 主流程 positional 命令面不受影响。\n\n/**\n * 解析 CLI 版本:发布产物(dist/index.mjs)读 `../package.json`,源码运行\n * (src/cli/runner.ts)读 `../../package.json`;只接受包名匹配的 JSON,避免误读。\n * 不允许硬编码版本——否则 release 后 `--version` 会与 npm 包版本脱节。\n */\nfunction resolveCliVersion(): string {\n const require = createRequire(import.meta.url)\n for (const specifier of ['../package.json', '../../package.json']) {\n try {\n const pkg = require(specifier) as { name?: string, version?: string }\n if (pkg?.name === 'dependfix' && pkg.version) {\n return pkg.version\n }\n } catch {\n // 继续尝试下一个相对路径(源码 / 产物两种布局)\n }\n }\n return '0.0.0-unknown'\n}\n\nexport const dependfixCommand = defineCommand({\n meta: {\n name: 'dependfix',\n version: resolveCliVersion(),\n description: '自动化处理 Dependabot / Code Scanning 安全告警的修复工具',\n },\n args: argsDef,\n async run({ rawArgs }) {\n // skills 子命令路由(install / doctor)\n if (rawArgs[0] === 'skills') {\n await runCommand(skillsCommand, { rawArgs: rawArgs.slice(1) })\n return\n }\n\n let exitCode = 1\n try {\n exitCode = await runApp(rawArgs)\n } catch (error: unknown) {\n const appError = toAppError(error, 'CLI_EXECUTION_FAILED')\n console.error(`Error: ${appError.message}`)\n exitCode = 1\n }\n process.exitCode = exitCode\n },\n})\n\n// ---------------------------------------------------------------------------\n// 入口封装:补 skills 子命令的 --help 路由\n// ---------------------------------------------------------------------------\n// citty runMain 在 runCommand 前拦截内建 --help,且 resolveSubCommand 依赖\n// subCommands 字段下钻;dependfixCommand 无该字段(见上),故 skills 相关\n// --help 会被截获为主命令 usage。此处显式路由到 skills usage。\n\nexport async function runDependfixMain(rawArgs: string[] = process.argv.slice(2)): Promise<void> {\n if (rawArgs[0] === 'skills' && (rawArgs.includes('--help') || rawArgs.includes('-h'))) {\n console.info(await renderUsage(skillsCommand))\n process.exitCode = 0\n return\n }\n await runMain(dependfixCommand, { showUsage })\n}\n"],"mappings":";;;;;;;;;;AA8BA,MAAa,UAAU;CACnB,MAAM;EACF,MAAM;EACN,aAAa;EACb,UAAU;EACV,SAAS;CACb;CACA,MAAM;EACF,MAAM;EACN,aAAa;EACb,OAAO;GAAC;GAAK;GAAc;EAAc;CAC7C;CACA,cAAc;EACV,MAAM;EACN,aAAa;CACjB;CACA,OAAO;EACH,MAAM;EACN,aAAa;CACjB;CACA,eAAe;EACX,MAAM;EACN,aAAa;CACjB;CACA,gBAAgB;EACZ,MAAM;EACN,aAAa;CACjB;CACA,gBAAgB;EACZ,MAAM;EACN,aAAa;CACjB;CACA,oBAAoB;EAChB,MAAM;EACN,aAAa;CACjB;CACA,uBAAuB;EACnB,MAAM;EACN,aAAa;CACjB;CACA,sBAAsB;EAClB,MAAM;EACN,aAAa;EACb,SAAS;CACb;CACA,WAAW;EACP,MAAM;EACN,aAAa;EACb,qBAAqB;CACzB;CACA,aAAa;EACT,MAAM;EACN,aAAa;EACb,qBAAqB;CACzB;CACA,QAAQ;EACJ,MAAM;EACN,aAAa;EACb,qBAAqB;CACzB;CACA,oBAAoB;EAChB,MAAM;EACN,aAAa;EACb,qBAAqB;CACzB;CACA,yBAAyB;EACrB,MAAM;EACN,aAAa;EACb,qBAAqB;CACzB;CACA,gBAAgB;EACZ,MAAM;EACN,aAAa;CACjB;CACA,gBAAgB;EACZ,MAAM;EACN,aAAa;CACjB;CACA,iBAAiB;EACb,MAAM;EACN,aAAa;CACjB;CACA,iBAAiB;EACb,MAAM;EACN,aAAa;EACb,qBAAqB;CACzB;CACA,gBAAgB;EACZ,MAAM;EACN,aAAa;EACb,qBAAqB;CACzB;CACA,uBAAuB;EACnB,MAAM;EACN,aAAa;EACb,qBAAqB;CACzB;CACA,wBAAwB;EACpB,MAAM;EACN,aAAa;CACjB;CACA,6BAA6B;EACzB,MAAM;EACN,aAAa;EACb,SAAS;CACb;CACA,mBAAmB;EACf,MAAM;EACN,aAAa;EACb,SAAS;CACb;CACA,eAAe;EACX,MAAM;EACN,aAAa;EACb,SAAS;CACb;CACA,kBAAkB;EACd,MAAM;EACN,aAAa;EACb,SAAS;CACb;CACA,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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dependfix",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "一个自动化修复 github security 告警的方案",
|
|
5
5
|
"author": "CaoMeiYouRen",
|
|
6
6
|
"license": "MIT",
|
|
@@ -38,34 +38,34 @@
|
|
|
38
38
|
"dependfix": "./dist/bin.mjs"
|
|
39
39
|
},
|
|
40
40
|
"engines": {
|
|
41
|
-
"node": "
|
|
41
|
+
"node": "^22.23.4 || ^24.22.1"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@octokit/rest": "^22.0.1",
|
|
45
45
|
"citty": "^0.2.2",
|
|
46
46
|
"dayjs": "^1.11.23",
|
|
47
|
-
"dotenv": "^
|
|
48
|
-
"fs-extra": "^11.4.
|
|
47
|
+
"dotenv": "^18.0.9",
|
|
48
|
+
"fs-extra": "^11.4.1",
|
|
49
49
|
"isomorphic-unfetch": "^4.0.2",
|
|
50
50
|
"lodash-es": "^4.18.1",
|
|
51
51
|
"tslib": "^2.8.1",
|
|
52
|
-
"yaml": "^2.9.
|
|
52
|
+
"yaml": "^2.9.1",
|
|
53
53
|
"zx": "^8.8.5",
|
|
54
|
-
"@dependfix/core": "0.
|
|
55
|
-
"@dependfix/engine": "0.
|
|
56
|
-
"@dependfix/skills": "0.
|
|
54
|
+
"@dependfix/core": "0.7.0",
|
|
55
|
+
"@dependfix/engine": "0.6.0",
|
|
56
|
+
"@dependfix/skills": "0.3.0"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"@types/debug": "^4.1.13",
|
|
60
60
|
"@types/fs-extra": "^11.0.4",
|
|
61
61
|
"@types/lodash-es": "^4.17.12",
|
|
62
|
-
"@types/node": "^26.
|
|
62
|
+
"@types/node": "^26.6.6",
|
|
63
63
|
"cross-env": "^10.0.0",
|
|
64
64
|
"debug": "^4.4.3",
|
|
65
|
-
"nock": "^
|
|
65
|
+
"nock": "^15.0.5",
|
|
66
66
|
"rimraf": "^6.1.3",
|
|
67
|
-
"tsdown": "^0.23.
|
|
68
|
-
"tsx": "^4.23.
|
|
67
|
+
"tsdown": "^0.23.4",
|
|
68
|
+
"tsx": "^4.23.15",
|
|
69
69
|
"typescript": "^6.0.3"
|
|
70
70
|
},
|
|
71
71
|
"scripts": {
|