@miphamai/cli 0.40.0 → 0.41.0

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/bin/mipham.ts CHANGED
@@ -1204,49 +1204,20 @@ npm: https://www.npmjs.com/package/@miphamai/cli`)
1204
1204
  const handled = await runWorkflowCLI()
1205
1205
  if (handled) return
1206
1206
 
1207
- // ── Unknown command detection ──────────────────────────────────────────────
1208
- // After all known subcommands are checked, any remaining positional argument
1209
- // is probably a typo. Show error + suggestions instead of silently launching CLI.
1210
- const KNOWN_COMMANDS = [
1211
- 'update',
1212
- 'upgrade',
1213
- 'plugin',
1214
- 'workflow',
1215
- 'daemon',
1216
- 'attach',
1217
- 'agents',
1218
- 'agent',
1219
- 'goal',
1220
- 'schedule',
1221
- 'token',
1222
- 'help',
1223
- ]
1224
- const firstArg = process.argv.slice(2).find((a) => !a.startsWith('-'))
1225
- if (firstArg) {
1226
- // Levenshtein distance to find closest match
1227
- const distance = (a: string, b: string): number => {
1228
- const m = a.length
1229
- const n = b.length
1230
- const dp: number[][] = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0))
1231
- for (let i = 0; i <= m; i++) dp[i]![0] = i
1232
- for (let j = 0; j <= n; j++) dp[0]![j] = j
1233
- for (let i = 1; i <= m; i++)
1234
- for (let j = 1; j <= n; j++)
1235
- dp[i]![j] =
1236
- a[i - 1] === b[j - 1]
1237
- ? dp[i - 1]![j - 1]!
1238
- : 1 + Math.min(dp[i - 1]![j]!, dp[i]![j - 1]!, dp[i - 1]![j - 1]!)
1239
- return dp[m]![n]!
1240
- }
1241
-
1242
- const best = KNOWN_COMMANDS.map((cmd) => ({ cmd, dist: distance(firstArg, cmd) }))
1243
- .filter((c) => c.dist <= 3)
1244
- .sort((a, b) => a.dist - b.dist)
1245
- const suggestions = best.map((c) => `'mipham ${c.cmd}'`).join(', ')
1246
-
1247
- console.error(`Unknown command: mipham ${firstArg}`)
1248
- if (suggestions) {
1249
- console.error(`Did you mean: ${suggestions}?`)
1207
+ // ── Unknown command / option detection ─────────────────────────────────────
1208
+ // After all known subcommands are checked, any remaining argument is probably
1209
+ // a typo. Show error + suggestions instead of silently launching the CLI.
1210
+ const { detectUnknownArgument } = await import('../src/shared/arg-validation')
1211
+ const unknown = detectUnknownArgument(process.argv.slice(2))
1212
+ if (unknown) {
1213
+ const isOption = unknown.kind === 'option'
1214
+ const prefix = isOption ? '' : 'mipham '
1215
+ console.error(`Unknown ${unknown.kind}: ${prefix}${unknown.arg}`)
1216
+ if (unknown.suggestions.length > 0) {
1217
+ const rendered = unknown.suggestions
1218
+ .map((s) => (isOption ? `'${s}'` : `'mipham ${s}'`))
1219
+ .join(', ')
1220
+ console.error(`Did you mean: ${rendered}?`)
1250
1221
  }
1251
1222
  console.error(`Run 'mipham --help' for usage.`)
1252
1223
  process.exit(1)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miphamai/cli",
3
- "version": "0.40.0",
3
+ "version": "0.41.0",
4
4
  "description": "Mipham Code — Multi-model open-core intelligent coding terminal by MiphamAI",
5
5
  "keywords": [
6
6
  "ai",
@@ -0,0 +1,78 @@
1
+ /**
2
+ * CLI argument validation for the `mipham` entry point.
3
+ *
4
+ * After every known subcommand (`update`, `daemon`, `attach`, ...) has been
5
+ * dispatched, any remaining argument is a likely typo. This module classifies
6
+ * a leftover argument as an unknown command (positional) or unknown option
7
+ * (leading `-`/`--`) and suggests the closest known alternative via
8
+ * Levenshtein distance, instead of silently launching the interactive CLI.
9
+ */
10
+
11
+ export interface UnknownArgument {
12
+ kind: 'command' | 'option'
13
+ arg: string
14
+ suggestions: string[]
15
+ }
16
+
17
+ const KNOWN_COMMANDS = [
18
+ 'update',
19
+ 'upgrade',
20
+ 'plugin',
21
+ 'workflow',
22
+ 'daemon',
23
+ 'attach',
24
+ 'agents',
25
+ 'agent',
26
+ 'goal',
27
+ 'schedule',
28
+ 'token',
29
+ 'help',
30
+ ]
31
+
32
+ const KNOWN_FLAGS = ['--version', '-v', '-V', '--help', '-h', '--dump-config', '--safe-mode']
33
+
34
+ /** Levenshtein edit distance between two strings. */
35
+ function levenshtein(a: string, b: string): number {
36
+ const m = a.length
37
+ const n = b.length
38
+ const dp: number[][] = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0))
39
+ for (let i = 0; i <= m; i++) dp[i]![0] = i
40
+ for (let j = 0; j <= n; j++) dp[0]![j] = j
41
+ for (let i = 1; i <= m; i++)
42
+ for (let j = 1; j <= n; j++)
43
+ dp[i]![j] =
44
+ a[i - 1] === b[j - 1]
45
+ ? dp[i - 1]![j - 1]!
46
+ : 1 + Math.min(dp[i - 1]![j]!, dp[i]![j - 1]!, dp[i - 1]![j - 1]!)
47
+ return dp[m]![n]!
48
+ }
49
+
50
+ /** Closest candidates within `maxDist` edits, nearest first. */
51
+ function closest(target: string, candidates: string[], maxDist = 3): string[] {
52
+ return candidates
53
+ .map((c) => ({ c, dist: levenshtein(target, c) }))
54
+ .filter((x) => x.dist <= maxDist)
55
+ .sort((a, b) => a.dist - b.dist)
56
+ .map((x) => x.c)
57
+ }
58
+
59
+ /**
60
+ * Return the first unknown argument (command or option), or null if all
61
+ * arguments are recognized. Positional commands are checked first so an
62
+ * unknown command keeps its existing message; unknown options (e.g.
63
+ * `--cersion`, a typo of `--version`) are reported when no positional
64
+ * command is present, instead of falling through to the interactive CLI.
65
+ */
66
+ export function detectUnknownArgument(args: string[]): UnknownArgument | null {
67
+ const firstArg = args.find((a) => !a.startsWith('-'))
68
+ if (firstArg && !KNOWN_COMMANDS.includes(firstArg)) {
69
+ return { kind: 'command', arg: firstArg, suggestions: closest(firstArg, KNOWN_COMMANDS) }
70
+ }
71
+
72
+ const unknownFlag = args.find((a) => a.startsWith('-') && !KNOWN_FLAGS.includes(a))
73
+ if (unknownFlag) {
74
+ return { kind: 'option', arg: unknownFlag, suggestions: closest(unknownFlag, KNOWN_FLAGS) }
75
+ }
76
+
77
+ return null
78
+ }
@@ -9,7 +9,7 @@
9
9
  export const PACKAGE_NAME = '@miphamai/cli' as const
10
10
 
11
11
  /** 当前发布版本 */
12
- export const PACKAGE_VERSION = '0.40.0' as const
12
+ export const PACKAGE_VERSION = '0.41.0' as const
13
13
 
14
14
  /** npm install 全局安装命令 */
15
15
  export const NPM_INSTALL_COMMAND = `npm install -g ${PACKAGE_NAME}` as const