@skill-map/cli 0.41.0 → 0.43.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.
@@ -1975,9 +1975,80 @@ type TProviderKindIcon = {
1975
1975
  kind: 'svg';
1976
1976
  path: string;
1977
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
+ }
1978
2029
  interface IProvider extends IExtensionBase {
1979
2030
  /** Discriminant injected by the loader from the folder structure. */
1980
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;
1981
2052
  /**
1982
2053
  * Catalog of node kinds this Provider emits. Populated by the loader
1983
2054
  * from the `<plugin>/kinds/<kindName>/` directory layout: each subfolder
@@ -3712,34 +3783,28 @@ declare function applyExportQuery(scan: {
3712
3783
  }, query: IExportQuery): IExportSubset;
3713
3784
 
3714
3785
  /**
3715
- * `scan_node_tags` adapter, tags · dual-source persistence layer.
3786
+ * `scan_node_tags` adapter, tags · single-source persistence layer.
3716
3787
  *
3717
- * One row per `(node_path, tag, source)` triple. Projected at persist
3718
- * time from BOTH `frontmatter.tags` (with `source='author'`) and
3719
- * `sidecar.annotations.tags` (with `source='user'`). The same tag
3720
- * string MAY appear under both sources for the same node, the PK
3721
- * accepts the pair; search returns the node once via DISTINCT, the
3722
- * UI renders both chips with their attribution.
3788
+ * One row per `(node_path, tag)` pair. Projected at persist time from
3789
+ * the node's `sidecar.annotations.tags` (the only tag source).
3723
3790
  *
3724
3791
  * Belongs to the `scan_*` family, replaced wholesale per scan.
3725
3792
  * Cached nodes' tag rows are projected from the cached
3726
- * `node.frontmatter.tags` / `node.sidecar.annotations.tags` (both
3727
- * already in memory at persist time), so the rebuild is cheap
3728
- * regardless of cache hit / miss. See `spec/db-schema.md`
3729
- * § scan_node_tags for the normative shape and replace-all semantics.
3793
+ * `node.sidecar.annotations.tags` (already in memory at persist time),
3794
+ * so the rebuild is cheap regardless of cache hit / miss. See
3795
+ * `spec/db-schema.md` § scan_node_tags for the normative shape and
3796
+ * replace-all semantics.
3730
3797
  */
3731
3798
 
3732
3799
  /**
3733
3800
  * In-memory tag record buffered during scan and flushed to
3734
3801
  * `scan_node_tags` by `persistScanResult`. One entry per
3735
- * `(node_path, tag, source)` projected from a node's frontmatter tags
3736
- * (`source: 'author'`) or sidecar annotations tags
3737
- * (`source: 'user'`).
3802
+ * `(node_path, tag)` pair projected from a node's sidecar annotations
3803
+ * tags (`sidecar.annotations.tags`).
3738
3804
  */
3739
3805
  interface ITagRecord {
3740
3806
  nodePath: string;
3741
3807
  tag: string;
3742
- source: 'author' | 'user';
3743
3808
  }
3744
3809
 
3745
3810
  /**
@@ -3922,25 +3987,23 @@ interface StoragePort {
3922
3987
  /**
3923
3988
  * Read-only access to `scan_node_tags`. Writes happen exclusively
3924
3989
  * via `scans.persist({...})` (the persistence layer projects from
3925
- * `node.frontmatter.tags` and `node.sidecar.annotations.tags`); this
3990
+ * `node.sidecar.annotations.tags`, the only tag source); this
3926
3991
  * namespace is read-only.
3927
3992
  */
3928
3993
  tags: {
3929
- /** Every tag row for a single node. Author entries first, then user. */
3994
+ /** Every tag row for a single node, ordered by tag name. */
3930
3995
  listForNode(nodePath: string): Promise<ITagRecord[]>;
3931
3996
  /**
3932
3997
  * Bulk variant for the BFF nodes-list route. Returns rows for every
3933
- * path in `paths`, sorted `nodePath` ASC, then `source` ASC, then
3934
- * `tag` ASC. Empty `paths` returns `[]` without a query.
3998
+ * path in `paths`, sorted `tag` ASC. Empty `paths` returns `[]`
3999
+ * without a query.
3935
4000
  */
3936
4001
  listForPaths(paths: readonly string[]): Promise<ITagRecord[]>;
3937
4002
  /**
3938
- * Find every node carrying `tag`. Optional `source` narrows to one
3939
- * side of the dual surface (matches `sm list --tag <name>
3940
- * --tag-source author|user`); absent matches the union (default
3941
- * `sm list --tag`).
4003
+ * Find every node carrying `tag` in its `.sm` sidecar
4004
+ * (`annotations.tags`). Drives `sm list --tag <name>`.
3942
4005
  */
3943
- findNodes(tag: string, source?: 'author' | 'user'): Promise<string[]>;
4006
+ findNodes(tag: string): Promise<string[]>;
3944
4007
  };
3945
4008
  issues: {
3946
4009
  /** Every issue from the latest scan, in insertion order. */
@@ -101,11 +101,11 @@ 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.41.0",
104
+ version: "0.43.0",
105
105
  description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
106
106
  license: "MIT",
107
107
  type: "module",
108
- homepage: "https://skill-map.dev",
108
+ homepage: "https://skill-map.ai",
109
109
  repository: {
110
110
  type: "git",
111
111
  url: "git+https://github.com/crystian/skill-map.git",
@@ -454,10 +454,10 @@ function buildSchemaValidators() {
454
454
  hook: "extension-hook"
455
455
  };
456
456
  const pluginManifestValidator = ajv.compile({
457
- $ref: "https://skill-map.dev/spec/v0/plugins-registry.schema.json#/$defs/PluginManifest"
457
+ $ref: "https://skill-map.ai/spec/v0/plugins-registry.schema.json#/$defs/PluginManifest"
458
458
  });
459
459
  const contributionValidators = /* @__PURE__ */ new Map();
460
- const VIEW_SLOTS_ID = "https://skill-map.dev/spec/v0/view-slots.schema.json";
460
+ const VIEW_SLOTS_ID = "https://skill-map.ai/spec/v0/view-slots.schema.json";
461
461
  function getContributionValidator(slot) {
462
462
  if (!KNOWN_SLOT_NAMES.has(slot)) return null;
463
463
  const existing = contributionValidators.get(slot);
@@ -1050,21 +1050,8 @@ function loadConfigForScope(opts) {
1050
1050
  }
1051
1051
 
1052
1052
  // core/config/active-provider.ts
1053
- var DETECTION_RULES = [
1054
- { providerId: "claude", marker: ".claude" },
1055
- // `gemini` retired 2026-05-22: Google replaced the Gemini CLI with the
1056
- // Antigravity CLI (released 2026-05-19; Gemini CLI sunsets 2026-06-18).
1057
- // Antigravity adopted the open-standard `.agents/` instead of a
1058
- // vendor-specific directory, so detection of a Google CLI project
1059
- // falls through to the universal `agent-skills` lens (`.agents/`
1060
- // already classifies via that neutral provider). The lens can still
1061
- // be set manually via `sm config set activeProvider antigravity`.
1062
- { providerId: "openai", marker: ".codex" },
1063
- { providerId: "openai", marker: "AGENTS.md" },
1064
- { providerId: "cursor", marker: ".cursor" }
1065
- ];
1066
- function resolveActiveProvider(cwd) {
1067
- const detected = detectProvidersFromFilesystem(cwd);
1053
+ function resolveActiveProvider(cwd, providers = []) {
1054
+ const detected = detectProvidersFromFilesystem(cwd, providers);
1068
1055
  const fromConfig = readConfigValue("activeProvider", { cwd });
1069
1056
  if (typeof fromConfig === "string" && fromConfig.length > 0) {
1070
1057
  return { resolved: fromConfig, source: "config", detected };
@@ -1074,14 +1061,16 @@ function resolveActiveProvider(cwd) {
1074
1061
  }
1075
1062
  return { resolved: null, source: "none", detected };
1076
1063
  }
1077
- function detectProvidersFromFilesystem(cwd) {
1064
+ function detectProvidersFromFilesystem(cwd, providers) {
1078
1065
  const seen = /* @__PURE__ */ new Set();
1079
1066
  const out = [];
1080
- for (const rule of DETECTION_RULES) {
1081
- if (seen.has(rule.providerId)) continue;
1082
- if (!existsSync5(join6(cwd, rule.marker))) continue;
1083
- seen.add(rule.providerId);
1084
- 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);
1085
1074
  }
1086
1075
  return out;
1087
1076
  }
@@ -3152,7 +3141,11 @@ async function runScanInternal(_kernel, options) {
3152
3141
  const scanStartedEvent = makeEvent("scan.started", { roots: options.roots });
3153
3142
  emitter.emit(scanStartedEvent);
3154
3143
  await hookDispatcher.dispatch("scan.started", scanStartedEvent);
3155
- const activeProviderId = resolveActiveProviderOption(options.activeProvider, options.roots);
3144
+ const activeProviderId = resolveActiveProviderOption(
3145
+ options.activeProvider,
3146
+ options.roots,
3147
+ exts.providers
3148
+ );
3156
3149
  const walked = await walkAndExtract({
3157
3150
  providers: exts.providers,
3158
3151
  extractors: exts.extractors,
@@ -3361,12 +3354,12 @@ function validateRoots(roots) {
3361
3354
  }
3362
3355
  }
3363
3356
  }
3364
- function resolveActiveProviderOption(optionValue, roots) {
3357
+ function resolveActiveProviderOption(optionValue, roots, providers) {
3365
3358
  if (optionValue !== void 0) return optionValue;
3366
3359
  for (const root of roots) {
3367
3360
  const absRoot = isAbsolute4(root) ? root : resolve11(root);
3368
3361
  if (!existsSync11(absRoot)) continue;
3369
- const detected = resolveActiveProvider(absRoot).resolved;
3362
+ const detected = resolveActiveProvider(absRoot, providers).resolved;
3370
3363
  if (detected !== null) return detected;
3371
3364
  }
3372
3365
  return null;