@stalfh233/omc-cli 0.6.22 → 0.6.23

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/dist/cli.js CHANGED
@@ -23,7 +23,7 @@ import { formatDoctorHumanSummary, unsupportedCapabilities } from "./version-fam
23
23
  import { beginWriteFace, configureOutput, endWriteFace, humanSummary, maybeWarnMergedStreams, stripOutputFlags, writeEnvelope } from "./cli-output.js";
24
24
  import { beginUsage, completeUsage, currentUsageArgsDigest, ensureProjectIgnore, priorIdenticalSuccess, recordUsageOutput, usageReport } from "./usage-log.js";
25
25
  import { artifactFetchHint, artifactRoot, boundResult, emitInvalid, emitResult, emitThrown, emitTransportResult, outputBudget, readArtifact, withTransport, } from "./command-transport.js";
26
- import { CLI_DEFAULT_OUTPUT_BYTES, assertSafeOutputPath, createArtifact, pruneArtifacts } from "./artifacts.js";
26
+ import { CLI_DEFAULT_OUTPUT_BYTES, assertSafeOutputPath, pruneArtifacts } from "./artifacts.js";
27
27
  import { artifactPathMissHint, nullProjectionHint, partialPathMissHint, projectJsonPath, projectJsonPathFanout, projectResultExtract } from "./path-projection.js";
28
28
  import { basename, join, resolve } from "node:path";
29
29
  import { buildToolRegistry } from "./tools.js";
@@ -1671,12 +1671,16 @@ async function cmdGraphPull(environment, concurrency, transport, path, applicati
1671
1671
  }
1672
1672
  }
1673
1673
  function cmdGraphReferences(kind, code, transport, requestedEnvironment) {
1674
- if (!["service", "model", "field"].includes(kind))
1675
- return emitInvalid(transport, { message: "kind 必须是 service / model / field(同 rule.references 第一版边界)" });
1674
+ if (kind !== undefined && !["service", "model", "field"].includes(kind))
1675
+ return emitInvalid(transport, { message: "kind 必须是 service / model / field" });
1676
1676
  try {
1677
- const result = queryGraphReferences(kind, code);
1677
+ const kinds = kind === undefined ? ["service", "model", "field"] : [kind];
1678
+ const results = kinds.map((one) => queryGraphReferences(one, code));
1679
+ const result = results[0];
1678
1680
  if (requestedEnvironment !== undefined && requestedEnvironment !== result.index.environmentName)
1679
1681
  return emitResult({ status: "refused", blocker: "graph-environment-mismatch", message: `索引已绑定环境 ${result.index.environmentName},不是 ${requestedEnvironment};请先 omc graph pull --env ${requestedEnvironment}`, index: result.index }, transport);
1682
+ if (kind === undefined)
1683
+ return emitResult({ status: "ok", code, index: result.index, references: Object.fromEntries(results.map((item) => [item.kind, { matches: item.matches, ...(item.coverage !== undefined ? { coverage: item.coverage } : {}), ...(item.note !== undefined ? { note: item.note } : {}) }])) }, transport);
1680
1684
  return emitResult({
1681
1685
  status: "ok",
1682
1686
  kind: result.kind,
@@ -1707,24 +1711,28 @@ function cmdGraphReferencesBatch(kind, codeList, transport) {
1707
1711
  return emitThrown(error, transport);
1708
1712
  }
1709
1713
  }
1710
- function cmdGraphEdges(fromSchema, toSchema, requestedEnvironment, transport) {
1714
+ function cmdGraphEdges(fromSchema, toSchema, kind, offset, limit, requestedEnvironment, transport) {
1711
1715
  try {
1712
- const result = queryGraphEdges(fromSchema, toSchema);
1716
+ if (kind !== undefined && !["service", "model", "field"].includes(kind))
1717
+ return emitInvalid(transport, { message: "kind 必须是 service / model / field" });
1718
+ const pageOffset = offset ?? 0;
1719
+ const pageLimit = limit ?? 50;
1720
+ if (!Number.isSafeInteger(pageOffset) || pageOffset < 0 || !Number.isSafeInteger(pageLimit) || pageLimit < 1)
1721
+ return emitInvalid(transport, { message: "offset 必须是非负整数,limit 必须是正整数" });
1722
+ const result = queryGraphEdges(fromSchema, toSchema, kind, pageOffset, pageLimit);
1713
1723
  if (requestedEnvironment !== undefined && requestedEnvironment !== result.index.environmentName)
1714
1724
  return emitResult({ status: "refused", blocker: "graph-environment-mismatch", message: `索引已绑定环境 ${result.index.environmentName},不是 ${requestedEnvironment};请先 omc graph pull --env ${requestedEnvironment}`, index: result.index }, transport);
1715
- if (result.total > 200) {
1716
- const artifact = createArtifact(artifactRoot(), JSON.stringify(result.edges, null, 2));
1717
- return emitResult({ status: "ok", total: result.total, index: result.index, artifactId: artifact.artifactId, sha256: artifact.sha256, edges: { count: result.edges.length, truncated: true } }, transport);
1718
- }
1719
1725
  return emitResult({ status: "ok", ...result }, transport);
1720
1726
  }
1721
1727
  catch (error) {
1722
1728
  return emitThrown(error, transport);
1723
1729
  }
1724
1730
  }
1725
- function cmdGraphStatus(transport) {
1731
+ function cmdGraphStatus(requestedEnvironment, transport) {
1726
1732
  try {
1727
1733
  const status = readGraphStatus();
1734
+ if (requestedEnvironment !== undefined && requestedEnvironment !== status.index.environmentName)
1735
+ return emitResult({ status: "refused", blocker: "graph-environment-mismatch", message: `索引已绑定环境 ${status.index.environmentName},不是 ${requestedEnvironment}`, index: status.index }, transport);
1728
1736
  // r31a-F16:这里是**第四个**白名单式重建(前三次:compact() 投影、references
1729
1737
  // 覆盖声明、mcp-graph 的 explore)。白名单每漏一个键就等于没修,且不会被任何
1730
1738
  // 单测发现——所以改成展开,新增字段自动透传。
@@ -2595,14 +2603,25 @@ async function main() {
2595
2603
  const codes = options.applicationCodes ? String(options.applicationCodes).split(",").map((code) => code.trim()).filter(Boolean) : undefined;
2596
2604
  commandExitCode = await withTransport(options, request, (transport) => cmdGraphPull(options.environment ?? options.env, parsed, transport, options.path, codes, options.allApplications === true));
2597
2605
  });
2598
- graphCommand.command("references [kind] [code]").option("--batch <codes>").option("--env <environment>", "本子命令不接触环境;该参数仅用于提示索引绑定环境").option("--max-output-bytes <bytes>").addHelpText("after", "\n离线查询(零 wire):kind = service|model|field,语义同 rule.references 第一版边界;支持 --batch code1,code2 共享同一 index 块。\n").action(async (kind, code, options) => {
2599
- commandExitCode = await withTransport(options, request, (transport) => options.batch ? cmdGraphReferencesBatch(kind ?? "model", options.batch, transport) : cmdGraphReferences(kind ?? "", code ?? "", transport, options.environment ?? options.env));
2606
+ graphCommand.command("references [kind] [code]").option("--batch <codes>").option("--env <environment>", "本子命令不接触环境;该参数仅用于提示索引绑定环境").option("--max-output-bytes <bytes>").addHelpText("after", "\n离线查询(零 wire):省略 kind 时=service|model|field 三类全查;支持 --batch code1,code2(例如 --batch svc_a,svc_b)。\n").action(async (kind, code, options) => {
2607
+ // r42b 接线(2026-11-08,r42a-F8):commander 把首个位置参数绑给 [kind],
2608
+ // 「references <code>」这种最自然写法会把 code 当 kind 拒掉,而
2609
+ // cmdGraphReferences 的三类全查分支只认 kind===undefined——help 承诺的
2610
+ // 「省略 kind」在 CLI 面不可达。这里做智能重绑:首参不是合法 kind 且
2611
+ // 第二参缺席时,视首参为 code。
2612
+ let boundKind = kind;
2613
+ let boundCode = code;
2614
+ if (kind !== undefined && !["service", "model", "field"].includes(kind) && code === undefined) {
2615
+ boundKind = undefined;
2616
+ boundCode = kind;
2617
+ }
2618
+ commandExitCode = await withTransport(options, request, (transport) => options.batch ? cmdGraphReferencesBatch(boundKind ?? "model", options.batch, transport) : cmdGraphReferences(boundKind, boundCode ?? "", transport, options.environment ?? options.env));
2600
2619
  });
2601
2620
  graphCommand.command("references-batch <kind> <code>").option("--max-output-bytes <bytes>").action(async (kind, code, options) => {
2602
2621
  commandExitCode = await withTransport(options, request, (transport) => cmdGraphReferencesBatch(kind, code, transport));
2603
2622
  });
2604
- graphCommand.command("edges").option("--env <environment>", "本子命令不接触环境;该参数仅用于提示索引绑定环境").option("--from-schema <code>").option("--to-schema <code>").option("--max-output-bytes <bytes>").addHelpText("after", "\n离线全图边清单(零 wire);--env 仅校验索引绑定环境,与 references 一致。\n").action(async (options) => {
2605
- commandExitCode = await withTransport(options, request, (transport) => cmdGraphEdges(options.fromSchema, options.toSchema, options.environment ?? options.env, transport));
2623
+ graphCommand.command("edges").option("--env <environment>", "本子命令不接触环境;该参数仅用于提示索引绑定环境").option("--from-schema <code>").option("--to-schema <code>").option("--kind <kind>").option("--offset <n>").option("--limit <n>").option("--max-output-bytes <bytes>").addHelpText("after", "\n离线全图边清单(零 wire);支持 --kind model|field|service 与 --offset/--limit 分页。\n").action(async (options) => {
2624
+ commandExitCode = await withTransport(options, request, (transport) => cmdGraphEdges(options.fromSchema, options.toSchema, options.kind, options.offset === undefined ? undefined : Number(options.offset), options.limit === undefined ? undefined : Number(options.limit), options.environment ?? options.env, transport));
2606
2625
  });
2607
2626
  graphCommand.command("impact <kind> <code>").option("--max-output-bytes <bytes>").addHelpText("after", "\n影响面(离线):跳1=引用该编码的规则(同 references),跳2=这些规则还读写哪些 service/model/field——改前看爆炸半径。截断上限 50 规则×每类 20 码。\n").action(async (kind, code, options) => {
2608
2627
  commandExitCode = await withTransport(options, request, (transport) => cmdGraphImpact(kind, code, transport));
@@ -2610,8 +2629,8 @@ async function main() {
2610
2629
  graphCommand.command("search <term...>").option("--max-output-bytes <bytes>").addHelpText("after", "\n检索规则的 code+name(FTS5,末词前缀匹配;**不含规则体/配置内容**)。找到\"关于 X 的规则\"拿 code,再走 references/impact。中文必须是全词或词首通配(如 报名* / 增加预算*)——子串不命中:FTS5 把「增加预算」当一个 token,搜「预算」为 0。\n").action(async (term, options) => {
2611
2630
  commandExitCode = await withTransport(options, request, (transport) => cmdGraphSearch(term.join(" "), transport));
2612
2631
  });
2613
- graphCommand.command("status").option("--max-output-bytes <bytes>").action(async (options) => {
2614
- commandExitCode = await withTransport(options, request, (transport) => cmdGraphStatus(transport));
2632
+ graphCommand.command("status").option("--env <environment>").option("--environment <environment>").option("--max-output-bytes <bytes>").action(async (options) => {
2633
+ commandExitCode = await withTransport(options, request, (transport) => cmdGraphStatus(options.environment ?? options.env, transport));
2615
2634
  });
2616
2635
  // MCP stdio server (host registration: cpdocs installer's omc-graph entry;
2617
2636
  // top-level verb like `codegraph serve --mcp`, not a graph subcommand).
package/dist/graph.js CHANGED
@@ -46,7 +46,7 @@ function nodeSqlite() {
46
46
  * staleness is handled by reporting pulledAt + identity, refreshed by an
47
47
  * explicit re-pull (no incremental watermarks, no write-back hooks).
48
48
  */
49
- export const GRAPH_SCHEMA_VERSION = "6";
49
+ export const GRAPH_SCHEMA_VERSION = "7";
50
50
  const STALE_LOCK_MS = 15 * 60 * 1000;
51
51
  export const DEFAULT_PULL_CONCURRENCY = 16;
52
52
  export const MAX_PULL_CONCURRENCY = 32;
@@ -202,7 +202,13 @@ export async function pullRuleGraph(session, concurrency = DEFAULT_PULL_CONCURRE
202
202
  const services = [];
203
203
  await Promise.all(schemaCodes.map(async (schemaCode) => {
204
204
  try {
205
- const rows = wireRows((await wireCall(session, { path: ENDPOINTS.fieldList, query: { schemaCode, isPublish: "true" } })).data);
205
+ let rows = wireRows((await wireCall(session, { path: ENDPOINTS.fieldList, query: { schemaCode, isPublish: "true" } })).data);
206
+ // Several applications expose fields only on the draft face. An empty
207
+ // published response is not evidence that the model has no fields.
208
+ if (rows.length === 0)
209
+ rows = wireRows((await wireCall(session, { path: ENDPOINTS.fieldList, query: { schemaCode, isPublish: "false" } })).data);
210
+ if (rows.length === 0)
211
+ gaps.push({ kind: "field-list", ref: schemaCode, note: "published and draft faces are empty" });
206
212
  for (const row of rows) {
207
213
  const code = String(row.code ?? row.propertyCode ?? "");
208
214
  if (code)
@@ -219,7 +225,7 @@ export async function pullRuleGraph(session, concurrency = DEFAULT_PULL_CONCURRE
219
225
  for (const row of rows) {
220
226
  const code = String(row.code ?? "");
221
227
  if (code)
222
- services.push({ code, name: String(row.name ?? code) });
228
+ services.push({ code, name: String(row.name ?? code), applicationCode });
223
229
  }
224
230
  }
225
231
  catch (error) {
@@ -504,7 +510,7 @@ export async function publishGraphPull(session, concurrency = DEFAULT_PULL_CONCU
504
510
  const first = parts[0];
505
511
  const data = parts.length === 1 ? first : {
506
512
  ...first,
507
- meta: { ...first.meta, applicationCode: "*", coverage: { applicationCodes: parts.flatMap((part) => part.meta.coverage.applicationCodes), tenantApplications: parts.flatMap((part) => part.meta.coverage.applicationCodes) } },
513
+ meta: { ...first.meta, applicationCode: "*", applicationResolution: { code: "*", source: "wildcard" }, coverage: { applicationCodes: parts.flatMap((part) => part.meta.coverage.applicationCodes), tenantApplications: parts.flatMap((part) => part.meta.coverage.applicationCodes) } },
508
514
  rules: parts.flatMap((part) => part.rules),
509
515
  edges: parts.flatMap((part) => part.edges),
510
516
  models: parts.flatMap((part) => part.models),
@@ -552,7 +558,7 @@ export async function publishGraphPull(session, concurrency = DEFAULT_PULL_CONCU
552
558
  CREATE TABLE gaps (kind TEXT NOT NULL, ref TEXT NOT NULL, note TEXT);
553
559
  CREATE TABLE models (schema_code TEXT PRIMARY KEY, name TEXT NOT NULL, application_code TEXT);
554
560
  CREATE TABLE fields (schema_code TEXT NOT NULL, code TEXT NOT NULL, name TEXT NOT NULL, application_code TEXT);
555
- CREATE TABLE services (code TEXT PRIMARY KEY, name TEXT NOT NULL);
561
+ CREATE TABLE services (code TEXT PRIMARY KEY, name TEXT NOT NULL, application_code TEXT);
556
562
  `);
557
563
  const insertMeta = db.prepare("INSERT INTO meta (key, value) VALUES (?, ?)");
558
564
  // `applicationResolution` is an object; String(value) would persist
@@ -575,9 +581,9 @@ export async function publishGraphPull(session, concurrency = DEFAULT_PULL_CONCU
575
581
  const insertField = db.prepare("INSERT INTO fields (schema_code, code, name, application_code) VALUES (?, ?, ?, ?)");
576
582
  for (const row of data.fields)
577
583
  insertField.run(row.schemaCode, row.code, row.name, row.applicationCode ?? null);
578
- const insertService = db.prepare("INSERT OR IGNORE INTO services (code, name) VALUES (?, ?)");
584
+ const insertService = db.prepare("INSERT OR REPLACE INTO services (code, name, application_code) VALUES (?, ?, ?)");
579
585
  for (const row of data.services)
580
- insertService.run(row.code, row.name);
586
+ insertService.run(row.code, row.name, row.applicationCode ?? null);
581
587
  // FTS5 mirrors rules (code+name) for `graph search`; one rebuild after
582
588
  // the bulk insert is cheaper than per-row triggers on a wholesale rebuild.
583
589
  db.exec(`
@@ -613,7 +619,7 @@ function openGraphIndex() {
613
619
  for (const row of db.prepare("SELECT key, value FROM meta").all())
614
620
  meta[row.key] = row.value;
615
621
  if (meta.schemaVersion !== GRAPH_SCHEMA_VERSION)
616
- throw new GraphIndexAbsentError(`索引 schema 版本不匹配(${meta.schemaVersion ?? "unknown"} ≠ ${GRAPH_SCHEMA_VERSION});旧版索引已作废,请重跑 omc graph pull(schema 升版需重拉)`);
622
+ throw new GraphIndexAbsentError(`索引 schema 版本不匹配(${meta.schemaVersion ?? "unknown"} ≠ ${GRAPH_SCHEMA_VERSION});当前版本需要 schema ${GRAPH_SCHEMA_VERSION},旧版索引已作废,请重跑 omc graph pull(schema 升版需重拉)`);
617
623
  const ruleCount = db.prepare("SELECT COUNT(*) AS n FROM rules").get().n;
618
624
  const modelCount = db.prepare("SELECT COUNT(DISTINCT schema_code) AS n FROM rules").get().n;
619
625
  const entityCounts = {
@@ -649,7 +655,13 @@ function openGraphIndex() {
649
655
  tenantApplications = parsedCoverage.tenantApplications.filter((code) => typeof code === "string");
650
656
  }
651
657
  catch { /* legacy index */ }
652
- const coverage = { applicationCodes, ...(tenantApplications ? { tenantApplications } : {}) };
658
+ const fieldRows = db.prepare("SELECT application_code AS applicationCode, COUNT(*) AS n FROM fields GROUP BY application_code").all();
659
+ const countsByApplication = {};
660
+ for (const row of fieldRows)
661
+ if (row.applicationCode)
662
+ countsByApplication[row.applicationCode] = Number(row.n);
663
+ const fieldApplications = Object.keys(countsByApplication);
664
+ const coverage = { applicationCodes, ...(tenantApplications ? { tenantApplications } : {}), fields: { coveredApplications: fieldApplications, notCovered: applicationCodes.filter((code) => !fieldApplications.includes(code)), countsByApplication } };
653
665
  return {
654
666
  db,
655
667
  summary: {
@@ -721,7 +733,7 @@ export function queryGraphReferences(kind, code) {
721
733
  const { db, summary } = openGraphIndex();
722
734
  try {
723
735
  const rows = db
724
- .prepare("SELECT schema_code, rule_code, node_code, node_type, via FROM edges WHERE kind = ? AND code = ? ORDER BY schema_code, rule_code, node_code, via")
736
+ .prepare("SELECT schema_code, rule_code, node_code, node_type, via, application_code FROM edges WHERE kind = ? AND code = ? ORDER BY schema_code, rule_code, node_code, via")
725
737
  .all(kind, code);
726
738
  const matches = rows.map((row) => ({
727
739
  schemaCode: row.schema_code,
@@ -731,6 +743,7 @@ export function queryGraphReferences(kind, code) {
731
743
  via: row.via,
732
744
  referrer: { schemaCode: row.schema_code, ruleCode: row.rule_code },
733
745
  isSelf: row.schema_code === code,
746
+ ...(row.application_code ? { applicationCode: row.application_code } : {}),
734
747
  }));
735
748
  const gapped = summary.gaps.count > 0;
736
749
  // r35a-F4(真机 2026-11-06):探针拿 matches:[] 差点把一个**被表单引用**的
@@ -772,7 +785,7 @@ export function queryGraphReferencesBatch(kind, codes) {
772
785
  db.close();
773
786
  }
774
787
  }
775
- export function queryGraphEdges(fromSchema, toSchema) {
788
+ export function queryGraphEdges(fromSchema, toSchema, kind, offset = 0, limit = 50) {
776
789
  const { db, summary } = openGraphIndex();
777
790
  try {
778
791
  const conditions = [];
@@ -785,10 +798,15 @@ export function queryGraphEdges(fromSchema, toSchema) {
785
798
  conditions.push("code = ?");
786
799
  params.push(toSchema);
787
800
  }
801
+ if (kind) {
802
+ conditions.push("kind = ?");
803
+ params.push(kind);
804
+ }
788
805
  const where = conditions.length ? ` WHERE ${conditions.join(" AND ")}` : "";
789
806
  const total = Number(db.prepare(`SELECT COUNT(*) AS n FROM edges${where}`).get(...params).n);
790
- const rows = db.prepare(`SELECT kind, code, schema_code, rule_code, node_code, node_type, via, application_code FROM edges${where} ORDER BY schema_code, code, rule_code, node_code, via`).all(...params);
791
- return { edges: rows.map((row) => ({ kind: row.kind, code: row.code, schemaCode: row.schema_code, ruleCode: row.rule_code, nodeCode: row.node_code ?? "", nodeType: row.node_type ?? "", via: row.via, ...(row.application_code ? { applicationCode: row.application_code } : {}) })), index: summary, total };
807
+ const rows = db.prepare(`SELECT kind, code, schema_code, rule_code, node_code, node_type, via, application_code FROM edges${where} ORDER BY schema_code, code, rule_code, node_code, via LIMIT ? OFFSET ?`).all(...params, limit, offset);
808
+ const edges = rows.map((row) => ({ kind: row.kind, code: row.code, schemaCode: row.schema_code, ruleCode: row.rule_code, nodeCode: row.node_code ?? "", nodeType: row.node_type ?? "", via: row.via, ...(row.application_code ? { applicationCode: row.application_code } : {}) }));
809
+ return { edges, index: summary, total, truncated: offset + edges.length < total, ...(offset + edges.length < total ? { nextOffset: offset + edges.length } : {}) };
792
810
  }
793
811
  finally {
794
812
  db.close();
@@ -804,7 +822,7 @@ export function readGraphStatus() {
804
822
  dbBytes: statSync(dbPath).size,
805
823
  dbPath,
806
824
  dbId: sha256(dbPath).slice(0, 12),
807
- mcpHint: `MCP 工具按注册时的 --path 读库;要让 MCP 看到本次拉取,请执行 omc graph pull --path <MCP 的配置根> --env <env>。`,
825
+ mcpHint: `MCP 工具按注册时的 --path 读库(当前索引根:${dirname(dirname(dbPath))});要让 MCP 看到本次拉取,请执行 omc graph pull --path ${dirname(dirname(dbPath))} --env <env>。`,
808
826
  ageMinutes: summary.ageMinutes,
809
827
  staleAfterMinutes: summary.staleAfterMinutes,
810
828
  ...(summary.staleWarning ? { warning: summary.staleWarning } : {}),
package/dist/mcp-graph.js CHANGED
@@ -130,7 +130,7 @@ function entityInventory() {
130
130
  return [
131
131
  ...db.prepare("SELECT schema_code, name, application_code FROM models").all().map((r) => ({ kind: "model", schemaCode: r.schema_code, code: r.schema_code, name: r.name ?? "", applicationCode: r.application_code ?? undefined })),
132
132
  ...db.prepare("SELECT schema_code, code, name, application_code FROM fields").all().map((r) => ({ kind: "field", schemaCode: r.schema_code, code: r.code, name: r.name ?? "", applicationCode: r.application_code ?? undefined })),
133
- ...db.prepare("SELECT code, name FROM services").all().map((r) => ({ kind: "service", code: r.code, name: r.name ?? "" })),
133
+ ...db.prepare("SELECT code, name, application_code FROM services").all().map((r) => ({ kind: "service", code: r.code, name: r.name ?? "", applicationCode: r.application_code ?? undefined })),
134
134
  ];
135
135
  }
136
136
  finally {
@@ -199,10 +199,10 @@ export async function executeGraphTool(name, rawArgs) {
199
199
  const loweredQuery = primaryQuery.toLocaleLowerCase();
200
200
  const allCandidates = entityInventory().filter((r) => (r.code.toLocaleLowerCase().includes(loweredQuery) || r.name.toLocaleLowerCase().includes(loweredQuery)) &&
201
201
  (!schemaCode || (r.kind !== "service" && r.schemaCode === schemaCode)) &&
202
- (!applicationCode || r.kind === "service" || r.applicationCode === applicationCode));
202
+ (!applicationCode || r.applicationCode === applicationCode));
203
203
  const candidatesForQuery = (term) => entityInventory().filter((r) => (r.code.toLocaleLowerCase().includes(term.toLocaleLowerCase()) || r.name.toLocaleLowerCase().includes(term.toLocaleLowerCase())) &&
204
204
  (!schemaCode || (r.kind !== "service" && r.schemaCode === schemaCode)) &&
205
- (!applicationCode || r.kind === "service" || r.applicationCode === applicationCode));
205
+ (!applicationCode || r.applicationCode === applicationCode));
206
206
  const perQueryModels = queries.map((term) => new Set(candidatesForQuery(term).filter((r) => r.kind === "model").map((r) => r.schemaCode)));
207
207
  const perQueryFieldModels = queries.map((term) => new Set(candidatesForQuery(term).filter((r) => r.kind === "field").map((r) => r.schemaCode)));
208
208
  const combine = (sets) => {
@@ -226,7 +226,7 @@ export async function executeGraphTool(name, rawArgs) {
226
226
  for (const one of kinds) {
227
227
  const subject = subjects.find((s) => s.kind === one);
228
228
  const ref = queryGraphReferences(one, subject?.code ?? query);
229
- const scopedMatches = subject && one === "field" && schemaCode ? ref.matches.filter((row) => row.schemaCode === schemaCode) : ref.matches;
229
+ const scopedMatches = ref.matches.filter((row) => !applicationCode || row.applicationCode === applicationCode).filter((row) => subject && one === "field" && schemaCode ? row.schemaCode === schemaCode : true);
230
230
  const capped = cap(scopedMatches, offset, limit);
231
231
  // r35a-F4c(真机 2026-11-06):**MCP 面是第三个消费者**——CLI 的 references
232
232
  // 与 impact 都带 coverage 了,这里若不显式透传,MCP 用户看到的仍是
@@ -237,7 +237,6 @@ export async function executeGraphTool(name, rawArgs) {
237
237
  offset: capped.offset,
238
238
  limit: capped.limit,
239
239
  ...(capped.truncated ? { truncated: true, nextOffset: capped.nextOffset, note: `共 ${capped.total} 条,已给 ${capped.rows.length} 条,offset=${capped.offset} 取下一段(调用时传 offset=${capped.nextOffset}、limit=${capped.limit})` } : {}),
240
- ...(ref.coverage ? { coverage: ref.coverage } : {}),
241
240
  ...(ref.gapWarning ? { gapWarning: ref.gapWarning } : {}),
242
241
  };
243
242
  if (scopedMatches.length > 0 || kind !== undefined || subject !== undefined) {
@@ -257,7 +256,6 @@ export async function executeGraphTool(name, rawArgs) {
257
256
  return m;
258
257
  }, new Map()).values()],
259
258
  } : {}),
260
- ...(imp.coverage ? { coverage: imp.coverage } : {}),
261
259
  };
262
260
  }
263
261
  }
@@ -274,10 +272,10 @@ export async function executeGraphTool(name, rawArgs) {
274
272
  query,
275
273
  ...(queries.length > 1 ? { queries, match: args.match === "all" ? "all" : "any" } : {}),
276
274
  hitModels: [...hitModels],
277
- hitFields: [...hitFieldModels].flatMap((schema) => entityInventory().filter((r) => r.kind === "field" && r.schemaCode === schema).map((r) => ({ schemaCode: schema, code: r.code, name: r.name }))),
275
+ ...(() => { const page = cap([...hitFieldModels].flatMap((schema) => entityInventory().filter((r) => r.kind === "field" && r.schemaCode === schema && (!applicationCode || r.applicationCode === applicationCode)).map((r) => ({ schemaCode: schema, code: r.code, name: r.name }))), offset, limit); return { hitFields: page.rows, hitFieldsCount: page.total, ...(page.truncated ? { hitFieldsTruncated: true, hitFieldsNextOffset: page.nextOffset } : {}) }; })(),
278
276
  found: resolvedEntity !== undefined,
279
- ...(resolvedEntity ? { resolved: { kind: resolvedEntity.kind, schemaCode: "schemaCode" in resolvedEntity ? resolvedEntity.schemaCode : "", code: resolvedEntity.code, matchedOn: { on: resolvedEntity.name.includes(query) ? "name" : "code", fragment: query } } } : {}),
280
- ...(resolvedEntity ? { otherCandidates: allCandidates.filter((r) => r.code !== resolvedEntity.code).slice(0, 5).map((r) => ({ schemaCode: r.schemaCode ?? r.code, name: r.name })) } : {}),
277
+ ...(resolvedEntity ? { resolved: { kind: resolvedEntity.kind, schemaCode: "schemaCode" in resolvedEntity ? resolvedEntity.schemaCode : "", code: resolvedEntity.code, matchedOn: { on: resolvedEntity.name.includes(query) ? "name" : "code", fragment: query }, ...(resolvedEntity.applicationCode ? { applicationCode: resolvedEntity.applicationCode } : {}) } } : {}),
278
+ ...(resolvedEntity ? { otherCandidates: allCandidates.filter((r) => r.code !== resolvedEntity.code).slice(0, 5).map((r) => ({ schemaCode: r.schemaCode ?? r.code, name: r.name, ...(r.applicationCode ? { applicationCode: r.applicationCode } : {}) })) } : {}),
281
279
  ...(ambiguous ? { ambiguous: true, candidates: exactSame.map((r) => ({ schemaCode: r.schemaCode, code: r.code, name: r.name })) } : {}),
282
280
  ...(kind !== undefined ? { kind } : {}),
283
281
  index,
@@ -291,7 +289,8 @@ export async function executeGraphTool(name, rawArgs) {
291
289
  ...(search.note ? { searchNote: `${searchNote}\n${search.note}` } : {}),
292
290
  // 旧句「索引不含规则条件叶子与流程绑定」漏了表单与流程权限(r35a-F4 实证
293
291
  // 探针据此差点误删被表单引用的字段)。改为指向结构化 coverage,不再靠散文。
294
- note: `零命中只代表 references[].coverage.scanned 面没有命中;本索引覆盖应用:${index.coverage.applicationCodes.join("、") || "(未知)"},不代表其他应用没有结果;notScanned 面(尤其 form 与 workflow.permissions)未扫描,不可据此判定可安全删除或无影响`,
292
+ coverage: { ...index.coverage, scanned: ["businessrule", "datarule", "listview"], notScanned: ["form", "workflow.permissions", "workflow.bindings", "onlinejs", "rule.conditions"] },
293
+ note: `零命中只代表 references[].coverage.scanned 面没有命中;字段面覆盖应用:${index.coverage.fields?.coveredApplications.join("、") || "(未知)"};notScanned 面(尤其 form 与 workflow.permissions)未扫描,不可据此判定可安全删除或无影响`,
295
294
  });
296
295
  }
297
296
  return toolError(`未知工具:${name}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stalfh233/omc-cli",
3
- "version": "0.6.22",
3
+ "version": "0.6.23",
4
4
  "description": "CloudPivot 8.6 configuration capability CLI — governed, fail-closed, coverage-manifest bounded.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -55,7 +55,7 @@
55
55
  },
56
56
  "dependencies": {
57
57
  "commander": "14.0.3",
58
- "omc-core": "0.6.22",
58
+ "omc-core": "0.6.23",
59
59
  "proper-lockfile": "4.1.2",
60
60
  "write-file-atomic": "6.0.0",
61
61
  "zod": "^3.23.8",
@@ -73,6 +73,6 @@
73
73
  "directory": "packages/omc-cli"
74
74
  },
75
75
  "optionalDependencies": {
76
- "@stalfh233/omc-fake-platform": "0.6.22"
76
+ "@stalfh233/omc-fake-platform": "0.6.23"
77
77
  }
78
78
  }