@longzai-intelligence-issues/cli 0.0.1 → 0.0.2

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.
@@ -18,11 +18,11 @@ import * as z from 'zod';
18
18
  import { fail, info, loadConfigOrExit, ok, repoRoot, violation } from './context.utils.js';
19
19
 
20
20
  /**
21
- * 门禁与观测命令组:lint / numbering / docs / health / triage
21
+ * 注册表 lint 门禁命令(12 核心规则 + 棘轮基线三态)
22
22
  *
23
- * 判据口径:exit 0 / exit 1 违规(报文逐条输出后退出)。
23
+ * @param program - CLI 程序实例
24
24
  */
25
- export function registerGateCommands(program: Command): void {
25
+ function registerLintCommand(program: Command): void {
26
26
  defineCommand(program, {
27
27
  name: 'lint',
28
28
  description: '注册表 lint(12 核心规则 + 棘轮基线三态)',
@@ -31,33 +31,51 @@ export function registerGateCommands(program: Command): void {
31
31
  { flags: '--strict-legacy', description: '旧格式状态行一并计违规' },
32
32
  { flags: '--write-baseline', description: '再生棘轮基线(须配 --issue)' },
33
33
  { flags: '--issue <issue>', description: '收编指针(write-baseline 必填)' },
34
+ { flags: '--format <format>', description: 'text | json(json 为机器可读违规清单)' },
34
35
  { flags: '--config <path>', description: '显式配置路径' },
35
36
  ],
36
- schema: z
37
- .object({
38
- dir: z.union([z.string(), z.array(z.string())]).optional(),
39
- strictLegacy: z.boolean().optional(),
40
- writeBaseline: z.boolean().optional(),
41
- issue: z.string().optional(),
42
- config: z.string().optional(),
43
- })
44
- .passthrough(),
37
+ schema: z.looseObject({
38
+ dir: z.union([z.string(), z.array(z.string())]).optional(),
39
+ strictLegacy: z.boolean().optional(),
40
+ writeBaseline: z.boolean().optional(),
41
+ issue: z.string().optional(),
42
+ format: z.enum(['text', 'json']).optional(),
43
+ config: z.string().optional(),
44
+ }),
45
45
  action: async (opts) => {
46
+ /**
47
+ * 仓库根
48
+ */
46
49
  const root = repoRoot();
50
+
51
+ /**
52
+ * 解析后配置与命中路径
53
+ */
47
54
  const { config, configFilePath } = await loadConfigOrExit(opts.config);
48
55
 
49
56
  /**
50
57
  * 限定目录归一
51
58
  */
52
- const dirRels = opts.dir === undefined ? undefined : Array.isArray(opts.dir) ? opts.dir : [opts.dir];
59
+ const dirRels =
60
+ opts.dir === undefined ? undefined : Array.isArray(opts.dir) ? opts.dir : [opts.dir];
53
61
 
54
62
  /**
55
63
  * strictLegacy 覆盖(命令级旗标优先)
56
64
  */
57
- const effective = opts.strictLegacy === true ? { ...config, lint: { ...config.lint, strictLegacy: true } } : config;
65
+ const effective =
66
+ opts.strictLegacy === true
67
+ ? { ...config, lint: { ...config.lint, strictLegacy: true } }
68
+ : config;
58
69
 
59
70
  if (opts.writeBaseline === true) {
60
- const regen = runWriteLintBaseline({ root, config: effective, issuePointer: opts.issue ?? '' });
71
+ /**
72
+ * 棘轮基线再生结果
73
+ */
74
+ const regen = runWriteLintBaseline({
75
+ root,
76
+ config: effective,
77
+ issuePointer: opts.issue ?? '',
78
+ });
61
79
 
62
80
  if (!regen.ok) {
63
81
  fail(regen.error);
@@ -68,36 +86,73 @@ export function registerGateCommands(program: Command): void {
68
86
  return;
69
87
  }
70
88
 
89
+ /**
90
+ * lint 判定结果(active + stale 违规与基线计数)
91
+ */
71
92
  const result = runLint({ root, config: effective, dirRels });
72
93
 
73
- for (const v of result.violations) {
74
- violation(`${v.file}:${v.line} [${v.rule}] ${v.message}`);
94
+ if (opts.format === 'json') {
95
+ process.stdout.write(
96
+ `${JSON.stringify(
97
+ {
98
+ command: 'issues:lint',
99
+ ok: result.violations.length === 0,
100
+ violationCount: result.violations.length,
101
+ suppressed: result.suppressed,
102
+ baselineCount: result.baselineCount,
103
+ violations: result.violations,
104
+ },
105
+ null,
106
+ 2,
107
+ )}\n`,
108
+ );
109
+ } else {
110
+ for (const v of result.violations) {
111
+ violation(`${v.file}:${v.line} [${v.rule}] ${v.message}`);
112
+ }
75
113
  }
76
114
 
77
115
  if (result.violations.length > 0) {
78
- process.stderr.write(`错误: issues:lint ${result.violations.length} 项违规|存量豁免 ${result.suppressed}|基线 ${result.baselineCount} 条\n`);
116
+ if (opts.format !== 'json') {
117
+ process.stderr.write(
118
+ `错误: issues:lint ${result.violations.length} 项违规|存量豁免 ${result.suppressed}|基线 ${result.baselineCount} 条\n`,
119
+ );
120
+ }
121
+
79
122
  process.exit(1);
80
123
  }
81
124
 
82
- ok(`issues:lint 通过(0 新增违规|存量豁免 ${result.suppressed}|基线 ${result.baselineCount} 条)`);
125
+ if (opts.format !== 'json') {
126
+ ok(
127
+ `issues:lint 通过(0 新增违规|存量豁免 ${result.suppressed}|基线 ${result.baselineCount} 条)`,
128
+ );
129
+ }
83
130
  },
84
131
  });
132
+ }
85
133
 
134
+ /**
135
+ * 编号门禁命令组(父命令 numbering + next / check / audit 三子命令)
136
+ *
137
+ * @param program - CLI 程序实例
138
+ */
139
+ function registerNumberingCommands(program: Command): void {
86
140
  /**
87
- * numbering 三子命令
141
+ * numbering 三子命令共用 schema
88
142
  */
89
- const numberingSchema = z
90
- .object({
91
- scope: z.string().optional(),
92
- dir: z.string().optional(),
93
- config: z.string().optional(),
94
- })
95
- .passthrough();
143
+ const numberingSchema = z.looseObject({
144
+ scope: z.string().optional(),
145
+ dir: z.string().optional(),
146
+ format: z.enum(['text', 'json']).optional(),
147
+ config: z.string().optional(),
148
+ });
96
149
 
97
150
  /**
98
151
  * numbering 父命令(子命令 next/check/audit)
99
152
  */
100
- const numbering = program.command('numbering').description('编号门禁(next 取号 / check 判定 / audit 覆盖面审计)');
153
+ const numbering = program
154
+ .command('numbering')
155
+ .description('编号门禁(next 取号 / check 判定 / audit 覆盖面审计)');
101
156
 
102
157
  defineCommand(numbering, {
103
158
  name: 'next',
@@ -105,19 +160,37 @@ export function registerGateCommands(program: Command): void {
105
160
  options: [
106
161
  { flags: '--scope <scope>', description: '目标 scope key' },
107
162
  { flags: '--dir <dir>', description: '目标注册表目录' },
163
+ { flags: '--format <format>', description: 'text | json(json 为机器可读取号结果)' },
108
164
  { flags: '--config <path>', description: '显式配置路径' },
109
165
  ],
110
166
  schema: numberingSchema,
111
167
  action: async (opts) => {
168
+ /**
169
+ * 仓库根
170
+ */
112
171
  const root = repoRoot();
172
+
173
+ /**
174
+ * 解析后配置
175
+ */
113
176
  const { config } = await loadConfigOrExit(opts.config);
114
177
 
178
+ /**
179
+ * 取号结果(下一可用编号)
180
+ */
115
181
  const result = runNumberingNext({ root, config, scopeKey: opts.scope, dirRel: opts.dir });
116
182
 
117
183
  if (!result.ok) {
118
184
  fail(result.error);
119
185
  }
120
186
 
187
+ if (opts.format === 'json') {
188
+ process.stdout.write(
189
+ `${JSON.stringify({ command: 'numbering:next', ok: true, target: result.target, number: result.number }, null, 2)}\n`,
190
+ );
191
+ return;
192
+ }
193
+
121
194
  info(`${result.target} 下一可用编号:${result.number}(已跳过保留号与已占用号)`);
122
195
  },
123
196
  });
@@ -127,55 +200,130 @@ export function registerGateCommands(program: Command): void {
127
200
  description: '编号门禁判定(缺省全量;--dir 目录增量)',
128
201
  options: [
129
202
  { flags: '--dir <dir>', description: '目录增量档' },
203
+ { flags: '--format <format>', description: 'text | json(json 为机器可读违例清单)' },
130
204
  { flags: '--config <path>', description: '显式配置路径' },
131
205
  ],
132
206
  schema: numberingSchema,
133
207
  action: async (opts) => {
208
+ /**
209
+ * 仓库根
210
+ */
134
211
  const root = repoRoot();
212
+
213
+ /**
214
+ * 解析后配置
215
+ */
135
216
  const { config } = await loadConfigOrExit(opts.config);
136
217
 
218
+ /**
219
+ * 编号门禁判定结果
220
+ */
137
221
  const result = runNumberingCheck({ root, config, dirRel: opts.dir });
138
222
 
139
- for (const f of result.findings) {
140
- violation(`[${f.kind}] ${f.message}`);
223
+ if (opts.format === 'json') {
224
+ process.stdout.write(
225
+ `${JSON.stringify(
226
+ {
227
+ command: 'numbering:check',
228
+ ok: result.findings.length === 0,
229
+ scopeCount: result.scopeCount,
230
+ scannedFiles: result.scannedFiles,
231
+ findings: result.findings,
232
+ },
233
+ null,
234
+ 2,
235
+ )}\n`,
236
+ );
237
+ } else {
238
+ for (const f of result.findings) {
239
+ violation(`[${f.kind}] ${f.message}`);
240
+ }
141
241
  }
142
242
 
143
243
  if (result.findings.length > 0) {
144
- process.stderr.write(`错误: numbering:check ${result.findings.length} 项违规(scope ${result.scopeCount} 个 / 扫描面 ${result.scannedFiles} 文件)\n`);
244
+ if (opts.format !== 'json') {
245
+ process.stderr.write(
246
+ `错误: numbering:check ${result.findings.length} 项违规(scope ${result.scopeCount} 个 / 扫描面 ${result.scannedFiles} 文件)\n`,
247
+ );
248
+ }
249
+
145
250
  process.exit(1);
146
251
  }
147
252
 
148
- ok(`numbering:check 通过(scope ${result.scopeCount} / 扫描面 ${result.scannedFiles} 文件 / 零违规)`);
253
+ if (opts.format !== 'json') {
254
+ ok(
255
+ `numbering:check 通过(scope ${result.scopeCount} 个 / 扫描面 ${result.scannedFiles} 文件 / 零违规)`,
256
+ );
257
+ }
149
258
  },
150
259
  });
151
260
 
152
261
  defineCommand(numbering, {
153
262
  name: 'audit',
154
263
  description: '覆盖面审计(未登记编号区 / pending 失效)',
155
- options: [{ flags: '--config <path>', description: '显式配置路径' }],
264
+ options: [
265
+ { flags: '--format <format>', description: 'text | json(json 为机器可读发现清单)' },
266
+ { flags: '--config <path>', description: '显式配置路径' },
267
+ ],
156
268
  schema: numberingSchema,
157
269
  action: async (opts) => {
270
+ /**
271
+ * 仓库根
272
+ */
158
273
  const root = repoRoot();
274
+
275
+ /**
276
+ * 解析后配置
277
+ */
159
278
  const { config } = await loadConfigOrExit(opts.config);
160
279
 
280
+ /**
281
+ * 覆盖面审计结果(未登记编号区 / pending 失效)
282
+ */
161
283
  const result = runNumberingAudit({ root, config });
162
284
 
163
- for (const f of result.findings) {
164
- violation(`[${f.kind}] ${f.message}`);
285
+ if (opts.format === 'json') {
286
+ process.stdout.write(
287
+ `${JSON.stringify(
288
+ {
289
+ command: 'numbering:audit',
290
+ ok: result.findings.length === 0,
291
+ scannedFiles: result.scannedFiles,
292
+ findings: result.findings,
293
+ },
294
+ null,
295
+ 2,
296
+ )}\n`,
297
+ );
298
+ } else {
299
+ for (const f of result.findings) {
300
+ violation(`[${f.kind}] ${f.message}`);
301
+ }
165
302
  }
166
303
 
167
304
  if (result.findings.length > 0) {
168
- process.stderr.write(`错误: numbering:audit ${result.findings.length} 项违规(扫描面 ${result.scannedFiles} 文件)\n`);
305
+ if (opts.format !== 'json') {
306
+ process.stderr.write(
307
+ `错误: numbering:audit ${result.findings.length} 项违规(扫描面 ${result.scannedFiles} 文件)\n`,
308
+ );
309
+ }
310
+
169
311
  process.exit(1);
170
312
  }
171
313
 
172
- ok(`numbering:audit 通过(扫描面 ${result.scannedFiles} 文件 / 零未登记编号区)`);
314
+ if (opts.format !== 'json') {
315
+ ok(`numbering:audit 通过(扫描面 ${result.scannedFiles} 文件 / 零未登记编号区)`);
316
+ }
173
317
  },
174
318
  });
319
+ }
175
320
 
176
- /**
177
- * docs 两子命令
178
- */
321
+ /**
322
+ * 文档 issue 引用检查命令(父命令 docs + 子命令 check)
323
+ *
324
+ * @param program - CLI 程序实例
325
+ */
326
+ function registerDocsCommand(program: Command): void {
179
327
  /**
180
328
  * docs 父命令(子命令 check / fix)
181
329
  */
@@ -189,13 +337,27 @@ export function registerGateCommands(program: Command): void {
189
337
  { flags: '--issue <issue>', description: '收编指针' },
190
338
  { flags: '--config <path>', description: '显式配置路径' },
191
339
  ],
192
- schema: z.object({ writeBaseline: z.boolean().optional(), issue: z.string().optional(), config: z.string().optional() }).passthrough(),
340
+ schema: z.looseObject({
341
+ writeBaseline: z.boolean().optional(),
342
+ issue: z.string().optional(),
343
+ config: z.string().optional(),
344
+ }),
193
345
  action: async (opts) => {
346
+ /**
347
+ * 仓库根
348
+ */
194
349
  const root = repoRoot();
350
+
351
+ /**
352
+ * 解析后配置与命中路径
353
+ */
195
354
  const { config, configFilePath } = await loadConfigOrExit(opts.config);
196
355
 
197
356
  if (opts.writeBaseline === true) {
198
357
  try {
358
+ /**
359
+ * docs 基线再生结果
360
+ */
199
361
  const regen = runWriteDocsBaseline({ root, config, issuePointer: opts.issue ?? '' });
200
362
 
201
363
  if (!regen.ok) {
@@ -211,6 +373,9 @@ export function registerGateCommands(program: Command): void {
211
373
  }
212
374
 
213
375
  try {
376
+ /**
377
+ * docs 引用检查结果
378
+ */
214
379
  const result = runDocsCheck({ root, config });
215
380
 
216
381
  for (const v of result.violations) {
@@ -218,7 +383,9 @@ export function registerGateCommands(program: Command): void {
218
383
  }
219
384
 
220
385
  if (result.violations.length > 0) {
221
- process.stderr.write(`错误: docs:check ${result.violations.length} 项违规(扫描面 ${result.scannedFiles} 文件 / 豁免 ${result.suppressed})\n`);
386
+ process.stderr.write(
387
+ `错误: docs:check ${result.violations.length} 项违规(扫描面 ${result.scannedFiles} 文件 / 豁免 ${result.suppressed})\n`,
388
+ );
222
389
  process.exit(1);
223
390
  }
224
391
 
@@ -228,32 +395,78 @@ export function registerGateCommands(program: Command): void {
228
395
  }
229
396
  },
230
397
  });
398
+ }
231
399
 
400
+ /**
401
+ * 黄灯巡检命令(状态分布 / 最老账龄 / A-B-C 分类,JSON)
402
+ *
403
+ * @param program - CLI 程序实例
404
+ */
405
+ function registerHealthCommand(program: Command): void {
232
406
  defineCommand(program, {
233
407
  name: 'health',
234
408
  description: '黄灯巡检(状态分布 / 最老账龄 / A-B-C 分类,JSON)',
235
409
  options: [{ flags: '--config <path>', description: '显式配置路径' }],
236
- schema: z.object({ config: z.string().optional() }).passthrough(),
410
+ schema: z.looseObject({ config: z.string().optional() }),
237
411
  action: async (opts) => {
412
+ /**
413
+ * 仓库根
414
+ */
238
415
  const root = repoRoot();
416
+
417
+ /**
418
+ * 解析后配置
419
+ */
239
420
  const { config } = await loadConfigOrExit(opts.config);
240
421
 
241
422
  process.stdout.write(`${JSON.stringify(runHealth({ root, config }), null, 2)}\n`);
242
423
  },
243
424
  });
425
+ }
244
426
 
427
+ /**
428
+ * 黄灯分诊台账命令(append-only 落 .lzi/issues/runs/ledger/)
429
+ *
430
+ * @param program - CLI 程序实例
431
+ */
432
+ function registerTriageCommand(program: Command): void {
245
433
  defineCommand(program, {
246
434
  name: 'triage',
247
435
  description: '黄灯分诊台账(append-only 落 .lzi/issues/runs/ledger/)',
248
436
  options: [{ flags: '--config <path>', description: '显式配置路径' }],
249
- schema: z.object({ config: z.string().optional() }).passthrough(),
437
+ schema: z.looseObject({ config: z.string().optional() }),
250
438
  action: async (opts) => {
439
+ /**
440
+ * 仓库根
441
+ */
251
442
  const root = repoRoot();
443
+
444
+ /**
445
+ * 解析后配置
446
+ */
252
447
  const { config } = await loadConfigOrExit(opts.config);
253
448
 
449
+ /**
450
+ * 分诊台账落盘结果(台账文件路径与黄灯条目集)
451
+ */
254
452
  const { ledgerPath, ledger } = runTriage({ root, config });
255
453
 
256
454
  ok(`issues:triage 台账已落盘: ${ledgerPath}(黄灯 ${ledger.items.length} 项)`);
257
455
  },
258
456
  });
259
457
  }
458
+
459
+ /**
460
+ * 门禁与观测命令组:lint / numbering / docs / health / triage
461
+ *
462
+ * 判据口径:exit 0 过 / exit 1 违规(报文逐条输出后退出)。
463
+ *
464
+ * @param program - CLI 程序实例
465
+ */
466
+ export function registerGateCommands(program: Command): void {
467
+ registerLintCommand(program);
468
+ registerNumberingCommands(program);
469
+ registerDocsCommand(program);
470
+ registerHealthCommand(program);
471
+ registerTriageCommand(program);
472
+ }
@@ -11,6 +11,7 @@ import type { Command } from '@longzai-intelligence-cli/core';
11
11
  import { registerCloseCommands } from './close.commands.js';
12
12
  import { registerGateCommands } from './gate.commands.js';
13
13
  import { registerIssueCommands } from './issue.commands.js';
14
+ import { registerMigrateCommands } from './migrate.commands.js';
14
15
  import { registerScaffoldCommands } from './scaffold.commands.js';
15
16
 
16
17
  /**
@@ -26,4 +27,6 @@ export function registerCommands(program: Command): void {
26
27
  registerGateCommands(program);
27
28
 
28
29
  registerCloseCommands(program);
30
+
31
+ registerMigrateCommands(program);
29
32
  }