ccli-core 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +112 -0
- package/dist/commands/adc.js +1 -1
- package/dist/commands/adc.js.map +1 -1
- package/dist/commands/ccli/list.d.ts +2 -0
- package/dist/commands/ccli/list.js +43 -0
- package/dist/commands/ccli/list.js.map +1 -0
- package/dist/commands/ccli/log.d.ts +2 -0
- package/dist/commands/ccli/log.js +78 -0
- package/dist/commands/ccli/log.js.map +1 -0
- package/dist/commands/ccli/update.d.ts +2 -0
- package/dist/commands/ccli/update.js +42 -0
- package/dist/commands/ccli/update.js.map +1 -0
- package/dist/commands/ccli.js +3 -169
- package/dist/commands/ccli.js.map +1 -1
- package/dist/index.d.ts +9 -4
- package/dist/index.js +31 -18
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# ccli-core
|
|
2
|
+
|
|
3
|
+
cli-core 是工具链基础库,提供命令上下文、日志系统、Shell 工具函数,以及 `adc` / `ccli` 工具链命令。
|
|
4
|
+
|
|
5
|
+
## 接入方式
|
|
6
|
+
|
|
7
|
+
在命令文件中按需导入:
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import {
|
|
11
|
+
getJsMeta, // 获取当前命令的上下文路径(projectRoot / cmdName / assetsDir 等)
|
|
12
|
+
initCmdLogger, // 接入日志系统
|
|
13
|
+
exec, // 执行 shell 命令(async,失败自动写 error log)
|
|
14
|
+
execDetailed, // 同 exec 但返回 { stdout, stderr, exitCode },不抛异常
|
|
15
|
+
sleep, // sleep(ms: number): Promise<void>
|
|
16
|
+
escapeChars, // shell 特殊字符转义
|
|
17
|
+
promiseForEach, // 串行异步遍历
|
|
18
|
+
} from "ccli-core";
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 日志系统
|
|
22
|
+
|
|
23
|
+
所有通过 `adc` 创建的命令模板已自动包含日志接入。手动为已有命令接入,在文件顶部加一行:
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { initCmdLogger } from "ccli-core";
|
|
27
|
+
initCmdLogger(import.meta);
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
每次运行时会自动:
|
|
31
|
+
|
|
32
|
+
- 将 stdout/stderr 完整保存到 `~/ccli/logs/YYYYMMDD/HHmmss-cmdname.log`
|
|
33
|
+
- 在 `~/ccli/logs/index.jsonl` 中追加一条元数据记录(命令名、参数、结果、耗时、日志路径)
|
|
34
|
+
|
|
35
|
+
查看运行记录:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
ccli log # 展示最近 30 条运行记录(表格)
|
|
39
|
+
ccli log -n 50 # 最近 50 条
|
|
40
|
+
ccli log -c yt-srt # 只看 yt-srt 命令的记录
|
|
41
|
+
ccli log --failed # 只看失败记录
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## API 说明
|
|
45
|
+
|
|
46
|
+
### `getJsMeta(importMeta)`
|
|
47
|
+
|
|
48
|
+
获取当前命令的运行时上下文:
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
const { projectRoot, cmdName, assetsDir, jsDir, srcDir, cwd } = getJsMeta(import.meta);
|
|
52
|
+
// projectRoot 工作区根目录(含 package.json)
|
|
53
|
+
// cmdName 当前命令名(取自 @cmdName 注解)
|
|
54
|
+
// assetsDir <projectRoot>/assets
|
|
55
|
+
// jsDir 当前 JS 文件所在目录(dist/commands/...)
|
|
56
|
+
// srcDir 对应的 TS 源文件目录(src/commands/...)
|
|
57
|
+
// cwd process.cwd()
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### `exec(cmd, options?)`
|
|
61
|
+
|
|
62
|
+
异步执行 shell 命令,失败时自动写入 error log 并抛出异常:
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
await exec("git pull");
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### `execDetailed(cmd, options?)`
|
|
69
|
+
|
|
70
|
+
同 `exec`,但不抛异常,返回结构化结果:
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
const { stdout, stderr, exitCode } = await execDetailed("git status");
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### `sleep(ms)`
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
await sleep(2000); // 等待 2 秒
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### `escapeChars(str)`
|
|
83
|
+
|
|
84
|
+
转义 shell 特殊字符,用于拼接命令字符串。
|
|
85
|
+
|
|
86
|
+
### `promiseForEach(arr, asyncFn)`
|
|
87
|
+
|
|
88
|
+
串行异步遍历,依次等待每个 item 处理完成。
|
|
89
|
+
|
|
90
|
+
## 工具链命令
|
|
91
|
+
|
|
92
|
+
cli-core 全局注册以下命令:
|
|
93
|
+
|
|
94
|
+
| 命令 | 作用 |
|
|
95
|
+
|------|------|
|
|
96
|
+
| `adc <name> [desc]` | 在 `src/commands/<name>/` 创建命令模板,自动编译注册并用 VSCode 打开 |
|
|
97
|
+
| `ccli update` | 编译 workspace(tsc),扫描 TS 源文件中的 `@cmdName` 注解同步 `package.json` bin,执行 `npm link` |
|
|
98
|
+
| `ccli list` | 列出当前已注册的所有命令(表格) |
|
|
99
|
+
| `ccli log [options]` | 查看历史运行记录(表格) |
|
|
100
|
+
|
|
101
|
+
## 命令注册机制
|
|
102
|
+
|
|
103
|
+
cli-core 通过扫描 `src/commands/` 下所有 `.ts` 文件中的 `@cmdName` 注解来确定需要注册的命令:
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
#!/usr/bin/env node
|
|
107
|
+
/**
|
|
108
|
+
* @cmdName my-tool ← 只有声明了此注解的文件才会被注册为独立命令
|
|
109
|
+
*/
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
注解所在文件对应的编译产物路径会写入 `package.json` 的 `bin` 字段。
|
package/dist/commands/adc.js
CHANGED
|
@@ -50,7 +50,7 @@ program
|
|
|
50
50
|
catch {
|
|
51
51
|
// code 命令不可用时静默忽略
|
|
52
52
|
}
|
|
53
|
-
execSync("
|
|
53
|
+
execSync("ccli update", { stdio: "inherit", cwd: workspaceRoot });
|
|
54
54
|
console.log(`\n✅ 命令已就绪`);
|
|
55
55
|
console.log(` 命令名:${cmdSlug}`);
|
|
56
56
|
console.log(` 源文件:${targetPath}`);
|
package/dist/commands/adc.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adc.js","sourceRoot":"","sources":["../../src/commands/adc.ts"],"names":[],"mappings":";AACA;;;;GAIG;AACH,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD,SAAS,gBAAgB,CAAC,GAAW;IACnC,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACzC,MAAM,IAAI,GAAG,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACpD,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CAAC,WAAW,GAAG,EAAE,CAAC,CAAC;IACpC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,OAAO;KACJ,SAAS,CAAC,+BAA+B,CAAC;KAC1C,WAAW,CAAC,4BAA4B,CAAC;KACzC,MAAM,CACL,CACE,OAAe,EACf,OAAe,0BAA0B,EACzC,aAAsB,EACtB,EAAE;IACF,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IACpC,MAAM,OAAO,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAC1C,OAAO,CAAC,GAAG,CAAC,UAAU,OAAO,QAAQ,IAAI,EAAE,CAAC,CAAC;IAE7C,MAAM,OAAO,GAAG,gBAAgB,CAAC,OAAO,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC,OAAO,CACzE,aAAa,EACb,IAAI,CACL,CAAC;IAEF,IAAI,UAAkB,CAAC;IACvB,IAAI,aAAa,EAAE,CAAC;QAClB,IAAI,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;QAC9D,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAClC,IACE,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC;gBAC3B,EAAE,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,WAAW,EAAE,EACvC,CAAC;gBACD,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,GAAG,OAAO,KAAK,CAAC,CAAC;YAC1D,CAAC;iBAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;gBACvC,YAAY,IAAI,KAAK,CAAC;YACxB,CAAC;QACH,CAAC;QACD,UAAU,GAAG,YAAY,CAAC;IAC5B,CAAC;SAAM,CAAC;QACN,UAAU,GAAG,IAAI,CAAC,IAAI,CACpB,aAAa,EACb,mBAAmB,EACnB,GAAG,OAAO,KAAK,CAChB,CAAC;IACJ,CAAC;IAED,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5D,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAEtC,IAAI,CAAC;QACH,QAAQ,CAAC,SAAS,UAAU,GAAG,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;IACzD,CAAC;IAAC,MAAM,CAAC;QACP,kBAAkB;IACpB,CAAC;IAED,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"adc.js","sourceRoot":"","sources":["../../src/commands/adc.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n/**\n * @cmdName adc\n * 在 workspace 中快速创建新命令文件并注册到全局。\n * 必须在 workspace 根目录下运行。\n */\nimport path from \"path\";\nimport fs from \"fs\";\nimport { execSync } from \"child_process\";\nimport { program } from \"commander\";\nimport { COMMAND_TEMPLATE } from \"../template.js\";\n\nfunction normalizeCmdSlug(raw: string): string {\n const n = raw.trim().replace(/\\\\/g, \"/\");\n const rest = n.startsWith(\"tool/\") ? n.slice(5) : n;\n if (!rest || rest.startsWith(\"/\")) {\n throw new Error(`无效的命令名: ${raw}`);\n }\n return rest;\n}\n\nprogram\n .arguments(\"<cmdname> [desc] [targetPath]\")\n .description(\"在 workspace 中创建新命令文件并注册到全局\")\n .action(\n (\n cmdname: string,\n desc: string = \"No description provided.\",\n targetPathArg?: string\n ) => {\n const workspaceRoot = process.cwd();\n const cmdSlug = normalizeCmdSlug(cmdname);\n console.log(`创建命令文件:${cmdSlug}\\n描述:${desc}`);\n\n const content = COMMAND_TEMPLATE.replace(/\\$\\{cmdname\\}/g, cmdSlug).replace(\n /\\$\\{desc\\}/g,\n desc\n );\n\n let targetPath: string;\n if (targetPathArg) {\n let resolvedPath = path.resolve(workspaceRoot, targetPathArg);\n if (!resolvedPath.endsWith(\".ts\")) {\n if (\n fs.existsSync(resolvedPath) &&\n fs.statSync(resolvedPath).isDirectory()\n ) {\n resolvedPath = path.join(resolvedPath, `${cmdSlug}.ts`);\n } else if (!path.extname(resolvedPath)) {\n resolvedPath += \".ts\";\n }\n }\n targetPath = resolvedPath;\n } else {\n targetPath = path.join(\n workspaceRoot,\n \"src/commands/tool\",\n `${cmdSlug}.ts`\n );\n }\n\n fs.mkdirSync(path.dirname(targetPath), { recursive: true });\n fs.writeFileSync(targetPath, content);\n\n try {\n execSync(`code \"${targetPath}\"`, { stdio: \"inherit\" });\n } catch {\n // code 命令不可用时静默忽略\n }\n\n execSync(\"ccli update\", { stdio: \"inherit\", cwd: workspaceRoot });\n\n console.log(`\\n✅ 命令已就绪`);\n console.log(` 命令名:${cmdSlug}`);\n console.log(` 源文件:${targetPath}`);\n }\n )\n .parse(process.argv);\n"]}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
import Table from "cli-table3";
|
|
6
|
+
export const listCmd = new Command("list")
|
|
7
|
+
.description("列出 workspace 当前已注册的所有命令(在 workspace 根目录下运行)")
|
|
8
|
+
.action(() => {
|
|
9
|
+
const workspaceRoot = process.cwd();
|
|
10
|
+
const packageJsonPath = path.join(workspaceRoot, "package.json");
|
|
11
|
+
try {
|
|
12
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
13
|
+
const bin = packageJson.bin || {};
|
|
14
|
+
const commandNames = Object.keys(bin);
|
|
15
|
+
if (commandNames.length === 0) {
|
|
16
|
+
console.log(chalk.yellow("目前没有注册任何命令。"));
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
console.log(chalk.cyan(`\n🚀 当前已注册命令 (${commandNames.length}):\n`));
|
|
20
|
+
const table = new Table({
|
|
21
|
+
head: [chalk.green("领域"), chalk.green("命令名称"), chalk.green("源文件路径")],
|
|
22
|
+
colWidths: [15, 25, 60],
|
|
23
|
+
});
|
|
24
|
+
for (const [name, filePath] of Object.entries(bin)) {
|
|
25
|
+
const parts = filePath.split("/");
|
|
26
|
+
let domain = "unknown";
|
|
27
|
+
const commandsIdx = parts.indexOf("commands");
|
|
28
|
+
if (commandsIdx !== -1 && parts.length > commandsIdx + 1) {
|
|
29
|
+
domain = parts[commandsIdx + 1];
|
|
30
|
+
if (domain.endsWith(".js"))
|
|
31
|
+
domain = "root";
|
|
32
|
+
}
|
|
33
|
+
table.push([chalk.blue(domain), chalk.yellow(name), filePath]);
|
|
34
|
+
}
|
|
35
|
+
console.log(table.toString());
|
|
36
|
+
console.log("");
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
console.error(chalk.red("读取 package.json 失败:"), error.message);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../../src/commands/ccli/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,MAAM,YAAY,CAAC;AAE/B,MAAM,CAAC,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC;KACvC,WAAW,CAAC,6CAA6C,CAAC;KAC1D,MAAM,CAAC,GAAG,EAAE;IACX,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IACpC,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,cAAc,CAAC,CAAC;IAEjE,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC,CAAC;QACzE,MAAM,GAAG,GAA2B,WAAW,CAAC,GAAG,IAAI,EAAE,CAAC;QAC1D,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAEtC,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC9B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC;YACzC,OAAO;QACT,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,iBAAiB,YAAY,CAAC,MAAM,MAAM,CAAC,CAAC,CAAC;QAEpE,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC;YACtB,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACpE,SAAS,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC;SACxB,CAAC,CAAC;QAEH,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YACnD,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAClC,IAAI,MAAM,GAAG,SAAS,CAAC;YACvB,MAAM,WAAW,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAC9C,IAAI,WAAW,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,WAAW,GAAG,CAAC,EAAE,CAAC;gBACzD,MAAM,GAAG,KAAK,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC;gBAChC,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;oBAAE,MAAM,GAAG,MAAM,CAAC;YAC9C,CAAC;YACD,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC;QACjE,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC9B,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAClB,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QACpB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,qBAAqB,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;QAC/D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC","sourcesContent":["import { Command } from \"commander\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport chalk from \"chalk\";\nimport Table from \"cli-table3\";\n\nexport const listCmd = new Command(\"list\")\n .description(\"列出 workspace 当前已注册的所有命令(在 workspace 根目录下运行)\")\n .action(() => {\n const workspaceRoot = process.cwd();\n const packageJsonPath = path.join(workspaceRoot, \"package.json\");\n\n try {\n const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, \"utf8\"));\n const bin: Record<string, string> = packageJson.bin || {};\n const commandNames = Object.keys(bin);\n\n if (commandNames.length === 0) {\n console.log(chalk.yellow(\"目前没有注册任何命令。\"));\n return;\n }\n\n console.log(chalk.cyan(`\\n🚀 当前已注册命令 (${commandNames.length}):\\n`));\n\n const table = new Table({\n head: [chalk.green(\"领域\"), chalk.green(\"命令名称\"), chalk.green(\"源文件路径\")],\n colWidths: [15, 25, 60],\n });\n\n for (const [name, filePath] of Object.entries(bin)) {\n const parts = filePath.split(\"/\");\n let domain = \"unknown\";\n const commandsIdx = parts.indexOf(\"commands\");\n if (commandsIdx !== -1 && parts.length > commandsIdx + 1) {\n domain = parts[commandsIdx + 1];\n if (domain.endsWith(\".js\")) domain = \"root\";\n }\n table.push([chalk.blue(domain), chalk.yellow(name), filePath]);\n }\n\n console.log(table.toString());\n console.log(\"\");\n } catch (error: any) {\n console.error(chalk.red(\"读取 package.json 失败:\"), error.message);\n process.exit(1);\n }\n });\n"]}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { homedir } from "os";
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
import Table from "cli-table3";
|
|
7
|
+
function formatDuration(ms) {
|
|
8
|
+
return ms < 1000 ? `${ms}ms` : `${(ms / 1000).toFixed(1)}s`;
|
|
9
|
+
}
|
|
10
|
+
function shortenPath(p) {
|
|
11
|
+
return p.replace(homedir(), "~");
|
|
12
|
+
}
|
|
13
|
+
function truncateCmd(cmd, args, maxLen = 38) {
|
|
14
|
+
const full = args.length ? `${cmd} ${args.join(" ")}` : cmd;
|
|
15
|
+
return full.length > maxLen ? full.slice(0, maxLen - 3) + "..." : full;
|
|
16
|
+
}
|
|
17
|
+
function readIndex() {
|
|
18
|
+
const indexFile = path.join(homedir(), "ccli", "logs", "index.jsonl");
|
|
19
|
+
if (!fs.existsSync(indexFile))
|
|
20
|
+
return [];
|
|
21
|
+
const lines = fs.readFileSync(indexFile, "utf8").trim().split("\n");
|
|
22
|
+
const entries = [];
|
|
23
|
+
for (const line of lines) {
|
|
24
|
+
if (!line.trim())
|
|
25
|
+
continue;
|
|
26
|
+
try {
|
|
27
|
+
entries.push(JSON.parse(line));
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// 跳过损坏行
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return entries;
|
|
34
|
+
}
|
|
35
|
+
export const logCmd = new Command("log")
|
|
36
|
+
.description("查看命令历史运行记录(存储于 ~/ccli/logs/)")
|
|
37
|
+
.option("-n, --tail <n>", "显示最近 n 条", "30")
|
|
38
|
+
.option("-c, --cmd <name>", "按命令名过滤")
|
|
39
|
+
.option("--failed", "只显示失败记录")
|
|
40
|
+
.action((opts) => {
|
|
41
|
+
let entries = readIndex();
|
|
42
|
+
if (opts.cmd) {
|
|
43
|
+
entries = entries.filter((e) => e.cmd === opts.cmd);
|
|
44
|
+
}
|
|
45
|
+
if (opts.failed) {
|
|
46
|
+
entries = entries.filter((e) => e.exitCode !== 0);
|
|
47
|
+
}
|
|
48
|
+
entries.sort((a, b) => b.ts.localeCompare(a.ts));
|
|
49
|
+
const tail = Math.max(1, parseInt(opts.tail, 10) || 30);
|
|
50
|
+
entries = entries.slice(0, tail);
|
|
51
|
+
if (entries.length === 0) {
|
|
52
|
+
console.log(chalk.yellow("暂无运行记录。运行任意带 initCmdLogger 的命令后再试。"));
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const table = new Table({
|
|
56
|
+
head: [
|
|
57
|
+
chalk.cyan("命令"),
|
|
58
|
+
chalk.cyan("结果"),
|
|
59
|
+
chalk.cyan("耗时"),
|
|
60
|
+
chalk.cyan("日志位置"),
|
|
61
|
+
],
|
|
62
|
+
colWidths: [40, 14, 8, 48],
|
|
63
|
+
style: { compact: false },
|
|
64
|
+
});
|
|
65
|
+
for (const entry of entries) {
|
|
66
|
+
const cmdDisplay = truncateCmd(entry.cmd, entry.args);
|
|
67
|
+
const resultDisplay = entry.exitCode === 0
|
|
68
|
+
? chalk.green("✅ 成功")
|
|
69
|
+
: chalk.red(`❌ 失败(${entry.exitCode})`);
|
|
70
|
+
const durationDisplay = formatDuration(entry.duration);
|
|
71
|
+
const pathDisplay = shortenPath(entry.logFile);
|
|
72
|
+
table.push([cmdDisplay, resultDisplay, durationDisplay, pathDisplay]);
|
|
73
|
+
}
|
|
74
|
+
console.log(chalk.bold(`\n最近 ${entries.length} 条运行记录:\n`));
|
|
75
|
+
console.log(table.toString());
|
|
76
|
+
console.log();
|
|
77
|
+
});
|
|
78
|
+
//# sourceMappingURL=log.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.js","sourceRoot":"","sources":["../../../src/commands/ccli/log.ts"],"names":[],"mappings":"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","sourcesContent":["import { Command } from \"commander\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport { homedir } from \"os\";\nimport chalk from \"chalk\";\nimport Table from \"cli-table3\";\nimport type { LogEntry } from \"../../utils/cmdLogger.js\";\n\nfunction formatDuration(ms: number): string {\n return ms < 1000 ? `${ms}ms` : `${(ms / 1000).toFixed(1)}s`;\n}\n\nfunction shortenPath(p: string): string {\n return p.replace(homedir(), \"~\");\n}\n\nfunction truncateCmd(cmd: string, args: string[], maxLen = 38): string {\n const full = args.length ? `${cmd} ${args.join(\" \")}` : cmd;\n return full.length > maxLen ? full.slice(0, maxLen - 3) + \"...\" : full;\n}\n\nfunction readIndex(): LogEntry[] {\n const indexFile = path.join(homedir(), \"ccli\", \"logs\", \"index.jsonl\");\n if (!fs.existsSync(indexFile)) return [];\n\n const lines = fs.readFileSync(indexFile, \"utf8\").trim().split(\"\\n\");\n const entries: LogEntry[] = [];\n for (const line of lines) {\n if (!line.trim()) continue;\n try {\n entries.push(JSON.parse(line) as LogEntry);\n } catch {\n // 跳过损坏行\n }\n }\n return entries;\n}\n\nexport const logCmd = new Command(\"log\")\n .description(\"查看命令历史运行记录(存储于 ~/ccli/logs/)\")\n .option(\"-n, --tail <n>\", \"显示最近 n 条\", \"30\")\n .option(\"-c, --cmd <name>\", \"按命令名过滤\")\n .option(\"--failed\", \"只显示失败记录\")\n .action((opts: { tail: string; cmd?: string; failed?: boolean }) => {\n let entries = readIndex();\n\n if (opts.cmd) {\n entries = entries.filter((e) => e.cmd === opts.cmd);\n }\n if (opts.failed) {\n entries = entries.filter((e) => e.exitCode !== 0);\n }\n\n entries.sort((a, b) => b.ts.localeCompare(a.ts));\n\n const tail = Math.max(1, parseInt(opts.tail, 10) || 30);\n entries = entries.slice(0, tail);\n\n if (entries.length === 0) {\n console.log(chalk.yellow(\"暂无运行记录。运行任意带 initCmdLogger 的命令后再试。\"));\n return;\n }\n\n const table = new Table({\n head: [\n chalk.cyan(\"命令\"),\n chalk.cyan(\"结果\"),\n chalk.cyan(\"耗时\"),\n chalk.cyan(\"日志位置\"),\n ],\n colWidths: [40, 14, 8, 48],\n style: { compact: false },\n });\n\n for (const entry of entries) {\n const cmdDisplay = truncateCmd(entry.cmd, entry.args);\n const resultDisplay =\n entry.exitCode === 0\n ? chalk.green(\"✅ 成功\")\n : chalk.red(`❌ 失败(${entry.exitCode})`);\n const durationDisplay = formatDuration(entry.duration);\n const pathDisplay = shortenPath(entry.logFile);\n\n table.push([cmdDisplay, resultDisplay, durationDisplay, pathDisplay]);\n }\n\n console.log(chalk.bold(`\\n最近 ${entries.length} 条运行记录:\\n`));\n console.log(table.toString());\n console.log();\n });\n"]}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { execSync } from "child_process";
|
|
3
|
+
import { syncCommands, npmLink } from "../../index.js";
|
|
4
|
+
export const updateCmd = new Command("update")
|
|
5
|
+
.description("编译 workspace (tsc),扫描 dist/commands,同步 package.json bin,执行 npm link(在 workspace 根目录下运行)")
|
|
6
|
+
.action(() => {
|
|
7
|
+
const workspaceRoot = process.cwd();
|
|
8
|
+
console.log("\n[1/5] 正在编译 workspace (tsc)...");
|
|
9
|
+
execSync("npx tsc", { stdio: "inherit", cwd: workspaceRoot });
|
|
10
|
+
console.log(" - 编译完成。");
|
|
11
|
+
console.log("\n[2/5] 正在清理旧命令并扫描命令文件...");
|
|
12
|
+
const { previous, added } = syncCommands({ projectRoot: workspaceRoot });
|
|
13
|
+
if (previous.length > 0) {
|
|
14
|
+
console.log(` - 已清理旧命令 (${previous.length} 个): ${previous.join(", ")}`);
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
console.log(" - 无需清理旧命令。");
|
|
18
|
+
}
|
|
19
|
+
console.log("\n[3/5] 扫描命令文件完成。");
|
|
20
|
+
if (added.length > 0) {
|
|
21
|
+
console.log(` - 发现 ${added.length} 个命令: ${added.join(", ")}`);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
console.log(" - 未发现任何命令。");
|
|
25
|
+
}
|
|
26
|
+
console.log("\n[4/5] 正在注册命令到全局 (npm link)...");
|
|
27
|
+
try {
|
|
28
|
+
npmLink(workspaceRoot);
|
|
29
|
+
console.log(" - 注册成功。");
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
console.error(" - 注册失败:", error.message);
|
|
33
|
+
}
|
|
34
|
+
console.log("\n[5/5] 更新完成!");
|
|
35
|
+
console.log("---------------------------------------");
|
|
36
|
+
if (added.length > 0) {
|
|
37
|
+
console.log("你可以直接运行以下命令:");
|
|
38
|
+
added.forEach((cmd) => console.log(` $ ${cmd}`));
|
|
39
|
+
}
|
|
40
|
+
console.log("---------------------------------------\n");
|
|
41
|
+
});
|
|
42
|
+
//# sourceMappingURL=update.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update.js","sourceRoot":"","sources":["../../../src/commands/ccli/update.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAEvD,MAAM,CAAC,MAAM,SAAS,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC;KAC3C,WAAW,CACV,yFAAyF,CAC1F;KACA,MAAM,CAAC,GAAG,EAAE;IACX,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAEpC,OAAO,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC;IAC/C,QAAQ,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,aAAa,EAAE,CAAC,CAAC;IAC9D,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAEzB,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;IAEzC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,YAAY,CAAC,EAAE,WAAW,EAAE,aAAa,EAAE,CAAC,CAAC;IAEzE,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,OAAO,CAAC,GAAG,CAAC,eAAe,QAAQ,CAAC,MAAM,QAAQ,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC3E,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAC9B,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;IACjC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,OAAO,CAAC,GAAG,CAAC,UAAU,KAAK,CAAC,MAAM,SAAS,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjE,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAC9B,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC;IAC/C,IAAI,CAAC;QACH,OAAO,CAAC,aAAa,CAAC,CAAC;QACvB,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAC3B,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QACpB,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IAC5C,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IAC7B,OAAO,CAAC,GAAG,CAAC,yCAAyC,CAAC,CAAC;IACvD,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QAC5B,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC,CAAC;IACpD,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,2CAA2C,CAAC,CAAC;AAC3D,CAAC,CAAC,CAAC","sourcesContent":["import { Command } from \"commander\";\nimport { execSync } from \"child_process\";\nimport { syncCommands, npmLink } from \"../../index.js\";\n\nexport const updateCmd = new Command(\"update\")\n .description(\n \"编译 workspace (tsc),扫描 dist/commands,同步 package.json bin,执行 npm link(在 workspace 根目录下运行)\"\n )\n .action(() => {\n const workspaceRoot = process.cwd();\n\n console.log(\"\\n[1/5] 正在编译 workspace (tsc)...\");\n execSync(\"npx tsc\", { stdio: \"inherit\", cwd: workspaceRoot });\n console.log(\" - 编译完成。\");\n\n console.log(\"\\n[2/5] 正在清理旧命令并扫描命令文件...\");\n\n const { previous, added } = syncCommands({ projectRoot: workspaceRoot });\n\n if (previous.length > 0) {\n console.log(` - 已清理旧命令 (${previous.length} 个): ${previous.join(\", \")}`);\n } else {\n console.log(\" - 无需清理旧命令。\");\n }\n\n console.log(\"\\n[3/5] 扫描命令文件完成。\");\n if (added.length > 0) {\n console.log(` - 发现 ${added.length} 个命令: ${added.join(\", \")}`);\n } else {\n console.log(\" - 未发现任何命令。\");\n }\n\n console.log(\"\\n[4/5] 正在注册命令到全局 (npm link)...\");\n try {\n npmLink(workspaceRoot);\n console.log(\" - 注册成功。\");\n } catch (error: any) {\n console.error(\" - 注册失败:\", error.message);\n }\n\n console.log(\"\\n[5/5] 更新完成!\");\n console.log(\"---------------------------------------\");\n if (added.length > 0) {\n console.log(\"你可以直接运行以下命令:\");\n added.forEach((cmd) => console.log(` $ ${cmd}`));\n }\n console.log(\"---------------------------------------\\n\");\n });\n"]}
|
package/dist/commands/ccli.js
CHANGED
|
@@ -5,177 +5,11 @@
|
|
|
5
5
|
* 必须在 workspace 根目录下运行(list / update 子命令依赖 process.cwd() 为 workspace 根)。
|
|
6
6
|
*/
|
|
7
7
|
import { Command } from "commander";
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
11
|
-
import { execSync } from "child_process";
|
|
12
|
-
import chalk from "chalk";
|
|
13
|
-
import Table from "cli-table3";
|
|
14
|
-
import { syncCommands, npmLink } from "../index.js";
|
|
8
|
+
import { listCmd } from "./ccli/list.js";
|
|
9
|
+
import { logCmd } from "./ccli/log.js";
|
|
10
|
+
import { updateCmd } from "./ccli/update.js";
|
|
15
11
|
const program = new Command();
|
|
16
12
|
program.name("ccli").description("ccli 工具链管理命令(list / log / update)");
|
|
17
|
-
// ═══════════════════════════════════════════════
|
|
18
|
-
// ccli list 子命令
|
|
19
|
-
// ═══════════════════════════════════════════════
|
|
20
|
-
// 👁️ ccli list:读取 workspace package.json → 打印已注册命令表格
|
|
21
|
-
const listCmd = new Command("list")
|
|
22
|
-
.description("列出 workspace 当前已注册的所有命令(在 workspace 根目录下运行)")
|
|
23
|
-
.action(() => {
|
|
24
|
-
const workspaceRoot = process.cwd();
|
|
25
|
-
const packageJsonPath = path.join(workspaceRoot, "package.json");
|
|
26
|
-
try {
|
|
27
|
-
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
28
|
-
const bin = packageJson.bin || {};
|
|
29
|
-
const commandNames = Object.keys(bin);
|
|
30
|
-
if (commandNames.length === 0) {
|
|
31
|
-
console.log(chalk.yellow("目前没有注册任何命令。"));
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
console.log(chalk.cyan(`\n🚀 当前已注册命令 (${commandNames.length}):\n`));
|
|
35
|
-
const table = new Table({
|
|
36
|
-
head: [chalk.green("领域"), chalk.green("命令名称"), chalk.green("源文件路径")],
|
|
37
|
-
colWidths: [15, 25, 60],
|
|
38
|
-
});
|
|
39
|
-
for (const [name, filePath] of Object.entries(bin)) {
|
|
40
|
-
const parts = filePath.split("/");
|
|
41
|
-
let domain = "unknown";
|
|
42
|
-
const commandsIdx = parts.indexOf("commands");
|
|
43
|
-
if (commandsIdx !== -1 && parts.length > commandsIdx + 1) {
|
|
44
|
-
domain = parts[commandsIdx + 1];
|
|
45
|
-
if (domain.endsWith(".js"))
|
|
46
|
-
domain = "root";
|
|
47
|
-
}
|
|
48
|
-
table.push([chalk.blue(domain), chalk.yellow(name), filePath]);
|
|
49
|
-
}
|
|
50
|
-
console.log(table.toString());
|
|
51
|
-
console.log("");
|
|
52
|
-
}
|
|
53
|
-
catch (error) {
|
|
54
|
-
console.error(chalk.red("读取 package.json 失败:"), error.message);
|
|
55
|
-
process.exit(1);
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
// ═══════════════════════════════════════════════
|
|
59
|
-
// ccli log 子命令(辅助函数)
|
|
60
|
-
// ═══════════════════════════════════════════════
|
|
61
|
-
function formatDuration(ms) {
|
|
62
|
-
return ms < 1000 ? `${ms}ms` : `${(ms / 1000).toFixed(1)}s`;
|
|
63
|
-
}
|
|
64
|
-
function shortenPath(p) {
|
|
65
|
-
return p.replace(homedir(), "~");
|
|
66
|
-
}
|
|
67
|
-
function truncateCmd(cmd, args, maxLen = 38) {
|
|
68
|
-
const full = args.length ? `${cmd} ${args.join(" ")}` : cmd;
|
|
69
|
-
return full.length > maxLen ? full.slice(0, maxLen - 3) + "..." : full;
|
|
70
|
-
}
|
|
71
|
-
function readIndex() {
|
|
72
|
-
const indexFile = path.join(homedir(), "ccli", "logs", "index.jsonl");
|
|
73
|
-
if (!fs.existsSync(indexFile))
|
|
74
|
-
return [];
|
|
75
|
-
const lines = fs.readFileSync(indexFile, "utf8").trim().split("\n");
|
|
76
|
-
const entries = [];
|
|
77
|
-
for (const line of lines) {
|
|
78
|
-
if (!line.trim())
|
|
79
|
-
continue;
|
|
80
|
-
try {
|
|
81
|
-
entries.push(JSON.parse(line));
|
|
82
|
-
}
|
|
83
|
-
catch {
|
|
84
|
-
// 跳过损坏行
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return entries;
|
|
88
|
-
}
|
|
89
|
-
// 👁️ ccli log:查看历史运行记录(含 -n、-c、--failed 选项)
|
|
90
|
-
const logCmd = new Command("log")
|
|
91
|
-
.description("查看命令历史运行记录(存储于 ~/ccli/logs/)")
|
|
92
|
-
.option("-n, --tail <n>", "显示最近 n 条", "30")
|
|
93
|
-
.option("-c, --cmd <name>", "按命令名过滤")
|
|
94
|
-
.option("--failed", "只显示失败记录")
|
|
95
|
-
.action((opts) => {
|
|
96
|
-
let entries = readIndex();
|
|
97
|
-
if (opts.cmd) {
|
|
98
|
-
entries = entries.filter((e) => e.cmd === opts.cmd);
|
|
99
|
-
}
|
|
100
|
-
if (opts.failed) {
|
|
101
|
-
entries = entries.filter((e) => e.exitCode !== 0);
|
|
102
|
-
}
|
|
103
|
-
// 按时间倒序(最新在上)
|
|
104
|
-
entries.sort((a, b) => b.ts.localeCompare(a.ts));
|
|
105
|
-
const tail = Math.max(1, parseInt(opts.tail, 10) || 30);
|
|
106
|
-
entries = entries.slice(0, tail);
|
|
107
|
-
if (entries.length === 0) {
|
|
108
|
-
console.log(chalk.yellow("暂无运行记录。运行任意带 initCmdLogger 的命令后再试。"));
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
const table = new Table({
|
|
112
|
-
head: [
|
|
113
|
-
chalk.cyan("命令"),
|
|
114
|
-
chalk.cyan("结果"),
|
|
115
|
-
chalk.cyan("耗时"),
|
|
116
|
-
chalk.cyan("日志位置"),
|
|
117
|
-
],
|
|
118
|
-
colWidths: [40, 14, 8, 48],
|
|
119
|
-
style: { compact: false },
|
|
120
|
-
});
|
|
121
|
-
for (const entry of entries) {
|
|
122
|
-
const cmdDisplay = truncateCmd(entry.cmd, entry.args);
|
|
123
|
-
const resultDisplay = entry.exitCode === 0
|
|
124
|
-
? chalk.green("✅ 成功")
|
|
125
|
-
: chalk.red(`❌ 失败(${entry.exitCode})`);
|
|
126
|
-
const durationDisplay = formatDuration(entry.duration);
|
|
127
|
-
const pathDisplay = shortenPath(entry.logFile);
|
|
128
|
-
table.push([cmdDisplay, resultDisplay, durationDisplay, pathDisplay]);
|
|
129
|
-
}
|
|
130
|
-
console.log(chalk.bold(`\n最近 ${entries.length} 条运行记录:\n`));
|
|
131
|
-
console.log(table.toString());
|
|
132
|
-
console.log();
|
|
133
|
-
});
|
|
134
|
-
// ═══════════════════════════════════════════════
|
|
135
|
-
// ccli update 子命令
|
|
136
|
-
// ═══════════════════════════════════════════════
|
|
137
|
-
// 👁️ ccli update:tsc → syncCommands → npmLink(逻辑封装在 action 中,避免模块顶层副作用)
|
|
138
|
-
const updateCmd = new Command("update")
|
|
139
|
-
.description("编译 workspace (tsc),扫描 dist/commands,同步 package.json bin,执行 npm link(在 workspace 根目录下运行)")
|
|
140
|
-
.action(() => {
|
|
141
|
-
const workspaceRoot = process.cwd();
|
|
142
|
-
console.log("\n[1/5] 正在编译 workspace (tsc)...");
|
|
143
|
-
execSync("npx tsc", { stdio: "inherit", cwd: workspaceRoot });
|
|
144
|
-
console.log(" - 编译完成。");
|
|
145
|
-
console.log("\n[2/5] 正在清理旧命令并扫描命令文件...");
|
|
146
|
-
const { previous, added } = syncCommands({ projectRoot: workspaceRoot });
|
|
147
|
-
if (previous.length > 0) {
|
|
148
|
-
console.log(` - 已清理旧命令 (${previous.length} 个): ${previous.join(", ")}`);
|
|
149
|
-
}
|
|
150
|
-
else {
|
|
151
|
-
console.log(" - 无需清理旧命令。");
|
|
152
|
-
}
|
|
153
|
-
console.log("\n[3/5] 扫描命令文件完成。");
|
|
154
|
-
if (added.length > 0) {
|
|
155
|
-
console.log(` - 发现 ${added.length} 个命令: ${added.join(", ")}`);
|
|
156
|
-
}
|
|
157
|
-
else {
|
|
158
|
-
console.log(" - 未发现任何命令。");
|
|
159
|
-
}
|
|
160
|
-
console.log("\n[4/5] 正在注册命令到全局 (npm link)...");
|
|
161
|
-
try {
|
|
162
|
-
npmLink(workspaceRoot);
|
|
163
|
-
console.log(" - 注册成功。");
|
|
164
|
-
}
|
|
165
|
-
catch (error) {
|
|
166
|
-
console.error(" - 注册失败:", error.message);
|
|
167
|
-
}
|
|
168
|
-
console.log("\n[5/5] 更新完成!");
|
|
169
|
-
console.log("---------------------------------------");
|
|
170
|
-
if (added.length > 0) {
|
|
171
|
-
console.log("你可以直接运行以下命令:");
|
|
172
|
-
added.forEach((cmd) => console.log(` $ ${cmd}`));
|
|
173
|
-
}
|
|
174
|
-
console.log("---------------------------------------\n");
|
|
175
|
-
});
|
|
176
|
-
// ═══════════════════════════════════════════════
|
|
177
|
-
// 注册子命令并解析
|
|
178
|
-
// ═══════════════════════════════════════════════
|
|
179
13
|
program.addCommand(listCmd);
|
|
180
14
|
program.addCommand(logCmd);
|
|
181
15
|
program.addCommand(updateCmd);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ccli.js","sourceRoot":"","sources":["../../src/commands/ccli.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n/**\n * @cmdName ccli\n * ccli 父命令:list / log / update 三个子命令。\n * 必须在 workspace 根目录下运行(list / update 子命令依赖 process.cwd() 为 workspace 根)。\n */\nimport { Command } from \"commander\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport { homedir } from \"os\";\nimport { execSync } from \"child_process\";\nimport chalk from \"chalk\";\nimport Table from \"cli-table3\";\nimport type { LogEntry } from \"../utils/cmdLogger.js\";\nimport { syncCommands, npmLink } from \"../index.js\";\n\nconst program = new Command();\nprogram.name(\"ccli\").description(\"ccli 工具链管理命令(list / log / update)\");\n\n// ═══════════════════════════════════════════════\n// ccli list 子命令\n// ═══════════════════════════════════════════════\n\n// 👁️ ccli list:读取 workspace package.json → 打印已注册命令表格\nconst listCmd = new Command(\"list\")\n .description(\"列出 workspace 当前已注册的所有命令(在 workspace 根目录下运行)\")\n .action(() => {\n const workspaceRoot = process.cwd();\n const packageJsonPath = path.join(workspaceRoot, \"package.json\");\n\n try {\n const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, \"utf8\"));\n const bin: Record<string, string> = packageJson.bin || {};\n const commandNames = Object.keys(bin);\n\n if (commandNames.length === 0) {\n console.log(chalk.yellow(\"目前没有注册任何命令。\"));\n return;\n }\n\n console.log(chalk.cyan(`\\n🚀 当前已注册命令 (${commandNames.length}):\\n`));\n\n const table = new Table({\n head: [chalk.green(\"领域\"), chalk.green(\"命令名称\"), chalk.green(\"源文件路径\")],\n colWidths: [15, 25, 60],\n });\n\n for (const [name, filePath] of Object.entries(bin)) {\n const parts = filePath.split(\"/\");\n let domain = \"unknown\";\n const commandsIdx = parts.indexOf(\"commands\");\n if (commandsIdx !== -1 && parts.length > commandsIdx + 1) {\n domain = parts[commandsIdx + 1];\n if (domain.endsWith(\".js\")) domain = \"root\";\n }\n table.push([chalk.blue(domain), chalk.yellow(name), filePath]);\n }\n\n console.log(table.toString());\n console.log(\"\");\n } catch (error: any) {\n console.error(chalk.red(\"读取 package.json 失败:\"), error.message);\n process.exit(1);\n }\n });\n\n// ═══════════════════════════════════════════════\n// ccli log 子命令(辅助函数)\n// ═══════════════════════════════════════════════\n\nfunction formatDuration(ms: number): string {\n return ms < 1000 ? `${ms}ms` : `${(ms / 1000).toFixed(1)}s`;\n}\n\nfunction shortenPath(p: string): string {\n return p.replace(homedir(), \"~\");\n}\n\nfunction truncateCmd(cmd: string, args: string[], maxLen = 38): string {\n const full = args.length ? `${cmd} ${args.join(\" \")}` : cmd;\n return full.length > maxLen ? full.slice(0, maxLen - 3) + \"...\" : full;\n}\n\nfunction readIndex(): LogEntry[] {\n const indexFile = path.join(homedir(), \"ccli\", \"logs\", \"index.jsonl\");\n if (!fs.existsSync(indexFile)) return [];\n\n const lines = fs.readFileSync(indexFile, \"utf8\").trim().split(\"\\n\");\n const entries: LogEntry[] = [];\n for (const line of lines) {\n if (!line.trim()) continue;\n try {\n entries.push(JSON.parse(line) as LogEntry);\n } catch {\n // 跳过损坏行\n }\n }\n return entries;\n}\n\n// 👁️ ccli log:查看历史运行记录(含 -n、-c、--failed 选项)\nconst logCmd = new Command(\"log\")\n .description(\"查看命令历史运行记录(存储于 ~/ccli/logs/)\")\n .option(\"-n, --tail <n>\", \"显示最近 n 条\", \"30\")\n .option(\"-c, --cmd <name>\", \"按命令名过滤\")\n .option(\"--failed\", \"只显示失败记录\")\n .action((opts: { tail: string; cmd?: string; failed?: boolean }) => {\n let entries = readIndex();\n\n if (opts.cmd) {\n entries = entries.filter((e) => e.cmd === opts.cmd);\n }\n if (opts.failed) {\n entries = entries.filter((e) => e.exitCode !== 0);\n }\n\n // 按时间倒序(最新在上)\n entries.sort((a, b) => b.ts.localeCompare(a.ts));\n\n const tail = Math.max(1, parseInt(opts.tail, 10) || 30);\n entries = entries.slice(0, tail);\n\n if (entries.length === 0) {\n console.log(chalk.yellow(\"暂无运行记录。运行任意带 initCmdLogger 的命令后再试。\"));\n return;\n }\n\n const table = new Table({\n head: [\n chalk.cyan(\"命令\"),\n chalk.cyan(\"结果\"),\n chalk.cyan(\"耗时\"),\n chalk.cyan(\"日志位置\"),\n ],\n colWidths: [40, 14, 8, 48],\n style: { compact: false },\n });\n\n for (const entry of entries) {\n const cmdDisplay = truncateCmd(entry.cmd, entry.args);\n const resultDisplay =\n entry.exitCode === 0\n ? chalk.green(\"✅ 成功\")\n : chalk.red(`❌ 失败(${entry.exitCode})`);\n const durationDisplay = formatDuration(entry.duration);\n const pathDisplay = shortenPath(entry.logFile);\n\n table.push([cmdDisplay, resultDisplay, durationDisplay, pathDisplay]);\n }\n\n console.log(chalk.bold(`\\n最近 ${entries.length} 条运行记录:\\n`));\n console.log(table.toString());\n console.log();\n });\n\n// ═══════════════════════════════════════════════\n// ccli update 子命令\n// ═══════════════════════════════════════════════\n\n// 👁️ ccli update:tsc → syncCommands → npmLink(逻辑封装在 action 中,避免模块顶层副作用)\nconst updateCmd = new Command(\"update\")\n .description(\"编译 workspace (tsc),扫描 dist/commands,同步 package.json bin,执行 npm link(在 workspace 根目录下运行)\")\n .action(() => {\n const workspaceRoot = process.cwd();\n\n console.log(\"\\n[1/5] 正在编译 workspace (tsc)...\");\n execSync(\"npx tsc\", { stdio: \"inherit\", cwd: workspaceRoot });\n console.log(\" - 编译完成。\");\n\n console.log(\"\\n[2/5] 正在清理旧命令并扫描命令文件...\");\n\n const { previous, added } = syncCommands({ projectRoot: workspaceRoot });\n\n if (previous.length > 0) {\n console.log(` - 已清理旧命令 (${previous.length} 个): ${previous.join(\", \")}`);\n } else {\n console.log(\" - 无需清理旧命令。\");\n }\n\n console.log(\"\\n[3/5] 扫描命令文件完成。\");\n if (added.length > 0) {\n console.log(` - 发现 ${added.length} 个命令: ${added.join(\", \")}`);\n } else {\n console.log(\" - 未发现任何命令。\");\n }\n\n console.log(\"\\n[4/5] 正在注册命令到全局 (npm link)...\");\n try {\n npmLink(workspaceRoot);\n console.log(\" - 注册成功。\");\n } catch (error: any) {\n console.error(\" - 注册失败:\", error.message);\n }\n\n console.log(\"\\n[5/5] 更新完成!\");\n console.log(\"---------------------------------------\");\n if (added.length > 0) {\n console.log(\"你可以直接运行以下命令:\");\n added.forEach((cmd) => console.log(` $ ${cmd}`));\n }\n console.log(\"---------------------------------------\\n\");\n });\n\n// ═══════════════════════════════════════════════\n// 注册子命令并解析\n// ═══════════════════════════════════════════════\n\nprogram.addCommand(listCmd);\nprogram.addCommand(logCmd);\nprogram.addCommand(updateCmd);\n\nprogram.parse(process.argv);\n"]}
|
|
1
|
+
{"version":3,"file":"ccli.js","sourceRoot":"","sources":["../../src/commands/ccli.ts"],"names":[],"mappings":";AACA;;;;GAIG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAC9B,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,mCAAmC,CAAC,CAAC;AAEtE,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;AAC5B,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;AAC3B,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;AAE9B,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC","sourcesContent":["#!/usr/bin/env node\n/**\n * @cmdName ccli\n * ccli 父命令:list / log / update 三个子命令。\n * 必须在 workspace 根目录下运行(list / update 子命令依赖 process.cwd() 为 workspace 根)。\n */\nimport { Command } from \"commander\";\nimport { listCmd } from \"./ccli/list.js\";\nimport { logCmd } from \"./ccli/log.js\";\nimport { updateCmd } from \"./ccli/update.js\";\n\nconst program = new Command();\nprogram.name(\"ccli\").description(\"ccli 工具链管理命令(list / log / update)\");\n\nprogram.addCommand(listCmd);\nprogram.addCommand(logCmd);\nprogram.addCommand(updateCmd);\n\nprogram.parse(process.argv);\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export interface SyncOptions {
|
|
2
2
|
/** 项目根目录(含 package.json) */
|
|
3
3
|
projectRoot: string;
|
|
4
|
+
/** TS 源码命令目录,默认 {projectRoot}/src/commands */
|
|
5
|
+
srcCommandsDir?: string;
|
|
4
6
|
/** 编译产物命令目录,默认 {projectRoot}/dist/commands */
|
|
5
7
|
commandsDir?: string;
|
|
6
8
|
/** package.json 路径,默认 {projectRoot}/package.json */
|
|
@@ -16,15 +18,18 @@ export interface SyncResult {
|
|
|
16
18
|
}
|
|
17
19
|
/** 递归收集目录下所有 .js 文件的绝对路径 */
|
|
18
20
|
export declare function getAllJsFiles(dir: string): string[];
|
|
21
|
+
/** 递归收集目录下所有 .ts 文件的绝对路径 */
|
|
22
|
+
export declare function getAllTsFiles(dir: string): string[];
|
|
19
23
|
/**
|
|
20
|
-
*
|
|
21
|
-
* 无注解时按文件名推断(index.js 取父目录名)。
|
|
24
|
+
* 从文件内容中提取显式 @cmdName 注解,无注解返回 null。
|
|
22
25
|
*/
|
|
23
26
|
export declare function getCmdName(filePath: string): string | null;
|
|
24
27
|
/**
|
|
25
|
-
* 扫描
|
|
28
|
+
* 扫描 srcCommandsDir 下所有 .ts 文件,提取 @cmdName 注解,
|
|
29
|
+
* 返回 `{ cmdName: relativeDistPathFromProjectRoot }` 映射。
|
|
30
|
+
* 只有显式声明 @cmdName 的文件才会被注册,子命令文件不会误入。
|
|
26
31
|
*/
|
|
27
|
-
export declare function collectCommands(
|
|
32
|
+
export declare function collectCommands(srcCommandsDir: string, distCommandsDir: string, projectRoot: string): Record<string, string>;
|
|
28
33
|
/** 批量取消旧命令的全局链接 */
|
|
29
34
|
export declare function unlinkOldCommands(oldBin: Record<string, string>, projectRoot: string): void;
|
|
30
35
|
/** 执行 npm link 将当前包注册到全局 */
|
package/dist/index.js
CHANGED
|
@@ -20,31 +20,44 @@ export function getAllJsFiles(dir) {
|
|
|
20
20
|
}
|
|
21
21
|
return results;
|
|
22
22
|
}
|
|
23
|
+
/** 递归收集目录下所有 .ts 文件的绝对路径 */
|
|
24
|
+
export function getAllTsFiles(dir) {
|
|
25
|
+
if (!fs.existsSync(dir))
|
|
26
|
+
return [];
|
|
27
|
+
const results = [];
|
|
28
|
+
for (const file of fs.readdirSync(dir)) {
|
|
29
|
+
const fullPath = path.join(dir, file);
|
|
30
|
+
if (fs.statSync(fullPath).isDirectory()) {
|
|
31
|
+
results.push(...getAllTsFiles(fullPath));
|
|
32
|
+
}
|
|
33
|
+
else if (path.extname(file) === ".ts") {
|
|
34
|
+
results.push(fullPath);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return results;
|
|
38
|
+
}
|
|
23
39
|
/**
|
|
24
|
-
*
|
|
25
|
-
* 无注解时按文件名推断(index.js 取父目录名)。
|
|
40
|
+
* 从文件内容中提取显式 @cmdName 注解,无注解返回 null。
|
|
26
41
|
*/
|
|
27
42
|
export function getCmdName(filePath) {
|
|
28
43
|
const content = fs.readFileSync(filePath, "utf8");
|
|
29
44
|
const match = content.match(/@cmdName\s+(\S+)/);
|
|
30
|
-
|
|
31
|
-
return match[1];
|
|
32
|
-
const fileName = path.basename(filePath, ".js");
|
|
33
|
-
if (fileName === "index") {
|
|
34
|
-
return path.basename(path.dirname(filePath));
|
|
35
|
-
}
|
|
36
|
-
return fileName;
|
|
45
|
+
return match ? match[1] : null;
|
|
37
46
|
}
|
|
38
47
|
/**
|
|
39
|
-
* 扫描
|
|
48
|
+
* 扫描 srcCommandsDir 下所有 .ts 文件,提取 @cmdName 注解,
|
|
49
|
+
* 返回 `{ cmdName: relativeDistPathFromProjectRoot }` 映射。
|
|
50
|
+
* 只有显式声明 @cmdName 的文件才会被注册,子命令文件不会误入。
|
|
40
51
|
*/
|
|
41
|
-
export function collectCommands(
|
|
52
|
+
export function collectCommands(srcCommandsDir, distCommandsDir, projectRoot) {
|
|
42
53
|
const bin = {};
|
|
43
|
-
for (const
|
|
44
|
-
const cmdName = getCmdName(
|
|
45
|
-
if (cmdName)
|
|
46
|
-
|
|
47
|
-
|
|
54
|
+
for (const tsFile of getAllTsFiles(srcCommandsDir)) {
|
|
55
|
+
const cmdName = getCmdName(tsFile);
|
|
56
|
+
if (!cmdName)
|
|
57
|
+
continue;
|
|
58
|
+
const rel = path.relative(srcCommandsDir, tsFile);
|
|
59
|
+
const jsFile = path.join(distCommandsDir, rel.replace(/\.ts$/, ".js"));
|
|
60
|
+
bin[cmdName] = `./${path.relative(projectRoot, jsFile)}`;
|
|
48
61
|
}
|
|
49
62
|
return bin;
|
|
50
63
|
}
|
|
@@ -76,13 +89,13 @@ export function npmLink(projectRoot) {
|
|
|
76
89
|
* 注意:本函数不执行 npm link,由调用方在适当时机调用 `npmLink()`。
|
|
77
90
|
*/
|
|
78
91
|
export function syncCommands(options) {
|
|
79
|
-
const { projectRoot, commandsDir = path.join(projectRoot, "dist", "commands"), packageJsonPath = path.join(projectRoot, "package.json"), } = options;
|
|
92
|
+
const { projectRoot, srcCommandsDir = path.join(projectRoot, "src", "commands"), commandsDir = path.join(projectRoot, "dist", "commands"), packageJsonPath = path.join(projectRoot, "package.json"), } = options;
|
|
80
93
|
const raw = fs.readFileSync(packageJsonPath, "utf8");
|
|
81
94
|
const packageJson = JSON.parse(raw);
|
|
82
95
|
const oldBin = packageJson.bin ?? {};
|
|
83
96
|
const previous = Object.keys(oldBin);
|
|
84
97
|
unlinkOldCommands(oldBin, projectRoot);
|
|
85
|
-
const binMap = collectCommands(commandsDir, projectRoot);
|
|
98
|
+
const binMap = collectCommands(srcCommandsDir, commandsDir, projectRoot);
|
|
86
99
|
const added = Object.keys(binMap);
|
|
87
100
|
packageJson.bin = binMap;
|
|
88
101
|
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + "\n", "utf8");
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\nimport { execSync } from \"child_process\";\n\n// ──────────────────────────────────────────────\n// 类型\n// ──────────────────────────────────────────────\n\nexport interface SyncOptions {\n /** 项目根目录(含 package.json) */\n projectRoot: string;\n /** TS 源码命令目录,默认 {projectRoot}/src/commands */\n srcCommandsDir?: string;\n /** 编译产物命令目录,默认 {projectRoot}/dist/commands */\n commandsDir?: string;\n /** package.json 路径,默认 {projectRoot}/package.json */\n packageJsonPath?: string;\n}\n\nexport interface SyncResult {\n /** 本次同步前已注册的命令名列表 */\n previous: string[];\n /** 本次扫描到的命令名列表 */\n added: string[];\n /** 最终写入 bin 字段的映射 */\n binMap: Record<string, string>;\n}\n\n// ──────────────────────────────────────────────\n// 框架 API:命令扫描 & 注册\n// ──────────────────────────────────────────────\n\n/** 递归收集目录下所有 .js 文件的绝对路径 */\nexport function getAllJsFiles(dir: string): string[] {\n if (!fs.existsSync(dir)) return [];\n const results: string[] = [];\n for (const file of fs.readdirSync(dir)) {\n const fullPath = path.join(dir, file);\n if (fs.statSync(fullPath).isDirectory()) {\n results.push(...getAllJsFiles(fullPath));\n } else if (path.extname(file) === \".js\") {\n results.push(fullPath);\n }\n }\n return results;\n}\n\n/** 递归收集目录下所有 .ts 文件的绝对路径 */\nexport function getAllTsFiles(dir: string): string[] {\n if (!fs.existsSync(dir)) return [];\n const results: string[] = [];\n for (const file of fs.readdirSync(dir)) {\n const fullPath = path.join(dir, file);\n if (fs.statSync(fullPath).isDirectory()) {\n results.push(...getAllTsFiles(fullPath));\n } else if (path.extname(file) === \".ts\") {\n results.push(fullPath);\n }\n }\n return results;\n}\n\n/**\n * 从文件内容中提取显式 @cmdName 注解,无注解返回 null。\n */\nexport function getCmdName(filePath: string): string | null {\n const content = fs.readFileSync(filePath, \"utf8\");\n const match = content.match(/@cmdName\\s+(\\S+)/);\n return match ? match[1] : null;\n}\n\n/**\n * 扫描 srcCommandsDir 下所有 .ts 文件,提取 @cmdName 注解,\n * 返回 `{ cmdName: relativeDistPathFromProjectRoot }` 映射。\n * 只有显式声明 @cmdName 的文件才会被注册,子命令文件不会误入。\n */\nexport function collectCommands(\n srcCommandsDir: string,\n distCommandsDir: string,\n projectRoot: string\n): Record<string, string> {\n const bin: Record<string, string> = {};\n for (const tsFile of getAllTsFiles(srcCommandsDir)) {\n const cmdName = getCmdName(tsFile);\n if (!cmdName) continue;\n const rel = path.relative(srcCommandsDir, tsFile);\n const jsFile = path.join(distCommandsDir, rel.replace(/\\.ts$/, \".js\"));\n bin[cmdName] = `./${path.relative(projectRoot, jsFile)}`;\n }\n return bin;\n}\n\n/** 批量取消旧命令的全局链接 */\nexport function unlinkOldCommands(\n oldBin: Record<string, string>,\n projectRoot: string\n): void {\n for (const cmdName of Object.keys(oldBin)) {\n try {\n execSync(`npm unlink -g ${cmdName}`, {\n stdio: \"ignore\",\n cwd: projectRoot,\n });\n } catch {\n // 忽略找不到命令的错误\n }\n }\n}\n\n/** 执行 npm link 将当前包注册到全局 */\nexport function npmLink(projectRoot: string): void {\n execSync(\"npm link --force\", { stdio: \"ignore\", cwd: projectRoot });\n}\n\n/**\n * 完整同步流程:\n * 1. 读取 package.json 中现有 bin\n * 2. 取消旧命令全局链接\n * 3. 扫描 commandsDir,收集新命令\n * 4. 写回 package.json bin 字段\n *\n * 注意:本函数不执行 npm link,由调用方在适当时机调用 `npmLink()`。\n */\nexport function syncCommands(options: SyncOptions): SyncResult {\n const {\n projectRoot,\n srcCommandsDir = path.join(projectRoot, \"src\", \"commands\"),\n commandsDir = path.join(projectRoot, \"dist\", \"commands\"),\n packageJsonPath = path.join(projectRoot, \"package.json\"),\n } = options;\n\n const raw = fs.readFileSync(packageJsonPath, \"utf8\");\n const packageJson = JSON.parse(raw) as {\n bin?: Record<string, string>;\n [key: string]: unknown;\n };\n\n const oldBin: Record<string, string> = packageJson.bin ?? {};\n const previous = Object.keys(oldBin);\n\n unlinkOldCommands(oldBin, projectRoot);\n\n const binMap = collectCommands(srcCommandsDir, commandsDir, projectRoot);\n const added = Object.keys(binMap);\n\n packageJson.bin = binMap;\n fs.writeFileSync(\n packageJsonPath,\n JSON.stringify(packageJson, null, 2) + \"\\n\",\n \"utf8\"\n );\n\n return { previous, added, binMap };\n}\n\n// ──────────────────────────────────────────────\n// 通用工具函数\n// ──────────────────────────────────────────────\n\nexport { getJsMeta } from \"./utils/getJsMeta.js\";\nexport { exec, execDetailed, ExecError } from \"./utils/exec.js\";\nexport type { ExecOptions, ExecResult, ExecErrorDetails } from \"./utils/exec.js\";\nexport { sleep } from \"./utils/sleep.js\";\nexport { escapeChars } from \"./utils/escapeChars.js\";\nexport { promiseForEach } from \"./utils/promise/promiseForEach.js\";\nexport { initCmdLogger } from \"./utils/cmdLogger.js\";\nexport type { LogEntry } from \"./utils/cmdLogger.js\";\n\n// ──────────────────────────────────────────────\n// 命令模板\n// ──────────────────────────────────────────────\n\nexport { COMMAND_TEMPLATE } from \"./template.js\";\n"]}
|