@skill-map/cli 0.40.1 → 0.42.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.
Files changed (39) hide show
  1. package/dist/cli/tutorial/sm-tutorial/SKILL.md +10 -1
  2. package/dist/cli.js +429 -95
  3. package/dist/cli.js.map +1 -1
  4. package/dist/index.js +39 -29
  5. package/dist/index.js.map +1 -1
  6. package/dist/kernel/index.d.ts +101 -0
  7. package/dist/kernel/index.js +39 -29
  8. package/dist/kernel/index.js.map +1 -1
  9. package/dist/migrations/001_initial.sql +8 -0
  10. package/dist/ui/chunk-5GD2GBPS.js +2190 -0
  11. package/dist/ui/{chunk-4HTOYDCM.js → chunk-5WJRN3LD.js} +1 -1
  12. package/dist/ui/{chunk-3SI3TVER.js → chunk-C2YUQODZ.js} +2 -2
  13. package/dist/ui/{chunk-NGIFGXW7.js → chunk-CFJBTDAA.js} +1 -1
  14. package/dist/ui/{chunk-YWWD62BR.js → chunk-HEJCH7BA.js} +1 -1
  15. package/dist/ui/chunk-HFPA56IM.js +1 -0
  16. package/dist/ui/chunk-HHPSCDLM.js +315 -0
  17. package/dist/ui/chunk-HP375T2O.js +2 -0
  18. package/dist/ui/chunk-HWP3HM55.js +123 -0
  19. package/dist/ui/{chunk-ZAEGBMF7.js → chunk-IUDL3NDH.js} +1 -1
  20. package/dist/ui/{chunk-Z3C2OSRL.js → chunk-JPYAASHN.js} +1 -1
  21. package/dist/ui/chunk-PZ6Q5AOT.js +1 -0
  22. package/dist/ui/chunk-XJL4DZ4M.js +1 -0
  23. package/dist/ui/{chunk-W2JMLJCF.js → chunk-XOHD5XWA.js} +1 -1
  24. package/dist/ui/chunk-YL6SWAFJ.js +1024 -0
  25. package/dist/ui/index.html +2 -2
  26. package/dist/ui/main-7VYTTJP7.js +3 -0
  27. package/dist/ui/{styles-6H4GSOHY.css → styles-HI4A6IWA.css} +1 -1
  28. package/migrations/001_initial.sql +8 -0
  29. package/package.json +2 -2
  30. package/dist/ui/chunk-4X4GYACU.js +0 -123
  31. package/dist/ui/chunk-7Q3IO77R.js +0 -317
  32. package/dist/ui/chunk-FL6RV2IG.js +0 -2
  33. package/dist/ui/chunk-HGNE4UVQ.js +0 -1
  34. package/dist/ui/chunk-IS5ULQSF.js +0 -1
  35. package/dist/ui/chunk-KVWYVO6I.js +0 -1
  36. package/dist/ui/chunk-N4XX4WPE.js +0 -2190
  37. package/dist/ui/chunk-P7TXZKUX.js +0 -2
  38. package/dist/ui/chunk-UVVXMEZT.js +0 -1025
  39. package/dist/ui/main-F7N5RV4Y.js +0 -3
@@ -1021,6 +1021,20 @@ interface ScanResult {
1021
1021
  providers: string[];
1022
1022
  /** Implementation metadata. Populated by `runScan` for self-describing output. */
1023
1023
  scannedBy?: ScanScannedBy;
1024
+ /**
1025
+ * Effective recommended cap on the number of files the walker accepted
1026
+ * during this scan (`scan.maxNodes` from settings, default 256). The UI
1027
+ * raises the "oversized graph" banner when
1028
+ * `stats.filesWalked >= recommendedNodeLimit`. Absent on synthetic fixtures
1029
+ * that bypass the walker.
1030
+ */
1031
+ recommendedNodeLimit?: number;
1032
+ /**
1033
+ * Override applied via `--max-nodes <N>` on the verb that ran the scan, or
1034
+ * `null` when no override was passed (the value above came from the
1035
+ * setting). Bidirectional: can raise OR lower the recommended limit.
1036
+ */
1037
+ overrideMaxNodes?: number | null;
1024
1038
  nodes: Node[];
1025
1039
  links: Link[];
1026
1040
  issues: Issue[];
@@ -1961,9 +1975,80 @@ type TProviderKindIcon = {
1961
1975
  kind: 'svg';
1962
1976
  path: string;
1963
1977
  };
1978
+ /**
1979
+ * Presentation contract for the Provider's OWN identity, distinct from
1980
+ * its per-kind visuals (`IProviderKindUi`). Drives the active-lens
1981
+ * dropdown label, the topbar lens chip, and the per-node provider chip
1982
+ * on cards. Reaches the UI via the `providerRegistry` field embedded in
1983
+ * REST envelopes (sibling of `kindRegistry`). Unlike kind colors
1984
+ * (normalised across Providers so every `agent` paints the same),
1985
+ * Provider colors are deliberately distinct so the chip tells the user
1986
+ * at a glance which platform a node came from. Mirrors
1987
+ * `spec/schemas/extensions/provider.schema.json#/properties/ui`.
1988
+ */
1989
+ interface IProviderUi {
1990
+ /**
1991
+ * Human-readable Provider name shown in the lens dropdown, the topbar
1992
+ * lens chip, and the per-node provider chip (e.g. `'Claude'`,
1993
+ * `'OpenAI Codex'`, `'Antigravity'`, `'Open Skills'`, `'Markdown'`).
1994
+ */
1995
+ label: string;
1996
+ /** Base hex color (`#RRGGBB`) for the light-theme provider chip. */
1997
+ color: string;
1998
+ /** Optional dark-theme variant of `color`. Falls back to `color`. */
1999
+ colorDark?: string;
2000
+ /** Optional decorative emoji fallback when `icon` is absent. */
2001
+ emoji?: string;
2002
+ /** Optional discriminated icon descriptor (preferred over `emoji`). */
2003
+ icon?: TProviderKindIcon;
2004
+ /**
2005
+ * When `true`, the UI does NOT paint this Provider's chip on node
2006
+ * cards. Reserved for the universal `markdown` fallback (carried by
2007
+ * the majority of nodes, so badging every generic `.md` would be
2008
+ * noise). The Provider still appears in the lens dropdown and the
2009
+ * topbar lens chip; only the per-card badge is suppressed.
2010
+ */
2011
+ hideChip?: boolean;
2012
+ }
2013
+ /**
2014
+ * Auto-detection markers for the active-provider lens. The lens resolver
2015
+ * checks each marker path (relative to the scope root) and, when present,
2016
+ * suggests this Provider as a candidate lens. Replaces the former
2017
+ * hardcoded detection table: the detectable set now derives from the
2018
+ * registered Providers. Mirrors
2019
+ * `spec/schemas/extensions/provider.schema.json#/properties/detect`.
2020
+ */
2021
+ interface IProviderDetect {
2022
+ /**
2023
+ * Paths relative to the scope root whose existence signals this
2024
+ * Provider's presence (e.g. `['.claude']`, `['.codex', 'AGENTS.md']`).
2025
+ * A directory or a file both count; existence is the only test.
2026
+ */
2027
+ markers: string[];
2028
+ }
1964
2029
  interface IProvider extends IExtensionBase {
1965
2030
  /** Discriminant injected by the loader from the folder structure. */
1966
2031
  kind: 'provider';
2032
+ /**
2033
+ * Presentation metadata for the Provider's own identity (lens dropdown
2034
+ * label, topbar lens chip, per-node provider chip). Required so the UI
2035
+ * never hardcodes a closed provider list: it reads every registered
2036
+ * Provider's identity from the `providerRegistry` envelope field.
2037
+ * Distinct from `kinds[*].ui` (per-kind node visuals).
2038
+ *
2039
+ * Named `presentation`, NOT `ui`: the base `IExtensionBase.ui` field is
2040
+ * the view-contributions map (`Record<string, IViewContribution>`,
2041
+ * declared only by `extractor` / `analyzer` kinds). Providers leave that
2042
+ * inherited field undefined and carry their identity here instead.
2043
+ */
2044
+ presentation: IProviderUi;
2045
+ /**
2046
+ * Optional auto-detection markers for the active-provider lens. When
2047
+ * present, the lens resolver auto-suggests this Provider if any marker
2048
+ * path exists under the scope root. Absent means the Provider is never
2049
+ * auto-suggested (it can still be selected manually).
2050
+ */
2051
+ detect?: IProviderDetect;
1967
2052
  /**
1968
2053
  * Catalog of node kinds this Provider emits. Populated by the loader
1969
2054
  * from the `<plugin>/kinds/<kindName>/` directory layout: each subfolder
@@ -3330,6 +3415,22 @@ interface RunScanOptions {
3330
3415
  * pass `string | null` explicitly, never `undefined`.
3331
3416
  */
3332
3417
  activeProvider?: string | null;
3418
+ /**
3419
+ * Recommended cap on the number of nodes the walker classifies
3420
+ * (mirror of `scan.maxNodes` in settings, default 256). Threaded
3421
+ * through to `walkAndExtract` so the cap can fire and so
3422
+ * `ScanResult.recommendedNodeLimit` is populated. Absent → the
3423
+ * orchestrator falls back to 256 (the design default), keeping
3424
+ * out-of-band callers and synthetic fixtures safe.
3425
+ */
3426
+ recommendedNodeLimit?: number;
3427
+ /**
3428
+ * Per-invocation override of the recommended cap (when `--max-nodes
3429
+ * <N>` was passed). `null` (or absent) means no override; the
3430
+ * recommended limit applies. Bidirectional: any positive integer
3431
+ * replaces the recommended limit for the duration of this scan.
3432
+ */
3433
+ overrideMaxNodes?: number | null;
3333
3434
  }
3334
3435
  /**
3335
3436
  * Same as `runScan` but also returns the rename heuristic's `RenameOp[]`
@@ -101,7 +101,7 @@ import cl100k_base from "js-tiktoken/ranks/cl100k_base";
101
101
  // package.json
102
102
  var package_default = {
103
103
  name: "@skill-map/cli",
104
- version: "0.40.1",
104
+ version: "0.42.0",
105
105
  description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
106
106
  license: "MIT",
107
107
  type: "module",
@@ -775,6 +775,7 @@ var defaults_default = {
775
775
  strict: false,
776
776
  followSymlinks: false,
777
777
  maxFileSizeBytes: 1048576,
778
+ maxNodes: 256,
778
779
  watch: {
779
780
  debounceMs: 300
780
781
  },
@@ -1049,21 +1050,8 @@ function loadConfigForScope(opts) {
1049
1050
  }
1050
1051
 
1051
1052
  // core/config/active-provider.ts
1052
- var DETECTION_RULES = [
1053
- { providerId: "claude", marker: ".claude" },
1054
- // `gemini` retired 2026-05-22: Google replaced the Gemini CLI with the
1055
- // Antigravity CLI (released 2026-05-19; Gemini CLI sunsets 2026-06-18).
1056
- // Antigravity adopted the open-standard `.agents/` instead of a
1057
- // vendor-specific directory, so detection of a Google CLI project
1058
- // falls through to the universal `agent-skills` lens (`.agents/`
1059
- // already classifies via that neutral provider). The lens can still
1060
- // be set manually via `sm config set activeProvider antigravity`.
1061
- { providerId: "openai", marker: ".codex" },
1062
- { providerId: "openai", marker: "AGENTS.md" },
1063
- { providerId: "cursor", marker: ".cursor" }
1064
- ];
1065
- function resolveActiveProvider(cwd) {
1066
- const detected = detectProvidersFromFilesystem(cwd);
1053
+ function resolveActiveProvider(cwd, providers = []) {
1054
+ const detected = detectProvidersFromFilesystem(cwd, providers);
1067
1055
  const fromConfig = readConfigValue("activeProvider", { cwd });
1068
1056
  if (typeof fromConfig === "string" && fromConfig.length > 0) {
1069
1057
  return { resolved: fromConfig, source: "config", detected };
@@ -1073,14 +1061,16 @@ function resolveActiveProvider(cwd) {
1073
1061
  }
1074
1062
  return { resolved: null, source: "none", detected };
1075
1063
  }
1076
- function detectProvidersFromFilesystem(cwd) {
1064
+ function detectProvidersFromFilesystem(cwd, providers) {
1077
1065
  const seen = /* @__PURE__ */ new Set();
1078
1066
  const out = [];
1079
- for (const rule of DETECTION_RULES) {
1080
- if (seen.has(rule.providerId)) continue;
1081
- if (!existsSync5(join6(cwd, rule.marker))) continue;
1082
- seen.add(rule.providerId);
1083
- out.push(rule.providerId);
1067
+ for (const provider of providers) {
1068
+ if (seen.has(provider.id)) continue;
1069
+ const markers = provider.detect?.markers;
1070
+ if (!markers || markers.length === 0) continue;
1071
+ if (!markers.some((marker) => existsSync5(join6(cwd, marker)))) continue;
1072
+ seen.add(provider.id);
1073
+ out.push(provider.id);
1084
1074
  }
1085
1075
  return out;
1086
1076
  }
@@ -2855,17 +2845,26 @@ async function walkAndExtract(opts) {
2855
2845
  const walkOptions = opts.ignoreFilter ? { ignoreFilter: opts.ignoreFilter } : {};
2856
2846
  let filesWalked = 0;
2857
2847
  let index = 0;
2848
+ const effectiveMaxNodes = opts.overrideMaxNodes ?? opts.recommendedNodeLimit;
2849
+ let capReached = false;
2858
2850
  const activeProviders = opts.providers.filter((provider) => {
2859
2851
  if (!provider.gatedByActiveLens) return true;
2860
2852
  if (opts.activeProvider === null) return true;
2861
2853
  return provider.id === opts.activeProvider;
2862
2854
  });
2863
- for (const provider of activeProviders) {
2855
+ const advance = async (raw, provider) => {
2856
+ const advanced = await processRawNode(raw, provider, wctx, accum, claimedPaths, index + 1);
2857
+ if (advanced) index += 1;
2858
+ };
2859
+ outer: for (const provider of activeProviders) {
2864
2860
  for await (const raw of resolveProviderWalk(provider)(opts.roots, walkOptions)) {
2865
2861
  filesWalked += 1;
2866
2862
  if (claimedPaths.has(raw.path)) continue;
2867
- const advanced = await processRawNode(raw, provider, wctx, accum, claimedPaths, index + 1);
2868
- if (advanced) index += 1;
2863
+ if (accum.nodes.length >= effectiveMaxNodes) {
2864
+ capReached = true;
2865
+ break outer;
2866
+ }
2867
+ await advance(raw, provider);
2869
2868
  }
2870
2869
  }
2871
2870
  const orphanSidecars = discoverOrphanSidecars(opts.roots);
@@ -2876,6 +2875,9 @@ async function walkAndExtract(opts) {
2876
2875
  cachedPaths: accum.cachedPaths,
2877
2876
  frontmatterIssues: accum.frontmatterIssues,
2878
2877
  filesWalked,
2878
+ recommendedNodeLimit: opts.recommendedNodeLimit,
2879
+ overrideMaxNodes: opts.overrideMaxNodes,
2880
+ capReached,
2879
2881
  enrichments: [...accum.enrichmentBuffer.values()],
2880
2882
  extractorRuns: accum.extractorRuns,
2881
2883
  contributions: accum.contributionsBuffer,
@@ -3139,7 +3141,11 @@ async function runScanInternal(_kernel, options) {
3139
3141
  const scanStartedEvent = makeEvent("scan.started", { roots: options.roots });
3140
3142
  emitter.emit(scanStartedEvent);
3141
3143
  await hookDispatcher.dispatch("scan.started", scanStartedEvent);
3142
- const activeProviderId = resolveActiveProviderOption(options.activeProvider, options.roots);
3144
+ const activeProviderId = resolveActiveProviderOption(
3145
+ options.activeProvider,
3146
+ options.roots,
3147
+ exts.providers
3148
+ );
3143
3149
  const walked = await walkAndExtract({
3144
3150
  providers: exts.providers,
3145
3151
  extractors: exts.extractors,
@@ -3154,7 +3160,9 @@ async function runScanInternal(_kernel, options) {
3154
3160
  priorExtractorRuns: setup.priorExtractorRuns,
3155
3161
  providerFrontmatter: setup.providerFrontmatter,
3156
3162
  pluginStores: options.pluginStores,
3157
- activeProvider: activeProviderId
3163
+ activeProvider: activeProviderId,
3164
+ recommendedNodeLimit: options.recommendedNodeLimit ?? 256,
3165
+ overrideMaxNodes: options.overrideMaxNodes ?? null
3158
3166
  });
3159
3167
  const activeProvider = activeProviderId ? exts.providers.find((p) => p.id === activeProviderId) ?? null : null;
3160
3168
  const resolved = resolveSignals({
@@ -3322,6 +3330,8 @@ function buildScanReturn(walked, issues, renameOps, stats, options, setup) {
3322
3330
  roots: options.roots,
3323
3331
  providers: setup.exts.providers.map((a) => a.id),
3324
3332
  scannedBy: SCANNED_BY,
3333
+ recommendedNodeLimit: walked.recommendedNodeLimit,
3334
+ overrideMaxNodes: walked.overrideMaxNodes,
3325
3335
  nodes: walked.nodes,
3326
3336
  links: walked.internalLinks,
3327
3337
  issues,
@@ -3344,12 +3354,12 @@ function validateRoots(roots) {
3344
3354
  }
3345
3355
  }
3346
3356
  }
3347
- function resolveActiveProviderOption(optionValue, roots) {
3357
+ function resolveActiveProviderOption(optionValue, roots, providers) {
3348
3358
  if (optionValue !== void 0) return optionValue;
3349
3359
  for (const root of roots) {
3350
3360
  const absRoot = isAbsolute4(root) ? root : resolve11(root);
3351
3361
  if (!existsSync11(absRoot)) continue;
3352
- const detected = resolveActiveProvider(absRoot).resolved;
3362
+ const detected = resolveActiveProvider(absRoot, providers).resolved;
3353
3363
  if (detected !== null) return detected;
3354
3364
  }
3355
3365
  return null;