@stalfh233/omc-cli 0.6.5 → 0.6.6
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 +36 -3
- package/dist/tool-types.js +43 -2
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -43,6 +43,7 @@ else if (process.env.OMC_ASCII_STDOUT !== "0" && process.argv.some((arg) => arg
|
|
|
43
43
|
}
|
|
44
44
|
import { commandIndex, commandInventory, describeContract, describeContractFull, describeContractTool, describeExample, describeParam, describeSchemaOnly, describeTool, describeToolFull, filterTools } from "./discovery.js";
|
|
45
45
|
import { closestMatches } from "./closest-match.js";
|
|
46
|
+
import { KEY_ALIAS_HINTS } from "./tool-types.js";
|
|
46
47
|
import { repeatFullDescribe } from "./describe-cache.js";
|
|
47
48
|
import { buildCapabilityOverview, capabilityDetail, capabilityRouteIndex, compactCapabilities, executableCapabilityBundle, resolveCapabilityId, DISCOVERY_AUTHORITY } from "./capability-overview.js";
|
|
48
49
|
import { goalRoute } from "./goal-routes.js";
|
|
@@ -580,8 +581,9 @@ async function cmdCall(store, args) {
|
|
|
580
581
|
for (const key of Object.keys(parsedArgs)) {
|
|
581
582
|
if (schemaKeys.has(key) || recognized.has(key))
|
|
582
583
|
continue;
|
|
583
|
-
|
|
584
|
-
|
|
584
|
+
// r25 真机:`limt:5` 被就近指到 `filter`(共享 "fi" 就够过阈值),照提示改
|
|
585
|
+
// 会把「取 5 条」变成「按条件过滤」。短键走显式缩写表,语义才不跑偏。
|
|
586
|
+
ignoredReadKeys.push({ key, ...suggestArgumentKey(key, [...schemaKeys]) });
|
|
585
587
|
}
|
|
586
588
|
}
|
|
587
589
|
if (tool) {
|
|
@@ -655,7 +657,13 @@ async function cmdCall(store, args) {
|
|
|
655
657
|
}
|
|
656
658
|
// 读面未知键的非阻断回显:放在投影**之后**,否则会被 --jq 的窄投影吞掉。
|
|
657
659
|
if (ignoredReadKeys.length > 0 && typeof result === "object" && result !== null) {
|
|
658
|
-
const pairs = ignoredReadKeys.map((entry) =>
|
|
660
|
+
const pairs = ignoredReadKeys.map((entry) => {
|
|
661
|
+
if (entry.closest)
|
|
662
|
+
return `${entry.key}→${entry.closest}`;
|
|
663
|
+
if (entry.absentInTool)
|
|
664
|
+
return `${entry.key}(本工具没有 ${entry.absentInTool} 参数)`;
|
|
665
|
+
return entry.key;
|
|
666
|
+
});
|
|
659
667
|
result = {
|
|
660
668
|
...result,
|
|
661
669
|
ignoredArgs: {
|
|
@@ -679,6 +687,27 @@ function argvValues(args, name) {
|
|
|
679
687
|
}
|
|
680
688
|
return values;
|
|
681
689
|
}
|
|
690
|
+
/**
|
|
691
|
+
* 读面「拼错键」建议(r25 真机实证,2026-11-04):`limt:5` 曾按编辑距离被指到
|
|
692
|
+
* `filter`——两者共享 "fi" 片段就过阈值,但语义完全不同(取 5 条 vs 按条件过滤)。
|
|
693
|
+
* 短键(≤5 字符)靠距离算不准,这里先查显式缩写表(与 tool-types 的 KEY_ALIAS_HINTS
|
|
694
|
+
* 同源),命中且参数真实存在才用;否则退回 closestMatches。
|
|
695
|
+
*/
|
|
696
|
+
export function suggestArgumentKey(key, schemaKeys) {
|
|
697
|
+
const known = new Set(schemaKeys);
|
|
698
|
+
const aliases = KEY_ALIAS_HINTS[key.toLowerCase()] ?? [];
|
|
699
|
+
for (const candidate of aliases) {
|
|
700
|
+
if (known.has(candidate))
|
|
701
|
+
return { closest: candidate };
|
|
702
|
+
}
|
|
703
|
+
// 关键:若这个键明确是某个已知参数的缩写、而**本工具根本没有该参数**(实测
|
|
704
|
+
// `model.list` 就没有 `size`),不能退回最近匹配硬凑一个(`filter` 语义相反)——
|
|
705
|
+
// 返回 absentInTool,让回执直说「本工具没有这个参数」。
|
|
706
|
+
if (aliases.length > 0)
|
|
707
|
+
return { absentInTool: aliases[0] };
|
|
708
|
+
const closest = closestMatches(key, schemaKeys, 1)[0]?.name;
|
|
709
|
+
return closest !== undefined ? { closest } : {};
|
|
710
|
+
}
|
|
682
711
|
function resolvedDefaultEnvironment(store) {
|
|
683
712
|
try {
|
|
684
713
|
return store.resolveEnvironment(undefined).name;
|
|
@@ -1600,6 +1629,10 @@ function onboardingHelp() {
|
|
|
1600
1629
|
"方法论按需读:omc help <topic> 默认只回节索引(每节字节数);用 --section \"<标题>\" 取正文(可重复),确需整篇才 --all。找具体事实(错误码/字段名/命令名)不要整篇 dump:omc help --search \"<词>\"(跨 topic,命中节正文随结果返回)。",
|
|
1601
1630
|
"目标→工具一步定位:omc next \"<你要做什么>\"(离线、只读,给能力/说明/首批命令;不确定会明说并回退到 omc-requirement-analysis)。",
|
|
1602
1631
|
"大结果取值一步到位:读面回 detailArtifactId 时,用 omc call <tool> … --jq <path>(可重复;`[]` 映射数组,如 items[].fields.planNo)或 --extract/--extract-keys,省掉再跑一次 omc artifact get;artifact get 正文超 8KB 默认也只回形状(--jq/--keys 定向取,--full 才整包)。批量形状预检用 --args-list … --preflight(零 wire、零写入,一次报全)。",
|
|
1632
|
+
// r25 真机(2026-11-04):探针连跑同一读面 2–3 次想验证「第二次是否 unchanged」,
|
|
1633
|
+
// 结果三次都是全量,还去 describe 找 dedupe 参数(多花 3 轮)。读面确实不做跨调用
|
|
1634
|
+
// 结果去重——**明说**比留白强,省掉一次探索。
|
|
1635
|
+
"读面不做跨调用结果去重:同一读面同参数连跑 N 次就是 N 次真实读(不会回 unchanged,也没有 dedupe 开关)。要省就 --jq 定向取值或把结果落到自己的变量里复用;`unchanged:true` 只出现在写面回读缓存那一条路径。",
|
|
1603
1636
|
"工具发现:omc commands --json(含 capability id/lane/recipes;加 --compact 只回工具行、无目录散文)· omc capabilities(能力 id/lane/aliases/tools/依赖顺序)· omc capability <id> --executable(单能力完整执行包)。",
|
|
1604
1637
|
"单工具/契约:omc describe <tool>(schema+recipe+能力路由)· omc contract <lane>(wire 契约+能力路由);环境就绪:omc call discover_environment --env <env> --args '{}'。",
|
|
1605
1638
|
"写入两道门:对话层草稿确认(omc help omc-config-draft-review)+ 环境 writable 开关;生产环境每笔写需在 args 里带 confirmProduction:true。",
|
package/dist/tool-types.js
CHANGED
|
@@ -132,16 +132,46 @@ function keyRenameHint(issuePath, input, tool) {
|
|
|
132
132
|
return `(该对象在本工具里的键是 ${key},你传的是 ${synonyms.join("/")};键名对照见 omc-tool-selection 的 Key-name table)`;
|
|
133
133
|
}
|
|
134
134
|
/** Cheap closest-key suggestion: exact→prefix→shared-start bigrams, threshold keeps it honest. */
|
|
135
|
+
/**
|
|
136
|
+
* 常见参数缩写 → 规范键。r25 真机实证(2026-11-04):`limt:5` 被指到 `filter`
|
|
137
|
+
* (`limt` 与 `filter` 共享 "fi" 片段就够过阈值),照提示改会从"取 5 条"变成
|
|
138
|
+
* "按条件过滤",是**语义陷阱**;`fieldCodes` 被指到单数 `code`(真正要的是
|
|
139
|
+
* `codes`),照改会变成"只发一个字段"。短键与复数形态靠编辑距离算不准,
|
|
140
|
+
* 用显式小表兜住高频误拼,比调阈值可靠。
|
|
141
|
+
*/
|
|
142
|
+
export const KEY_ALIAS_HINTS = Object.freeze({
|
|
143
|
+
limt: ["size", "limit"],
|
|
144
|
+
limit: ["size"],
|
|
145
|
+
siz: ["size"],
|
|
146
|
+
offst: ["page", "offset"],
|
|
147
|
+
offset: ["page"],
|
|
148
|
+
fieldcodes: ["codes", "fieldCodes", "fields"],
|
|
149
|
+
codes: ["code"],
|
|
150
|
+
fliter: ["filter"],
|
|
151
|
+
filterr: ["filter"],
|
|
152
|
+
order: ["orders"],
|
|
153
|
+
orderby: ["orders"],
|
|
154
|
+
});
|
|
135
155
|
function closestSchemaKey(key, schemaKeys) {
|
|
136
156
|
if (schemaKeys.has(key))
|
|
137
157
|
return key;
|
|
158
|
+
// 0) 显式缩写表优先:命中且该键真实存在才用,避免"建议一个不存在的参数"。
|
|
159
|
+
for (const candidate of KEY_ALIAS_HINTS[key.toLowerCase()] ?? []) {
|
|
160
|
+
if (schemaKeys.has(candidate))
|
|
161
|
+
return candidate;
|
|
162
|
+
}
|
|
138
163
|
let best;
|
|
139
164
|
let bestScore = 0;
|
|
140
165
|
for (const candidate of schemaKeys) {
|
|
141
166
|
const lower = key.toLowerCase();
|
|
142
167
|
const candLower = candidate.toLowerCase();
|
|
143
168
|
let score = 0;
|
|
144
|
-
|
|
169
|
+
// 单复数/尾随 s 的差异要压过零散片段匹配:`fieldCodes`→`fieldCodes` 应胜过 →`code`。
|
|
170
|
+
if (candLower === lower)
|
|
171
|
+
score = 1;
|
|
172
|
+
else if (candLower === `${lower}s` || lower === `${candLower}s`)
|
|
173
|
+
score = 0.85;
|
|
174
|
+
else if (candLower.startsWith(lower) || lower.startsWith(candLower))
|
|
145
175
|
score = 0.9;
|
|
146
176
|
else if (candLower.includes(lower) || lower.includes(candLower))
|
|
147
177
|
score = 0.7;
|
|
@@ -174,13 +204,24 @@ export function parseToolInput(tool, input) {
|
|
|
174
204
|
const twoStep = unknown
|
|
175
205
|
.map((key) => TWO_STEP_GUIDANCE[tool.name]?.[key])
|
|
176
206
|
.filter((hint) => typeof hint === "string" && hint.length > 0);
|
|
207
|
+
// r25 真机实证:人读 `message` 已指向 `form.publish`,但 `issues[].message`
|
|
208
|
+
// 仍是 `unknown key; closest: none` —— 程序消费回执时会取 issues[],于是
|
|
209
|
+
// 漏掉发布指引(报告原文:「若用程序消费回执,会选 issues[],就会漏掉」)。
|
|
210
|
+
// 两条通道必须同源,否则等于修了一半。
|
|
211
|
+
const issueHintFor = (key) => {
|
|
212
|
+
const guidance = TWO_STEP_GUIDANCE[tool.name]?.[key];
|
|
213
|
+
if (guidance !== undefined)
|
|
214
|
+
return guidance;
|
|
215
|
+
const closest = closestSchemaKey(key, schemaKeys);
|
|
216
|
+
return closest === undefined ? "unknown key; no close match in this tool's params" : `unknown key; closest: ${closest}`;
|
|
217
|
+
};
|
|
177
218
|
throw new ToolInputError({
|
|
178
219
|
status: "error",
|
|
179
220
|
blocker: "tool-input-unknown-keys",
|
|
180
221
|
tool: tool.name,
|
|
181
222
|
message: `写面工具不接受未知键(会被静默丢弃):${unknown.join("、")}。${twoStep.length > 0 ? twoStep.join(" ") : `最接近的合法键:${suggestions.join(";")}。参数表:omc describe ${tool.name}`}`,
|
|
182
223
|
...(twoStep.length > 0 ? { nextCommand: `omc describe ${tool.name}` } : {}),
|
|
183
|
-
issues: unknown.map((key) => ({ path: [key], code: "unrecognized_key", message:
|
|
224
|
+
issues: unknown.map((key) => ({ path: [key], code: "unrecognized_key", message: issueHintFor(key) })),
|
|
184
225
|
});
|
|
185
226
|
}
|
|
186
227
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stalfh233/omc-cli",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.6",
|
|
4
4
|
"description": "CloudPivot 8.6 configuration capability CLI — governed, fail-closed, coverage-manifest bounded.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -52,9 +52,9 @@
|
|
|
52
52
|
"access": "public"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@stalfh233/omc-fake-platform": "0.6.
|
|
55
|
+
"@stalfh233/omc-fake-platform": "0.6.6",
|
|
56
56
|
"commander": "14.0.3",
|
|
57
|
-
"omc-core": "0.6.
|
|
57
|
+
"omc-core": "0.6.6",
|
|
58
58
|
"proper-lockfile": "4.1.2",
|
|
59
59
|
"write-file-atomic": "6.0.0",
|
|
60
60
|
"zod": "^3.23.8",
|