@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.
@@ -0,0 +1,7 @@
1
+ $ lzi-builder
2
+ ℹ lzi-builder v0.2.19 powered by rolldown v1.2.8
3
+ ℹ @longzai-intelligence-issues/cli tsconfig: tsconfig/build.json
4
+ ℹ Cleaning 3 files
5
+ TypeScript 7.0 尚未提供稳定的 API,仍处于实验阶段。部分选项将不可用。
6
+ ℹ @longzai-intelligence-issues/cli Granting execute permission to dist/index.d.ts
7
+ ℹ @longzai-intelligence-issues/cli Granting execute permission to dist/index.js
@@ -0,0 +1,7 @@
1
+ $ oxlint && oxfmt --check
2
+ Found 0 warnings and 0 errors.
3
+ Finished in 274ms on 13 files with 186 rules using 10 threads.
4
+ Checking formatting...
5
+
6
+ All matched files use the correct format.
7
+ Finished in 121ms on 22 files using 10 threads.
@@ -0,0 +1,4 @@
1
+ $ bun run typecheck:app && bun run typecheck:node && bun run typecheck:test
2
+ $ lzi-tsgo typecheck tsconfig/app.json
3
+ $ lzi-tsgo typecheck tsconfig/node.json
4
+ $ lzi-tsgo typecheck tsconfig/test.json
package/CHANGELOG.md CHANGED
@@ -5,6 +5,27 @@
5
5
  格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),
6
6
  本项目遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
7
7
 
8
+ ## [0.0.2] - 2026-09-14
9
+
10
+ ### 新增
11
+
12
+ - migrate-legacy 命令挂载(--issue/--dir 批量、--default-priority/--default-source/--default-filing-date 回填缺省、--dry-run)
13
+ - api 库入口再出口 defineConfig 与配置类型(exports '.' 指向 dist/api.js,bin 分离;采用仓单依赖接入)
14
+ - KNOWN_FLAGS 零漂移契约测试(格式指南 §七 旗标表 ↔ CLI 注册面双向全等)
15
+
16
+ ### 变更
17
+
18
+ - normalize-header 补 --dir 目录批量与 --status 显式灯位;lint/numbering next|check|audit 补 --format json 机器可读输出
19
+ - init 落盘 README 存根的指南链接按注册表目录深度推导,指南缺失时回退机器真源提示(不落死链)
20
+ - 库入口定名 main(exports '.' 与 package.json main 指向 dist/main.js,命中文件命名规范;api 名称废弃)
21
+ - migrate-legacy CLI 挂载 --status red|green 旗标
22
+ - 全命令 schema 由 z.object().passthrough() 迁移 z.looseObject(zod v4 官方路径);create --priority 收紧 P1|P2|P3 枚举校验(非法值此前静默降级 P2)
23
+ - lint 存量欠债清偿:类型断言/非空断言清零(show 零命改 ?? fail 窄化、init package.json 解析改 packageJsonSchema safeParse 守卫);gate/close 命令注册拆分为模块级函数(超 200 行门禁,注册面与行为不变);KNOWN_FLAGS 契约测试更名 commands.known-flags.test.ts(命中测试文件-源目录匹配规范);triage TSDoc 占位描述回填与 oxfmt 格式基线对齐
24
+
25
+ ### 修复
26
+
27
+ - show 命令位置参数改 defineCommand 第二通道(schema 去除 opts 永不携带的 ref 必填),修复全调用必死于参数校验层;prompts show 子动作同根因修复(位置参数 [action] 此前恒不达)
28
+
8
29
  ## [0.0.1] - 2026-09-14
9
30
 
10
31
  ### 新增
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- #!/usr/bin/env bun
1
+
2
2
  import "./rolldown-runtime-DCIJsbUY.js";
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env bun
2
- import"./rolldown-runtime-DCIJsbUY.js";import{createCli as e,defineCommand as t}from"@longzai-intelligence-cli/core";import{clackInteraction as n,picocolorsColor as r}from"@longzai-intelligence-cli/standard";import{TEMPLATE_FIELDS as i,evidenceCloseFlow as a,freezeIssue as o,listPrompts as s,normalizeHeaderFile as c,renderCriteriaBlockExample as l,renderIssueTemplate as u,renderIssueTemplateSkeleton as d,renderRegistryReadmeStub as f,rerunCloseFlow as p,runDocsCheck as m,runHealth as h,runLint as g,runNumberingAudit as _,runNumberingCheck as v,runNumberingNext as y,runTriage as b,runWriteDocsBaseline as x,runWriteLintBaseline as S,scanRegistryDir as C,showPrompt as w,unfreezeIssue as T}from"@longzai-intelligence-issues/ledger";import*as E from"zod";import{appendScopeToConfigText as D,loadLziIssuesConfig as O,renderConfigScaffoldFile as k}from"@longzai-intelligence-issues/config";import{join as A,relative as j,resolve as M}from"node:path";import{existsSync as N,mkdirSync as P,readFileSync as F,writeFileSync as I}from"node:fs";function L(){return M(process.cwd())}async function R(e){let t=await O(e===void 0?{}:{configPath:e});return t.ok||(process.stderr.write(`错误: ${t.reason}\n`),process.exit(1)),{config:t.config,configFilePath:t.configFilePath}}function z(e){process.stdout.write(`[INFO] ${e}\n`)}function B(e){process.stdout.write(`[SUCCESS] ${e}\n`)}function V(e){process.stdout.write(`${e}\n`)}function H(e){process.stderr.write(`错误: ${e}\n`),process.exit(1)}function U(e,t){let n=null,r=-1;for(let i of e.scopes)for(let e of i.paths)(e===`.`||e===``||t===e||t.startsWith(`${e}/`))&&e.length>r&&(n=i.key,r=e.length);return n}function W(e){t(e,{name:`verify-close`,description:`issue 收口(缺省零执行证据收口;--rerun 证书化真跑)`,options:[{flags:`--issue <issue>`,description:`issue 文件路径(可重复)`},{flags:`--summary <summary>`,description:`收口概要(批量禁用)`},{flags:`--dry-run`,description:`只呈现不落盘/不执行`},{flags:`--rerun`,description:`证书化真跑(重度命令,仅人类明令)`},{flags:`--criterion <criterion>`,description:`显式判据 label|channel|cwd|command(可重复)`},{flags:`--criteria-from-issue`,description:`从各案 criteria 块取判据`},{flags:`--confirm-heavy`,description:`重度命令确认`},{flags:`--allow-full-criterion`,description:`全量判据豁免(须留痕)`},{flags:`--config <path>`,description:`显式配置路径`}],schema:E.object({issue:E.union([E.string(),E.array(E.string())]).optional(),summary:E.string().optional(),dryRun:E.boolean().optional(),rerun:E.boolean().optional(),criterion:E.union([E.string(),E.array(E.string())]).optional(),criteriaFromIssue:E.boolean().optional(),confirmHeavy:E.boolean().optional(),allowFullCriterion:E.boolean().optional(),config:E.string().optional()}).passthrough(),action:async e=>{let t=L(),{config:n}=await R(e.config),r=e.issue===void 0?[]:Array.isArray(e.issue)?e.issue:[e.issue];r.length===0&&H(`缺少 --issue <路径>(可重复指定批量)`);let i=e.criterion===void 0?[]:Array.isArray(e.criterion)?e.criterion:[e.criterion];if(e.rerun===!0){let a=p({root:t,config:n,files:r,explicitCriteria:i,criteriaFromIssue:e.criteriaFromIssue===!0,summaryOverride:r.length===1?e.summary:void 0,dryRun:e.dryRun===!0,allowFullCriterion:e.allowFullCriterion===!0,confirmHeavy:e.confirmHeavy===!0});a.gateBlocked&&(process.stdout.write(`${a.gateMessage}\n`),process.exit(2));for(let e of a.cases)V(`${e.file}: ${e.ok?`通过`:`失败`}——${e.message}`);z(`批次收口汇总:${a.succeeded}/${a.cases.length} 案翻绿`),a.succeeded<a.cases.length&&process.exit(1);return}(i.length>0||e.criteriaFromIssue===!0||e.confirmHeavy===!0||e.allowFullCriterion===!0)&&H(`证据收口(缺省模式)不接受 --criterion/--criteria-from-issue/--confirm-heavy/--allow-full-criterion(属 --rerun 真跑旗标)`);let o=a({root:t,config:n,files:r,summaryOverride:r.length===1?e.summary:void 0,dryRun:e.dryRun===!0});for(let e of o.cases)V(`${e.file}: ${e.ok?`通过`:`失败`}——${e.message}`);z(`批次证据收口汇总:${o.succeeded}/${o.cases.length} 案翻绿(全程零命令执行)`),o.succeeded<o.cases.length&&process.exit(1)}}),t(e,{name:`freeze`,description:`冻结 issue(--reason 须含「用户裁定」)`,options:[{flags:`--issue <issue>`,description:`issue 文件路径`},{flags:`--reason <reason>`,description:`裁定理由(须含「用户裁定」)`},{flags:`--config <path>`,description:`显式配置路径`}],schema:E.object({issue:E.string().optional(),reason:E.string().optional(),config:E.string().optional()}).passthrough(),action:async e=>{let t=L();await R(e.config),(e.issue===void 0||e.reason===void 0)&&H(`freeze 须 --issue <路径> 与 --reason <裁定理由>`);let n=o({root:t,file:e.issue,reason:e.reason});n.ok||H(n.message),B(`${e.issue}: ${n.message}`)}}),t(e,{name:`unfreeze`,description:`解冻 issue(--note 须含「用户裁定」;恢复 🔴)`,options:[{flags:`--issue <issue>`,description:`issue 文件路径`},{flags:`--note <note>`,description:`解冻注记(须含「用户裁定」)`},{flags:`--config <path>`,description:`显式配置路径`}],schema:E.object({issue:E.string().optional(),note:E.string().optional(),config:E.string().optional()}).passthrough(),action:async e=>{let t=L();await R(e.config),(e.issue===void 0||e.note===void 0)&&H(`unfreeze 须 --issue <路径> 与 --note <解冻注记>`);let n=T({root:t,file:e.issue,note:e.note});n.ok||H(n.message),B(`${e.issue}: ${n.message}`)}}),t(e,{name:`normalize-header`,description:`方言头部迁移(字段规范序 / 零丢失守卫 / 幂等)`,options:[{flags:`--issue <issue>`,description:`issue 文件路径(可重复)`},{flags:`--config <path>`,description:`显式配置路径`}],schema:E.object({issue:E.union([E.string(),E.array(E.string())]).optional(),config:E.string().optional()}).passthrough(),action:async e=>{let t=L();await R(e.config);let n=e.issue===void 0?[]:Array.isArray(e.issue)?e.issue:[e.issue];n.length===0&&H(`缺少 --issue <路径>(可重复)`);let r=0;for(let e of n){let n=c({root:t,file:e});V(`${e}: ${n.outcome}(${n.message})`),r+=+(n.outcome===`failed`)}r>0&&process.exit(1)}}),t(e,{name:`prompts`,description:`推荐提示词(list / show <id>)`,arguments:[{signature:`[action]`,description:`list | show`}],options:[{flags:`--id <id>`,description:`提示词 id(show 用)`},{flags:`--config <path>`,description:`显式配置路径`}],schema:E.object({action:E.string().optional(),id:E.string().optional(),config:E.string().optional()}).passthrough(),action:async e=>{let{config:t}=await R(e.config),n={builtin:t.prompts.builtin,extraDir:t.prompts.extraDir===void 0?void 0:`${L()}/${t.prompts.extraDir}`};if((e.action??`list`)===`show`){let t=e.id??H(`show 须 --id <提示词 id>`),r=w(t,n)??H(`提示词「${t}」未命中(lzi-issues prompts list 查看全部)`);process.stdout.write(`${r.content}\n`);return}for(let e of s(n))process.stdout.write(`${e.id}[${e.source}] ${e.description}\n`)}})}function G(e){t(e,{name:`lint`,description:`注册表 lint(12 核心规则 + 棘轮基线三态)`,options:[{flags:`--dir <dir>`,description:`限定注册表目录(可重复)`},{flags:`--strict-legacy`,description:`旧格式状态行一并计违规`},{flags:`--write-baseline`,description:`再生棘轮基线(须配 --issue)`},{flags:`--issue <issue>`,description:`收编指针(write-baseline 必填)`},{flags:`--config <path>`,description:`显式配置路径`}],schema:E.object({dir:E.union([E.string(),E.array(E.string())]).optional(),strictLegacy:E.boolean().optional(),writeBaseline:E.boolean().optional(),issue:E.string().optional(),config:E.string().optional()}).passthrough(),action:async e=>{let t=L(),{config:n,configFilePath:r}=await R(e.config),i=e.dir===void 0?void 0:Array.isArray(e.dir)?e.dir:[e.dir],a=e.strictLegacy===!0?{...n,lint:{...n.lint,strictLegacy:!0}}:n;if(e.writeBaseline===!0){let n=S({root:t,config:a,issuePointer:e.issue??``});n.ok||H(n.error),I(r,n.content),B(`基线已再生(${n.entryCount} 条,收编指针 ${e.issue})——棘轮只许缩短`);return}let o=g({root:t,config:a,dirRels:i});for(let e of o.violations)V(`${e.file}:${e.line} [${e.rule}] ${e.message}`);o.violations.length>0&&(process.stderr.write(`错误: issues:lint ${o.violations.length} 项违规|存量豁免 ${o.suppressed}|基线 ${o.baselineCount} 条\n`),process.exit(1)),B(`issues:lint 通过(0 新增违规|存量豁免 ${o.suppressed}|基线 ${o.baselineCount} 条)`)}});let n=E.object({scope:E.string().optional(),dir:E.string().optional(),config:E.string().optional()}).passthrough(),r=e.command(`numbering`).description(`编号门禁(next 取号 / check 判定 / audit 覆盖面审计)`);t(r,{name:`next`,description:`取号(--scope <key> 或 --dir <目录> 互斥;只读不写)`,options:[{flags:`--scope <scope>`,description:`目标 scope key`},{flags:`--dir <dir>`,description:`目标注册表目录`},{flags:`--config <path>`,description:`显式配置路径`}],schema:n,action:async e=>{let t=L(),{config:n}=await R(e.config),r=y({root:t,config:n,scopeKey:e.scope,dirRel:e.dir});r.ok||H(r.error),z(`${r.target} 下一可用编号:${r.number}(已跳过保留号与已占用号)`)}}),t(r,{name:`check`,description:`编号门禁判定(缺省全量;--dir 目录增量)`,options:[{flags:`--dir <dir>`,description:`目录增量档`},{flags:`--config <path>`,description:`显式配置路径`}],schema:n,action:async e=>{let t=L(),{config:n}=await R(e.config),r=v({root:t,config:n,dirRel:e.dir});for(let e of r.findings)V(`[${e.kind}] ${e.message}`);r.findings.length>0&&(process.stderr.write(`错误: numbering:check ${r.findings.length} 项违规(scope ${r.scopeCount} 个 / 扫描面 ${r.scannedFiles} 文件)\n`),process.exit(1)),B(`numbering:check 通过(scope ${r.scopeCount} 个 / 扫描面 ${r.scannedFiles} 文件 / 零违规)`)}}),t(r,{name:`audit`,description:`覆盖面审计(未登记编号区 / pending 失效)`,options:[{flags:`--config <path>`,description:`显式配置路径`}],schema:n,action:async e=>{let t=L(),{config:n}=await R(e.config),r=_({root:t,config:n});for(let e of r.findings)V(`[${e.kind}] ${e.message}`);r.findings.length>0&&(process.stderr.write(`错误: numbering:audit ${r.findings.length} 项违规(扫描面 ${r.scannedFiles} 文件)\n`),process.exit(1)),B(`numbering:audit 通过(扫描面 ${r.scannedFiles} 文件 / 零未登记编号区)`)}});let i=e.command(`docs`).description(`文档 issue 引用检查与机械修复`);t(i,{name:`check`,description:`文档 issue 引用检查(不完整 slug 前缀唯一解析 / 棘轮基线)`,options:[{flags:`--write-baseline`,description:`再生 docs 基线(须配 --issue)`},{flags:`--issue <issue>`,description:`收编指针`},{flags:`--config <path>`,description:`显式配置路径`}],schema:E.object({writeBaseline:E.boolean().optional(),issue:E.string().optional(),config:E.string().optional()}).passthrough(),action:async e=>{let t=L(),{config:n,configFilePath:r}=await R(e.config);if(e.writeBaseline===!0)try{let i=x({root:t,config:n,issuePointer:e.issue??``});i.ok||H(i.error),I(r,i.content),B(`docs 基线已再生(${i.entryCount} 条)`);return}catch(e){H(e instanceof Error?e.message:String(e))}try{let e=m({root:t,config:n});for(let t of e.violations)V(`${t.file}:${t.line} [${t.rule}] ${t.message}`);e.violations.length>0&&(process.stderr.write(`错误: docs:check ${e.violations.length} 项违规(扫描面 ${e.scannedFiles} 文件 / 豁免 ${e.suppressed})\n`),process.exit(1)),B(`docs:check 通过(扫描面 ${e.scannedFiles} 文件 / 零违规)`)}catch(e){H(e instanceof Error?e.message:String(e))}}}),t(e,{name:`health`,description:`黄灯巡检(状态分布 / 最老账龄 / A-B-C 分类,JSON)`,options:[{flags:`--config <path>`,description:`显式配置路径`}],schema:E.object({config:E.string().optional()}).passthrough(),action:async e=>{let t=L(),{config:n}=await R(e.config);process.stdout.write(`${JSON.stringify(h({root:t,config:n}),null,2)}\n`)}}),t(e,{name:`triage`,description:`黄灯分诊台账(append-only 落 .lzi/issues/runs/ledger/)`,options:[{flags:`--config <path>`,description:`显式配置路径`}],schema:E.object({config:E.string().optional()}).passthrough(),action:async e=>{let t=L(),{config:n}=await R(e.config),{ledgerPath:r,ledger:i}=b({root:t,config:n});B(`issues:triage 台账已落盘: ${r}(黄灯 ${i.items.length} 项)`)}})}function K(e){t(e,{name:`create`,description:`建档(门禁取号 → 标准模板落盘 → 编号自检)`,options:[{flags:`--title <title>`,description:`一句话标题(必填)`},{flags:`--slug <slug>`,description:`slug(缺省由标题派生)`},{flags:`--priority <priority>`,description:`P1 | P2 | P3(缺省 P2)`},{flags:`--type <type>`,description:`议题类型(缺省 task)`},{flags:`--scope <scope>`,description:`scope key(缺省按 cwd 解析)`},{flags:`--source <source>`,description:`来源说明`},{flags:`--dry-run`,description:`只呈现不落盘`},{flags:`--config <path>`,description:`显式配置路径`}],schema:E.object({title:E.string(),slug:E.string().optional(),priority:E.string().optional(),type:E.string().optional(),scope:E.string().optional(),source:E.string().optional(),dryRun:E.boolean().optional(),config:E.string().optional()}).passthrough(),action:async e=>{let t=L(),{config:n}=await R(e.config),r=j(t,process.cwd()),i=e.scope??U(n,r)??H(`cwd「${r}」未命中任何 scope 覆盖面——候选:${n.scopes.map(e=>e.key).join(`、`)}(或显式 --scope)`),a=n.scopes.find(e=>e.key===i)??H(`scope「${i}」未登记`),o=y({root:t,config:n,scopeKey:i});o.ok||H(o.error);let s=e.slug??e.title.toLowerCase().replace(/[\s_]+/g,`-`).replace(/[^a-z0-9-]/g,``).replace(/-+/g,`-`).replace(/^-|-$/g,``),c=/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(s)?s:H(`slug 须为纯 ASCII kebab(小写字母/数字/连字符,禁中文)——中文标题请显式提供 --slug <slug>`),l=u({number:o.ok?o.number:`0001`,slug:c,title:e.title,priority:e.priority===`P1`||e.priority===`P3`?e.priority:`P2`,scopeTitle:a.title,date:new Date().toISOString().slice(0,10),source:e.source??`CLI 建档`}),d=A(t,a.registryDir),f=A(d,`${o.ok?o.number:`0001`}-${c}.md`);if(e.dryRun===!0){z(`(dry-run)将落盘:${j(t,f)}`),process.stdout.write(l);return}P(d,{recursive:!0}),N(f)&&H(`文件已存在:${f}`),I(f,l),B(`已建档:${j(t,f)}`);let p=y({root:t,config:n,scopeKey:i});z(`编号自检:下一可用编号 ${p.ok?p.number:`—`}(${a.registryDir} 目录口径)`),C(d).length}}),t(e,{name:`list`,description:`列出注册表 issue(按 scope 分组)`,options:[{flags:`--scope <scope>`,description:`限定 scope`},{flags:`--status <status>`,description:`限定状态灯位(red/yellow/green/frozen/canceled)`},{flags:`--config <path>`,description:`显式配置路径`}],schema:E.object({scope:E.string().optional(),status:E.string().optional(),config:E.string().optional()}).passthrough(),action:async e=>{let t=L(),{config:n}=await R(e.config);for(let r of n.scopes){if(e.scope!==void 0&&r.key!==e.scope)continue;let n=C(A(t,r.registryDir)),i=e.status===void 0?n:n.filter(t=>t.status===e.status);process.stdout.write(`# ${r.key}(${r.title})— ${i.length} 项\n`);for(let e of i)process.stdout.write(`${e.id} ${e.status.padEnd(7)} ${e.title}\n`)}}}),t(e,{name:`show`,description:`展示单份 issue(ref 支持 编号 / 编号-slug / 不完整前缀;多义列候选)`,arguments:[{signature:`<ref>`,description:`issue 引用`}],options:[{flags:`--config <path>`,description:`显式配置路径`}],schema:E.object({ref:E.string()}).passthrough(),action:async e=>{let t=e,n=t.ref??t._?.[0]??H(`缺少 issue 引用(show <ref>)`),r=L(),{config:i}=await R(t.config),a=/^(\d{3,4})(?:-([a-z0-9-]+))?$/.exec(n);a===null&&H(`引用「${n}」不合法(编号或 编号-slug 形态)`);let o=[];for(let e of i.scopes)for(let t of C(A(r,e.registryDir)))t.id===a[1]&&(a[2]===void 0||t.slug===a[2])&&o.push({scopeKey:e.key,file:`${e.registryDir}/${t.file}`,slug:t.slug,title:t.title,record:t});if(o.length===0)for(let e of i.scopes)for(let t of C(A(r,e.registryDir)))t.id===a[1]&&a[2]!==void 0&&t.slug.startsWith(a[2])&&o.push({scopeKey:e.key,file:`${e.registryDir}/${t.file}`,slug:t.slug,title:t.title,record:t});if(o.length===0&&H(`引用「${n}」无命中`),o.length>1){process.stderr.write(`错误: 引用「${n}」命中不唯一——候选:\n`);for(let e of o)process.stderr.write(` ${e.scopeKey}/${e.record.id}-${e.slug}(${e.file})\n`);process.exit(1)}let s=o[0];z(`命中:${s.scopeKey} ${s.file}`),process.stdout.write(s.record.rawContent)}})}function q(e){t(e,{name:`init`,description:`初始化 lzi-issues:生成/追加 lzi-issues.config.ts、创建注册表目录并落格式存根`,options:[{flags:`--key <key>`,description:`scope key(缺省按 package.json 名推导)`},{flags:`--title <title>`,description:`scope 人类可读名`},{flags:`--registry-dir <dir>`,description:`注册表目录(缺省 docs/issues)`},{flags:`--paths <paths...>`,description:`覆盖路径 glob(缺省 .)`}],schema:E.object({key:E.string().optional(),title:E.string().optional(),registryDir:E.string().optional(),paths:E.array(E.string()).optional()}).passthrough(),action:async e=>{let t=L(),n=A(t,`lzi-issues.config.ts`),r=e.key??(N(A(t,`package.json`))?(()=>{try{return(JSON.parse(F(A(t,`package.json`),`utf8`)).name??`root`).replace(/^@[^/]+\//,``).replace(/[^a-z0-9-]/g,`-`)}catch{return`root`}})():`root`),i={key:r,title:e.title??`${r} 域`,paths:e.paths??[`.`],registryDir:e.registryDir??`docs/issues`};if(N(n))try{let e=D(F(n,`utf8`),i);I(n,e),z(`已追加 scope「${r}」到 ${n}`)}catch(e){H(e instanceof Error?e.message:String(e))}else I(n,k(i)),B(`已生成 ${n}`);let a=A(t,i.registryDir),o=A(a,`README.md`);P(a,{recursive:!0}),N(o)?z(`注册表 README 已存在,跳过(幂等不覆盖)`):(I(o,f(i.title,`../../docs/guides/issue-file-format.md`)),B(`已创建注册表目录 ${i.registryDir} 并落格式存根 README.md`)),B(`后续:lzi-issues template 查看标准模板;lzi-issues create --title <标题> 建档`)}}),t(e,{name:`template`,description:`输出 issue 标准格式模板(--with-criteria 附判据块示例)`,options:[{flags:`--with-criteria`,description:`附 criteria 判据块示例`},{flags:`--format <format>`,description:`text | json(json 返回字段契约)`}],schema:E.object({withCriteria:E.boolean().optional(),format:E.string().optional()}).passthrough(),action:async e=>{let t=d();if(e.format===`json`){process.stdout.write(`${JSON.stringify({template:t,fields:i},null,2)}\n`);return}process.stdout.write(t),e.withCriteria===!0&&process.stdout.write(`\n${l()}`)}})}function J(e){q(e),K(e),G(e),W(e)}async function Y(){let t=e({name:`lzi-issues`,description:`Longzai Intelligence Issues CLI(文件化注册表 · Local First)`,interaction:n,color:r});J(t),await t.parseAsync(process.argv)}await Y();export{};
2
+ import"./rolldown-runtime-DCIJsbUY.js";import{createCli as e,defineCommand as t}from"@longzai-intelligence-cli/core";import{clackInteraction as n,picocolorsColor as r}from"@longzai-intelligence-cli/standard";import{TEMPLATE_FIELDS as i,evidenceCloseFlow as a,freezeIssue as o,listPrompts as s,migrateLegacyFile as c,normalizeHeaderFile as l,renderCriteriaBlockExample as u,renderIssueTemplate as d,renderIssueTemplateSkeleton as f,renderRegistryReadmeStub as p,rerunCloseFlow as m,runDocsCheck as h,runHealth as g,runLint as _,runNumberingAudit as v,runNumberingCheck as y,runNumberingNext as b,runTriage as x,runWriteDocsBaseline as S,runWriteLintBaseline as C,scanRegistryDir as w,showPrompt as T,unfreezeIssue as E}from"@longzai-intelligence-issues/ledger";import{join as D,relative as O,resolve as k}from"node:path";import*as A from"zod";import{appendScopeToConfigText as j,loadLziIssuesConfig as M,renderConfigScaffoldFile as N}from"@longzai-intelligence-issues/config";import{existsSync as P,mkdirSync as F,readFileSync as I,writeFileSync as L}from"node:fs";function R(){return k(process.cwd())}async function z(e){let t=await M(e===void 0?{}:{configPath:e});return t.ok||(process.stderr.write(`错误: ${t.reason}\n`),process.exit(1)),{config:t.config,configFilePath:t.configFilePath}}function B(e){process.stdout.write(`[INFO] ${e}\n`)}function V(e){process.stdout.write(`[SUCCESS] ${e}\n`)}function H(e){process.stdout.write(`${e}\n`)}function U(e){process.stderr.write(`错误: ${e}\n`),process.exit(1)}function W(e,t){let n=null,r=-1;for(let i of e.scopes)for(let e of i.paths)(e===`.`||e===``||t===e||t.startsWith(`${e}/`))&&e.length>r&&(n=i.key,r=e.length);return n}function ee(e){t(e,{name:`verify-close`,description:`issue 收口(缺省零执行证据收口;--rerun 证书化真跑)`,options:[{flags:`--issue <issue>`,description:`issue 文件路径(可重复)`},{flags:`--summary <summary>`,description:`收口概要(批量禁用)`},{flags:`--dry-run`,description:`只呈现不落盘/不执行`},{flags:`--rerun`,description:`证书化真跑(重度命令,仅人类明令)`},{flags:`--criterion <criterion>`,description:`显式判据 label|channel|cwd|command(可重复)`},{flags:`--criteria-from-issue`,description:`从各案 criteria 块取判据`},{flags:`--confirm-heavy`,description:`重度命令确认`},{flags:`--allow-full-criterion`,description:`全量判据豁免(须留痕)`},{flags:`--config <path>`,description:`显式配置路径`}],schema:A.looseObject({issue:A.union([A.string(),A.array(A.string())]).optional(),summary:A.string().optional(),dryRun:A.boolean().optional(),rerun:A.boolean().optional(),criterion:A.union([A.string(),A.array(A.string())]).optional(),criteriaFromIssue:A.boolean().optional(),confirmHeavy:A.boolean().optional(),allowFullCriterion:A.boolean().optional(),config:A.string().optional()}),action:async e=>{let t=R(),{config:n}=await z(e.config),r=e.issue===void 0?[]:Array.isArray(e.issue)?e.issue:[e.issue];r.length===0&&U(`缺少 --issue <路径>(可重复指定批量)`);let i=e.criterion===void 0?[]:Array.isArray(e.criterion)?e.criterion:[e.criterion];if(e.rerun===!0){let a=m({root:t,config:n,files:r,explicitCriteria:i,criteriaFromIssue:e.criteriaFromIssue===!0,summaryOverride:r.length===1?e.summary:void 0,dryRun:e.dryRun===!0,allowFullCriterion:e.allowFullCriterion===!0,confirmHeavy:e.confirmHeavy===!0});a.gateBlocked&&(process.stdout.write(`${a.gateMessage}\n`),process.exit(2));for(let e of a.cases)H(`${e.file}: ${e.ok?`通过`:`失败`}——${e.message}`);B(`批次收口汇总:${a.succeeded}/${a.cases.length} 案翻绿`),a.succeeded<a.cases.length&&process.exit(1);return}(i.length>0||e.criteriaFromIssue===!0||e.confirmHeavy===!0||e.allowFullCriterion===!0)&&U(`证据收口(缺省模式)不接受 --criterion/--criteria-from-issue/--confirm-heavy/--allow-full-criterion(属 --rerun 真跑旗标)`);let o=a({root:t,config:n,files:r,summaryOverride:r.length===1?e.summary:void 0,dryRun:e.dryRun===!0});for(let e of o.cases)H(`${e.file}: ${e.ok?`通过`:`失败`}——${e.message}`);B(`批次证据收口汇总:${o.succeeded}/${o.cases.length} 案翻绿(全程零命令执行)`),o.succeeded<o.cases.length&&process.exit(1)}})}function G(e){t(e,{name:`freeze`,description:`冻结 issue(--reason 须含「用户裁定」)`,options:[{flags:`--issue <issue>`,description:`issue 文件路径`},{flags:`--reason <reason>`,description:`裁定理由(须含「用户裁定」)`},{flags:`--config <path>`,description:`显式配置路径`}],schema:A.looseObject({issue:A.string().optional(),reason:A.string().optional(),config:A.string().optional()}),action:async e=>{let t=R();await z(e.config),(e.issue===void 0||e.reason===void 0)&&U(`freeze 须 --issue <路径> 与 --reason <裁定理由>`);let n=o({root:t,file:e.issue,reason:e.reason});n.ok||U(n.message),V(`${e.issue}: ${n.message}`)}})}function K(e){t(e,{name:`unfreeze`,description:`解冻 issue(--note 须含「用户裁定」;恢复 🔴)`,options:[{flags:`--issue <issue>`,description:`issue 文件路径`},{flags:`--note <note>`,description:`解冻注记(须含「用户裁定」)`},{flags:`--config <path>`,description:`显式配置路径`}],schema:A.looseObject({issue:A.string().optional(),note:A.string().optional(),config:A.string().optional()}),action:async e=>{let t=R();await z(e.config),(e.issue===void 0||e.note===void 0)&&U(`unfreeze 须 --issue <路径> 与 --note <解冻注记>`);let n=E({root:t,file:e.issue,note:e.note});n.ok||U(n.message),V(`${e.issue}: ${n.message}`)}})}function q(e){t(e,{name:`normalize-header`,description:`方言头部迁移(字段规范序 / 零丢失守卫 / 幂等)`,options:[{flags:`--issue <issue>`,description:`issue 文件路径(可重复,与 --dir 互斥)`},{flags:`--dir <dir>`,description:`注册表目录批量(目录内全部编号文件,与 --issue 互斥)`},{flags:`--status <status>`,description:`无可识别状态行时的显式灯位 red|yellow|green|canceled(frozen 须经 freeze)`},{flags:`--config <path>`,description:`显式配置路径`}],schema:A.looseObject({issue:A.union([A.string(),A.array(A.string())]).optional(),dir:A.string().optional(),status:A.enum([`red`,`yellow`,`green`,`canceled`]).optional(),config:A.string().optional()}),action:async e=>{let t=R();await z(e.config),e.issue!==void 0&&e.dir!==void 0&&U(`--issue 与 --dir 互斥(单文件指定或目录批量二选一)`);let n=e.dir===void 0?e.issue===void 0?[]:Array.isArray(e.issue)?e.issue:[e.issue]:w(D(t,e.dir)).map(t=>`${e.dir}/${t.file}`);n.length===0&&U(`缺少 --issue <路径>(可重复)或 --dir <注册表目录>`);let r=0;for(let i of n){let n=l({root:t,file:i,status:e.status});H(`${i}: ${n.outcome}(${n.message})`),r+=+(n.outcome===`failed`)}r>0&&process.exit(1)}})}function J(e){t(e,{name:`prompts`,description:`推荐提示词(list / show <id>)`,arguments:[{signature:`[action]`,description:`list | show`}],options:[{flags:`--id <id>`,description:`提示词 id(show 用)`},{flags:`--config <path>`,description:`显式配置路径`}],schema:A.looseObject({id:A.string().optional(),config:A.string().optional()}),action:async(e,t)=>{let{config:n}=await z(e.config),r={builtin:n.prompts.builtin,extraDir:n.prompts.extraDir===void 0?void 0:`${R()}/${n.prompts.extraDir}`};if((t[0]??`list`)===`show`){let t=e.id??U(`show 须 --id <提示词 id>`),n=T(t,r)??U(`提示词「${t}」未命中(lzi-issues prompts list 查看全部)`);process.stdout.write(`${n.content}\n`);return}for(let e of s(r))process.stdout.write(`${e.id}[${e.source}] ${e.description}\n`)}})}function Y(e){ee(e),G(e),K(e),q(e),J(e)}function X(e){t(e,{name:`lint`,description:`注册表 lint(12 核心规则 + 棘轮基线三态)`,options:[{flags:`--dir <dir>`,description:`限定注册表目录(可重复)`},{flags:`--strict-legacy`,description:`旧格式状态行一并计违规`},{flags:`--write-baseline`,description:`再生棘轮基线(须配 --issue)`},{flags:`--issue <issue>`,description:`收编指针(write-baseline 必填)`},{flags:`--format <format>`,description:`text | json(json 为机器可读违规清单)`},{flags:`--config <path>`,description:`显式配置路径`}],schema:A.looseObject({dir:A.union([A.string(),A.array(A.string())]).optional(),strictLegacy:A.boolean().optional(),writeBaseline:A.boolean().optional(),issue:A.string().optional(),format:A.enum([`text`,`json`]).optional(),config:A.string().optional()}),action:async e=>{let t=R(),{config:n,configFilePath:r}=await z(e.config),i=e.dir===void 0?void 0:Array.isArray(e.dir)?e.dir:[e.dir],a=e.strictLegacy===!0?{...n,lint:{...n.lint,strictLegacy:!0}}:n;if(e.writeBaseline===!0){let n=C({root:t,config:a,issuePointer:e.issue??``});n.ok||U(n.error),L(r,n.content),V(`基线已再生(${n.entryCount} 条,收编指针 ${e.issue})——棘轮只许缩短`);return}let o=_({root:t,config:a,dirRels:i});if(e.format===`json`)process.stdout.write(`${JSON.stringify({command:`issues:lint`,ok:o.violations.length===0,violationCount:o.violations.length,suppressed:o.suppressed,baselineCount:o.baselineCount,violations:o.violations},null,2)}\n`);else for(let e of o.violations)H(`${e.file}:${e.line} [${e.rule}] ${e.message}`);o.violations.length>0&&(e.format!==`json`&&process.stderr.write(`错误: issues:lint ${o.violations.length} 项违规|存量豁免 ${o.suppressed}|基线 ${o.baselineCount} 条\n`),process.exit(1)),e.format!==`json`&&V(`issues:lint 通过(0 新增违规|存量豁免 ${o.suppressed}|基线 ${o.baselineCount} 条)`)}})}function Z(e){let n=A.looseObject({scope:A.string().optional(),dir:A.string().optional(),format:A.enum([`text`,`json`]).optional(),config:A.string().optional()}),r=e.command(`numbering`).description(`编号门禁(next 取号 / check 判定 / audit 覆盖面审计)`);t(r,{name:`next`,description:`取号(--scope <key> 或 --dir <目录> 互斥;只读不写)`,options:[{flags:`--scope <scope>`,description:`目标 scope key`},{flags:`--dir <dir>`,description:`目标注册表目录`},{flags:`--format <format>`,description:`text | json(json 为机器可读取号结果)`},{flags:`--config <path>`,description:`显式配置路径`}],schema:n,action:async e=>{let t=R(),{config:n}=await z(e.config),r=b({root:t,config:n,scopeKey:e.scope,dirRel:e.dir});if(r.ok||U(r.error),e.format===`json`){process.stdout.write(`${JSON.stringify({command:`numbering:next`,ok:!0,target:r.target,number:r.number},null,2)}\n`);return}B(`${r.target} 下一可用编号:${r.number}(已跳过保留号与已占用号)`)}}),t(r,{name:`check`,description:`编号门禁判定(缺省全量;--dir 目录增量)`,options:[{flags:`--dir <dir>`,description:`目录增量档`},{flags:`--format <format>`,description:`text | json(json 为机器可读违例清单)`},{flags:`--config <path>`,description:`显式配置路径`}],schema:n,action:async e=>{let t=R(),{config:n}=await z(e.config),r=y({root:t,config:n,dirRel:e.dir});if(e.format===`json`)process.stdout.write(`${JSON.stringify({command:`numbering:check`,ok:r.findings.length===0,scopeCount:r.scopeCount,scannedFiles:r.scannedFiles,findings:r.findings},null,2)}\n`);else for(let e of r.findings)H(`[${e.kind}] ${e.message}`);r.findings.length>0&&(e.format!==`json`&&process.stderr.write(`错误: numbering:check ${r.findings.length} 项违规(scope ${r.scopeCount} 个 / 扫描面 ${r.scannedFiles} 文件)\n`),process.exit(1)),e.format!==`json`&&V(`numbering:check 通过(scope ${r.scopeCount} 个 / 扫描面 ${r.scannedFiles} 文件 / 零违规)`)}}),t(r,{name:`audit`,description:`覆盖面审计(未登记编号区 / pending 失效)`,options:[{flags:`--format <format>`,description:`text | json(json 为机器可读发现清单)`},{flags:`--config <path>`,description:`显式配置路径`}],schema:n,action:async e=>{let t=R(),{config:n}=await z(e.config),r=v({root:t,config:n});if(e.format===`json`)process.stdout.write(`${JSON.stringify({command:`numbering:audit`,ok:r.findings.length===0,scannedFiles:r.scannedFiles,findings:r.findings},null,2)}\n`);else for(let e of r.findings)H(`[${e.kind}] ${e.message}`);r.findings.length>0&&(e.format!==`json`&&process.stderr.write(`错误: numbering:audit ${r.findings.length} 项违规(扫描面 ${r.scannedFiles} 文件)\n`),process.exit(1)),e.format!==`json`&&V(`numbering:audit 通过(扫描面 ${r.scannedFiles} 文件 / 零未登记编号区)`)}})}function Q(e){let n=e.command(`docs`).description(`文档 issue 引用检查与机械修复`);t(n,{name:`check`,description:`文档 issue 引用检查(不完整 slug 前缀唯一解析 / 棘轮基线)`,options:[{flags:`--write-baseline`,description:`再生 docs 基线(须配 --issue)`},{flags:`--issue <issue>`,description:`收编指针`},{flags:`--config <path>`,description:`显式配置路径`}],schema:A.looseObject({writeBaseline:A.boolean().optional(),issue:A.string().optional(),config:A.string().optional()}),action:async e=>{let t=R(),{config:n,configFilePath:r}=await z(e.config);if(e.writeBaseline===!0)try{let i=S({root:t,config:n,issuePointer:e.issue??``});i.ok||U(i.error),L(r,i.content),V(`docs 基线已再生(${i.entryCount} 条)`);return}catch(e){U(e instanceof Error?e.message:String(e))}try{let e=h({root:t,config:n});for(let t of e.violations)H(`${t.file}:${t.line} [${t.rule}] ${t.message}`);e.violations.length>0&&(process.stderr.write(`错误: docs:check ${e.violations.length} 项违规(扫描面 ${e.scannedFiles} 文件 / 豁免 ${e.suppressed})\n`),process.exit(1)),V(`docs:check 通过(扫描面 ${e.scannedFiles} 文件 / 零违规)`)}catch(e){U(e instanceof Error?e.message:String(e))}}})}function $(e){t(e,{name:`health`,description:`黄灯巡检(状态分布 / 最老账龄 / A-B-C 分类,JSON)`,options:[{flags:`--config <path>`,description:`显式配置路径`}],schema:A.looseObject({config:A.string().optional()}),action:async e=>{let t=R(),{config:n}=await z(e.config);process.stdout.write(`${JSON.stringify(g({root:t,config:n}),null,2)}\n`)}})}function te(e){t(e,{name:`triage`,description:`黄灯分诊台账(append-only 落 .lzi/issues/runs/ledger/)`,options:[{flags:`--config <path>`,description:`显式配置路径`}],schema:A.looseObject({config:A.string().optional()}),action:async e=>{let t=R(),{config:n}=await z(e.config),{ledgerPath:r,ledger:i}=x({root:t,config:n});V(`issues:triage 台账已落盘: ${r}(黄灯 ${i.items.length} 项)`)}})}function ne(e){X(e),Z(e),Q(e),$(e),te(e)}function re(e){t(e,{name:`create`,description:`建档(门禁取号 → 标准模板落盘 → 编号自检)`,options:[{flags:`--title <title>`,description:`一句话标题(必填)`},{flags:`--slug <slug>`,description:`slug(缺省由标题派生)`},{flags:`--priority <priority>`,description:`P1 | P2 | P3(缺省 P2)`},{flags:`--type <type>`,description:`议题类型(缺省 task)`},{flags:`--scope <scope>`,description:`scope key(缺省按 cwd 解析)`},{flags:`--source <source>`,description:`来源说明`},{flags:`--dry-run`,description:`只呈现不落盘`},{flags:`--config <path>`,description:`显式配置路径`}],schema:A.looseObject({title:A.string(),slug:A.string().optional(),priority:A.enum([`P1`,`P2`,`P3`]).optional(),type:A.string().optional(),scope:A.string().optional(),source:A.string().optional(),dryRun:A.boolean().optional(),config:A.string().optional()}),action:async e=>{let t=R(),{config:n}=await z(e.config),r=O(t,process.cwd()),i=e.scope??W(n,r)??U(`cwd「${r}」未命中任何 scope 覆盖面——候选:${n.scopes.map(e=>e.key).join(`、`)}(或显式 --scope)`),a=n.scopes.find(e=>e.key===i)??U(`scope「${i}」未登记`),o=b({root:t,config:n,scopeKey:i});o.ok||U(o.error);let s=e.slug??e.title.toLowerCase().replace(/[\s_]+/g,`-`).replace(/[^a-z0-9-]/g,``).replace(/-+/g,`-`).replace(/^-|-$/g,``),c=/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(s)?s:U(`slug 须为纯 ASCII kebab(小写字母/数字/连字符,禁中文)——中文标题请显式提供 --slug <slug>`),l=d({number:o.ok?o.number:`0001`,slug:c,title:e.title,priority:e.priority??`P2`,scopeTitle:a.title,date:new Date().toISOString().slice(0,10),source:e.source??`CLI 建档`}),u=D(t,a.registryDir),f=D(u,`${o.ok?o.number:`0001`}-${c}.md`);if(e.dryRun===!0){B(`(dry-run)将落盘:${O(t,f)}`),process.stdout.write(l);return}F(u,{recursive:!0}),P(f)&&U(`文件已存在:${f}`),L(f,l),V(`已建档:${O(t,f)}`);let p=b({root:t,config:n,scopeKey:i});B(`编号自检:下一可用编号 ${p.ok?p.number:`—`}(${a.registryDir} 目录口径)`),w(u).length}}),t(e,{name:`list`,description:`列出注册表 issue(按 scope 分组)`,options:[{flags:`--scope <scope>`,description:`限定 scope`},{flags:`--status <status>`,description:`限定状态灯位(red/yellow/green/frozen/canceled)`},{flags:`--config <path>`,description:`显式配置路径`}],schema:A.looseObject({scope:A.string().optional(),status:A.string().optional(),config:A.string().optional()}),action:async e=>{let t=R(),{config:n}=await z(e.config);for(let r of n.scopes){if(e.scope!==void 0&&r.key!==e.scope)continue;let n=w(D(t,r.registryDir)),i=e.status===void 0?n:n.filter(t=>t.status===e.status);process.stdout.write(`# ${r.key}(${r.title})— ${i.length} 项\n`);for(let e of i)process.stdout.write(`${e.id} ${e.status.padEnd(7)} ${e.title}\n`)}}}),t(e,{name:`show`,description:`展示单份 issue(ref 支持 编号 / 编号-slug / 不完整前缀;多义列候选)`,arguments:[{signature:`<ref>`,description:`issue 引用`}],options:[{flags:`--config <path>`,description:`显式配置路径`}],schema:A.looseObject({config:A.string().optional()}),action:async(e,t)=>{let n=t[0]??U(`缺少 issue 引用(show <ref>)`),r=R(),{config:i}=await z(e.config),a=/^(\d{3,4})(?:-([a-z0-9-]+))?$/.exec(n);a===null&&U(`引用「${n}」不合法(编号或 编号-slug 形态)`);let o=[];for(let e of i.scopes)for(let t of w(D(r,e.registryDir)))t.id===a[1]&&(a[2]===void 0||t.slug===a[2])&&o.push({scopeKey:e.key,file:`${e.registryDir}/${t.file}`,slug:t.slug,title:t.title,record:t});if(o.length===0)for(let e of i.scopes)for(let t of w(D(r,e.registryDir)))t.id===a[1]&&a[2]!==void 0&&t.slug.startsWith(a[2])&&o.push({scopeKey:e.key,file:`${e.registryDir}/${t.file}`,slug:t.slug,title:t.title,record:t});if(o.length>1){process.stderr.write(`错误: 引用「${n}」命中不唯一——候选:\n`);for(let e of o)process.stderr.write(` ${e.scopeKey}/${e.record.id}-${e.slug}(${e.file})\n`);process.exit(1)}let s=o[0]??U(`引用「${n}」无命中`);B(`命中:${s.scopeKey} ${s.file}`),process.stdout.write(s.record.rawContent)}})}function ie(e){t(e,{name:`migrate-legacy`,description:`存量注册表清偿(五方言解析→状态词规范化→处置段对齐→必填回填;零丢失守卫;幂等)`,options:[{flags:`--issue <issue>`,description:`issue 文件路径(可重复,与 --dir 互斥)`},{flags:`--dir <dir>`,description:`注册表目录批量(与 --issue 互斥)`},{flags:`--default-priority <priority>`,description:`优先级缺省回填 P1|P2|P3`},{flags:`--default-source <source>`,description:`来源缺省回填文案`},{flags:`--default-filing-date <date>`,description:`立案日期缺省回填(YYYY-MM-DD)`},{flags:`--status <status>`,description:`无状态行历史文档的显式灯位 red|green(人工定档,引擎承接机械迁移)`},{flags:`--dry-run`,description:`只呈现不落盘`},{flags:`--config <path>`,description:`显式配置路径`}],schema:A.looseObject({issue:A.union([A.string(),A.array(A.string())]).optional(),dir:A.string().optional(),defaultPriority:A.enum([`P1`,`P2`,`P3`]).optional(),defaultSource:A.string().optional(),defaultFilingDate:A.string().optional(),status:A.enum([`red`,`green`]).optional(),dryRun:A.boolean().optional(),config:A.string().optional()}),action:async e=>{let t=R(),{config:n}=await z(e.config);e.issue!==void 0&&e.dir!==void 0&&U(`--issue 与 --dir 互斥(单文件指定或目录批量二选一)`);let r=e.dir===void 0?e.issue===void 0?[]:Array.isArray(e.issue)?e.issue:[e.issue]:w(D(t,e.dir)).map(t=>`${e.dir}/${t.file}`);r.length===0&&U(`缺少 --issue <路径>(可重复)或 --dir <注册表目录>`);let i={rewritten:0,unchanged:0,skipped:0,failed:0};for(let a of r){let r=c({root:t,file:a,config:n,defaultPriority:e.defaultPriority,defaultSource:e.defaultSource,defaultFilingDate:e.defaultFilingDate,status:e.status,dryRun:e.dryRun===!0});i[r.outcome]+=1,H(`${a}: ${r.outcome}(${r.message};变换 ${r.transformations.length} 项${e.dryRun===!0&&r.outcome===`rewritten`?`,dry-run 未落盘`:``})`)}B(`migrate-legacy 汇总:rewritten ${i.rewritten}|unchanged ${i.unchanged}|skipped(交人工)${i.skipped}|failed ${i.failed}`),i.failed>0&&process.exit(1),V(`清偿批次完成(failed=0;skipped 案逐条列出待人工处置)`)}})}const ae=A.looseObject({name:A.string().optional()});function oe(e){t(e,{name:`init`,description:`初始化 lzi-issues:生成/追加 lzi-issues.config.ts、创建注册表目录并落格式存根`,options:[{flags:`--key <key>`,description:`scope key(缺省按 package.json 名推导)`},{flags:`--title <title>`,description:`scope 人类可读名`},{flags:`--registry-dir <dir>`,description:`注册表目录(缺省 docs/issues)`},{flags:`--paths <paths...>`,description:`覆盖路径 glob(缺省 .)`}],schema:A.looseObject({key:A.string().optional(),title:A.string().optional(),registryDir:A.string().optional(),paths:A.array(A.string()).optional()}),action:async e=>{let t=R(),n=D(t,`lzi-issues.config.ts`),r=e.key??(P(D(t,`package.json`))?(()=>{try{let e=ae.safeParse(JSON.parse(I(D(t,`package.json`),`utf8`)));return((e.success?e.data.name:void 0)??`root`).replace(/^@[^/]+\//,``).replace(/[^a-z0-9-]/g,`-`)}catch{return`root`}})():`root`),i={key:r,title:e.title??`${r} 域`,paths:e.paths??[`.`],registryDir:e.registryDir??`docs/issues`};if(P(n))try{let e=j(I(n,`utf8`),i);L(n,e),B(`已追加 scope「${r}」到 ${n}`)}catch(e){U(e instanceof Error?e.message:String(e))}else L(n,N(i)),V(`已生成 ${n}`);let a=D(t,i.registryDir),o=D(a,`README.md`),s=`${`../`.repeat(i.registryDir.split(`/`).length)}docs/guides/issue-file-format.md`,c=P(D(t,`docs`,`guides`,`issue-file-format.md`))?s:null;F(a,{recursive:!0}),P(o)?B(`注册表 README 已存在,跳过(幂等不覆盖)`):(L(o,p(i.title,c)),V(`已创建注册表目录 ${i.registryDir} 并落格式存根 README.md${c===null?`(本仓无指南文件,存根指向 lzi-issues template 机器真源)`:``}`)),V(`后续:lzi-issues template 查看标准模板;lzi-issues create --title <标题> 建档`)}}),t(e,{name:`template`,description:`输出 issue 标准格式模板(--with-criteria 附判据块示例)`,options:[{flags:`--with-criteria`,description:`附 criteria 判据块示例`},{flags:`--format <format>`,description:`text | json(json 返回字段契约)`}],schema:A.looseObject({withCriteria:A.boolean().optional(),format:A.string().optional()}),action:async e=>{let t=f();if(e.format===`json`){process.stdout.write(`${JSON.stringify({template:t,fields:i},null,2)}\n`);return}process.stdout.write(t),e.withCriteria===!0&&process.stdout.write(`\n${u()}`)}})}function se(e){oe(e),re(e),ne(e),Y(e),ie(e)}async function ce(){let t=e({name:`lzi-issues`,description:`Longzai Intelligence Issues CLI(文件化注册表 · Local First)`,interaction:n,color:r});se(t),await t.parseAsync(process.argv)}await ce();export{};
package/dist/main.d.ts ADDED
@@ -0,0 +1,4 @@
1
+
2
+ import "./rolldown-runtime-DCIJsbUY.js";
3
+ import { LintBaselineEntry, LziIssuesConfig, LziIssuesConfigInput, ScopeEntry, ScopeEntryInput, defineConfig, parseLziIssuesConfig } from "@longzai-intelligence-issues/config";
4
+ export { type LintBaselineEntry, type LziIssuesConfig, type LziIssuesConfigInput, type ScopeEntry, type ScopeEntryInput, defineConfig, parseLziIssuesConfig };
package/dist/main.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env bun
2
+ import"./rolldown-runtime-DCIJsbUY.js";import{defineConfig as e,parseLziIssuesConfig as t}from"@longzai-intelligence-issues/config";export{e as defineConfig,t as parseLziIssuesConfig};
@@ -1,12 +1,16 @@
1
1
  import { defineConfig } from '@longzai-intelligence-builder/esm';
2
2
 
3
3
  /**
4
- * CLI 应用构建配置:node 平台入口(bin 由 package.json 声明,shebang 注入)
4
+ * CLI 应用构建配置:node 平台双入口(bin 由 package.json 声明,shebang 注入;
5
+ * api 为采用仓库入口——import 不触发 CLI main)
5
6
  */
6
7
  export default defineConfig({
7
8
  entry: {
8
9
  index: 'src/index.ts',
10
+ main: 'src/main.ts',
9
11
  },
10
12
  platform: 'node',
11
- banner: '#!/usr/bin/env bun',
13
+ banner: {
14
+ js: '#!/usr/bin/env bun',
15
+ },
12
16
  });
package/package.json CHANGED
@@ -1,18 +1,19 @@
1
1
  {
2
2
  "name": "@longzai-intelligence-issues/cli",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "license": "UNLICENSED",
5
- "type": "module",
6
5
  "bin": {
7
6
  "lzi-issues": "./dist/index.js"
8
7
  },
9
- "main": "./dist/index.js",
10
- "types": "./dist/index.d.ts",
8
+ "type": "module",
9
+ "main": "./dist/main.js",
10
+ "types": "./dist/main.d.ts",
11
11
  "exports": {
12
12
  ".": {
13
- "types": "./dist/index.d.ts",
14
- "import": "./dist/index.js"
15
- }
13
+ "types": "./dist/main.d.ts",
14
+ "import": "./dist/main.js"
15
+ },
16
+ "./bin": "./dist/index.js"
16
17
  },
17
18
  "scripts": {
18
19
  "build": "lzi-builder",
@@ -30,17 +31,13 @@
30
31
  "clean": "lzi-dev-cli clean"
31
32
  },
32
33
  "dependencies": {
33
- "@longzai-intelligence-issues/config": "0.0.1",
34
- "@longzai-intelligence-issues/ledger": "0.0.1",
35
34
  "@longzai-intelligence-cli/core": "^0.0.8",
36
- "zod": "^4.4.3",
37
- "@longzai-intelligence-cli/standard": "^0.0.8"
35
+ "@longzai-intelligence-cli/standard": "^0.0.8",
36
+ "@longzai-intelligence-issues/config": "0.0.2",
37
+ "@longzai-intelligence-issues/ledger": "0.0.2",
38
+ "zod": "^4.4.3"
38
39
  },
39
40
  "devDependencies": {
40
- "@longzai-intelligence-builder/cli": "^0.2.16",
41
- "@longzai-intelligence-tsgo/cli": "^0.0.1",
42
- "@types/bun": "latest",
43
- "@typescript/native-preview": "beta",
44
- "oxlint": "^1.77.0"
41
+ "@types/bun": "latest"
45
42
  }
46
- }
43
+ }
@@ -0,0 +1,243 @@
1
+ import { createCli } from '@longzai-intelligence-cli/core';
2
+ import { clackInteraction, picocolorsColor } from '@longzai-intelligence-cli/standard';
3
+ import { describe, expect, test } from 'bun:test';
4
+ import { readFileSync } from 'node:fs';
5
+ import { join } from 'node:path';
6
+
7
+ import { registerCommands } from '@/commands/index.js';
8
+
9
+ /**
10
+ * 指南路径(仓库根 docs/guides/issue-file-format.md 的 §七 旗标契约)
11
+ */
12
+ const GUIDE_PATH = join(
13
+ import.meta.dir,
14
+ '..',
15
+ '..',
16
+ '..',
17
+ '..',
18
+ 'docs',
19
+ 'guides',
20
+ 'issue-file-format.md',
21
+ );
22
+
23
+ /**
24
+ * commander 命令实例的最小内省面(.name() / .commands / .options[].long)
25
+ *
26
+ * commander 类型对运行时结构公开,此处以结构守卫窄化避免类型断言放宽。
27
+ */
28
+ type IntrospectedCommand = {
29
+ name: () => string;
30
+ commands: IntrospectedCommand[];
31
+ options: Array<{ long: string | undefined }>;
32
+ };
33
+
34
+ /**
35
+ * 结构守卫:候选对象是否具备可内省的 commander 命令形态
36
+ *
37
+ * @param value - 候选对象
38
+ * @returns 是则为内省形态
39
+ */
40
+ function asIntrospected(value: unknown): value is IntrospectedCommand {
41
+ if (typeof value !== 'object' || value === null) {
42
+ return false;
43
+ }
44
+
45
+ /**
46
+ * name 方法
47
+ */
48
+ const name = Reflect.get(value, 'name');
49
+
50
+ /**
51
+ * 子命令数组
52
+ */
53
+ const commands = Reflect.get(value, 'commands');
54
+
55
+ /**
56
+ * 选项数组
57
+ */
58
+ const options = Reflect.get(value, 'options');
59
+
60
+ return typeof name === 'function' && Array.isArray(commands) && Array.isArray(options);
61
+ }
62
+
63
+ /**
64
+ * 收集命令注册的长旗标集合(剔除 commander 内建 --help)
65
+ *
66
+ * @param command - 内省形态的命令实例
67
+ * @returns 长旗标 token 集合(如 --dir)
68
+ */
69
+ function collectLongFlags(command: IntrospectedCommand): Set<string> {
70
+ /**
71
+ * 长旗标收集器
72
+ */
73
+ const flags = new Set<string>();
74
+
75
+ for (const option of command.options) {
76
+ if (typeof option.long === 'string' && option.long !== '--help') {
77
+ flags.add(option.long);
78
+ }
79
+ }
80
+
81
+ return flags;
82
+ }
83
+
84
+ /**
85
+ * 展开命令树为「命令路径 → 长旗标集合」表(仅叶子命令;父分组如 numbering/docs 不入表)
86
+ *
87
+ * @param command - 当前命令
88
+ * @param prefix - 路径前缀段
89
+ * @param sink - 收集表
90
+ * @returns 收集表(叶子命令路径全集)
91
+ */
92
+ function flattenCommandTree(
93
+ command: IntrospectedCommand,
94
+ prefix: string[],
95
+ sink: Map<string, Set<string>>,
96
+ ): Map<string, Set<string>> {
97
+ /**
98
+ * 自身路径
99
+ */
100
+ const path = prefix.join(' ');
101
+
102
+ if (path !== '' && command.commands.length === 0) {
103
+ sink.set(path, collectLongFlags(command));
104
+ }
105
+
106
+ for (const child of command.commands) {
107
+ flattenCommandTree(child, [...prefix, child.name()], sink);
108
+ }
109
+
110
+ return sink;
111
+ }
112
+
113
+ /**
114
+ * 解析指南 §七 的旗标契约表
115
+ *
116
+ * @param guide - 指南全文
117
+ * @returns 命令名 → 旗标 token 集合
118
+ */
119
+ function parseGuideFlags(guide: string): Map<string, Set<string>> {
120
+ /**
121
+ * §七 节正文(## 七、 起至 ## 八、 前)
122
+ */
123
+ const sectionMatch = /## 七、[^\n]*\n([\s\S]*?)(?=\n## 八、)/.exec(guide);
124
+
125
+ expect(sectionMatch).not.toBeNull();
126
+
127
+ /**
128
+ * 契约表
129
+ */
130
+ const table = new Map<string, Set<string>>();
131
+
132
+ for (const line of (sectionMatch?.[1] ?? '').split('\n')) {
133
+ /**
134
+ * 行形态:- `lzi-issues <命令路径>`:`--flag` `--flag` …
135
+ */
136
+ const lineMatch = /^- `lzi-issues ([a-z][a-z-]*(?: [a-z][a-z-]*)*)`:(.*)$/.exec(line.trim());
137
+
138
+ if (lineMatch === null) {
139
+ continue;
140
+ }
141
+
142
+ /**
143
+ * 命令路径段
144
+ */
145
+ const commandPath = lineMatch[1] ?? '';
146
+
147
+ /**
148
+ * 行内反引号旗标 token
149
+ */
150
+ const tokens = new Set<string>(
151
+ (lineMatch[2] ?? '').match(/`--[a-z-]+`/g)?.map((token) => token.slice(1, -1)) ?? [],
152
+ );
153
+
154
+ table.set(commandPath, tokens);
155
+ }
156
+
157
+ return table;
158
+ }
159
+
160
+ describe('KNOWN_FLAGS 零漂移(指南 §七 ↔ CLI 注册旗标)', () => {
161
+ test('指南宣称的每个命令旗标集与 CLI 注册面逐命令全等', () => {
162
+ /**
163
+ * 指南契约表
164
+ */
165
+ const guideTable = parseGuideFlags(readFileSync(GUIDE_PATH, 'utf8'));
166
+
167
+ expect(guideTable.size).toBeGreaterThan(0);
168
+
169
+ /**
170
+ * CLI 程序实例
171
+ */
172
+ const program = createCli({
173
+ name: 'lzi-issues',
174
+ description: '测试内省用实例',
175
+ interaction: clackInteraction,
176
+ color: picocolorsColor,
177
+ });
178
+
179
+ registerCommands(program);
180
+
181
+ /**
182
+ * 可内省窄化(守卫不通过直接失败——内省面缺失即测试环境异常)
183
+ */
184
+ if (!asIntrospected(program)) {
185
+ throw new Error('commander Command 实例不具备可内省形态(name/commands/options)');
186
+ }
187
+
188
+ /**
189
+ * CLI 注册面(命令路径 → 长旗标集合)
190
+ */
191
+ const cliTable = flattenCommandTree(program, [], new Map());
192
+
193
+ for (const [commandPath, guideFlags] of guideTable) {
194
+ /**
195
+ * CLI 命中命令
196
+ */
197
+ const cliFlags = cliTable.get(commandPath);
198
+
199
+ expect(cliFlags, `指南宣称命令「${commandPath}」但 CLI 未注册`).toBeDefined();
200
+
201
+ expect([...(cliFlags ?? [])].sort(), `命令「${commandPath}」指南与 CLI 旗标面漂移`).toEqual(
202
+ [...guideFlags].sort(),
203
+ );
204
+ }
205
+ });
206
+
207
+ test('CLI 注册的每个命令都在指南 §七 宣称(反向零漂移)', () => {
208
+ /**
209
+ * 指南契约表
210
+ */
211
+ const guideTable = parseGuideFlags(readFileSync(GUIDE_PATH, 'utf8'));
212
+
213
+ /**
214
+ * CLI 程序实例
215
+ */
216
+ const program = createCli({
217
+ name: 'lzi-issues',
218
+ description: '测试内省用实例',
219
+ interaction: clackInteraction,
220
+ color: picocolorsColor,
221
+ });
222
+
223
+ registerCommands(program);
224
+
225
+ /**
226
+ * 可内省窄化(守卫不通过直接失败——内省面缺失即测试环境异常)
227
+ */
228
+ if (!asIntrospected(program)) {
229
+ throw new Error('commander Command 实例不具备可内省形态(name/commands/options)');
230
+ }
231
+
232
+ /**
233
+ * CLI 注册面
234
+ */
235
+ const cliTable = flattenCommandTree(program, [], new Map());
236
+
237
+ for (const commandPath of cliTable.keys()) {
238
+ expect(guideTable.has(commandPath), `CLI 注册命令「${commandPath}」未在指南 §七 宣称`).toBe(
239
+ true,
240
+ );
241
+ }
242
+ });
243
+ });