@ultime/cli 0.0.0-stage → 0.1.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/CHANGELOG.md ADDED
@@ -0,0 +1,6 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 - 2026-11-04
4
+
5
+ - 首次发布:通用命令行设施——`parseArgs` / `runCli` / `processContext` 与相关类型。
6
+ - 零领域依赖,不内置任何命令;`ultime` 的 init / build / schema 归 `@ultime/core`。
@@ -0,0 +1,30 @@
1
+ # Contributing
2
+
3
+ > 给贡献者与 AI 的最短协作规则。命令见 [README](./README.md)。
4
+
5
+ ## 工作流
6
+
7
+ 1. 从 `main` 切 `feat/<主题>` / `fix/<主题>`。
8
+ 2. 完成后 `git rebase main` 再合并;主分支不保留中间提交。
9
+ 3. 一次提交一件事,提交信息用 `type(scope): subject`。
10
+
11
+ ## 改动规则
12
+
13
+ - CLI 只做参数 / 路径 / IO / 退出码;配置合法性与产物内容归 `@ultime/core/tools`。
14
+ - 不 import core 内部源码,只用包名 + exports。
15
+ - 不自维护字段清单;`schema` 从 core 的 studio / tools 投影。
16
+ - 新增命令或退出码:同步 README、`skills.md`、测试与版本戳。
17
+
18
+ ## 验证
19
+
20
+ ```bash
21
+ pnpm check
22
+ pnpm test
23
+ pnpm typecheck
24
+ npm pack --dry-run
25
+ ```
26
+
27
+ ## 版本
28
+
29
+ - 0.x 阶段命令面与退出码仍可能调整;破坏性变更写进 README「版本与兼容」。
30
+ - 发布顺序:studio → core → cli。
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ultime contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,74 @@
1
- # Temporary Holding Version
1
+ # cli
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ **通用命令行设施**:参数解析、命令注册、上下文与退出码。零领域依赖,不内置任何 `ultime` 命令。
4
+
5
+ [![license](https://img.shields.io/badge/license-MIT-black)](./LICENSE)
6
+ [![node](https://img.shields.io/badge/node-%3E%3D22.18-black)](./package.json)
7
+
8
+ ```bash
9
+ pnpm add @ultime/cli
10
+ ```
11
+
12
+ 需要 Node >= 22.18;包发的是编译后的 ESM(`dist/`),类型来自 `src/*.ts`。
13
+
14
+ ## 用它做什么
15
+
16
+ 包作者只写自己的命令,参数、help、未知命令与退出码交给本包:
17
+
18
+ ```ts
19
+ import { runCli, processContext } from '@ultime/cli'
20
+
21
+ const spec = {
22
+ name: 'my-cli',
23
+ version: '0.1.0',
24
+ usage: 'my-cli <command>',
25
+ commands: {
26
+ build: {
27
+ help: 'my-cli build [--check]',
28
+ run: (args, ctx) => {
29
+ ctx.out(`check=${Boolean(args.flags.check)}`)
30
+ return 0
31
+ },
32
+ },
33
+ },
34
+ }
35
+
36
+ process.exitCode = runCli(spec, process.argv.slice(2), processContext())
37
+ ```
38
+
39
+ ## 出口
40
+
41
+ | 入口 | 提供 |
42
+ |---|---|
43
+ | `@ultime/cli` | `parseArgs` / `runCli` / `processContext` / `CliContext` / `CommandSpec` / `CliSpec` |
44
+
45
+ `parseArgs` 只认 `command`、`--flag`、`--key value` / `--key=value`;`-h/--help` 与 `-v/--version` 是保留 flag。
46
+
47
+ ## 边界
48
+
49
+ - **零领域依赖**:不 import core / kit / viewer,也不知道 token、组件或设计稿。
50
+ - **不引第三方解析 / 着色库**:解析用现成的零依赖实现,着色直接用 ANSI;Node 内置的 `util.parseArgs` / `util.styleText` 是后续替换首选,等出现嵌套子命令或补全需求再评估 `commander` 一类工具。
51
+ - **不复用于 skills CLI**:skills 通过复制安装(不带 `node_modules`),且支持 Node 20;本包是 Node 22.18 的内部设施,两者只共享契约与夹具,不共享运行时。
52
+ - **不内置命令**:`ultime init/build/schema` 属于 `@ultime/core`;kit / viewer 将来各发自己的 bin。
53
+ - **不做 IO 策略**:读盘写盘、配置路径由命令自己决定;本包只提供 `cwd` 与 `out/err`。
54
+
55
+ ## 目录
56
+
57
+ ```text
58
+ src/
59
+ args.ts 参数解析(纯函数)
60
+ context.ts 命令上下文(cwd / out / err)
61
+ run.ts 命令注册、help、未知命令与退出码
62
+ index.ts 公共出口
63
+ tests/ 参数与运行时分发
64
+ ```
65
+
66
+ ## 版本与兼容
67
+
68
+ - 当前 `0.1.x`;0.x 阶段 API 仍可能调整。
69
+ - 破坏性变更写进 [CHANGELOG](./CHANGELOG.md),并在本节目说明迁移方式。
70
+ - 发布顺序:本包是基础设施,先于 studio / core。
71
+
72
+ ## 许可
73
+
74
+ MIT,见 [LICENSE](./LICENSE)。
package/SECURITY.md ADDED
@@ -0,0 +1,14 @@
1
+ # Security Policy
2
+
3
+ > 漏洞与凭据泄露的报告方式;不在这里披露细节。
4
+
5
+ ## 报告
6
+
7
+ - 不要在公开 Issue 中粘贴密钥、客户数据或可利用细节。
8
+ - 通过仓库的私有安全通道联系维护者;没有通道时先开一个不含敏感信息的 Issue,说明「需要私下同步」。
9
+
10
+ ## 范围
11
+
12
+ - CLI 读取项目配置与 seed,写产物文件;不执行配置里的任意命令,也不发起网络请求。
13
+ - 主要风险是路径穿越、覆盖非预期文件、把凭据写进配置或产物。
14
+ - 发现泄露先轮换凭据,再修代码;修复进入 `fix/<主题>` 分支。
package/dist/index.js ADDED
@@ -0,0 +1,80 @@
1
+ // src/args.ts
2
+ function parseArgs(argv) {
3
+ const flags = {};
4
+ const positional = [];
5
+ let command = "";
6
+ for (let i = 0; i < argv.length; i += 1) {
7
+ const token = argv[i];
8
+ if (token === "-h" || token === "--help") {
9
+ flags.help = true;
10
+ continue;
11
+ }
12
+ if (token === "-v" || token === "--version") {
13
+ flags.version = true;
14
+ continue;
15
+ }
16
+ if (token.startsWith("--")) {
17
+ const [key, inline] = token.slice(2).split("=", 2);
18
+ if (inline != null) {
19
+ flags[key] = inline;
20
+ continue;
21
+ }
22
+ const next = argv[i + 1];
23
+ if (next && !next.startsWith("-")) {
24
+ flags[key] = next;
25
+ i += 1;
26
+ } else flags[key] = true;
27
+ continue;
28
+ }
29
+ if (!command) command = token;
30
+ else positional.push(token);
31
+ }
32
+ return {
33
+ command,
34
+ flags,
35
+ positional
36
+ };
37
+ }
38
+ // src/context.ts
39
+ function processContext() {
40
+ return {
41
+ cwd: process.cwd(),
42
+ out: (line) => console.log(line),
43
+ err: (line) => console.error(line)
44
+ };
45
+ }
46
+ // src/run.ts
47
+ /**
48
+ * run.ts — 命令注册与退出码
49
+ * ---------------------------------------------------------------------------
50
+ * 通用设施:只负责版本 / 帮助 / 未知命令 / 异常转退出码;
51
+ * 具体命令由调用方注册,cli 包不出现任何领域命令。
52
+ */
53
+ function runCli(spec, argv, ctx = processContext()) {
54
+ const args = parseArgs(argv);
55
+ if (args.flags.version) {
56
+ ctx.out(spec.version);
57
+ return 0;
58
+ }
59
+ if (args.flags.help) {
60
+ ctx.out(args.command && spec.commands[args.command]?.help ? spec.commands[args.command].help : spec.usage);
61
+ return 0;
62
+ }
63
+ if (!args.command) {
64
+ ctx.out(spec.usage);
65
+ return 1;
66
+ }
67
+ const command = spec.commands[args.command];
68
+ if (!command) {
69
+ ctx.err(`✗ 未知命令:${args.command}\n`);
70
+ ctx.out(spec.usage);
71
+ return 1;
72
+ }
73
+ try {
74
+ return command.run(args, ctx);
75
+ } catch (error) {
76
+ ctx.err(`✗ ${error.message}`);
77
+ return 1;
78
+ }
79
+ }
80
+ export { parseArgs, processContext, runCli };
package/package.json CHANGED
@@ -1,6 +1,57 @@
1
1
  {
2
2
  "name": "@ultime/cli",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Ultime · cli:通用命令行设施(参数 / 命令注册 / 上下文 / 退出码),零领域依赖",
5
+ "keywords": [
6
+ "cli",
7
+ "codegen",
8
+ "css-variables",
9
+ "design-system",
10
+ "design-tokens",
11
+ "dtcg"
12
+ ],
13
+ "homepage": "https://github.com/ultime-space/cli#readme",
14
+ "bugs": {
15
+ "url": "https://github.com/ultime-space/cli/issues"
16
+ },
17
+ "license": "MIT",
18
+ "author": "Ultime contributors",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/ultime-space/cli.git"
22
+ },
23
+ "files": [
24
+ "dist",
25
+ "src",
26
+ "README.md",
27
+ "CHANGELOG.md",
28
+ "CONTRIBUTING.md",
29
+ "SECURITY.md"
30
+ ],
31
+ "type": "module",
32
+ "exports": {
33
+ ".": {
34
+ "types": "./src/index.ts",
35
+ "import": "./dist/index.js"
36
+ }
37
+ },
38
+ "publishConfig": {
39
+ "access": "public"
40
+ },
41
+ "scripts": {
42
+ "build": "vp build",
43
+ "check": "vp check",
44
+ "test": "vp test",
45
+ "typecheck": "tsc -p tsconfig.json --noEmit",
46
+ "prepublishOnly": "pnpm check && pnpm test && pnpm build"
47
+ },
48
+ "devDependencies": {
49
+ "@types/node": "^22.20.5",
50
+ "tsx": "^4.23.15",
51
+ "typescript": "^5.9.3",
52
+ "vite-plus": "1.4.0"
53
+ },
54
+ "engines": {
55
+ "node": ">=22.18"
56
+ }
57
+ }
package/src/args.ts ADDED
@@ -0,0 +1,49 @@
1
+ /**
2
+ * args.ts — 命令行参数解析(纯函数)
3
+ * ---------------------------------------------------------------------------
4
+ * 只认三种形态:`command`、`--flag`、`--key value` / `--key=value`;
5
+ * 不引第三方解析库 —— CLI 必须零依赖可跑。
6
+ */
7
+
8
+ export interface ParsedArgs {
9
+ command: string
10
+ flags: Record<string, string | true>
11
+ positional: string[]
12
+ }
13
+
14
+ export function parseArgs(argv: string[]): ParsedArgs {
15
+ const flags: Record<string, string | true> = {}
16
+ const positional: string[] = []
17
+ let command = ''
18
+
19
+ for (let i = 0; i < argv.length; i += 1) {
20
+ const token = argv[i]
21
+ if (token === '-h' || token === '--help') {
22
+ flags.help = true
23
+ continue
24
+ }
25
+ if (token === '-v' || token === '--version') {
26
+ flags.version = true
27
+ continue
28
+ }
29
+ if (token.startsWith('--')) {
30
+ const [key, inline] = token.slice(2).split('=', 2)
31
+ if (inline != null) {
32
+ flags[key] = inline
33
+ continue
34
+ }
35
+ const next = argv[i + 1]
36
+ if (next && !next.startsWith('-')) {
37
+ flags[key] = next
38
+ i += 1
39
+ } else {
40
+ flags[key] = true
41
+ }
42
+ continue
43
+ }
44
+ if (!command) command = token
45
+ else positional.push(token)
46
+ }
47
+
48
+ return { command, flags, positional }
49
+ }
package/src/context.ts ADDED
@@ -0,0 +1,15 @@
1
+ /** 命令上下文:把「往哪写、怎么写」抽出来,测试里换成内存收集器即可,不用起子进程。 */
2
+ export interface CliContext {
3
+ /** 工作目录:配置与产物路径都相对它解析 */
4
+ cwd: string
5
+ out: (line: string) => void
6
+ err: (line: string) => void
7
+ }
8
+
9
+ export function processContext(): CliContext {
10
+ return {
11
+ cwd: process.cwd(),
12
+ out: (line) => console.log(line),
13
+ err: (line) => console.error(line),
14
+ }
15
+ }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export { parseArgs, type ParsedArgs } from './args.ts'
2
+ export { processContext, type CliContext } from './context.ts'
3
+ export { runCli, type CommandSpec, type CliSpec } from './run.ts'
package/src/run.ts ADDED
@@ -0,0 +1,54 @@
1
+ /**
2
+ * run.ts — 命令注册与退出码
3
+ * ---------------------------------------------------------------------------
4
+ * 通用设施:只负责版本 / 帮助 / 未知命令 / 异常转退出码;
5
+ * 具体命令由调用方注册,cli 包不出现任何领域命令。
6
+ */
7
+
8
+ import { parseArgs, type ParsedArgs } from './args.ts'
9
+ import { processContext, type CliContext } from './context.ts'
10
+
11
+ export interface CommandSpec {
12
+ /** 单条命令的 help;缺省时回落到全局 usage */
13
+ help?: string
14
+ run: (args: ParsedArgs, ctx: CliContext) => number
15
+ }
16
+
17
+ export interface CliSpec {
18
+ /** 用于错误与帮助文案,例如 `ultime` */
19
+ name: string
20
+ version: string
21
+ usage: string
22
+ commands: Record<string, CommandSpec>
23
+ }
24
+
25
+ export function runCli(spec: CliSpec, argv: string[], ctx: CliContext = processContext()): number {
26
+ const args = parseArgs(argv)
27
+
28
+ if (args.flags.version) {
29
+ ctx.out(spec.version)
30
+ return 0
31
+ }
32
+ if (args.flags.help) {
33
+ ctx.out(args.command && spec.commands[args.command]?.help ? spec.commands[args.command]!.help! : spec.usage)
34
+ return 0
35
+ }
36
+ if (!args.command) {
37
+ ctx.out(spec.usage)
38
+ return 1
39
+ }
40
+
41
+ const command = spec.commands[args.command]
42
+ if (!command) {
43
+ ctx.err(`✗ 未知命令:${args.command}\n`)
44
+ ctx.out(spec.usage)
45
+ return 1
46
+ }
47
+
48
+ try {
49
+ return command.run(args, ctx)
50
+ } catch (error) {
51
+ ctx.err(`✗ ${(error as Error).message}`)
52
+ return 1
53
+ }
54
+ }