@skill-map/cli 0.12.0 → 0.13.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/dist/cli.js CHANGED
@@ -50,7 +50,18 @@ function tx(template, vars = {}) {
50
50
 
51
51
  // cli/i18n/entry.texts.ts
52
52
  var ENTRY_TEXTS = {
53
- bareNoProject: 'No skill-map project found in {{cwd}}.\nRun "sm init" to bootstrap one, or "sm --help" to see all commands.\n'
53
+ bareNoProject: 'No skill-map project found in {{cwd}}.\nRun "sm init" to bootstrap one, or "sm --help" to see all commands.\n',
54
+ parseErrorHeadline: "sm: {{message}}",
55
+ parseErrorUnknownOption: "unknown option '{{name}}'",
56
+ parseErrorUnknownOptionForVerb: "{{verb}}: unknown option '{{name}}'",
57
+ parseErrorUnknownCommand: "unknown command '{{name}}'",
58
+ parseErrorIncompleteCommand: "incomplete command '{{name}}'",
59
+ parseErrorSubcommandList: "Available subcommands: {{suggestions}}.",
60
+ parseErrorVerbUsage: "{{verb}}: {{message}}",
61
+ parseErrorFlagSuggestion: "Did you mean '{{suggestion}}'?",
62
+ parseErrorVerbSuggestion: "Did you mean {{suggestions}}?",
63
+ parseErrorVerbHelpHint: "Run 'sm help {{verb}}' for usage.",
64
+ parseErrorFooter: "Run 'sm help' to see the full command list."
54
65
  };
55
66
 
56
67
  // kernel/ports/logger.ts
@@ -264,6 +275,139 @@ var ExitCode = {
264
275
  NotFound: 5
265
276
  };
266
277
 
278
+ // cli/util/parse-error.ts
279
+ function isClipanionParseError(err) {
280
+ if (!err || typeof err !== "object") return false;
281
+ const e = err;
282
+ if (typeof e.name !== "string") return false;
283
+ if (!Array.isArray(e.input)) return false;
284
+ return e.name === "UnknownSyntaxError" || e.name === "AmbiguousSyntaxError";
285
+ }
286
+ function formatParseError(params) {
287
+ const { args: args2, verbPaths, error } = params;
288
+ const firstToken = args2[0] ?? "";
289
+ const offendingFlag = extractOffendingFlag(error.message);
290
+ if (offendingFlag !== null) {
291
+ const verbPrefix2 = matchedVerbPrefix(args2, verbPaths);
292
+ const suggestion2 = suggestFlag(offendingFlag);
293
+ const headline = verbPrefix2 ? tx(ENTRY_TEXTS.parseErrorUnknownOptionForVerb, { verb: verbPrefix2, name: offendingFlag }) : tx(ENTRY_TEXTS.parseErrorUnknownOption, { name: offendingFlag });
294
+ return renderError(headline, suggestion2);
295
+ }
296
+ if (firstToken === "") {
297
+ return renderError(error.message.trim(), null);
298
+ }
299
+ const verbPrefix = matchedVerbPrefix(args2, verbPaths);
300
+ if (verbPrefix) {
301
+ const headline = tx(ENTRY_TEXTS.parseErrorVerbUsage, { verb: verbPrefix, message: error.message.trim() });
302
+ return renderError(headline, tx(ENTRY_TEXTS.parseErrorVerbHelpHint, { verb: verbPrefix }));
303
+ }
304
+ const subcommands = subcommandsUnder(firstToken, verbPaths);
305
+ if (subcommands.length > 0) {
306
+ const headline = tx(ENTRY_TEXTS.parseErrorIncompleteCommand, { name: firstToken });
307
+ const suggestion2 = tx(ENTRY_TEXTS.parseErrorSubcommandList, {
308
+ suggestions: formatSuggestionList(subcommands)
309
+ });
310
+ return renderError(headline, suggestion2);
311
+ }
312
+ const candidates = closestVerbs(firstToken, verbPaths);
313
+ const suggestion = candidates.length > 0 ? tx(ENTRY_TEXTS.parseErrorVerbSuggestion, { suggestions: formatSuggestionList(candidates) }) : null;
314
+ return renderError(tx(ENTRY_TEXTS.parseErrorUnknownCommand, { name: firstToken }), suggestion);
315
+ }
316
+ function subcommandsUnder(namespace, verbPaths) {
317
+ const matches = verbPaths.filter((path) => path.length >= 2 && path[0] === namespace).map((path) => path.join(" ")).sort();
318
+ return matches.slice(0, 3);
319
+ }
320
+ function matchedVerbPrefix(args2, verbPaths) {
321
+ const leading = [];
322
+ for (const tok of args2) {
323
+ if (tok.startsWith("-")) break;
324
+ leading.push(tok);
325
+ }
326
+ if (leading.length === 0) return "";
327
+ let best = [];
328
+ for (const path of verbPaths) {
329
+ if (path.length > leading.length) continue;
330
+ const matches = path.every((tok, i) => leading[i] === tok);
331
+ if (matches && path.length > best.length) best = path;
332
+ }
333
+ return best.join(" ");
334
+ }
335
+ function renderError(headline, suggestion) {
336
+ const lines = [tx(ENTRY_TEXTS.parseErrorHeadline, { message: headline })];
337
+ if (suggestion) lines.push(suggestion);
338
+ lines.push(ENTRY_TEXTS.parseErrorFooter);
339
+ return lines.join("\n") + "\n";
340
+ }
341
+ function extractOffendingFlag(message) {
342
+ const match = /Unsupported option name \("([^"]+)"\)/.exec(message);
343
+ return match ? match[1] : null;
344
+ }
345
+ function suggestFlag(token) {
346
+ if (!token.startsWith("-")) return null;
347
+ if (token.startsWith("--")) return null;
348
+ if (token.length <= 2) return null;
349
+ const longForm = "-" + token;
350
+ return tx(ENTRY_TEXTS.parseErrorFlagSuggestion, { suggestion: longForm });
351
+ }
352
+ function editDistance(a, b, max) {
353
+ if (a === b) return 0;
354
+ if (Math.abs(a.length - b.length) > max) return max + 1;
355
+ const m = a.length;
356
+ const n = b.length;
357
+ if (m === 0) return n;
358
+ if (n === 0) return m;
359
+ let prev = Array.from({ length: n + 1 }, (_, i) => i);
360
+ let curr = new Array(n + 1);
361
+ for (let i = 1; i <= m; i++) {
362
+ const rowMin = fillEditRow({ a, b, i, prev, curr });
363
+ if (rowMin > max) return max + 1;
364
+ [prev, curr] = [curr, prev];
365
+ }
366
+ return prev[n];
367
+ }
368
+ function fillEditRow(args2) {
369
+ const { a, b, i, prev, curr } = args2;
370
+ curr[0] = i;
371
+ let rowMin = curr[0];
372
+ for (let j = 1; j < curr.length; j++) {
373
+ const cost = a.charCodeAt(i - 1) === b.charCodeAt(j - 1) ? 0 : 1;
374
+ const value = Math.min(curr[j - 1] + 1, prev[j] + 1, prev[j - 1] + cost);
375
+ curr[j] = value;
376
+ if (value < rowMin) rowMin = value;
377
+ }
378
+ return rowMin;
379
+ }
380
+ function closestVerbs(typed, verbPaths) {
381
+ const target = typed.toLowerCase();
382
+ const distanceCap = target.length <= 4 ? 2 : 3;
383
+ const ranked = [];
384
+ for (const path of verbPaths) {
385
+ const verb = path.join(" ");
386
+ const head = path[0];
387
+ const distHead = editDistance(target, head.toLowerCase(), distanceCap);
388
+ const distFull = editDistance(target, verb.replace(/\s+/g, "").toLowerCase(), distanceCap);
389
+ const distance = Math.min(distHead, distFull);
390
+ if (distance <= distanceCap) ranked.push({ verb, distance });
391
+ }
392
+ ranked.sort((a, b) => a.distance - b.distance || a.verb.localeCompare(b.verb));
393
+ const seen = /* @__PURE__ */ new Set();
394
+ const out = [];
395
+ for (const { verb } of ranked) {
396
+ const head = verb.split(" ")[0];
397
+ if (seen.has(head)) continue;
398
+ seen.add(head);
399
+ out.push(verb);
400
+ if (out.length === 3) break;
401
+ }
402
+ return out;
403
+ }
404
+ function formatSuggestionList(items) {
405
+ const quoted = items.map((s) => `'${s}'`);
406
+ if (quoted.length <= 1) return quoted[0] ?? "";
407
+ if (quoted.length === 2) return `${quoted[0]} or ${quoted[1]}`;
408
+ return `${quoted.slice(0, -1).join(", ")}, or ${quoted[quoted.length - 1]}`;
409
+ }
410
+
267
411
  // cli/util/runtime-context.ts
268
412
  import { homedir } from "os";
269
413
  function defaultRuntimeContext() {
@@ -7034,7 +7178,7 @@ import { Command as Command8, Option as Option8 } from "clipanion";
7034
7178
  // package.json
7035
7179
  var package_default = {
7036
7180
  name: "@skill-map/cli",
7037
- version: "0.12.0",
7181
+ version: "0.13.0",
7038
7182
  description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
7039
7183
  license: "MIT",
7040
7184
  type: "module",
@@ -12509,7 +12653,7 @@ var PLACEHOLDER_HTML = `<!doctype html>
12509
12653
  </head>
12510
12654
  <body>
12511
12655
  <h1>skill-map server is running</h1>
12512
- <p>The UI bundle was not found. Run <code>npm run build --workspace=ui</code> from the repo root, or pass <code>--ui-dist &lt;path&gt;</code>, then restart <code>sm serve</code>.</p>
12656
+ <p>The UI bundle was not found. If you installed <code>@skill-map/cli</code> from npm, this is a packaging bug \u2014 please report it. If you're developing in the monorepo, run <code>npm run build --workspace=ui</code> from the repo root and restart <code>sm serve</code> (or pass <code>--ui-dist &lt;path&gt;</code> to point at a custom build).</p>
12513
12657
  <p>The REST API is available at <code>/api/health</code>.</p>
12514
12658
  </body>
12515
12659
  </html>
@@ -13115,18 +13259,14 @@ function validateWatcherDebounce(value) {
13115
13259
  // server/paths.ts
13116
13260
  import { existsSync as existsSync17, statSync as statSync5 } from "fs";
13117
13261
  import { dirname as dirname9, isAbsolute as isAbsolute5, join as join14, resolve as resolve19 } from "path";
13118
- var DEFAULT_UI_REL = join14("ui", "dist", "browser");
13262
+ import { fileURLToPath as fileURLToPath5 } from "url";
13263
+ var DEFAULT_UI_REL = join14("ui", "dist", "ui", "browser");
13264
+ var PACKAGE_UI_REL = "ui";
13119
13265
  var INDEX_HTML2 = "index.html";
13120
13266
  function resolveDefaultUiDist(ctx) {
13121
- let current = resolve19(ctx.cwd);
13122
- for (let i = 0; i < 64; i++) {
13123
- const candidate = join14(current, DEFAULT_UI_REL);
13124
- if (isUiBundleDir(candidate)) return candidate;
13125
- const parent = dirname9(current);
13126
- if (parent === current) return null;
13127
- current = parent;
13128
- }
13129
- return null;
13267
+ const bundled = resolvePackageBundledUi();
13268
+ if (bundled !== null) return bundled;
13269
+ return walkUpForUi(ctx.cwd);
13130
13270
  }
13131
13271
  function resolveExplicitUiDist(ctx, raw) {
13132
13272
  return isAbsolute5(raw) ? raw : resolve19(ctx.cwd, raw);
@@ -13140,6 +13280,39 @@ function isUiBundleDir(path) {
13140
13280
  return false;
13141
13281
  }
13142
13282
  }
13283
+ function resolvePackageBundledUi() {
13284
+ let here;
13285
+ try {
13286
+ here = dirname9(fileURLToPath5(import.meta.url));
13287
+ } catch {
13288
+ return null;
13289
+ }
13290
+ return resolvePackageBundledUiFrom(here);
13291
+ }
13292
+ function resolvePackageBundledUiFrom(here) {
13293
+ let current = here;
13294
+ for (let i = 0; i < 8; i++) {
13295
+ const candidate = join14(current, PACKAGE_UI_REL);
13296
+ if (isUiBundleDir(candidate)) return candidate;
13297
+ const distHere = join14(current, "dist", PACKAGE_UI_REL);
13298
+ if (isUiBundleDir(distHere)) return distHere;
13299
+ const parent = dirname9(current);
13300
+ if (parent === current) return null;
13301
+ current = parent;
13302
+ }
13303
+ return null;
13304
+ }
13305
+ function walkUpForUi(startDir) {
13306
+ let current = resolve19(startDir);
13307
+ for (let i = 0; i < 64; i++) {
13308
+ const candidate = join14(current, DEFAULT_UI_REL);
13309
+ if (isUiBundleDir(candidate)) return candidate;
13310
+ const parent = dirname9(current);
13311
+ if (parent === current) return null;
13312
+ current = parent;
13313
+ }
13314
+ return null;
13315
+ }
13143
13316
 
13144
13317
  // server/index.ts
13145
13318
  async function createServer(options, extra = {}) {
@@ -14072,6 +14245,25 @@ var logLevel = resolveLogLevel({
14072
14245
  configureLogger(new Logger({ level: logLevel, stream: process.stderr }));
14073
14246
  var bareArgs = args.length === 0 ? resolveBareDefault() : null;
14074
14247
  var routedArgs = routeHelpArgs(bareArgs ?? args, cli);
14248
+ try {
14249
+ cli.process(routedArgs, {
14250
+ stdin: process.stdin,
14251
+ stdout: process.stdout,
14252
+ stderr: process.stderr
14253
+ });
14254
+ } catch (err) {
14255
+ if (isClipanionParseError(err)) {
14256
+ process.stderr.write(
14257
+ formatParseError({
14258
+ args: routedArgs,
14259
+ verbPaths: registeredVerbPaths(cli),
14260
+ error: err
14261
+ })
14262
+ );
14263
+ process.exit(ExitCode.Error);
14264
+ }
14265
+ throw err;
14266
+ }
14075
14267
  var exitCode = await cli.run(routedArgs, {
14076
14268
  stdin: process.stdin,
14077
14269
  stdout: process.stdout,