cool-workflow 0.2.3 → 0.2.5
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/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.gemini-plugin/mcp.json +10 -0
- package/.gemini-plugin/plugin.json +40 -0
- package/.opencode-plugin/mcp.json +10 -0
- package/.opencode-plugin/plugin.json +40 -0
- package/README.md +202 -48
- package/apps/architecture-review/app.json +1 -1
- package/apps/architecture-review-fast/app.json +1 -1
- package/apps/end-to-end-golden-path/app.json +1 -1
- package/apps/pr-review-fix-ci/app.json +1 -1
- package/apps/release-cut/app.json +1 -1
- package/apps/research-synthesis/app.json +1 -1
- package/dist/cli/dispatch.js +33 -4
- package/dist/cli/entry.js +11 -19
- package/dist/cli/global-flags.js +113 -0
- package/dist/cli/io.js +6 -20
- package/dist/cli/parseargv.js +7 -1
- package/dist/core/capability-data.js +339 -10
- package/dist/core/format/help.js +80 -4
- package/dist/core/format/recovery-hint.js +32 -0
- package/dist/core/multi-agent/collaboration.js +35 -6
- package/dist/core/multi-agent/runtime.js +7 -0
- package/dist/core/multi-agent/trust-policy.js +7 -1
- package/dist/core/pipeline/contract.js +7 -0
- package/dist/core/pipeline/error-feedback.js +2 -2
- package/dist/core/trust/evidence-grounding.js +13 -1
- package/dist/core/util/cli-args.js +55 -0
- package/dist/core/version.js +1 -1
- package/dist/mcp/dispatch.js +22 -2
- package/dist/mcp/server.js +124 -13
- package/dist/mcp-server.js +20 -0
- package/dist/shell/commit.js +8 -2
- package/dist/shell/coordinator-io.js +73 -1
- package/dist/shell/drive.js +129 -64
- package/dist/shell/error-feedback-io.js +6 -0
- package/dist/shell/execution-backend/agent.js +205 -24
- package/dist/shell/execution-backend/container.js +44 -10
- package/dist/shell/execution-backend/local.js +47 -10
- package/dist/shell/fs-atomic.js +93 -12
- package/dist/shell/ledger-cli.js +9 -2
- package/dist/shell/multi-agent-cli.js +5 -1
- package/dist/shell/onramp.js +48 -5
- package/dist/shell/pipeline-cli.js +20 -2
- package/dist/shell/pipeline.js +2 -1
- package/dist/shell/reclamation-io.js +76 -7
- package/dist/shell/registry-cli.js +4 -0
- package/dist/shell/run-export.js +52 -4
- package/dist/shell/run-registry-io.js +1 -0
- package/dist/shell/run-store.js +156 -0
- package/dist/shell/scheduler-io.js +101 -10
- package/dist/shell/telemetry-ledger-io.js +36 -24
- package/dist/shell/trust-audit.js +104 -10
- package/dist/shell/workbench-host.js +121 -10
- package/dist/shell/workbench.js +79 -5
- package/dist/shell/worker-isolation.js +1 -1
- package/dist/shell/workflow-app-loader.js +67 -1
- package/dist/wiring/capability-table/basics.js +7 -2
- package/dist/wiring/capability-table/exec-backend.js +42 -24
- package/dist/wiring/capability-table/multi-agent.js +69 -69
- package/dist/wiring/capability-table/pipeline.js +60 -28
- package/dist/wiring/capability-table/registry-core.js +29 -6
- package/dist/wiring/capability-table/reporting.js +45 -39
- package/dist/wiring/capability-table/scheduling-registry.js +66 -60
- package/dist/wiring/capability-table/state.js +34 -34
- package/dist/wiring/capability-table/trust-ledger.js +66 -24
- package/dist/wiring/capability-table/workflow-apps.js +15 -15
- package/docs/agent-delegation-drive.7.md +17 -0
- package/docs/cli-mcp-parity.7.md +43 -13
- package/docs/contract-migration-tooling.7.md +4 -0
- package/docs/control-plane-scheduling.7.md +38 -0
- package/docs/cross-agent-ledger.7.md +20 -8
- package/docs/durable-state-and-locking.7.md +4 -0
- package/docs/evidence-adoption-reasoning-chain.7.md +4 -0
- package/docs/execution-backends.7.md +4 -0
- package/docs/fix.7.md +4 -4
- package/docs/getting-started.md +40 -32
- package/docs/index.md +17 -0
- package/docs/launch/demo.tape +4 -3
- package/docs/mcp-app-surface.7.md +6 -0
- package/docs/multi-agent-cli-mcp-surface.7.md +4 -0
- package/docs/multi-agent-eval-replay-harness.7.md +4 -0
- package/docs/multi-agent-operator-ux.7.md +4 -0
- package/docs/node-snapshot-diff-replay.7.md +4 -0
- package/docs/observability-cost-accounting.7.md +4 -0
- package/docs/project-index.md +43 -9
- package/docs/real-execution-backends.7.md +4 -0
- package/docs/release-and-migration.7.md +4 -0
- package/docs/release-tooling.7.md +22 -0
- package/docs/routine.7.md +22 -0
- package/docs/run-registry-control-plane.7.md +4 -0
- package/docs/run-retention-reclamation.7.md +28 -3
- package/docs/state-explosion-management.7.md +4 -0
- package/docs/team-collaboration.7.md +4 -0
- package/docs/trust-audit-anchor.7.md +4 -0
- package/docs/web-desktop-workbench.7.md +26 -2
- package/docs/workflow-app-framework.7.md +31 -0
- package/manifest/plugin.manifest.json +1 -1
- package/manifest/source-context-profiles.json +4 -3
- package/package.json +5 -2
- package/scripts/agents/agent-adapter-core.js +26 -0
- package/scripts/agents/claude-p-agent.js +4 -1
- package/scripts/agents/codex-agent.js +4 -0
- package/scripts/agents/cw-attest-wrap.js +1 -1
- package/scripts/agents/gemini-agent.js +4 -0
- package/scripts/agents/opencode-agent.js +5 -0
- package/scripts/block-unapproved-tag.js +160 -0
- package/scripts/bump-version.js +27 -10
- package/scripts/canonical-apps.js +4 -4
- package/scripts/children/batch-delegate-child.js +72 -4
- package/scripts/children/http-batch-delegate-child.js +132 -0
- package/scripts/children/http-delegate-child.js +8 -0
- package/scripts/dogfood-release.js +2 -2
- package/scripts/fake-date-for-reproduction.js +1 -1
- package/scripts/golden-path.js +4 -4
- package/scripts/lang-policy-check.js +82 -0
- package/scripts/purity-baseline.json +0 -30
- package/scripts/purity-gate.js +16 -3
- package/scripts/release-check.js +2 -1
- package/scripts/release-flow.js +264 -41
- package/scripts/release-gate.js +184 -0
- package/scripts/release-oneclick.js +438 -0
- package/scripts/release-tags.js +47 -0
- package/scripts/verdict-keygen.js +2 -2
- package/scripts/verify-bump-reproduction.js +193 -0
- package/scripts/verify-verdict-signature.js +1 -1
- package/scripts/version-sync-check.js +39 -22
- package/ui/workbench/app.css +41 -9
- package/ui/workbench/app.js +160 -27
- package/ui/workbench/index.html +2 -2
- package/docs/agent-framework.md +0 -177
- package/docs/designs/handoff-ledger.md +0 -145
- package/docs/dogfood/resume-drive-real-agent-2026-06-14.md +0 -40
- package/docs/launch/launch-kit.md +0 -195
- package/docs/launch/pre-launch-checklist.md +0 -53
- package/docs/readme-v0.1.87-full.md +0 -301
- package/docs/routines.md +0 -101
- package/docs/scheduled-tasks.md +0 -80
- package/scripts/agents/claude-p-agent.sh +0 -9
- package/scripts/block-unapproved-tag.sh +0 -60
- package/scripts/release-gate.sh +0 -94
- package/scripts/verify-bump-reproduction.sh +0 -148
- package/scripts/verify-container-selfref.js +0 -64
package/dist/cli/entry.js
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
// so byte content there is unaffected; cli-color-env.case.js exercises
|
|
16
16
|
// the FORCE_COLOR/NO_COLOR/CW_NO_COLOR branches explicitly.
|
|
17
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
-
exports.recoveryHint =
|
|
18
|
+
exports.recoveryHint = void 0;
|
|
19
19
|
exports.runCli = runCli;
|
|
20
20
|
exports.main = main;
|
|
21
21
|
const parseargv_1 = require("./parseargv");
|
|
@@ -25,6 +25,13 @@ const parseargv_2 = require("./parseargv");
|
|
|
25
25
|
const capability_table_1 = require("../core/capability-table");
|
|
26
26
|
const term_1 = require("../shell/term");
|
|
27
27
|
const io_1 = require("./io");
|
|
28
|
+
const recovery_hint_1 = require("../core/format/recovery-hint");
|
|
29
|
+
// recoveryHint now lives in core/format/recovery-hint.ts (a core/-layer
|
|
30
|
+
// file, so mcp/server.ts can use it too without crossing the mcp/-may-
|
|
31
|
+
// never-import-cli/ layer rule). Re-exported here so every existing
|
|
32
|
+
// importer of `recoveryHint` from this file keeps working unchanged.
|
|
33
|
+
var recovery_hint_2 = require("../core/format/recovery-hint");
|
|
34
|
+
Object.defineProperty(exports, "recoveryHint", { enumerable: true, get: function () { return recovery_hint_2.recoveryHint; } });
|
|
28
35
|
/** MILESTONE 2: `version` is now a pure projection of the capability
|
|
29
36
|
* table's `version` row — there is exactly one place its print text
|
|
30
37
|
* lives (core/capability-table.ts's cli handler), not a second
|
|
@@ -39,23 +46,6 @@ function printVersion() {
|
|
|
39
46
|
const result = row?.cli?.handler({ positionals: [], options: {} });
|
|
40
47
|
process.stdout.write(result?.text ?? "");
|
|
41
48
|
}
|
|
42
|
-
/** src/cli.ts:18-29 — map a top-level error message to ONE copy-pasteable
|
|
43
|
-
* recovery command. Content-based so it stays correct regardless of which
|
|
44
|
-
* call site threw; returns undefined rather than a wrong guess. */
|
|
45
|
-
function recoveryHint(message) {
|
|
46
|
-
const m = message.toLowerCase();
|
|
47
|
-
if (m.startsWith("unknown command"))
|
|
48
|
-
return "cw help";
|
|
49
|
-
if (m.includes("not configured") || m.includes("agent backend"))
|
|
50
|
-
return "cw doctor";
|
|
51
|
-
if (m.includes("missing") && m.includes("repo"))
|
|
52
|
-
return 'cw -q "<question>" -dir <project-folder>';
|
|
53
|
-
if (m.includes("app") && (m.includes("not found") || m.includes("not available")))
|
|
54
|
-
return "cw app list";
|
|
55
|
-
if (m.includes("run id") || m.includes("run not found"))
|
|
56
|
-
return "cw run list";
|
|
57
|
-
return undefined;
|
|
58
|
-
}
|
|
59
49
|
async function runCli(argv = process.argv.slice(2)) {
|
|
60
50
|
const args = (0, parseargv_1.parseArgv)(argv);
|
|
61
51
|
// Top-level flags: accept --version / -v / --help / -h before command lookup.
|
|
@@ -77,6 +67,8 @@ async function runCli(argv = process.argv.slice(2)) {
|
|
|
77
67
|
args.options["agent-command"] = "builtin:codex";
|
|
78
68
|
if (args.options.gemini)
|
|
79
69
|
args.options["agent-command"] = "builtin:gemini";
|
|
70
|
+
if (args.options.opencode)
|
|
71
|
+
args.options["agent-command"] = "builtin:opencode";
|
|
80
72
|
if (args.options.deepseek)
|
|
81
73
|
args.options["agent-command"] = "builtin:deepseek";
|
|
82
74
|
// -dir / --dir / -d is a second name for --repo; an explicit --repo wins
|
|
@@ -152,7 +144,7 @@ function main(argv = process.argv.slice(2)) {
|
|
|
152
144
|
return;
|
|
153
145
|
const message = error instanceof Error ? error.message : String(error);
|
|
154
146
|
process.stderr.write(`${(0, term_1.bold)("cw:", process.stderr)} ${(0, term_1.red)(message, process.stderr)}\n`);
|
|
155
|
-
const hint = recoveryHint(message);
|
|
147
|
+
const hint = (0, recovery_hint_1.recoveryHint)(message);
|
|
156
148
|
if (hint)
|
|
157
149
|
process.stderr.write(` ${(0, term_1.dim)("Try:", process.stderr)} ${hint}\n`);
|
|
158
150
|
process.exitCode = 1;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// cli/global-flags.ts — the shared global CLI flag set, plus the TTY-gated
|
|
3
|
+
// unknown-flag warning for capability rows that declare `flagsComplete`
|
|
4
|
+
// (core/capability-data.ts's CliBinding).
|
|
5
|
+
//
|
|
6
|
+
// WHY: an unknown flag used to be dropped without a sound — `cw doctor
|
|
7
|
+
// --jsno` ran fine, printed the human report, and exited 0, so a typo of
|
|
8
|
+
// `--json` was easy to miss. A full allowlist check for EVERY command is
|
|
9
|
+
// not safe (a missed name means a FALSE warning, and many scripted and
|
|
10
|
+
// conformance callers assert stderr === ""), so the design here is
|
|
11
|
+
// incremental and fail-open:
|
|
12
|
+
// - only a row that declares `flagsComplete: true` is ever checked, and
|
|
13
|
+
// a row earns that mark only after its handler's option reads have
|
|
14
|
+
// been checked against source, one by one;
|
|
15
|
+
// - the warning is ONE line to stderr, printed ONLY when stderr is a
|
|
16
|
+
// real interactive terminal (same injectable-stream gate as
|
|
17
|
+
// shell/workbench-host.ts's printServeHint) — a piped, scripted, or
|
|
18
|
+
// conformance run can never see it;
|
|
19
|
+
// - stdout bytes and the exit code never change.
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.GLOBAL_CLI_FLAGS = void 0;
|
|
22
|
+
exports.unknownFlagKeys = unknownFlagKeys;
|
|
23
|
+
exports.warnUnknownFlags = warnUnknownFlags;
|
|
24
|
+
/** Every option key the shared CLI front door reads for ANY command, so a
|
|
25
|
+
* `flagsComplete` row's check never flags them. Built by reading the
|
|
26
|
+
* actual consumers:
|
|
27
|
+
* - cli/entry.ts: `version`/`v`, `help`/`h`, the vendor shorthands
|
|
28
|
+
* `claude`/`codex`/`gemini`/`deepseek` (each sets `agent-command`),
|
|
29
|
+
* `repo`/`dir` (`--dir`/`-d` is a global alias for `--repo`),
|
|
30
|
+
* `verbose`, `no-color`, `full`, `quiet`, `question` (`-q`), and the
|
|
31
|
+
* `--resume --run <id>` continuation pair;
|
|
32
|
+
* - cli/dispatch.ts + core/util/cli-args.ts's wantsJson: `json`,
|
|
33
|
+
* `format`;
|
|
34
|
+
* - cli/parseargv.ts's short-alias table: `link` (`-l`),
|
|
35
|
+
* `agent-command` (`-a`) — the short spellings map to these long
|
|
36
|
+
* names before any handler sees them;
|
|
37
|
+
* - the doctor/fix handlers' shared working-dir override: `cwd`.
|
|
38
|
+
* Keys are the POST-parse spellings (what lands in `args.options`). */
|
|
39
|
+
exports.GLOBAL_CLI_FLAGS = [
|
|
40
|
+
"agent-command",
|
|
41
|
+
"claude",
|
|
42
|
+
"codex",
|
|
43
|
+
"cwd",
|
|
44
|
+
"deepseek",
|
|
45
|
+
"dir",
|
|
46
|
+
"format",
|
|
47
|
+
"full",
|
|
48
|
+
"gemini",
|
|
49
|
+
"h",
|
|
50
|
+
"help",
|
|
51
|
+
"json",
|
|
52
|
+
"link",
|
|
53
|
+
"no-color",
|
|
54
|
+
"question",
|
|
55
|
+
"quiet",
|
|
56
|
+
"repo",
|
|
57
|
+
"resume",
|
|
58
|
+
"run",
|
|
59
|
+
"v",
|
|
60
|
+
"verbose",
|
|
61
|
+
"version",
|
|
62
|
+
];
|
|
63
|
+
/** "changed-from" -> "changedFrom". A name with no dash comes back as-is. */
|
|
64
|
+
function kebabToCamel(name) {
|
|
65
|
+
return name.replace(/-([a-z0-9])/g, (_, ch) => ch.toUpperCase());
|
|
66
|
+
}
|
|
67
|
+
/** "changedFrom" -> "changed-from". A name with no capitals comes back as-is. */
|
|
68
|
+
function camelToKebab(name) {
|
|
69
|
+
return name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
70
|
+
}
|
|
71
|
+
/** The option key a declared flag help entry documents: the first token
|
|
72
|
+
* of its display name, without the leading dashes ("--changed-from REF"
|
|
73
|
+
* -> "changed-from"). */
|
|
74
|
+
function flagNameToKey(name) {
|
|
75
|
+
return name.trim().split(/\s+/)[0].replace(/^-+/, "");
|
|
76
|
+
}
|
|
77
|
+
/** The full accepted-key set for one row: the row's declared flags plus
|
|
78
|
+
* the global set, each in BOTH its kebab-case and camelCase spelling. */
|
|
79
|
+
function knownKeysFor(cli) {
|
|
80
|
+
const known = new Set();
|
|
81
|
+
const add = (key) => {
|
|
82
|
+
known.add(key);
|
|
83
|
+
known.add(kebabToCamel(key));
|
|
84
|
+
known.add(camelToKebab(key));
|
|
85
|
+
};
|
|
86
|
+
for (const name of exports.GLOBAL_CLI_FLAGS)
|
|
87
|
+
add(name);
|
|
88
|
+
for (const flag of cli.flags ?? [])
|
|
89
|
+
add(flagNameToKey(flag.name));
|
|
90
|
+
return known;
|
|
91
|
+
}
|
|
92
|
+
/** The option keys this row does not know about (pure; exported for unit
|
|
93
|
+
* tests). Empty unless the row declares `flagsComplete`. */
|
|
94
|
+
function unknownFlagKeys(cli, options) {
|
|
95
|
+
if (!cli.flagsComplete)
|
|
96
|
+
return [];
|
|
97
|
+
const known = knownKeysFor(cli);
|
|
98
|
+
return Object.keys(options).filter((key) => !known.has(key));
|
|
99
|
+
}
|
|
100
|
+
/** Write ONE warning line to `stream` when a `flagsComplete` row got an
|
|
101
|
+
* option it does not read — and only when the stream is a real TTY, so
|
|
102
|
+
* no piped caller, script, or conformance case can ever see it. stdout
|
|
103
|
+
* and the exit code are untouched either way. */
|
|
104
|
+
function warnUnknownFlags(cli, options, stream = process.stderr) {
|
|
105
|
+
if (!stream.isTTY)
|
|
106
|
+
return;
|
|
107
|
+
const unknown = unknownFlagKeys(cli, options);
|
|
108
|
+
if (unknown.length === 0)
|
|
109
|
+
return;
|
|
110
|
+
const label = unknown.length === 1 ? "flag" : "flags";
|
|
111
|
+
const list = unknown.map((key) => `--${key}`).join(", ");
|
|
112
|
+
stream.write(`cw: warning: unknown ${label} ${list} (see: cw help ${cli.path[0]})\n`);
|
|
113
|
+
}
|
package/dist/cli/io.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// cli/io.ts — shared CLI input/output helpers.
|
|
3
3
|
//
|
|
4
|
-
// Byte-exact port of src/cli/io.ts in the old build. Pure + zero-dep
|
|
5
|
-
//
|
|
4
|
+
// Byte-exact port of src/cli/io.ts in the old build. Pure + zero-dep JSON
|
|
5
|
+
// stdout, plus one impure TTY-aware help formatter. See SPEC/cli-surface.md
|
|
6
|
+
// "Shared io helpers". Arg-coercion helpers (`required`/`optionalArg`/
|
|
7
|
+
// `wantsJson`) moved to core/util/cli-args.ts (architecture-review P2) —
|
|
8
|
+
// wiring/capability-table/*.ts needed them directly, and a cli/-layer file
|
|
9
|
+
// may not be imported by wiring/ (scripts/purity-gate.js's layer rule).
|
|
6
10
|
//
|
|
7
11
|
// MILESTONE 11 (reporting/observability) adds `styledHelp` — the one
|
|
8
12
|
// place `formatHelp()`'s plain text gets its "Cool Workflow" header
|
|
@@ -11,10 +15,7 @@
|
|
|
11
15
|
// shell/term.ts's env/TTY read.
|
|
12
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
17
|
exports.styledHelp = styledHelp;
|
|
14
|
-
exports.required = required;
|
|
15
|
-
exports.optionalArg = optionalArg;
|
|
16
18
|
exports.printJson = printJson;
|
|
17
|
-
exports.wantsJson = wantsJson;
|
|
18
19
|
const help_1 = require("../core/format/help");
|
|
19
20
|
const safe_json_1 = require("../core/format/safe-json");
|
|
20
21
|
const term_1 = require("../shell/term");
|
|
@@ -24,17 +25,6 @@ function styledHelp() {
|
|
|
24
25
|
const text = (0, help_1.formatHelp)();
|
|
25
26
|
return text.replace(/^Cool Workflow\n/, `${(0, term_1.bold)("Cool Workflow")}\n`);
|
|
26
27
|
}
|
|
27
|
-
/** Require a positional/option value or fail with a copy-pasteable recovery tip. */
|
|
28
|
-
function required(value, label) {
|
|
29
|
-
if (!value) {
|
|
30
|
-
throw new Error(`Missing ${label}.\n Tip: find run ids with "cw run list" or create one with "cw quickstart"`);
|
|
31
|
-
}
|
|
32
|
-
return value;
|
|
33
|
-
}
|
|
34
|
-
/** Normalize an optional CLI arg to a trimmed non-empty string, else undefined. */
|
|
35
|
-
function optionalArg(value) {
|
|
36
|
-
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
37
|
-
}
|
|
38
28
|
/** Machine payload to stdout (stdout = data; never colored, never chrome).
|
|
39
29
|
* Byte-capped via safeJsonStringify — an aggregate result too large to be
|
|
40
30
|
* useful (or large enough to blow V8's string limit) prints a small
|
|
@@ -42,7 +32,3 @@ function optionalArg(value) {
|
|
|
42
32
|
function printJson(value) {
|
|
43
33
|
process.stdout.write(`${(0, safe_json_1.safeJsonStringify)(value)}\n`);
|
|
44
34
|
}
|
|
45
|
-
/** True when the caller asked for JSON output (`--json` or `--format json`). */
|
|
46
|
-
function wantsJson(options) {
|
|
47
|
-
return Boolean(options.json || options.format === "json");
|
|
48
|
-
}
|
package/dist/cli/parseargv.js
CHANGED
|
@@ -140,13 +140,19 @@ function levenshtein(a, b) {
|
|
|
140
140
|
}
|
|
141
141
|
/** src/orchestrator.ts:875-887 — nearest known command by edit distance.
|
|
142
142
|
* Gives `undefined` when the input is under 2 chars, or when the best
|
|
143
|
-
* distance is over 3 or over half the input length.
|
|
143
|
+
* distance is over 3 or over half the input length. Never gives back the
|
|
144
|
+
* input itself: a caller only asks for a suggestion when the input did
|
|
145
|
+
* NOT resolve, so a distance-0 self-match (an alias token like
|
|
146
|
+
* `audit-run` that IS in KNOWN_COMMANDS but has no help rows of its own)
|
|
147
|
+
* was a hint that pointed at the very thing the user just typed. */
|
|
144
148
|
function suggestCommand(input) {
|
|
145
149
|
if (!input || input.length < 2)
|
|
146
150
|
return undefined;
|
|
147
151
|
let best;
|
|
148
152
|
let bestDistance = Infinity;
|
|
149
153
|
for (const candidate of exports.KNOWN_COMMANDS) {
|
|
154
|
+
if (candidate === input)
|
|
155
|
+
continue;
|
|
150
156
|
const distance = levenshtein(input, candidate);
|
|
151
157
|
if (distance < bestDistance) {
|
|
152
158
|
bestDistance = distance;
|