@skill-map/cli 0.68.1 → 0.70.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 (51) hide show
  1. package/dist/cli/tutorial/sm-tutorial/SKILL.md +8 -3
  2. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/agents-hub/providers/codex/en/agents-hub.md +2 -0
  3. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/agents-hub/providers/codex/es/agents-hub.md +2 -0
  4. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/todo-connectors/providers/codex/en/todo-bullet-agent.md +1 -0
  5. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/todo-connectors/providers/codex/en/todo-bullet-command.md +1 -0
  6. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/todo-connectors/providers/codex/en/todo-bullet-skill.md +1 -0
  7. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/todo-connectors/providers/codex/es/todo-bullet-agent.md +1 -0
  8. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/todo-connectors/providers/codex/es/todo-bullet-command.md +1 -0
  9. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/todo-connectors/providers/codex/es/todo-bullet-skill.md +1 -0
  10. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/harness/providers/codex/en/__PROVIDER__/skills/publish/SKILL.md +2 -2
  11. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/harness/providers/codex/es/__PROVIDER__/skills/publish/SKILL.md +2 -2
  12. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/master/providers/codex/en/.codex/agents/master-agent.toml +1 -1
  13. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/master/providers/codex/es/.codex/agents/master-agent.toml +1 -1
  14. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/portfolio/providers/codex/en/.codex/agents/content-editor.toml +1 -1
  15. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/portfolio/providers/codex/es/.codex/agents/content-editor.toml +1 -1
  16. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/prologue/providers/codex/en/.codex/agents/demo-agent.toml +1 -1
  17. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/prologue/providers/codex/es/.codex/agents/demo-agent.toml +1 -1
  18. package/dist/cli/tutorial/sm-tutorial/references/_core.md +57 -29
  19. package/dist/cli/tutorial/sm-tutorial/references/_manifest.json +70 -70
  20. package/dist/cli/tutorial/sm-tutorial/references/_manifest.yml +38 -38
  21. package/dist/cli/tutorial/sm-tutorial/references/fixtures.md +3 -3
  22. package/dist/cli/tutorial/sm-tutorial/references/part-authoring.md +25 -8
  23. package/dist/cli/tutorial/sm-tutorial/references/part-basic-daily.md +91 -38
  24. package/dist/cli/tutorial/sm-tutorial/references/part-basic-fundamentals.md +7 -8
  25. package/dist/cli/tutorial/sm-tutorial/references/part-basic-kickoff.md +1 -1
  26. package/dist/cli/tutorial/sm-tutorial/references/part-cli.md +1 -1
  27. package/dist/cli/tutorial/sm-tutorial/references/part-daily-loop.md +173 -56
  28. package/dist/cli/tutorial/sm-tutorial/references/part-fundamentals.md +14 -13
  29. package/dist/cli/tutorial/sm-tutorial/references/part-plugins.md +1 -1
  30. package/dist/cli/tutorial/sm-tutorial/references/part-project-kickoff.md +14 -12
  31. package/dist/cli/tutorial/sm-tutorial/references/part-settings.md +2 -2
  32. package/dist/cli/tutorial/sm-tutorial/scripts/lib/paths.js +6 -5
  33. package/dist/cli.js +963 -451
  34. package/dist/conformance/index.js +3 -3
  35. package/dist/index.js +17 -14
  36. package/dist/kernel/index.d.ts +54 -17
  37. package/dist/kernel/index.js +17 -14
  38. package/dist/migrations/001_initial.sql +7 -3
  39. package/dist/ui/{chunk-22EQLC23.js → chunk-EVNCL7FV.js} +21 -21
  40. package/dist/ui/chunk-GUGB4JY5.js +1 -0
  41. package/dist/ui/chunk-RJUHQQOF.js +3 -0
  42. package/dist/ui/{chunk-KMHXNOFZ.js → chunk-RSPEJBPT.js} +1 -1
  43. package/dist/ui/chunk-SQCXHF3J.js +2 -0
  44. package/dist/ui/index.html +1 -1
  45. package/dist/ui/main-K4O6LCIJ.js +4 -0
  46. package/migrations/001_initial.sql +7 -3
  47. package/package.json +2 -2
  48. package/dist/ui/chunk-K3ZRQNN5.js +0 -2
  49. package/dist/ui/chunk-PU5OP5RN.js +0 -1
  50. package/dist/ui/chunk-TLMV4LOQ.js +0 -3
  51. package/dist/ui/main-R7BIU4HU.js +0 -4
@@ -1,6 +1,6 @@
1
1
  // conformance/index.ts
2
2
 
3
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="47b9c38a-6cac-52c4-9dce-13fbbd5b35ec")}catch(e){}}();
3
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="b6a1c282-9829-57e4-bf63-a527fcb3c207")}catch(e){}}();
4
4
  import { spawnSync } from "child_process";
5
5
  import { cpSync, existsSync, mkdtempSync, readdirSync, readFileSync, rmSync, statSync } from "fs";
6
6
  import { tmpdir } from "os";
@@ -215,7 +215,7 @@ function grantFixturePluginTrust(scope, binary, env) {
215
215
  const db = new DatabaseSync(dbPath);
216
216
  try {
217
217
  const stmt = db.prepare(
218
- "INSERT INTO config_plugins (plugin_id, enabled, updated_at) VALUES (?, 1, 0) ON CONFLICT(plugin_id) DO UPDATE SET enabled = 1"
218
+ "INSERT INTO config_plugins (plugin_id, trusted, updated_at) VALUES (?, 1, 0) ON CONFLICT(plugin_id) DO UPDATE SET trusted = 1"
219
219
  );
220
220
  for (const id of ids) stmt.run(id);
221
221
  } finally {
@@ -453,4 +453,4 @@ export {
453
453
  runConformanceCase
454
454
  };
455
455
  //# sourceMappingURL=index.js.map
456
- //# debugId=47b9c38a-6cac-52c4-9dce-13fbbd5b35ec
456
+ //# debugId=b6a1c282-9829-57e4-bf63-a527fcb3c207
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // kernel/i18n/registry.texts.ts
2
2
 
3
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="f0a24ed1-f91a-59a8-b2b9-db8168690990")}catch(e){}}();
3
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="9e0186ab-b10f-5a49-8eef-f0668584ed01")}catch(e){}}();
4
4
  var REGISTRY_TEXTS = {
5
5
  duplicateExtension: "Extension already registered: {{kind}}:{{qualifiedId}}",
6
6
  unknownKind: "Unknown extension kind: {{kind}}",
@@ -102,7 +102,7 @@ import { Tiktoken as Tiktoken2 } from "js-tiktoken/lite";
102
102
  // package.json
103
103
  var package_default = {
104
104
  name: "@skill-map/cli",
105
- version: "0.68.1",
105
+ version: "0.70.0",
106
106
  description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
107
107
  license: "MIT",
108
108
  type: "module",
@@ -256,21 +256,22 @@ var PLUGIN_LOADER_TEXTS = {
256
256
  // schema. Both are GitHub blob URLs.
257
257
  invalidManifestExtensionShape: "{{relEntry}}: {{errors}}. See {{docUrl}}.",
258
258
  importExceededTimeout: "import exceeded {{timeoutMs}}ms; likely a top-level side effect (network call, infinite loop, large blocking work). Move side effects into the runtime methods (`detect` / `evaluate` / `render` / etc.).",
259
- disabledByConfig: "disabled by config_plugins or settings.json",
259
+ disabledByConfig: "disabled by settings.json (plugins.<id>.enabled)",
260
260
  /**
261
261
  * Reason stamped on a project-local disk plugin discovered but not
262
- * imported because the operator never granted local trust. Distinct
263
- * from `disabledByConfig` (an explicit toggle-off): this id has no
264
- * `config_plugins` override at all, so its code stays unexecuted until
265
- * `sm plugins enable` records local intent.
262
+ * imported because the operator never granted local import trust.
263
+ * Distinct from `disabledByConfig` (an explicit operational toggle-off
264
+ * in the config layers): this id is enabled but carries no
265
+ * `config_plugins` trust grant, so its code stays unexecuted until
266
+ * `sm plugins trust` records local consent.
266
267
  */
267
- untrustedNotLoaded: "not loaded: project-local plugin is untrusted until enabled. Run `sm plugins enable {{pluginId}}` to load it.",
268
+ untrustedNotLoaded: "not loaded: project-local plugin is enabled but not trusted on this machine. Run `sm plugins trust {{pluginId}}` to load it.",
268
269
  /**
269
270
  * One-time aggregate notice the runtime emits when project-local
270
271
  * plugins were found on disk but left unloaded for lack of trust. The
271
272
  * `{{count}}` plugins ride the scan without executing any code.
272
273
  */
273
- untrustedPluginsFoundNotice: "{{count}} project-local plugin(s) found in .skill-map/plugins/ but not loaded (untrusted). Their code did NOT run. Review with `sm plugins list`, then enable any you trust with `sm plugins enable <id>`.",
274
+ untrustedPluginsFoundNotice: "{{count}} project-local plugin(s) found in .skill-map/plugins/ but not loaded (untrusted). Their code did NOT run. Review with `sm plugins list`, then trust any you vetted with `sm plugins trust <id>`.",
274
275
  invalidManifestDirMismatch: "directory name '{{dirName}}' does not match manifest id '{{manifestId}}'. Rename the directory to match the id, or update the manifest id to match the directory.",
275
276
  idCollision: "Plugin '{{id}}' at {{pathA}} collides with the plugin at {{pathB}}. Rename one and rerun.",
276
277
  loadErrorPluginIdMismatch: "{{relEntry}}: extension declares pluginId '{{declared}}' but its plugin.json declares id '{{manifestId}}'. Remove the explicit pluginId from the extension; the loader injects it from plugin.json#/id.",
@@ -813,14 +814,16 @@ function installedDefaultEnabled(stability) {
813
814
  // kernel/scan/detect-providers.ts
814
815
  function detectProvidersFromFilesystem(cwd, providers) {
815
816
  const seen = /* @__PURE__ */ new Set();
816
- const out = [];
817
+ const matched = [];
817
818
  for (const provider of providers) {
818
819
  if (seen.has(provider.id)) continue;
819
820
  if (!isDetectableUnderCwd(cwd, provider)) continue;
820
821
  seen.add(provider.id);
821
- out.push(provider.id);
822
+ matched.push(provider);
822
823
  }
823
- return out;
824
+ const hasVendor = matched.some((p) => p.detect?.fallback !== true);
825
+ const kept = hasVendor ? matched.filter((p) => p.detect?.fallback !== true) : matched;
826
+ return kept.map((p) => p.id);
824
827
  }
825
828
  function isDetectableUnderCwd(cwd, provider) {
826
829
  if (!installedDefaultEnabled(provider.stability)) return false;
@@ -1816,7 +1819,7 @@ function lookupAllowedKinds(link, _indexes, ctx) {
1816
1819
  }
1817
1820
  function stripTriggerSigil(normalized) {
1818
1821
  if (!normalized) return null;
1819
- const trimmed = normalized.replace(/^[/@]/, "").trim();
1822
+ const trimmed = normalized.replace(/^[/@$]/, "").trim();
1820
1823
  return trimmed.length === 0 ? null : trimmed;
1821
1824
  }
1822
1825
  function indexNode(node, ctx, byName) {
@@ -4163,4 +4166,4 @@ export {
4163
4166
  runScanWithRenames
4164
4167
  };
4165
4168
  //# sourceMappingURL=index.js.map
4166
- //# debugId=f0a24ed1-f91a-59a8-b2b9-db8168690990
4169
+ //# debugId=9e0186ab-b10f-5a49-8eef-f0668584ed01
@@ -486,7 +486,7 @@ type TSettingValue = string | string[] | boolean | number | ISetting_KeyValueLis
486
486
  * to DISABLED, so the extension does not load (does not run, does not
487
487
  * register) unless the operator opts in (`sm plugins enable
488
488
  * <plugin>/<ext>`, the Settings toggle, or a `settings.json` /
489
- * `config_plugins` override). The opt-in is a plain enable override,
489
+ * `settings.local.json` override). The opt-in is a plain enable override,
490
490
  * once set it wins over the installed default exactly like any other
491
491
  * extension (so a deprecated extension can still be kept running during
492
492
  * a migration). The remaining values are presentation-only and default
@@ -2061,11 +2061,15 @@ interface IMigrateNodeFksReport {
2061
2061
  keys: Record<string, string>;
2062
2062
  }>;
2063
2063
  }
2064
- /** A single `config_plugins` override row as the kernel sees it. */
2065
- interface IPluginConfigRow {
2064
+ /**
2065
+ * A single `config_plugins` trust row as the kernel sees it. The table
2066
+ * is the per-machine import-trust store (the SECURITY axis); the
2067
+ * operational enable/disable toggle lives in the config layers, not
2068
+ * here. Keyed by the bare plugin id.
2069
+ */
2070
+ interface IPluginTrustRow {
2066
2071
  pluginId: string;
2067
- enabled: boolean;
2068
- configJson: string | null;
2072
+ trusted: boolean;
2069
2073
  updatedAt: number;
2070
2074
  }
2071
2075
  /** Discovered kernel migration file (one of `NNN_snake_case.sql`). */
@@ -2578,6 +2582,21 @@ interface IProviderUi {
2578
2582
  * topbar lens chip; only the per-card badge is suppressed.
2579
2583
  */
2580
2584
  hideChip?: boolean;
2585
+ /**
2586
+ * Single glyph this lens's runtime uses to invoke a skill / command,
2587
+ * surfaced as the `invokes` edge-kind glyph (and its tooltip example)
2588
+ * in the link-kind palette so the operator recognises the source
2589
+ * syntax instantly. `/` for the slash-invoking lenses (`claude`
2590
+ * commands + skills, `antigravity` skills + workflows), `$` for
2591
+ * `codex` (skills are `$skill`; `/` is reserved for Codex's own
2592
+ * built-in commands). Omitted for lenses with no `/`/`$` invocation
2593
+ * channel (the open-standard `agent-skills`, where skills activate by
2594
+ * `description`, and the non-lens `markdown` base): under those no
2595
+ * `invokes` edge arises, so the palette never paints the glyph.
2596
+ * Projected into `providerRegistry` and joined client-side against
2597
+ * the active lens.
2598
+ */
2599
+ invocationSigil?: string;
2581
2600
  }
2582
2601
  /**
2583
2602
  * Auto-detection markers for the active-provider lens. The lens resolver
@@ -2594,6 +2613,18 @@ interface IProviderDetect {
2594
2613
  * A directory or a file both count; existence is the only test.
2595
2614
  */
2596
2615
  markers: string[];
2616
+ /**
2617
+ * When `true`, this Provider is the open-standard FALLBACK lens: its
2618
+ * markers produce a detection candidate ONLY when no non-fallback
2619
+ * (vendor) Provider matched under the same scope. Reserved for
2620
+ * `agent-skills`, whose `.agents/` marker is the shared skill home that
2621
+ * vendor lenses (`codex`, `antigravity`) also populate; without this flag
2622
+ * a `.codex/` + `.agents/` project would falsely read as an ambiguous
2623
+ * `codex` vs `agent-skills` pair. Vendor Providers omit it (default
2624
+ * `false`) so two vendor markers still surface a real ambiguous prompt.
2625
+ * Mirrors `provider.schema.json#/properties/detect/properties/fallback`.
2626
+ */
2627
+ fallback?: boolean;
2597
2628
  }
2598
2629
  /**
2599
2630
  * Authoring targets for verbs that MATERIALISE files into this
@@ -4977,24 +5008,30 @@ interface StoragePort {
4977
5008
  */
4978
5009
  findActive(predicate: (issue: Issue) => boolean): Promise<IIssueRow[]>;
4979
5010
  };
4980
- pluginConfig: {
5011
+ /**
5012
+ * Per-machine plugin import-trust store (`config_plugins`, the SECURITY
5013
+ * axis). Keyed by bare plugin id. Written by `sm plugins trust /
5014
+ * untrust` and `PATCH /api/plugins/:id/trust`. The operational
5015
+ * enable/disable toggle lives in the config layers, NOT here.
5016
+ */
5017
+ trust: {
4981
5018
  /**
4982
- * Upsert the per-plugin enabled override into `config_plugins`.
4983
- * Caller is `sm plugins enable / disable`.
5019
+ * Upsert the per-plugin trust grant into `config_plugins`. Caller is
5020
+ * `sm plugins trust / untrust` (and the BFF trust route).
4984
5021
  */
4985
- set(pluginId: string, enabled: boolean): Promise<void>;
4986
- /** Read a single override; `undefined` when no row exists. */
5022
+ set(pluginId: string, trusted: boolean): Promise<void>;
5023
+ /** Read a single trust grant; `undefined` when no row exists. */
4987
5024
  get(pluginId: string): Promise<boolean | undefined>;
4988
- /** Every override row, sorted by `pluginId` for stable rendering. */
4989
- list(): Promise<IPluginConfigRow[]>;
4990
- /** Drop a single override row (no-op when absent). */
5025
+ /** Every trust row, sorted by `pluginId` for stable rendering. */
5026
+ list(): Promise<IPluginTrustRow[]>;
5027
+ /** Drop a single trust row (no-op when absent). */
4991
5028
  delete(pluginId: string): Promise<void>;
4992
5029
  /**
4993
- * Load every override into a map for quick lookup by id. Used by
4994
- * `loadPluginRuntime` to layer the DB overrides over the
4995
- * `settings.json` defaults at scan boot.
5030
+ * Load every trust grant into a map for quick lookup by bare plugin
5031
+ * id. Used by `loadPluginRuntime` to feed the import-trust gate at
5032
+ * scan boot.
4996
5033
  */
4997
- loadOverrideMap(): Promise<Map<string, boolean>>;
5034
+ loadTrustMap(): Promise<Map<string, boolean>>;
4998
5035
  };
4999
5036
  jobs: {
5000
5037
  /**
@@ -1,6 +1,6 @@
1
1
  // kernel/i18n/registry.texts.ts
2
2
 
3
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="7dc46367-403b-5856-87b8-23673c2d3f6c")}catch(e){}}();
3
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="37ae34a6-8712-5003-b7e6-767b1b040298")}catch(e){}}();
4
4
  var REGISTRY_TEXTS = {
5
5
  duplicateExtension: "Extension already registered: {{kind}}:{{qualifiedId}}",
6
6
  unknownKind: "Unknown extension kind: {{kind}}",
@@ -102,7 +102,7 @@ import { Tiktoken as Tiktoken2 } from "js-tiktoken/lite";
102
102
  // package.json
103
103
  var package_default = {
104
104
  name: "@skill-map/cli",
105
- version: "0.68.1",
105
+ version: "0.70.0",
106
106
  description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
107
107
  license: "MIT",
108
108
  type: "module",
@@ -256,21 +256,22 @@ var PLUGIN_LOADER_TEXTS = {
256
256
  // schema. Both are GitHub blob URLs.
257
257
  invalidManifestExtensionShape: "{{relEntry}}: {{errors}}. See {{docUrl}}.",
258
258
  importExceededTimeout: "import exceeded {{timeoutMs}}ms; likely a top-level side effect (network call, infinite loop, large blocking work). Move side effects into the runtime methods (`detect` / `evaluate` / `render` / etc.).",
259
- disabledByConfig: "disabled by config_plugins or settings.json",
259
+ disabledByConfig: "disabled by settings.json (plugins.<id>.enabled)",
260
260
  /**
261
261
  * Reason stamped on a project-local disk plugin discovered but not
262
- * imported because the operator never granted local trust. Distinct
263
- * from `disabledByConfig` (an explicit toggle-off): this id has no
264
- * `config_plugins` override at all, so its code stays unexecuted until
265
- * `sm plugins enable` records local intent.
262
+ * imported because the operator never granted local import trust.
263
+ * Distinct from `disabledByConfig` (an explicit operational toggle-off
264
+ * in the config layers): this id is enabled but carries no
265
+ * `config_plugins` trust grant, so its code stays unexecuted until
266
+ * `sm plugins trust` records local consent.
266
267
  */
267
- untrustedNotLoaded: "not loaded: project-local plugin is untrusted until enabled. Run `sm plugins enable {{pluginId}}` to load it.",
268
+ untrustedNotLoaded: "not loaded: project-local plugin is enabled but not trusted on this machine. Run `sm plugins trust {{pluginId}}` to load it.",
268
269
  /**
269
270
  * One-time aggregate notice the runtime emits when project-local
270
271
  * plugins were found on disk but left unloaded for lack of trust. The
271
272
  * `{{count}}` plugins ride the scan without executing any code.
272
273
  */
273
- untrustedPluginsFoundNotice: "{{count}} project-local plugin(s) found in .skill-map/plugins/ but not loaded (untrusted). Their code did NOT run. Review with `sm plugins list`, then enable any you trust with `sm plugins enable <id>`.",
274
+ untrustedPluginsFoundNotice: "{{count}} project-local plugin(s) found in .skill-map/plugins/ but not loaded (untrusted). Their code did NOT run. Review with `sm plugins list`, then trust any you vetted with `sm plugins trust <id>`.",
274
275
  invalidManifestDirMismatch: "directory name '{{dirName}}' does not match manifest id '{{manifestId}}'. Rename the directory to match the id, or update the manifest id to match the directory.",
275
276
  idCollision: "Plugin '{{id}}' at {{pathA}} collides with the plugin at {{pathB}}. Rename one and rerun.",
276
277
  loadErrorPluginIdMismatch: "{{relEntry}}: extension declares pluginId '{{declared}}' but its plugin.json declares id '{{manifestId}}'. Remove the explicit pluginId from the extension; the loader injects it from plugin.json#/id.",
@@ -813,14 +814,16 @@ function installedDefaultEnabled(stability) {
813
814
  // kernel/scan/detect-providers.ts
814
815
  function detectProvidersFromFilesystem(cwd, providers) {
815
816
  const seen = /* @__PURE__ */ new Set();
816
- const out = [];
817
+ const matched = [];
817
818
  for (const provider of providers) {
818
819
  if (seen.has(provider.id)) continue;
819
820
  if (!isDetectableUnderCwd(cwd, provider)) continue;
820
821
  seen.add(provider.id);
821
- out.push(provider.id);
822
+ matched.push(provider);
822
823
  }
823
- return out;
824
+ const hasVendor = matched.some((p) => p.detect?.fallback !== true);
825
+ const kept = hasVendor ? matched.filter((p) => p.detect?.fallback !== true) : matched;
826
+ return kept.map((p) => p.id);
824
827
  }
825
828
  function isDetectableUnderCwd(cwd, provider) {
826
829
  if (!installedDefaultEnabled(provider.stability)) return false;
@@ -1816,7 +1819,7 @@ function lookupAllowedKinds(link, _indexes, ctx) {
1816
1819
  }
1817
1820
  function stripTriggerSigil(normalized) {
1818
1821
  if (!normalized) return null;
1819
- const trimmed = normalized.replace(/^[/@]/, "").trim();
1822
+ const trimmed = normalized.replace(/^[/@$]/, "").trim();
1820
1823
  return trimmed.length === 0 ? null : trimmed;
1821
1824
  }
1822
1825
  function indexNode(node, ctx, byName) {
@@ -4163,4 +4166,4 @@ export {
4163
4166
  runScanWithRenames
4164
4167
  };
4165
4168
  //# sourceMappingURL=index.js.map
4166
- //# debugId=7dc46367-403b-5856-87b8-23673c2d3f6c
4169
+ //# debugId=37ae34a6-8712-5003-b7e6-767b1b040298
@@ -241,12 +241,16 @@ CREATE TABLE state_node_favorites (
241
241
 
242
242
  -- --- Config zone -----------------------------------------------------------
243
243
 
244
+ -- Per-machine plugin import-trust store (the SECURITY axis). The
245
+ -- operational enable/disable toggle lives in the config layers
246
+ -- (settings.json / settings.local.json), NOT here. Keyed by bare plugin
247
+ -- id. Redefined inline (greenfield, no migration file, no user_version
248
+ -- bump): the scan_meta.schema_fingerprint drift path rebuilds the cache.
244
249
  CREATE TABLE config_plugins (
245
250
  plugin_id TEXT PRIMARY KEY,
246
- enabled INTEGER NOT NULL DEFAULT 1,
247
- config_json TEXT,
251
+ trusted INTEGER NOT NULL DEFAULT 0,
248
252
  updated_at INTEGER NOT NULL,
249
- CONSTRAINT ck_config_plugins_enabled CHECK (enabled IN (0,1))
253
+ CONSTRAINT ck_config_plugins_trusted CHECK (trusted IN (0,1))
250
254
  );
251
255
 
252
256
  CREATE TABLE config_preferences (