@skill-map/cli 0.45.0 → 0.46.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 (44) hide show
  1. package/dist/cli/tutorial/sm-master/SKILL.md +29 -29
  2. package/dist/cli/tutorial/sm-master/references/fixture-templates.md +18 -13
  3. package/dist/cli/tutorial/sm-master/references/tour-authoring.md +35 -40
  4. package/dist/cli/tutorial/sm-master/references/tour-plugins.md +32 -32
  5. package/dist/cli/tutorial/sm-master/references/tour-settings.md +156 -75
  6. package/dist/cli/tutorial/sm-tutorial/SKILL.md +3 -3
  7. package/dist/cli.js +757 -452
  8. package/dist/conformance/index.js +4 -1
  9. package/dist/index.js +44 -17
  10. package/dist/kernel/index.d.ts +64 -9
  11. package/dist/kernel/index.js +44 -17
  12. package/dist/migrations/001_initial.sql +7 -0
  13. package/dist/ui/chunk-22CKFAEU.js +1 -0
  14. package/dist/ui/{chunk-I5AX4U2N.js → chunk-3YSNJXGB.js} +1 -1
  15. package/dist/ui/{chunk-MS6B7344.js → chunk-6AP364TB.js} +7 -7
  16. package/dist/ui/{chunk-VGPYYAVI.js → chunk-EPBUSS3I.js} +1 -1
  17. package/dist/ui/{chunk-IYM26L3O.js → chunk-ERUALZOV.js} +1 -1
  18. package/dist/ui/{chunk-5AD5ZV4I.js → chunk-EYBKZOMF.js} +1 -1
  19. package/dist/ui/chunk-F4RIBZ4P.js +1 -0
  20. package/dist/ui/chunk-HAWX5WNM.js +4 -0
  21. package/dist/ui/{chunk-QDUSFOBE.js → chunk-K365TVPA.js} +1 -1
  22. package/dist/ui/{chunk-CBI77N5U.js → chunk-L3JYFPSZ.js} +2 -2
  23. package/dist/ui/chunk-N3RUQDAR.js +1 -0
  24. package/dist/ui/{chunk-A7PRWMQD.js → chunk-P4E74ZOS.js} +1 -1
  25. package/dist/ui/{chunk-X227ITGS.js → chunk-RT7E4S5B.js} +1 -1
  26. package/dist/ui/{chunk-QNTAOR2L.js → chunk-RXQYLVSJ.js} +1 -1
  27. package/dist/ui/{chunk-MFLFIA7C.js → chunk-S32E6ZCZ.js} +1 -1
  28. package/dist/ui/{chunk-T3IVIRRJ.js → chunk-SF4FUT4U.js} +1 -1
  29. package/dist/ui/chunk-VNA3TMIO.js +1 -0
  30. package/dist/ui/{chunk-F7I6KMHX.js → chunk-VW2A6WZ3.js} +1 -1
  31. package/dist/ui/chunk-ZZJ7XWDX.js +1 -0
  32. package/dist/ui/index.html +1 -1
  33. package/dist/ui/main-NF5GO3JR.js +4 -0
  34. package/migrations/001_initial.sql +7 -0
  35. package/package.json +2 -2
  36. package/dist/cli.js.map +0 -1
  37. package/dist/conformance/index.js.map +0 -1
  38. package/dist/index.js.map +0 -1
  39. package/dist/kernel/index.js.map +0 -1
  40. package/dist/ui/chunk-27WQPOXP.js +0 -1
  41. package/dist/ui/chunk-555ST76V.js +0 -1
  42. package/dist/ui/chunk-IUZRAD7S.js +0 -1
  43. package/dist/ui/chunk-PZQHB7GS.js +0 -4
  44. package/dist/ui/main-ERCTR2PR.js +0 -3
package/dist/cli.js CHANGED
@@ -1,4 +1,6 @@
1
1
  // cli/entry.ts
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]="7c131b78-b7ad-5f03-b19c-1f70afdfeefb")}catch(e){}}();
2
4
  import { existsSync as existsSync32 } from "fs";
3
5
  import { Builtins, Cli as Cli2 } from "clipanion";
4
6
 
@@ -241,6 +243,123 @@ function bucketByKind(kind, instance, bag) {
241
243
  }
242
244
  }
243
245
 
246
+ // package.json
247
+ var package_default = {
248
+ name: "@skill-map/cli",
249
+ version: "0.46.0",
250
+ description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
251
+ license: "MIT",
252
+ type: "module",
253
+ homepage: "https://skill-map.ai",
254
+ repository: {
255
+ type: "git",
256
+ url: "git+https://github.com/crystian/skill-map.git",
257
+ directory: "src"
258
+ },
259
+ bugs: {
260
+ url: "https://github.com/crystian/skill-map/issues"
261
+ },
262
+ keywords: [
263
+ "skill-map",
264
+ "markdown",
265
+ "ai-agents",
266
+ "claude-code",
267
+ "graph"
268
+ ],
269
+ bin: {
270
+ sm: "bin/sm.js",
271
+ "skill-map": "bin/sm.js"
272
+ },
273
+ exports: {
274
+ ".": {
275
+ types: "./dist/index.d.ts",
276
+ import: "./dist/index.js"
277
+ },
278
+ "./kernel": {
279
+ types: "./dist/kernel/index.d.ts",
280
+ import: "./dist/kernel/index.js"
281
+ },
282
+ "./conformance": {
283
+ types: "./dist/conformance/index.d.ts",
284
+ import: "./dist/conformance/index.js"
285
+ }
286
+ },
287
+ files: [
288
+ "bin/",
289
+ "dist/",
290
+ "migrations/",
291
+ "README.md"
292
+ ],
293
+ scripts: {
294
+ build: "tsup",
295
+ dev: "tsup --watch",
296
+ "dev:serve": "node scripts/dev-serve.js",
297
+ typecheck: "tsc --noEmit",
298
+ lint: "eslint .",
299
+ "lint:fix": "eslint . --fix",
300
+ "build-built-ins": "node ../scripts/generate-built-ins.js",
301
+ "built-ins:check": "node ../scripts/generate-built-ins.js --check",
302
+ prebuild: "pnpm build-built-ins",
303
+ validate: "pnpm validate:compile && pnpm validate:test",
304
+ "validate:compile": "pnpm typecheck && pnpm lint && pnpm build && pnpm built-ins:check",
305
+ "validate:test": "pnpm test:ci",
306
+ pretest: "tsup",
307
+ "pretest:coverage": "tsup",
308
+ "pretest:coverage:html": "tsup",
309
+ test: "tsc --noEmit && SKILL_MAP_TELEMETRY=0 node --import tsx --test --test-reporter=./scripts/test-reporter.js --test-reporter-destination=stdout '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
310
+ "test:ci": "FORCE_COLOR=1 SKILL_MAP_TELEMETRY=0 node --import tsx --test --test-reporter=./scripts/test-reporter.js --test-reporter-destination=stdout '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
311
+ "test:spec": "SKILL_MAP_TELEMETRY=0 node --import tsx --test --test-reporter=spec '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
312
+ "test:coverage": "tsc --noEmit && SKILL_MAP_TELEMETRY=0 SKILL_MAP_SKIP_BENCHMARK=1 node --experimental-default-config-file --import tsx --test --experimental-test-coverage '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
313
+ "test:coverage:html": "tsc --noEmit && SKILL_MAP_TELEMETRY=0 SKILL_MAP_SKIP_BENCHMARK=1 c8 node --import tsx --test '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
314
+ clean: "rm -rf dist coverage"
315
+ },
316
+ dependencies: {
317
+ "@hono/node-server": "2.0.1",
318
+ "@sentry/node": "10.55.0",
319
+ "@skill-map/spec": "workspace:*",
320
+ ajv: "8.18.0",
321
+ "ajv-formats": "3.0.1",
322
+ chokidar: "5.0.0",
323
+ clipanion: "4.0.0-rc.4",
324
+ hono: "4.12.18",
325
+ ignore: "7.0.5",
326
+ "js-tiktoken": "1.0.21",
327
+ "js-yaml": "4.1.1",
328
+ kysely: "0.28.17",
329
+ "posthog-node": "5.35.6",
330
+ semver: "7.7.4",
331
+ "smol-toml": "1.6.1",
332
+ typanion: "3.14.0",
333
+ ws: "8.21.0"
334
+ },
335
+ devDependencies: {
336
+ "@eslint/js": "10.0.1",
337
+ "@stylistic/eslint-plugin": "5.10.0",
338
+ "@types/js-yaml": "4.0.9",
339
+ "@types/node": "24.12.2",
340
+ "@types/semver": "7.7.1",
341
+ "@types/ws": "8.18.1",
342
+ c8: "11.0.0",
343
+ eslint: "10.2.1",
344
+ "eslint-plugin-import-x": "4.16.2",
345
+ tsup: "8.5.1",
346
+ tsx: "4.22.3",
347
+ typescript: "5.9.3",
348
+ "typescript-eslint": "8.59.1"
349
+ },
350
+ engines: {
351
+ node: ">=24.0"
352
+ },
353
+ publishConfig: {
354
+ access: "public"
355
+ }
356
+ };
357
+
358
+ // version.ts
359
+ var VERSION = package_default.version;
360
+ var BINARY_NAME = "sm";
361
+ var BINARY_LABEL = "skill-map";
362
+
244
363
  // plugins/claude/providers/claude/schemas/skill.schema.json
245
364
  var skill_schema_default = {
246
365
  $schema: "https://json-schema.org/draft/2020-12/schema",
@@ -481,7 +600,7 @@ var claudeProvider = {
481
600
  read: { extensions: [".md"], parser: "frontmatter-yaml" },
482
601
  // Per spec § A.6, defaultRefreshAction values MUST be qualified action
483
602
  // ids. The summarize-* actions are not yet implemented as registry
484
- // entries (they ship later under the Claude bundle), but the qualified
603
+ // entries (they ship later under the Claude plugin), but the qualified
485
604
  // form is the contract: when those actions land, they will register
486
605
  // under `claude/summarize-<kind>` and the Provider resolves them
487
606
  // deterministically.
@@ -1192,7 +1311,7 @@ var markdown_schema_default = {
1192
1311
  $schema: "https://json-schema.org/draft/2020-12/schema",
1193
1312
  $id: "https://skill-map.ai/providers/core/v1/frontmatter/markdown.schema.json",
1194
1313
  title: "FrontmatterMarkdown",
1195
- description: "Frontmatter shape for nodes classified as `markdown` by the built-in `core/markdown` Provider, the universal fallback for any markdown file no vendor-specific Provider claims (Claude, OpenAI Codex, agent-skills, plus the metadata-only Antigravity bundle). The kind is named after the format because the file is a generic fallback; format-named kinds apply only as the generic fallback, a TOML file that IS a Codex agent still classifies as `agent`, not `toml`. Extends the spec's universal `frontmatter/base.schema.json` via $ref-by-$id with no additional fields. Ownership relocated from the Claude Provider in spec 0.18.0, markdown is provider-agnostic and lives under `core` so adding new vendor Providers does not require choosing which one owns the universal fallback.",
1314
+ description: "Frontmatter shape for nodes classified as `markdown` by the built-in `core/markdown` Provider, the universal fallback for any markdown file no vendor-specific Provider claims (Claude, OpenAI Codex, agent-skills, plus the metadata-only Antigravity plugin). The kind is named after the format because the file is a generic fallback; format-named kinds apply only as the generic fallback, a TOML file that IS a Codex agent still classifies as `agent`, not `toml`. Extends the spec's universal `frontmatter/base.schema.json` via $ref-by-$id with no additional fields. Ownership relocated from the Claude Provider in spec 0.18.0, markdown is provider-agnostic and lives under `core` so adding new vendor Providers does not require choosing which one owns the universal fallback.",
1196
1315
  allOf: [
1197
1316
  { $ref: "https://skill-map.ai/spec/v0/frontmatter/base.schema.json" }
1198
1317
  ],
@@ -1223,7 +1342,7 @@ var coreMarkdownProvider = {
1223
1342
  read: { extensions: [".md"], parser: "frontmatter-yaml" },
1224
1343
  // Per spec § A.6, defaultRefreshAction values MUST be qualified
1225
1344
  // action ids. The summarize-markdown action is not yet implemented
1226
- // as a registry entry (it ships later under the core bundle), but
1345
+ // as a registry entry (it ships later under the core plugin), but
1227
1346
  // the qualified form is the contract.
1228
1347
  //
1229
1348
  // UI presentation: same neutral teal that the per-vendor Providers
@@ -3957,123 +4076,6 @@ var UPDATE_CHECK_TEXTS = {
3957
4076
  availableHint: "Run `npm i -g @skill-map/cli@latest` to update."
3958
4077
  };
3959
4078
 
3960
- // package.json
3961
- var package_default = {
3962
- name: "@skill-map/cli",
3963
- version: "0.45.0",
3964
- description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
3965
- license: "MIT",
3966
- type: "module",
3967
- homepage: "https://skill-map.ai",
3968
- repository: {
3969
- type: "git",
3970
- url: "git+https://github.com/crystian/skill-map.git",
3971
- directory: "src"
3972
- },
3973
- bugs: {
3974
- url: "https://github.com/crystian/skill-map/issues"
3975
- },
3976
- keywords: [
3977
- "skill-map",
3978
- "markdown",
3979
- "ai-agents",
3980
- "claude-code",
3981
- "graph"
3982
- ],
3983
- bin: {
3984
- sm: "bin/sm.js",
3985
- "skill-map": "bin/sm.js"
3986
- },
3987
- exports: {
3988
- ".": {
3989
- types: "./dist/index.d.ts",
3990
- import: "./dist/index.js"
3991
- },
3992
- "./kernel": {
3993
- types: "./dist/kernel/index.d.ts",
3994
- import: "./dist/kernel/index.js"
3995
- },
3996
- "./conformance": {
3997
- types: "./dist/conformance/index.d.ts",
3998
- import: "./dist/conformance/index.js"
3999
- }
4000
- },
4001
- files: [
4002
- "bin/",
4003
- "dist/",
4004
- "migrations/",
4005
- "README.md"
4006
- ],
4007
- scripts: {
4008
- build: "tsup",
4009
- dev: "tsup --watch",
4010
- "dev:serve": "node scripts/dev-serve.js",
4011
- typecheck: "tsc --noEmit",
4012
- lint: "eslint .",
4013
- "lint:fix": "eslint . --fix",
4014
- "build-built-ins": "node ../scripts/generate-built-ins.js",
4015
- "built-ins:check": "node ../scripts/generate-built-ins.js --check",
4016
- prebuild: "pnpm build-built-ins",
4017
- validate: "pnpm validate:compile && pnpm validate:test",
4018
- "validate:compile": "pnpm typecheck && pnpm lint && pnpm build && pnpm built-ins:check",
4019
- "validate:test": "pnpm test:ci",
4020
- pretest: "tsup",
4021
- "pretest:coverage": "tsup",
4022
- "pretest:coverage:html": "tsup",
4023
- test: "tsc --noEmit && SKILL_MAP_TELEMETRY=0 node --import tsx --test --test-reporter=./scripts/test-reporter.js --test-reporter-destination=stdout '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
4024
- "test:ci": "FORCE_COLOR=1 SKILL_MAP_TELEMETRY=0 node --import tsx --test --test-reporter=./scripts/test-reporter.js --test-reporter-destination=stdout '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
4025
- "test:spec": "SKILL_MAP_TELEMETRY=0 node --import tsx --test --test-reporter=spec '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
4026
- "test:coverage": "tsc --noEmit && SKILL_MAP_TELEMETRY=0 SKILL_MAP_SKIP_BENCHMARK=1 node --experimental-default-config-file --import tsx --test --experimental-test-coverage '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
4027
- "test:coverage:html": "tsc --noEmit && SKILL_MAP_TELEMETRY=0 SKILL_MAP_SKIP_BENCHMARK=1 c8 node --import tsx --test '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
4028
- clean: "rm -rf dist coverage"
4029
- },
4030
- dependencies: {
4031
- "@hono/node-server": "2.0.1",
4032
- "@sentry/node": "10.55.0",
4033
- "@skill-map/spec": "workspace:*",
4034
- ajv: "8.18.0",
4035
- "ajv-formats": "3.0.1",
4036
- chokidar: "5.0.0",
4037
- clipanion: "4.0.0-rc.4",
4038
- hono: "4.12.18",
4039
- ignore: "7.0.5",
4040
- "js-tiktoken": "1.0.21",
4041
- "js-yaml": "4.1.1",
4042
- kysely: "0.28.17",
4043
- "posthog-node": "5.35.6",
4044
- semver: "7.7.4",
4045
- "smol-toml": "1.6.1",
4046
- typanion: "3.14.0",
4047
- ws: "8.21.0"
4048
- },
4049
- devDependencies: {
4050
- "@eslint/js": "10.0.1",
4051
- "@stylistic/eslint-plugin": "5.10.0",
4052
- "@types/js-yaml": "4.0.9",
4053
- "@types/node": "24.12.2",
4054
- "@types/semver": "7.7.1",
4055
- "@types/ws": "8.18.1",
4056
- c8: "11.0.0",
4057
- eslint: "10.2.1",
4058
- "eslint-plugin-import-x": "4.16.2",
4059
- tsup: "8.5.1",
4060
- tsx: "4.22.3",
4061
- typescript: "5.9.3",
4062
- "typescript-eslint": "8.59.1"
4063
- },
4064
- engines: {
4065
- node: ">=24.0"
4066
- },
4067
- publishConfig: {
4068
- access: "public"
4069
- }
4070
- };
4071
-
4072
- // version.ts
4073
- var VERSION = package_default.version;
4074
- var BINARY_NAME = "sm";
4075
- var BINARY_LABEL = "skill-map";
4076
-
4077
4079
  // cli/util/ansi.ts
4078
4080
  var ESC = {
4079
4081
  reset: "\x1B[0m",
@@ -4430,41 +4432,41 @@ var updateCheckHook = {
4430
4432
  };
4431
4433
 
4432
4434
  // plugins/built-ins.ts
4433
- var claudeProvider2 = { ...claudeProvider, pluginId: "claude", version: "0.45.0" };
4434
- var atDirectiveExtractor2 = { ...atDirectiveExtractor, pluginId: "claude", version: "0.45.0" };
4435
- var slashCommandExtractor2 = { ...slashCommandExtractor, pluginId: "claude", version: "0.45.0" };
4436
- var antigravityProvider2 = { ...antigravityProvider, pluginId: "antigravity", version: "0.45.0" };
4437
- var openaiProvider2 = { ...openaiProvider, pluginId: "openai", version: "0.45.0" };
4438
- var agentSkillsProvider2 = { ...agentSkillsProvider, pluginId: "agent-skills", version: "0.45.0" };
4439
- var coreMarkdownProvider2 = { ...coreMarkdownProvider, pluginId: "core", version: "0.45.0" };
4440
- var annotationsExtractor2 = { ...annotationsExtractor, pluginId: "core", version: "0.45.0" };
4441
- var externalUrlCounterExtractor2 = { ...externalUrlCounterExtractor, pluginId: "core", version: "0.45.0" };
4442
- var markdownLinkExtractor2 = { ...markdownLinkExtractor, pluginId: "core", version: "0.45.0" };
4443
- var mcpToolsExtractor2 = { ...mcpToolsExtractor, pluginId: "core", version: "0.45.0" };
4444
- var toolsCounterExtractor2 = { ...toolsCounterExtractor, pluginId: "core", version: "0.45.0" };
4445
- var annotationFieldUnknownAnalyzer2 = { ...annotationFieldUnknownAnalyzer, pluginId: "core", version: "0.45.0" };
4446
- var annotationOrphanAnalyzer2 = { ...annotationOrphanAnalyzer, pluginId: "core", version: "0.45.0" };
4447
- var annotationStaleAnalyzer2 = { ...annotationStaleAnalyzer, pluginId: "core", version: "0.45.0" };
4448
- var contributionOrphanAnalyzer2 = { ...contributionOrphanAnalyzer, pluginId: "core", version: "0.45.0" };
4449
- var issueCounterAnalyzer2 = { ...issueCounterAnalyzer, pluginId: "core", version: "0.45.0" };
4450
- var jobFileOrphanAnalyzer2 = { ...jobFileOrphanAnalyzer, pluginId: "core", version: "0.45.0" };
4451
- var linkConflictAnalyzer2 = { ...linkConflictAnalyzer, pluginId: "core", version: "0.45.0" };
4452
- var linkCounterAnalyzer2 = { ...linkCounterAnalyzer, pluginId: "core", version: "0.45.0" };
4453
- var linkSelfLoopAnalyzer2 = { ...linkSelfLoopAnalyzer, pluginId: "core", version: "0.45.0" };
4454
- var nameReservedAnalyzer2 = { ...nameReservedAnalyzer, pluginId: "core", version: "0.45.0" };
4455
- var nodeStabilityAnalyzer2 = { ...nodeStabilityAnalyzer, pluginId: "core", version: "0.45.0" };
4456
- var nodeSupersededAnalyzer2 = { ...nodeSupersededAnalyzer, pluginId: "core", version: "0.45.0" };
4457
- var referenceBrokenAnalyzer2 = { ...referenceBrokenAnalyzer, pluginId: "core", version: "0.45.0" };
4458
- var referenceRedundantAnalyzer2 = { ...referenceRedundantAnalyzer, pluginId: "core", version: "0.45.0" };
4459
- var schemaViolationAnalyzer2 = { ...schemaViolationAnalyzer, pluginId: "core", version: "0.45.0" };
4460
- var signalCollisionAnalyzer2 = { ...signalCollisionAnalyzer, pluginId: "core", version: "0.45.0" };
4461
- var triggerCollisionAnalyzer2 = { ...triggerCollisionAnalyzer, pluginId: "core", version: "0.45.0" };
4462
- var asciiFormatter2 = { ...asciiFormatter, pluginId: "core", version: "0.45.0" };
4463
- var jsonFormatter2 = { ...jsonFormatter, pluginId: "core", version: "0.45.0" };
4464
- var nodeBumpAction2 = { ...nodeBumpAction, pluginId: "core", version: "0.45.0" };
4465
- var nodeSupersedeAction2 = { ...nodeSupersedeAction, pluginId: "core", version: "0.45.0" };
4466
- var updateCheckHook2 = { ...updateCheckHook, pluginId: "core", version: "0.45.0" };
4467
- var builtInBundles = [
4435
+ var claudeProvider2 = { ...claudeProvider, pluginId: "claude", version: VERSION };
4436
+ var atDirectiveExtractor2 = { ...atDirectiveExtractor, pluginId: "claude", version: VERSION };
4437
+ var slashCommandExtractor2 = { ...slashCommandExtractor, pluginId: "claude", version: VERSION };
4438
+ var antigravityProvider2 = { ...antigravityProvider, pluginId: "antigravity", version: VERSION };
4439
+ var openaiProvider2 = { ...openaiProvider, pluginId: "openai", version: VERSION };
4440
+ var agentSkillsProvider2 = { ...agentSkillsProvider, pluginId: "agent-skills", version: VERSION };
4441
+ var coreMarkdownProvider2 = { ...coreMarkdownProvider, pluginId: "core", version: VERSION };
4442
+ var annotationsExtractor2 = { ...annotationsExtractor, pluginId: "core", version: VERSION };
4443
+ var externalUrlCounterExtractor2 = { ...externalUrlCounterExtractor, pluginId: "core", version: VERSION };
4444
+ var markdownLinkExtractor2 = { ...markdownLinkExtractor, pluginId: "core", version: VERSION };
4445
+ var mcpToolsExtractor2 = { ...mcpToolsExtractor, pluginId: "core", version: VERSION };
4446
+ var toolsCounterExtractor2 = { ...toolsCounterExtractor, pluginId: "core", version: VERSION };
4447
+ var annotationFieldUnknownAnalyzer2 = { ...annotationFieldUnknownAnalyzer, pluginId: "core", version: VERSION };
4448
+ var annotationOrphanAnalyzer2 = { ...annotationOrphanAnalyzer, pluginId: "core", version: VERSION };
4449
+ var annotationStaleAnalyzer2 = { ...annotationStaleAnalyzer, pluginId: "core", version: VERSION };
4450
+ var contributionOrphanAnalyzer2 = { ...contributionOrphanAnalyzer, pluginId: "core", version: VERSION };
4451
+ var issueCounterAnalyzer2 = { ...issueCounterAnalyzer, pluginId: "core", version: VERSION };
4452
+ var jobFileOrphanAnalyzer2 = { ...jobFileOrphanAnalyzer, pluginId: "core", version: VERSION };
4453
+ var linkConflictAnalyzer2 = { ...linkConflictAnalyzer, pluginId: "core", version: VERSION };
4454
+ var linkCounterAnalyzer2 = { ...linkCounterAnalyzer, pluginId: "core", version: VERSION };
4455
+ var linkSelfLoopAnalyzer2 = { ...linkSelfLoopAnalyzer, pluginId: "core", version: VERSION };
4456
+ var nameReservedAnalyzer2 = { ...nameReservedAnalyzer, pluginId: "core", version: VERSION };
4457
+ var nodeStabilityAnalyzer2 = { ...nodeStabilityAnalyzer, pluginId: "core", version: VERSION };
4458
+ var nodeSupersededAnalyzer2 = { ...nodeSupersededAnalyzer, pluginId: "core", version: VERSION };
4459
+ var referenceBrokenAnalyzer2 = { ...referenceBrokenAnalyzer, pluginId: "core", version: VERSION };
4460
+ var referenceRedundantAnalyzer2 = { ...referenceRedundantAnalyzer, pluginId: "core", version: VERSION };
4461
+ var schemaViolationAnalyzer2 = { ...schemaViolationAnalyzer, pluginId: "core", version: VERSION };
4462
+ var signalCollisionAnalyzer2 = { ...signalCollisionAnalyzer, pluginId: "core", version: VERSION };
4463
+ var triggerCollisionAnalyzer2 = { ...triggerCollisionAnalyzer, pluginId: "core", version: VERSION };
4464
+ var asciiFormatter2 = { ...asciiFormatter, pluginId: "core", version: VERSION };
4465
+ var jsonFormatter2 = { ...jsonFormatter, pluginId: "core", version: VERSION };
4466
+ var nodeBumpAction2 = { ...nodeBumpAction, pluginId: "core", version: VERSION };
4467
+ var nodeSupersedeAction2 = { ...nodeSupersedeAction, pluginId: "core", version: VERSION };
4468
+ var updateCheckHook2 = { ...updateCheckHook, pluginId: "core", version: VERSION };
4469
+ var builtInPlugins = [
4468
4470
  {
4469
4471
  id: "claude",
4470
4472
  description: "Claude Code platform integration. Classifies files under `.claude/{agents,commands,skills}` and detects Claude-flavored slash commands and at-directives in their bodies.",
@@ -4539,8 +4541,8 @@ function builtIns() {
4539
4541
  actions: [],
4540
4542
  hooks: []
4541
4543
  };
4542
- for (const bundle of builtInBundles) {
4543
- for (const ext of bundle.extensions) {
4544
+ for (const plugin of builtInPlugins) {
4545
+ for (const ext of plugin.extensions) {
4544
4546
  bucketBuiltIn(ext, out);
4545
4547
  }
4546
4548
  }
@@ -4548,8 +4550,8 @@ function builtIns() {
4548
4550
  }
4549
4551
  function listBuiltIns() {
4550
4552
  const out = [];
4551
- for (const bundle of builtInBundles) {
4552
- for (const x of bundle.extensions) {
4553
+ for (const plugin of builtInPlugins) {
4554
+ for (const x of plugin.extensions) {
4553
4555
  out.push(toExtensionRow(x));
4554
4556
  }
4555
4557
  }
@@ -4603,6 +4605,8 @@ var ENTRY_TEXTS = {
4603
4605
  parseErrorFlagSuggestion: "Did you mean '{{suggestion}}'?",
4604
4606
  parseErrorVerbSuggestion: "Did you mean {{suggestions}}?",
4605
4607
  parseErrorVerbHelpHint: "Run 'sm help {{verb}}' for usage.",
4608
+ /** Footer for the incomplete-namespace error, points at that namespace's overview. */
4609
+ parseErrorNamespaceHelpHint: "Run 'sm help {{name}}' to see all subcommands.",
4606
4610
  parseErrorFooter: "Run 'sm help' to see the full command list."
4607
4611
  };
4608
4612
 
@@ -4891,7 +4895,8 @@ function formatParseError(params) {
4891
4895
  const suggestion2 = tx(ENTRY_TEXTS.parseErrorSubcommandList, {
4892
4896
  suggestions: formatSuggestionList(subcommands)
4893
4897
  });
4894
- return renderError(headline, suggestion2);
4898
+ const footer = tx(ENTRY_TEXTS.parseErrorNamespaceHelpHint, { name: firstToken });
4899
+ return renderError(headline, suggestion2, footer);
4895
4900
  }
4896
4901
  const candidates = closestVerbs(firstToken, verbPaths);
4897
4902
  const suggestion = candidates.length > 0 ? tx(ENTRY_TEXTS.parseErrorVerbSuggestion, { suggestions: formatSuggestionList(candidates) }) : null;
@@ -4932,10 +4937,10 @@ function matchedVerbPrefix(args2, verbPaths) {
4932
4937
  }
4933
4938
  return best.join(" ");
4934
4939
  }
4935
- function renderError(headline, suggestion) {
4940
+ function renderError(headline, suggestion, footer = ENTRY_TEXTS.parseErrorFooter) {
4936
4941
  const lines = [tx(ENTRY_TEXTS.parseErrorHeadline, { message: headline })];
4937
4942
  if (suggestion) lines.push(suggestion);
4938
- lines.push(ENTRY_TEXTS.parseErrorFooter);
4943
+ lines.push(footer);
4939
4944
  return lines.join("\n") + "\n";
4940
4945
  }
4941
4946
  function extractOffendingFlag(message) {
@@ -5107,7 +5112,7 @@ async function initSentryCli(version, loadSdk = () => import("@sentry/node")) {
5107
5112
  const Sentry = await loadSdk();
5108
5113
  Sentry.init({
5109
5114
  dsn: SENTRY_DSN_NODE,
5110
- release: `@skill-map/cli@${version}`,
5115
+ release: `skill-map-cli@${version}`,
5111
5116
  environment: resolveTelemetryEnv(),
5112
5117
  // CLI and BFF share one Sentry project; the `surface` tag tells their
5113
5118
  // events apart in the shared issue stream.
@@ -5365,16 +5370,13 @@ function kernelLocalSettingsPath(scopeRoot) {
5365
5370
  // config/defaults.json
5366
5371
  var defaults_default = {
5367
5372
  schemaVersion: 1,
5368
- autoMigrate: true,
5369
5373
  allowEditSmFiles: false,
5370
5374
  tokenizer: "cl100k_base",
5371
- providers: [],
5372
5375
  roots: [],
5373
5376
  ignore: [],
5374
5377
  scan: {
5375
5378
  tokenize: true,
5376
5379
  strict: false,
5377
- followSymlinks: false,
5378
5380
  maxFileSizeBytes: 1048576,
5379
5381
  maxNodes: 256,
5380
5382
  watch: {
@@ -5383,9 +5385,6 @@ var defaults_default = {
5383
5385
  referencePaths: []
5384
5386
  },
5385
5387
  plugins: {},
5386
- history: {
5387
- share: false
5388
- },
5389
5388
  jobs: {
5390
5389
  ttlSeconds: 3600,
5391
5390
  graceMultiplier: 3,
@@ -5396,9 +5395,6 @@ var defaults_default = {
5396
5395
  completed: 2592e3,
5397
5396
  failed: null
5398
5397
  }
5399
- },
5400
- i18n: {
5401
- locale: "en"
5402
5398
  }
5403
5399
  };
5404
5400
 
@@ -7510,6 +7506,7 @@ async function loadScanResult(db) {
7510
7506
  version: metaRow.scannedByVersion,
7511
7507
  specVersion: metaRow.scannedBySpecVersion
7512
7508
  };
7509
+ const oversizedFiles = parseJsonArray(metaRow.oversizedFilesJson);
7513
7510
  return {
7514
7511
  schemaVersion: 1,
7515
7512
  scannedAt: metaRow.scannedAt,
@@ -7518,12 +7515,14 @@ async function loadScanResult(db) {
7518
7515
  scannedBy,
7519
7516
  recommendedNodeLimit: metaRow.recommendedNodeLimit,
7520
7517
  overrideMaxNodes: metaRow.overrideMaxNodes,
7518
+ oversizedFiles,
7521
7519
  nodes,
7522
7520
  links,
7523
7521
  issues,
7524
7522
  stats: {
7525
7523
  filesWalked: metaRow.statsFilesWalked,
7526
7524
  filesSkipped: metaRow.statsFilesSkipped,
7525
+ filesOversized: metaRow.filesOversized,
7527
7526
  nodesCount: nodes.length,
7528
7527
  linksCount: links.length,
7529
7528
  issuesCount: issues.length,
@@ -7546,12 +7545,14 @@ async function loadScanResult(db) {
7546
7545
  // scan overwrites scan_meta with the live values on next run.
7547
7546
  recommendedNodeLimit: 256,
7548
7547
  overrideMaxNodes: null,
7548
+ oversizedFiles: [],
7549
7549
  nodes,
7550
7550
  links,
7551
7551
  issues,
7552
7552
  stats: {
7553
7553
  filesWalked: 0,
7554
7554
  filesSkipped: 0,
7555
+ filesOversized: 0,
7555
7556
  nodesCount: nodes.length,
7556
7557
  linksCount: links.length,
7557
7558
  issuesCount: issues.length,
@@ -8128,7 +8129,15 @@ function metaToRow(result) {
8128
8129
  statsFilesWalked: result.stats.filesWalked,
8129
8130
  statsFilesSkipped: result.stats.filesSkipped,
8130
8131
  statsDurationMs: result.stats.durationMs,
8131
- ...projectNodeLimitColumns(result)
8132
+ ...projectNodeLimitColumns(result),
8133
+ ...projectOversizedColumns(result)
8134
+ };
8135
+ }
8136
+ function projectOversizedColumns(result) {
8137
+ const oversized = result.oversizedFiles ?? [];
8138
+ return {
8139
+ filesOversized: result.stats.filesOversized ?? oversized.length,
8140
+ oversizedFilesJson: oversized.length > 0 ? JSON.stringify(oversized) : null
8132
8141
  };
8133
8142
  }
8134
8143
  function projectNodeLimitColumns(result) {
@@ -9452,6 +9461,7 @@ var PLUGIN_LOADER_TEXTS = {
9452
9461
  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.",
9453
9462
  idCollision: "Plugin '{{id}}' at {{pathA}} collides with the plugin at {{pathB}}. Rename one and rerun.",
9454
9463
  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.",
9464
+ invalidManifestRedeclaredField: "{{relEntry}}: extension manifest declares {{fields}}, derived from the folder layout (structure-as-truth) and not a manifest field. Remove it: id is the leaf folder, kind the parent folder, provider kinds the `kinds/` catalog, formatter formatId the formatter folder name.",
9455
9465
  loadErrorStorageSchemaRead: "plugin '{{pluginId}}' failed to load schema for table '{{table}}': {{schemaPath}}: {{errDescription}}",
9456
9466
  loadErrorStorageSchemaCompile: "plugin '{{pluginId}}' failed to compile schema for table '{{table}}': {{schemaPath}}: {{errDescription}}",
9457
9467
  loadErrorStorageKvSchemaRead: "plugin '{{pluginId}}' failed to load KV schema: {{schemaPath}}: {{errDescription}}",
@@ -9556,7 +9566,7 @@ function extractDefault(mod) {
9556
9566
  if (!isRecord(mod)) return mod;
9557
9567
  return "default" in mod ? mod["default"] : mod;
9558
9568
  }
9559
- var LOADER_INJECTED_KEYS = /* @__PURE__ */ new Set(["pluginId", "id", "kind", "kinds", "formatId"]);
9569
+ var LOADER_INJECTED_KEYS = /* @__PURE__ */ new Set(["pluginId"]);
9560
9570
  function stripFunctionsAndPluginId(input) {
9561
9571
  if (!isRecord(input)) return input;
9562
9572
  const out = {};
@@ -10177,6 +10187,21 @@ var PluginLoader = class {
10177
10187
  manifest
10178
10188
  } };
10179
10189
  }
10190
+ const redeclared = DERIVED_MANIFEST_KEYS.filter((field) => field in exported);
10191
+ if (redeclared.length > 0) {
10192
+ return { ok: false, failure: {
10193
+ ...fail(
10194
+ pluginPath,
10195
+ pluginId,
10196
+ "invalid-manifest",
10197
+ tx(PLUGIN_LOADER_TEXTS.invalidManifestRedeclaredField, {
10198
+ relEntry,
10199
+ fields: redeclared.map((field) => `\`${field}\``).join(", ")
10200
+ })
10201
+ ),
10202
+ manifest
10203
+ } };
10204
+ }
10180
10205
  const manifestView = stripFunctionsAndPluginId(exported);
10181
10206
  if (kind === "hook") {
10182
10207
  const hookFailure = validateHookTriggers(pluginPath, pluginId, manifest, relEntry, exported, manifestView);
@@ -10233,8 +10258,7 @@ var PluginLoader = class {
10233
10258
  const instance = { ...exported, pluginId, id: pathId2, kind };
10234
10259
  if (kind === "formatter") instance["formatId"] = pathId2;
10235
10260
  if (kind === "provider" && discoveredKinds) {
10236
- const inlineKinds = isRecord(exported["kinds"]) ? exported["kinds"] : {};
10237
- instance["kinds"] = { ...inlineKinds, ...discoveredKinds };
10261
+ instance["kinds"] = discoveredKinds;
10238
10262
  }
10239
10263
  return { ok: true, extension: {
10240
10264
  kind,
@@ -10247,6 +10271,7 @@ var PluginLoader = class {
10247
10271
  } };
10248
10272
  }
10249
10273
  };
10274
+ var DERIVED_MANIFEST_KEYS = ["id", "kind", "kinds", "formatId"];
10250
10275
  var KIND_DIR_NAMES = [
10251
10276
  "providers",
10252
10277
  "extractors",
@@ -10365,11 +10390,11 @@ function makeEnabledResolver(cfg, dbOverrides) {
10365
10390
  function defaultResolveEnabled(_id) {
10366
10391
  return true;
10367
10392
  }
10368
- function isBuiltInExtensionEnabled(bundle, ext, resolveEnabled) {
10369
- return isBundleEntryEnabled(bundle, ext.id, resolveEnabled);
10393
+ function isBuiltInExtensionEnabled(plugin, ext, resolveEnabled) {
10394
+ return isPluginEntryEnabled(plugin, ext.id, resolveEnabled);
10370
10395
  }
10371
- function isBundleEntryEnabled(bundle, extId, resolveEnabled) {
10372
- return resolveEnabled(qualifiedExtensionId(bundle.id, extId));
10396
+ function isPluginEntryEnabled(plugin, extId, resolveEnabled) {
10397
+ return resolveEnabled(qualifiedExtensionId(plugin.id, extId));
10373
10398
  }
10374
10399
  function isPluginExtensionEnabled(ext, resolveEnabled) {
10375
10400
  return resolveEnabled(qualifiedExtensionId(ext.pluginId, ext.id));
@@ -10568,8 +10593,9 @@ async function* walkContent(roots, options) {
10568
10593
  if (!parser) throw new UnknownParserError(options.parser);
10569
10594
  const filter = options.ignoreFilter ?? buildIgnoreFilter();
10570
10595
  const extensions = options.extensions;
10596
+ const sizeLimit = buildSizeLimit(options);
10571
10597
  for (const root of roots) {
10572
- for await (const file of walkRoot(root, root, filter, extensions)) {
10598
+ for await (const file of walkRoot(root, root, filter, extensions, sizeLimit)) {
10573
10599
  const relPath = relative2(root, file).split(sep3).join("/");
10574
10600
  let raw;
10575
10601
  try {
@@ -10592,7 +10618,15 @@ async function* walkContent(roots, options) {
10592
10618
  }
10593
10619
  }
10594
10620
  }
10595
- async function* walkRoot(root, current, filter, extensions) {
10621
+ function buildSizeLimit(options) {
10622
+ const sizeLimit = {};
10623
+ if (options.maxFileSizeBytes !== void 0) {
10624
+ sizeLimit.maxFileSizeBytes = options.maxFileSizeBytes;
10625
+ }
10626
+ if (options.onOversizedFile) sizeLimit.onOversizedFile = options.onOversizedFile;
10627
+ return sizeLimit;
10628
+ }
10629
+ async function* walkRoot(root, current, filter, extensions, sizeLimit) {
10596
10630
  let entries;
10597
10631
  try {
10598
10632
  entries = await readdir(current, { withFileTypes: true, encoding: "utf8" });
@@ -10606,11 +10640,16 @@ async function* walkRoot(root, current, filter, extensions) {
10606
10640
  if (filter.ignores(rel)) continue;
10607
10641
  if (entry.isSymbolicLink()) continue;
10608
10642
  if (entry.isDirectory()) {
10609
- yield* walkRoot(root, full, filter, extensions);
10643
+ yield* walkRoot(root, full, filter, extensions, sizeLimit);
10610
10644
  } else if (entry.isFile() && hasMatchingExtension(name, extensions)) {
10611
10645
  try {
10612
10646
  const s = await lstat(full);
10613
- if (s.isFile()) yield full;
10647
+ if (!s.isFile()) continue;
10648
+ if (sizeLimit.maxFileSizeBytes !== void 0 && s.size > sizeLimit.maxFileSizeBytes) {
10649
+ sizeLimit.onOversizedFile?.({ path: rel, bytes: s.size });
10650
+ continue;
10651
+ }
10652
+ yield full;
10614
10653
  } catch {
10615
10654
  }
10616
10655
  }
@@ -10640,6 +10679,10 @@ function resolveProviderWalk(provider) {
10640
10679
  parser: read.parser
10641
10680
  };
10642
10681
  if (options?.ignoreFilter) walkOptions.ignoreFilter = options.ignoreFilter;
10682
+ if (options?.maxFileSizeBytes !== void 0) {
10683
+ walkOptions.maxFileSizeBytes = options.maxFileSizeBytes;
10684
+ }
10685
+ if (options?.onOversizedFile) walkOptions.onOversizedFile = options.onOversizedFile;
10643
10686
  return walkContent(roots, walkOptions);
10644
10687
  };
10645
10688
  }
@@ -10674,19 +10717,19 @@ function collectViewContributions(pluginId, extensionId, instance, out, options
10674
10717
  }
10675
10718
 
10676
10719
  // core/runtime/plugin-runtime/bucketing.ts
10677
- function bucketLoaded(loaded, bundle) {
10720
+ function bucketLoaded(loaded, runtime) {
10678
10721
  for (const ext of loaded) {
10679
10722
  const instance = ext.instance;
10680
10723
  if (!isExtensionInstance(instance)) continue;
10681
10724
  bucketByKind(ext.kind, instance, {
10682
- provider: bundle.extensions.providers,
10683
- extractor: bundle.extensions.extractors,
10684
- analyzer: bundle.extensions.analyzers,
10685
- formatter: bundle.extensions.formatters,
10686
- hook: bundle.extensions.hooks
10725
+ provider: runtime.extensions.providers,
10726
+ extractor: runtime.extensions.extractors,
10727
+ analyzer: runtime.extensions.analyzers,
10728
+ formatter: runtime.extensions.formatters,
10729
+ hook: runtime.extensions.hooks
10687
10730
  // `action` intentionally absent, see docstring.
10688
10731
  });
10689
- bundle.manifests.push({
10732
+ runtime.manifests.push({
10690
10733
  id: ext.id,
10691
10734
  pluginId: ext.pluginId,
10692
10735
  kind: ext.kind,
@@ -10694,8 +10737,8 @@ function bucketLoaded(loaded, bundle) {
10694
10737
  description: instance.description ?? "",
10695
10738
  ...ext.entryPath ? { entry: ext.entryPath } : {}
10696
10739
  });
10697
- collectAnnotationContributions(ext.pluginId, instance, bundle.annotationContributions);
10698
- collectViewContributions(ext.pluginId, ext.id, instance, bundle.viewContributions);
10740
+ collectAnnotationContributions(ext.pluginId, instance, runtime.annotationContributions);
10741
+ collectViewContributions(ext.pluginId, ext.id, instance, runtime.viewContributions);
10699
10742
  }
10700
10743
  }
10701
10744
  function collectAnnotationContributions(pluginId, instance, out) {
@@ -10753,8 +10796,8 @@ var PLUGIN_RUNTIME_TEXTS = {
10753
10796
  // core/runtime/plugin-runtime/warnings.ts
10754
10797
  var PLUGIN_ID_DISPLAY_CAP = 200;
10755
10798
  var PLUGIN_REASON_DISPLAY_CAP = 1e3;
10756
- function emitWarnings(bundle, printer) {
10757
- for (const warn of bundle.warnings) {
10799
+ function emitWarnings(runtime, printer) {
10800
+ for (const warn of runtime.warnings) {
10758
10801
  printer.warn(`${warn}
10759
10802
  `);
10760
10803
  }
@@ -10793,7 +10836,7 @@ async function loadPluginRuntime(opts = {}) {
10793
10836
  if (resolveEnabled) loaderOpts.resolveEnabled = resolveEnabled;
10794
10837
  const loader = createPluginLoader(loaderOpts);
10795
10838
  const discovered = await loader.discoverAndLoadAll();
10796
- const bundle = {
10839
+ const runtime = {
10797
10840
  extensions: { providers: [], extractors: [], analyzers: [], formatters: [], hooks: [] },
10798
10841
  annotationContributions: [],
10799
10842
  viewContributions: [],
@@ -10807,14 +10850,14 @@ async function loadPluginRuntime(opts = {}) {
10807
10850
  };
10808
10851
  for (const plugin of discovered) {
10809
10852
  if (plugin.status === "enabled") {
10810
- bucketLoaded(plugin.extensions ?? [], bundle);
10853
+ bucketLoaded(plugin.extensions ?? [], runtime);
10811
10854
  continue;
10812
10855
  }
10813
10856
  if (plugin.status === "disabled") continue;
10814
- bundle.warnings.push(formatWarning(plugin));
10857
+ runtime.warnings.push(formatWarning(plugin));
10815
10858
  }
10816
- enforceRootExclusivity(bundle.annotationContributions);
10817
- return bundle;
10859
+ enforceRootExclusivity(runtime.annotationContributions);
10860
+ return runtime;
10818
10861
  }
10819
10862
  var AnnotationContributionConflictError = class extends Error {
10820
10863
  /** The colliding root-exclusive key. */
@@ -10848,7 +10891,7 @@ function enforceRootExclusivity(catalog) {
10848
10891
  }
10849
10892
  }
10850
10893
  function emptyPluginRuntime() {
10851
- const bundle = {
10894
+ const runtime = {
10852
10895
  extensions: { providers: [], extractors: [], analyzers: [], formatters: [], hooks: [] },
10853
10896
  annotationContributions: [],
10854
10897
  viewContributions: [],
@@ -10860,7 +10903,7 @@ function emptyPluginRuntime() {
10860
10903
  emitWarnings(this, printer);
10861
10904
  }
10862
10905
  };
10863
- return bundle;
10906
+ return runtime;
10864
10907
  }
10865
10908
 
10866
10909
  // core/runtime/plugin-runtime/catalogs.ts
@@ -10878,12 +10921,12 @@ function collectRegisteredContributionKeys(composed) {
10878
10921
  return keys;
10879
10922
  }
10880
10923
  function filterBuiltInManifests(manifests, resolveEnabled) {
10881
- const bundleByPluginId = /* @__PURE__ */ new Map();
10882
- for (const bundle of builtInBundles) bundleByPluginId.set(bundle.id, bundle);
10924
+ const pluginById = /* @__PURE__ */ new Map();
10925
+ for (const plugin of builtInPlugins) pluginById.set(plugin.id, plugin);
10883
10926
  return manifests.filter((m) => {
10884
- const bundle = bundleByPluginId.get(m.pluginId);
10885
- if (!bundle) return true;
10886
- return isBundleEntryEnabled(bundle, m.id, resolveEnabled);
10927
+ const plugin = pluginById.get(m.pluginId);
10928
+ if (!plugin) return true;
10929
+ return isPluginEntryEnabled(plugin, m.id, resolveEnabled);
10887
10930
  });
10888
10931
  }
10889
10932
 
@@ -10926,9 +10969,9 @@ function composeScanExtensions(opts) {
10926
10969
  };
10927
10970
  }
10928
10971
  function accumulateBuiltInScanExtensions(buckets, resolveEnabled) {
10929
- for (const bundle of builtInBundles) {
10930
- for (const ext of bundle.extensions) {
10931
- if (!isBuiltInExtensionEnabled(bundle, ext, resolveEnabled)) continue;
10972
+ for (const plugin of builtInPlugins) {
10973
+ for (const ext of plugin.extensions) {
10974
+ if (!isBuiltInExtensionEnabled(plugin, ext, resolveEnabled)) continue;
10932
10975
  switch (ext.kind) {
10933
10976
  case "provider":
10934
10977
  buckets.providers.push(ext);
@@ -10959,10 +11002,10 @@ function composeFormatters(opts) {
10959
11002
  const resolveEnabled = opts.resolveEnabled ?? opts.pluginRuntime.resolveEnabled;
10960
11003
  const out = [];
10961
11004
  if (!noBuiltIns) {
10962
- for (const bundle of builtInBundles) {
10963
- for (const ext of bundle.extensions) {
11005
+ for (const plugin of builtInPlugins) {
11006
+ for (const ext of plugin.extensions) {
10964
11007
  if (ext.kind !== "formatter") continue;
10965
- if (!isBuiltInExtensionEnabled(bundle, ext, resolveEnabled)) continue;
11008
+ if (!isBuiltInExtensionEnabled(plugin, ext, resolveEnabled)) continue;
10966
11009
  out.push(ext);
10967
11010
  }
10968
11011
  }
@@ -10998,9 +11041,9 @@ function registerEnabledExtensions(kernel, pluginRuntime, options = {}) {
10998
11041
  );
10999
11042
  const merged = [...userContribs];
11000
11043
  if (!noBuiltIns) {
11001
- for (const bundle of builtInBundles) {
11002
- for (const ext of bundle.extensions) {
11003
- if (!isBundleEntryEnabled(bundle, ext.id, resolveEnabled)) continue;
11044
+ for (const plugin of builtInPlugins) {
11045
+ for (const ext of plugin.extensions) {
11046
+ if (!isPluginEntryEnabled(plugin, ext.id, resolveEnabled)) continue;
11004
11047
  collectViewContributions(ext.pluginId, ext.id, ext, merged);
11005
11048
  }
11006
11049
  }
@@ -11408,7 +11451,6 @@ var CONFIG_TEXTS = {
11408
11451
  listSectionScan: "Scan",
11409
11452
  listSectionJobs: "Jobs",
11410
11453
  listSectionRootsAndPlugins: "Roots & plugins",
11411
- listSectionHistory: "History",
11412
11454
  listSectionOther: "Other"
11413
11455
  };
11414
11456
 
@@ -11525,15 +11567,14 @@ var ConfigListCommand = class extends SmCommand {
11525
11567
  var SECTION_DEFS = [
11526
11568
  {
11527
11569
  title: CONFIG_TEXTS.listSectionGeneral,
11528
- exactKeys: ["autoMigrate", "schemaVersion", "tokenizer", "i18n.locale"]
11570
+ exactKeys: ["schemaVersion", "tokenizer"]
11529
11571
  },
11530
11572
  { title: CONFIG_TEXTS.listSectionScan, prefix: "scan.", stripPrefix: true },
11531
11573
  { title: CONFIG_TEXTS.listSectionJobs, prefix: "jobs.", stripPrefix: true },
11532
11574
  {
11533
11575
  title: CONFIG_TEXTS.listSectionRootsAndPlugins,
11534
- exactKeys: ["roots", "providers", "plugins", "ignore"]
11535
- },
11536
- { title: CONFIG_TEXTS.listSectionHistory, prefix: "history.", stripPrefix: true }
11576
+ exactKeys: ["roots", "plugins", "ignore"]
11577
+ }
11537
11578
  ];
11538
11579
  function renderConfigSections(rows, ansi) {
11539
11580
  const out = [];
@@ -12451,12 +12492,12 @@ function collectProviderScopes(specRoot) {
12451
12492
  }
12452
12493
  const pluginsRoot = resolve21(workspaceRoot, "plugins");
12453
12494
  if (!existsSync17(pluginsRoot)) return out;
12454
- for (const bundleEntry of readdirSync6(pluginsRoot)) {
12455
- const bundleDir = resolve21(pluginsRoot, bundleEntry);
12456
- if (!isDir(bundleDir)) continue;
12457
- const providersRoot = resolve21(bundleDir, "providers");
12495
+ for (const pluginEntry of readdirSync6(pluginsRoot)) {
12496
+ const pluginDir = resolve21(pluginsRoot, pluginEntry);
12497
+ if (!isDir(pluginDir)) continue;
12498
+ const providersRoot = resolve21(pluginDir, "providers");
12458
12499
  if (!isDir(providersRoot)) continue;
12459
- collectBundleProviderScopes(providersRoot, specRoot, out);
12500
+ collectPluginProviderScopes(providersRoot, specRoot, out);
12460
12501
  }
12461
12502
  return out;
12462
12503
  }
@@ -12467,7 +12508,7 @@ function isDir(path) {
12467
12508
  return false;
12468
12509
  }
12469
12510
  }
12470
- function collectBundleProviderScopes(providersRoot, specRoot, out) {
12511
+ function collectPluginProviderScopes(providersRoot, specRoot, out) {
12471
12512
  for (const entry of readdirSync6(providersRoot)) {
12472
12513
  const providerDir = resolve21(providersRoot, entry);
12473
12514
  if (!isDir(providerDir)) continue;
@@ -14214,6 +14255,22 @@ var HELP_TEXTS = {
14214
14255
  /** Trailing fragment for `humanFlagRow`'s `{{required}}` slot. */
14215
14256
  humanFlagRowRequiredFragment: " (required)",
14216
14257
  humanFooter: "Run `sm help {{name}} --format md` for the full reference.",
14258
+ // --- human group renderer (sm <namespace> --help, sm help <namespace>) ---
14259
+ /**
14260
+ * USAGE row for a command namespace (a prefix that owns subcommands but
14261
+ * is not itself a runnable verb, e.g. `plugins`, `db`). Mirrors
14262
+ * `humanUsageRow` but advertises the `<command>` slot instead of
14263
+ * positionals.
14264
+ */
14265
+ humanGroupUsageRow: " sm {{name}} <command> [options]",
14266
+ /** Section heading listing the subcommands of a namespace. */
14267
+ humanCommandsHeading: "COMMANDS",
14268
+ /** Aligned subcommand row; `{{name}}` is the subcommand relative to the namespace. */
14269
+ humanCommandRow: " {{name}}{{padding}} {{description}}",
14270
+ /** Footer for the namespace overview, points at per-subcommand help. */
14271
+ humanGroupFooter: "Run `sm {{name}} <command> --help` for flags and arguments.",
14272
+ /** Fallback header description when a namespace has no curated entry in `HELP_GROUPS`. */
14273
+ groupFallbackDescription: "{{category}} commands",
14217
14274
  // --- human compact overview (sm / sm --help / sm help, no verb) ---------
14218
14275
  /**
14219
14276
  * Compact-overview header. Replaces the Clipanion default ANSI banner.
@@ -14244,6 +14301,40 @@ var HELP_TEXTS = {
14244
14301
  compactExampleRow: " {{command}}{{padding}} {{description}}",
14245
14302
  compactFooter: "Run `sm <command> --help` for flags and arguments."
14246
14303
  };
14304
+ var HELP_GROUPS = {
14305
+ plugins: {
14306
+ description: "Discover, inspect, and toggle plugins",
14307
+ details: "A plugin is a directory of extensions (extractors, analyzers, actions,\nhooks, formatters, providers) discovered under the project plugins dir.\n\nUse `list` and `show` to inspect what loaded, `doctor` to diagnose load\nfailures, `enable` / `disable` to toggle extensions (persisted in the DB),\nand `create` / `upgrade` to scaffold and migrate your own."
14308
+ },
14309
+ config: {
14310
+ description: "Read and write project configuration",
14311
+ details: "Configuration is a layered merge: library defaults, the committed\n`settings.json`, the gitignored `settings.local.json`, env vars, then\nCLI flags, with later layers winning.\n\nUse `list` / `get` to read the effective values, `show --source` to see\nwhich layer set a key, and `set` / `reset` to write or revert one.\nPrivacy-sensitive keys (paths outside the project) require `--yes`."
14312
+ },
14313
+ db: {
14314
+ description: "Inspect and maintain the project database",
14315
+ details: "The project database is a single SQLite file at\n`.skill-map/skill-map.db`, holding the scan graph and plugin state.\n\nUse `backup` / `restore` around risky operations, `migrate` to apply\npending kernel and plugin migrations, `reset` to drop tables (or the\nwhole file), and `dump` / `shell` / `browser` to inspect the data."
14316
+ },
14317
+ job: {
14318
+ description: "Manage the background job queue",
14319
+ details: "Probabilistic and long-running work runs as jobs: queued, persisted in\nthe database, and resumable across restarts.\n\nUse `submit` to enqueue (or `--all` to fan out across nodes), `run` to\nexecute the claim-spawn-record loop, `status` / `list` / `show` to\ninspect, `preview` to render a job without executing it, and\n`cancel` / `prune` to clean up."
14320
+ },
14321
+ actions: {
14322
+ description: "Inspect the registered Action catalog",
14323
+ details: "An Action operates on one or more nodes and is either deterministic\n(in-process code) or probabilistic (a rendered prompt a runner executes).\n\nUse `list` for the catalog of registered action types and `show` for a\nsingle action's full manifest, including its preconditions and expected\nduration."
14324
+ },
14325
+ sidecar: {
14326
+ description: "Manage `.sm` annotation sidecars",
14327
+ details: "Skill-map's annotation layer lives in co-located `.sm` YAML sidecars\nnext to each node, leaving the vendor file untouched.\n\nUse `annotate` to scaffold an empty sidecar ready for editing, `refresh`\nto realign its drift hashes with the live node, and `prune` to delete\nsidecars whose `.md` no longer exists."
14328
+ },
14329
+ hooks: {
14330
+ description: "Install git hooks for sidecar drift",
14331
+ details: "Git hooks keep your sidecars in sync with the repo as you commit.\n\n`install` writes a pre-commit hook that auto-bumps staged sidecar drift\nbefore each commit."
14332
+ },
14333
+ conformance: {
14334
+ description: "Run the spec conformance suite",
14335
+ details: "The conformance suite checks an implementation against the spec.\n\n`run` executes the spec-owned cases plus every built-in Provider and\nreports the results."
14336
+ }
14337
+ };
14247
14338
 
14248
14339
  // cli/commands/help.ts
14249
14340
  var HelpCommand = class extends Command15 {
@@ -14282,18 +14373,23 @@ var HelpCommand = class extends Command15 {
14282
14373
  const verb = this.verbParts.join(" ").trim();
14283
14374
  if (verb) {
14284
14375
  const target = verbs.find((v) => v.name === verb);
14285
- if (!target) {
14286
- this.context.stderr.write(
14287
- tx(HELP_TEXTS.unknownVerb, {
14288
- glyph: errGlyph,
14289
- verb,
14290
- hint: ansi.dim(HELP_TEXTS.unknownVerbHint)
14291
- })
14292
- );
14293
- return ExitCode.NotFound;
14376
+ if (target) {
14377
+ this.context.stdout.write(renderSingle(target, format));
14378
+ return ExitCode.Ok;
14294
14379
  }
14295
- this.context.stdout.write(renderSingle(target, format));
14296
- return ExitCode.Ok;
14380
+ const subcommands = verbs.filter((v) => v.name.startsWith(verb + " "));
14381
+ if (subcommands.length > 0) {
14382
+ this.context.stdout.write(renderGroup(verb, subcommands, format));
14383
+ return ExitCode.Ok;
14384
+ }
14385
+ this.context.stderr.write(
14386
+ tx(HELP_TEXTS.unknownVerb, {
14387
+ glyph: errGlyph,
14388
+ verb,
14389
+ hint: ansi.dim(HELP_TEXTS.unknownVerbHint)
14390
+ })
14391
+ );
14392
+ return ExitCode.NotFound;
14297
14393
  }
14298
14394
  if (format === "human") {
14299
14395
  this.context.stdout.write(renderCompactOverview(verbs));
@@ -14480,6 +14576,51 @@ function renderSingle(verb, format) {
14480
14576
  }
14481
14577
  return renderSingleHuman(verb);
14482
14578
  }
14579
+ function renderGroup(group, subcommands, format) {
14580
+ if (format === "json" || format === "md") {
14581
+ const doc = {
14582
+ cliVersion: VERSION,
14583
+ specVersion: resolveSpecVersion(),
14584
+ globalFlags: [],
14585
+ verbs: subcommands
14586
+ };
14587
+ return format === "json" ? JSON.stringify(doc, null, 2) + "\n" : renderMarkdown2(doc);
14588
+ }
14589
+ return renderGroupHuman(group, subcommands);
14590
+ }
14591
+ function renderGroupHuman(group, subcommands) {
14592
+ const meta = HELP_GROUPS[group];
14593
+ const description = meta?.description ?? tx(HELP_TEXTS.groupFallbackDescription, { category: subcommands[0]?.category ?? "Other" });
14594
+ const out = [];
14595
+ out.push(tx(HELP_TEXTS.humanVerbHeader, { name: group, description }));
14596
+ out.push("");
14597
+ out.push(HELP_TEXTS.humanUsageHeading);
14598
+ out.push(tx(HELP_TEXTS.humanGroupUsageRow, { name: group }));
14599
+ if (meta?.details) out.push(...renderHumanDescription(meta.details));
14600
+ out.push(...renderGroupCommands(group, subcommands));
14601
+ out.push("");
14602
+ out.push(tx(HELP_TEXTS.humanGroupFooter, { name: group }));
14603
+ return out.join("\n") + "\n";
14604
+ }
14605
+ function renderGroupCommands(group, subcommands) {
14606
+ const out = ["", HELP_TEXTS.humanCommandsHeading];
14607
+ const sorted = [...subcommands].sort((a, b) => a.name.localeCompare(b.name));
14608
+ const labels = sorted.map((v) => v.name.slice(group.length + 1));
14609
+ const width = Math.max(...labels.map((l) => l.length));
14610
+ for (let i = 0; i < sorted.length; i++) {
14611
+ const verb = sorted[i];
14612
+ const label = labels[i];
14613
+ const { isStub, clean } = classifyDescription(verb.description);
14614
+ const description = (isStub ? HELP_TEXTS.compactStubMarker : "") + firstSentence(clean);
14615
+ const row = tx(HELP_TEXTS.humanCommandRow, {
14616
+ name: label,
14617
+ padding: padRight("", width - label.length),
14618
+ description
14619
+ });
14620
+ out.push(truncate2(row, COMPACT_ROW_MAX));
14621
+ }
14622
+ return out;
14623
+ }
14483
14624
  function renderSingleHuman(verb) {
14484
14625
  const out = [];
14485
14626
  out.push(buildHumanHeader(verb));
@@ -14611,7 +14752,7 @@ function routeHelpArgs(args2, cli2) {
14611
14752
  if (!shouldRouteHelp(args2)) return args2;
14612
14753
  const leading = leadingPositionals(args2);
14613
14754
  if (leading.length === 0) return args2;
14614
- const verbPath = longestVerbPrefix(leading, registeredVerbPaths(cli2));
14755
+ const verbPath = longestVerbOrGroupPrefix(leading, registeredVerbPaths(cli2));
14615
14756
  if (verbPath.length === 0) return args2;
14616
14757
  return ["help", ...verbPath];
14617
14758
  }
@@ -14630,14 +14771,15 @@ function leadingPositionals(args2) {
14630
14771
  }
14631
14772
  return out;
14632
14773
  }
14633
- function longestVerbPrefix(positionals, verbPaths) {
14634
- let best = [];
14635
- for (const path of verbPaths) {
14636
- if (path.length > positionals.length) continue;
14637
- const matches = path.every((tok, i) => positionals[i] === tok);
14638
- if (matches && path.length > best.length) best = path;
14774
+ function longestVerbOrGroupPrefix(positionals, verbPaths) {
14775
+ for (let len = positionals.length; len >= 1; len--) {
14776
+ const prefix = positionals.slice(0, len);
14777
+ const matches = verbPaths.some(
14778
+ (path) => path.length >= len && prefix.every((tok, i) => path[i] === tok)
14779
+ );
14780
+ if (matches) return prefix;
14639
14781
  }
14640
- return best;
14782
+ return [];
14641
14783
  }
14642
14784
  function registeredVerbPaths(cli2) {
14643
14785
  const rawDefs = cli2.definitions();
@@ -16157,7 +16299,18 @@ async function walkAndExtract(opts) {
16157
16299
  const accum = createWalkAccumulators();
16158
16300
  const wctx = buildWalkContext(opts);
16159
16301
  const claimedPaths = /* @__PURE__ */ new Set();
16160
- const walkOptions = opts.ignoreFilter ? { ignoreFilter: opts.ignoreFilter } : {};
16302
+ const oversizedFiles = [];
16303
+ const oversizedSeen = /* @__PURE__ */ new Set();
16304
+ const onOversizedFile = (info) => {
16305
+ if (oversizedSeen.has(info.path)) return;
16306
+ oversizedSeen.add(info.path);
16307
+ oversizedFiles.push(info);
16308
+ };
16309
+ const walkOptions = {
16310
+ ...opts.ignoreFilter ? { ignoreFilter: opts.ignoreFilter } : {},
16311
+ onOversizedFile,
16312
+ ...opts.maxFileSizeBytes !== void 0 ? { maxFileSizeBytes: opts.maxFileSizeBytes } : {}
16313
+ };
16161
16314
  let filesWalked = 0;
16162
16315
  let index = 0;
16163
16316
  const effectiveMaxNodes = opts.overrideMaxNodes ?? opts.recommendedNodeLimit;
@@ -16190,6 +16343,7 @@ async function walkAndExtract(opts) {
16190
16343
  cachedPaths: accum.cachedPaths,
16191
16344
  frontmatterIssues: accum.frontmatterIssues,
16192
16345
  filesWalked,
16346
+ oversizedFiles,
16193
16347
  recommendedNodeLimit: opts.recommendedNodeLimit,
16194
16348
  overrideMaxNodes: opts.overrideMaxNodes,
16195
16349
  capReached,
@@ -16477,7 +16631,8 @@ async function runScanInternal(_kernel, options) {
16477
16631
  pluginStores: options.pluginStores,
16478
16632
  activeProvider: activeProviderId,
16479
16633
  recommendedNodeLimit: options.recommendedNodeLimit ?? 256,
16480
- overrideMaxNodes: options.overrideMaxNodes ?? null
16634
+ overrideMaxNodes: options.overrideMaxNodes ?? null,
16635
+ ...options.maxFileSizeBytes !== void 0 ? { maxFileSizeBytes: options.maxFileSizeBytes } : {}
16481
16636
  });
16482
16637
  const activeProvider = activeProviderId ? exts.providers.find((p) => p.id === activeProviderId) ?? null : null;
16483
16638
  const resolved = resolveSignals({
@@ -16636,6 +16791,7 @@ function buildScanStats(walked, issues, start) {
16636
16791
  // Providers compete.
16637
16792
  filesWalked: walked.filesWalked,
16638
16793
  filesSkipped: 0,
16794
+ filesOversized: walked.oversizedFiles.length,
16639
16795
  nodesCount: walked.nodes.length,
16640
16796
  linksCount: walked.internalLinks.length,
16641
16797
  issuesCount: issues.length,
@@ -16652,6 +16808,7 @@ function buildScanReturn(walked, issues, renameOps, stats, options, setup) {
16652
16808
  scannedBy: SCANNED_BY,
16653
16809
  recommendedNodeLimit: walked.recommendedNodeLimit,
16654
16810
  overrideMaxNodes: walked.overrideMaxNodes,
16811
+ oversizedFiles: walked.oversizedFiles,
16655
16812
  nodes: walked.nodes,
16656
16813
  links: walked.internalLinks,
16657
16814
  issues,
@@ -17047,13 +17204,13 @@ var SCAN_RUNNER_TEXTS = {
17047
17204
  activeProviderAmbiguousUnderYes: "{{glyph}} Multiple provider markers detected ({{candidates}}) and --yes is set.\n {{hint}}\n",
17048
17205
  activeProviderAmbiguousUnderYesHint: "Set the lens explicitly with `sm config set activeProvider <id>` and re-run, or omit --yes for interactive selection.",
17049
17206
  /**
17050
- * Active lens points at a bundle the operator has disabled (via
17207
+ * Active lens points at a plugin the operator has disabled (via
17051
17208
  * `sm plugins disable <id>` or the Settings UI). Classification keeps
17052
17209
  * running because it's provider-driven, but the lens-gated extractors
17053
- * for the disabled bundle silently no-op. Without this warning the
17210
+ * for the disabled plugin silently no-op. Without this warning the
17054
17211
  * graph quietly differs from what the lens implies.
17055
17212
  */
17056
- activeProviderBundleDisabledWarning: 'activeProvider = "{{id}}" but the "{{id}}" plugin bundle is currently disabled; provider-specific extractors will not run. Re-enable the bundle with `sm plugins enable {{id}}` or switch the lens with `sm config set activeProvider <id>` to silence this warning.',
17213
+ activeProviderPluginDisabledWarning: 'activeProvider = "{{id}}" but the "{{id}}" plugin is currently disabled; provider-specific extractors will not run. Re-enable the plugin with `sm plugins enable {{id}}` or switch the lens with `sm config set activeProvider <id>` to silence this warning.',
17057
17214
  /**
17058
17215
  * Active-provider drift: the snapshot of provider markers persisted
17059
17216
  * when `activeProvider` was set (`activeProviderMarkers`) no longer
@@ -17222,7 +17379,6 @@ function persistActiveProvider(cwd, id, markers, printer) {
17222
17379
  target: "project",
17223
17380
  cwd
17224
17381
  });
17225
- printer.info(tx(SCAN_RUNNER_TEXTS.activeProviderAutodetected, { id }));
17226
17382
  } catch (err) {
17227
17383
  const message = err instanceof Error ? err.message : String(err);
17228
17384
  printer.warn(
@@ -17279,11 +17435,11 @@ function diffMarkers(snapshot, current) {
17279
17435
  }
17280
17436
  return { added, removed };
17281
17437
  }
17282
- function warnIfLensBundleDisabled(args2) {
17438
+ function warnIfLensPluginDisabled(args2) {
17283
17439
  if (args2.activeProvider === null) return;
17284
17440
  if (args2.resolveEnabled(args2.activeProvider)) return;
17285
17441
  args2.printer.warn(
17286
- tx(SCAN_RUNNER_TEXTS.activeProviderBundleDisabledWarning, {
17442
+ tx(SCAN_RUNNER_TEXTS.activeProviderPluginDisabledWarning, {
17287
17443
  id: args2.activeProvider
17288
17444
  })
17289
17445
  );
@@ -17448,10 +17604,12 @@ async function runScanForCommand(opts) {
17448
17604
  referenceablePaths,
17449
17605
  ctx.cwd,
17450
17606
  activeProvider,
17451
- cfg.scan.maxNodes
17607
+ cfg.scan.maxNodes,
17608
+ cfg.scan.maxFileSizeBytes
17452
17609
  );
17453
17610
  const willPersist = !opts.noBuiltIns && !opts.dryRun;
17454
- return willPersist ? runPersistPath(opts, dbPath, jobsDir, strict, loadPrior, runScanWith, extensions) : runEphemeralPath(opts, dbPath, strict, loadPrior, runScanWith);
17611
+ const scanned = await (willPersist ? runPersistPath(opts, dbPath, jobsDir, strict, loadPrior, runScanWith, extensions) : runEphemeralPath(opts, dbPath, strict, loadPrior, runScanWith));
17612
+ return scanned.kind === "ok" ? { ...scanned, lensAutoDetected: lens.autoDetected } : scanned;
17455
17613
  }
17456
17614
  function detectionProviders(extensions) {
17457
17615
  return extensions?.providers ?? [];
@@ -17480,12 +17638,20 @@ async function resolveActiveLens(opts, ctx, effectiveRoots, pluginRuntime, provi
17480
17638
  })
17481
17639
  };
17482
17640
  }
17483
- warnIfLensBundleDisabled({
17641
+ warnIfLensPluginDisabled({
17484
17642
  activeProvider: bootstrap.activeProvider,
17485
17643
  resolveEnabled: opts.resolveEnabledOverride ?? pluginRuntime.resolveEnabled,
17486
17644
  printer: opts.printer
17487
17645
  });
17488
- return { kind: "ok", activeProvider: bootstrap.activeProvider };
17646
+ return {
17647
+ kind: "ok",
17648
+ activeProvider: bootstrap.activeProvider,
17649
+ // Only when the lens was freshly auto-detected (not read from
17650
+ // config) does the caller announce it. The bootstrap no longer
17651
+ // prints it itself, to avoid interleaving stderr with the
17652
+ // stdout scan summary on a tty.
17653
+ autoDetected: bootstrap.source === "autodetect" ? bootstrap.activeProvider : null
17654
+ };
17489
17655
  }
17490
17656
  function emitReferenceWalkAdvisory(walk3, opts) {
17491
17657
  if (walk3.truncated) {
@@ -17553,7 +17719,7 @@ function makePriorLoader(noBuiltIns, strict) {
17553
17719
  return loaded;
17554
17720
  };
17555
17721
  }
17556
- function makeScanRunner(kernel, opts, effectiveRoots, ignoreFilter, strict, extensions, referenceablePaths, scanCwd, activeProvider, recommendedNodeLimit) {
17722
+ function makeScanRunner(kernel, opts, effectiveRoots, ignoreFilter, strict, extensions, referenceablePaths, scanCwd, activeProvider, recommendedNodeLimit, maxFileSizeBytes) {
17557
17723
  return async (prior, priorExtractorRuns, orphanJobFiles) => {
17558
17724
  if (opts.changed && prior === null) {
17559
17725
  opts.stderr.write(SCAN_RUNNER_TEXTS.changedNoPriorWarning);
@@ -17569,6 +17735,7 @@ function makeScanRunner(kernel, opts, effectiveRoots, ignoreFilter, strict, exte
17569
17735
  prior,
17570
17736
  activeProvider,
17571
17737
  recommendedNodeLimit,
17738
+ maxFileSizeBytes,
17572
17739
  ...priorExtractorRuns ? { priorExtractorRuns } : {},
17573
17740
  ...orphanJobFiles ? { orphanJobFiles } : {}
17574
17741
  });
@@ -17590,7 +17757,8 @@ function buildRunScanOptions(args2) {
17590
17757
  orphanJobFiles: orphanJobFiles ?? [],
17591
17758
  activeProvider: args2.activeProvider,
17592
17759
  recommendedNodeLimit: args2.recommendedNodeLimit,
17593
- overrideMaxNodes: opts.maxNodes ?? null
17760
+ overrideMaxNodes: opts.maxNodes ?? null,
17761
+ maxFileSizeBytes: args2.maxFileSizeBytes
17594
17762
  };
17595
17763
  if (args2.extensions) runOptions.extensions = args2.extensions;
17596
17764
  if (prior) {
@@ -17962,6 +18130,11 @@ async function runFirstScan(scopeRoot, strict, printer, stderr, stdin, ansi) {
17962
18130
  return ExitCode.Ok;
17963
18131
  }
17964
18132
  const result = outcome.result;
18133
+ if (outcome.lensAutoDetected) {
18134
+ printer.info(
18135
+ tx(SCAN_RUNNER_TEXTS.activeProviderAutodetected, { id: outcome.lensAutoDetected })
18136
+ );
18137
+ }
17965
18138
  const hasErrors = result.issues.some((i) => i.severity === "error");
17966
18139
  printer.info(
17967
18140
  tx(INIT_TEXTS.firstScanSummary, {
@@ -19480,10 +19653,10 @@ var PLUGINS_TEXTS = {
19480
19653
  pluginNotFoundHint: "Run `sm plugins list` for discovered ids and the qualified extension ids.",
19481
19654
  pluginLocked: '{{glyph}} Plugin "{{id}}" is locked by the host and cannot be toggled.\n {{hint}}\n',
19482
19655
  pluginLockedHint: "Locked plugins are mandatory for correct operation. To remove the lock, edit `src/kernel/config/locked-plugins.ts`.",
19483
- qualifiedIdNotFound: "{{glyph}} Qualified extension id not found: {{id}}\n The owning bundle '{{bundleId}}' does not declare an extension with id '{{extId}}'.\n {{hint}}\n",
19484
- qualifiedIdNotFoundHint: "Run `sm plugins list` to see what each bundle ships.",
19485
- qualifiedIdUnknownBundle: "{{glyph}} Qualified extension id references unknown bundle: {{bundleId}}\n {{hint}}\n",
19486
- qualifiedIdUnknownBundleHint: "Run `sm plugins list` for known bundle ids.",
19656
+ qualifiedIdNotFound: "{{glyph}} Qualified extension id not found: {{id}}\n The owning plugin '{{pluginId}}' does not declare an extension with id '{{extId}}'.\n {{hint}}\n",
19657
+ qualifiedIdNotFoundHint: "Run `sm plugins list` to see what each plugin ships.",
19658
+ qualifiedIdUnknownPlugin: "{{glyph}} Qualified extension id references unknown plugin: {{pluginId}}\n {{hint}}\n",
19659
+ qualifiedIdUnknownPluginHint: "Run `sm plugins list` for known plugin ids.",
19487
19660
  // Spec § A.10, `applicableKinds` filter on Extractors. When an extractor
19488
19661
  // declares a kind that no installed Provider emits, the load succeeds
19489
19662
  // (the Provider may arrive later) but `sm plugins doctor` surfaces a
@@ -19508,7 +19681,7 @@ var PLUGINS_TEXTS = {
19508
19681
  // --- doctor verb -----------------------------------------------------
19509
19682
  /**
19510
19683
  * One-line summary that opens the human doctor output. `enabled` is
19511
- * the count of enabled extensions across every bundle (every
19684
+ * the count of enabled extensions across every plugin (every
19512
19685
  * extension is independently toggle-able by its qualified id); the
19513
19686
  * value matches the row count rendered by `sm plugins list` once
19514
19687
  * disabled extensions are filtered out.
@@ -19552,10 +19725,10 @@ var PLUGINS_TEXTS = {
19552
19725
  /**
19553
19726
  * Macro expansion summary printed on stderr before the confirm
19554
19727
  * prompt (or before the `--yes` rejection). The block lists every
19555
- * qualified extension id the bare bundle id resolves to, so the
19728
+ * qualified extension id the bare plugin id resolves to, so the
19556
19729
  * user sees the exact set that would flip.
19557
19730
  */
19558
- bundleMacroHeader: "sm plugins {{verb}} {{bundleId}}: this will affect {{count}} extensions:\n",
19731
+ bundleMacroHeader: "sm plugins {{verb}} {{pluginId}}: this will affect {{count}} extensions:\n",
19559
19732
  bundleMacroRow: " - {{id}}\n",
19560
19733
  /**
19561
19734
  * Interactive prompt rendered to a TTY by the macro path. The
@@ -19575,7 +19748,7 @@ var PLUGINS_TEXTS = {
19575
19748
  * the directed re-run hint.
19576
19749
  */
19577
19750
  bundleMacroRequiresYes: "{{glyph}} Refusing to {{verb}} multiple extensions without confirmation.\n {{hint}}\n",
19578
- bundleMacroRequiresYesHint: "Re-run with --yes to apply, or pass a qualified id `<bundle>/<extension>` for a single extension.",
19751
+ bundleMacroRequiresYesHint: "Re-run with --yes to apply, or pass a qualified id `<plugin>/<extension>` for a single extension.",
19579
19752
  // --- list / show renderers ------------------------------------------
19580
19753
  rowStatusOk: "ok",
19581
19754
  rowStatusOff: "off",
@@ -19588,18 +19761,18 @@ var PLUGINS_TEXTS = {
19588
19761
  sourceBuiltIn: "built-in",
19589
19762
  sourceUser: "user",
19590
19763
  /**
19591
- * Compact bundle row: ` GLYPH ID(pad) N ext SOURCE`.
19764
+ * Compact plugin row: ` GLYPH ID(pad) N ext SOURCE`.
19592
19765
  * Padding for `id` and `count` is computed at render time so all rows
19593
19766
  * align regardless of length. The glyph is wrapped in color before the
19594
19767
  * template substitution.
19595
19768
  */
19596
- bundleRow: " {{glyph}} {{id}}{{count}} ext {{source}}",
19769
+ pluginRow: " {{glyph}} {{id}}{{count}} ext {{source}}",
19597
19770
  /**
19598
- * Indent applied to the names / reason lines under each bundle row.
19771
+ * Indent applied to the names / reason lines under each plugin row.
19599
19772
  * Kept as a single source of truth so the wrap math (`wrapNames`) and
19600
19773
  * the visible output stay in sync.
19601
19774
  */
19602
- bundleSubIndent: " ",
19775
+ pluginSubIndent: " ",
19603
19776
  listTipShow: "\nTip: `sm plugins show <id>` for kinds, versions, and per-extension status.\n",
19604
19777
  /** Show command, built-in header (no version row, no path). */
19605
19778
  detailHeaderBuiltIn: " {{glyph}} {{id}} {{source}} {{count}} extension{{plural}}\n",
@@ -19621,11 +19794,11 @@ var PLUGINS_TEXTS = {
19621
19794
  /** Extensions block heading, separated from the header by a blank line. */
19622
19795
  detailExtensionsBlock: "\n",
19623
19796
  /**
19624
- * Extension row inside the bundle detail. Every extension is
19797
+ * Extension row inside the plugin detail. Every extension is
19625
19798
  * independently toggle-able, so every row carries its own glyph
19626
19799
  * (✓ / ✕). Padding for {{kind}} and {{name}} is computed at render
19627
19800
  * time so columns align inside the block. `{{versionSuffix}}` is
19628
- * either ` v<x.y.z>` (user plugins) or empty (built-in bundles,
19801
+ * either ` v<x.y.z>` (user plugins) or empty (built-in plugins,
19629
19802
  * which inherit the CLI version and do not maintain per-extension
19630
19803
  * versions of their own).
19631
19804
  */
@@ -19633,7 +19806,7 @@ var PLUGINS_TEXTS = {
19633
19806
  detailVersionUnknown: "?",
19634
19807
  detailCompatUnknown: "?",
19635
19808
  /**
19636
- * Show command, single-extension header (qualified `<bundle>/<ext>` id
19809
+ * Show command, single-extension header (qualified `<plugin>/<ext>` id
19637
19810
  * shape). Mirrors `detailHeaderBuiltIn` but the count slot is replaced
19638
19811
  * by the kind so the reader sees at a glance whether they are looking
19639
19812
  * at an extractor, analyzer, etc. Version moves down into the field
@@ -19680,19 +19853,19 @@ var PLUGINS_TEXTS = {
19680
19853
  };
19681
19854
 
19682
19855
  // plugins/presentation-order.ts
19683
- var BUILT_IN_BUNDLE_PRESENTATION_ORDER = [
19856
+ var BUILT_IN_PLUGIN_PRESENTATION_ORDER = [
19684
19857
  "core",
19685
19858
  "claude",
19686
19859
  "antigravity",
19687
19860
  "openai",
19688
19861
  "agent-skills"
19689
19862
  ];
19690
- function sortBundlesForPresentation(bundles) {
19863
+ function sortPluginsForPresentation(plugins) {
19691
19864
  const orderIndex = (id) => {
19692
- const idx = BUILT_IN_BUNDLE_PRESENTATION_ORDER.indexOf(id);
19693
- return idx >= 0 ? idx : BUILT_IN_BUNDLE_PRESENTATION_ORDER.length;
19865
+ const idx = BUILT_IN_PLUGIN_PRESENTATION_ORDER.indexOf(id);
19866
+ return idx >= 0 ? idx : BUILT_IN_PLUGIN_PRESENTATION_ORDER.length;
19694
19867
  };
19695
- return [...bundles].sort((a, b) => {
19868
+ return [...plugins].sort((a, b) => {
19696
19869
  const ai = orderIndex(a.id);
19697
19870
  const bi = orderIndex(b.id);
19698
19871
  if (ai !== bi) return ai - bi;
@@ -19729,24 +19902,24 @@ async function loadAll(opts) {
19729
19902
  return loader.discoverAndLoadAll();
19730
19903
  }
19731
19904
  function builtInRows(resolveEnabled) {
19732
- return sortBundlesForPresentation(builtInBundles).map((bundle) => {
19733
- const extensions = bundle.extensions.map((ext) => extensionRowFromBuiltIn(ext, bundle, resolveEnabled));
19734
- const manifestSummary = bundle.extensions.map((ext) => `${ext.kind}:${qualifiedExtensionId(bundle.id, ext.id)}@${ext.version}`).join(", ");
19905
+ return sortPluginsForPresentation(builtInPlugins).map((plugin) => {
19906
+ const extensions = plugin.extensions.map((ext) => extensionRowFromBuiltIn(ext, plugin, resolveEnabled));
19907
+ const manifestSummary = plugin.extensions.map((ext) => `${ext.kind}:${qualifiedExtensionId(plugin.id, ext.id)}@${ext.version}`).join(", ");
19735
19908
  return {
19736
- id: bundle.id,
19909
+ id: plugin.id,
19737
19910
  enabled: extensions.some((e) => e.enabled),
19738
- description: bundle.description,
19911
+ description: plugin.description,
19739
19912
  extensions,
19740
19913
  manifestSummary
19741
19914
  };
19742
19915
  });
19743
19916
  }
19744
- function extensionRowFromBuiltIn(ext, bundle, resolveEnabled) {
19917
+ function extensionRowFromBuiltIn(ext, plugin, resolveEnabled) {
19745
19918
  const row = {
19746
19919
  id: ext.id,
19747
19920
  kind: ext.kind,
19748
19921
  version: ext.version,
19749
- enabled: resolveEnabled(qualifiedExtensionId(bundle.id, ext.id)),
19922
+ enabled: resolveEnabled(qualifiedExtensionId(plugin.id, ext.id)),
19750
19923
  description: ext.description ?? ""
19751
19924
  };
19752
19925
  if (ext.entry !== void 0) row.entry = ext.entry;
@@ -19782,7 +19955,7 @@ var PluginsListCommand = class extends SmCommand {
19782
19955
  static usage = Command22.Usage({
19783
19956
  category: "Plugins",
19784
19957
  description: "List discovered plugins and their load status.",
19785
- details: "Scans <cwd>/.skill-map/plugins (or --plugin-dir <path>). Built-in bundles (claude, core) are listed alongside user plugins."
19958
+ details: "Scans <cwd>/.skill-map/plugins (or --plugin-dir <path>). Built-in plugins (claude, core) are listed alongside user plugins."
19786
19959
  });
19787
19960
  pluginDir = Option21.String("--plugin-dir", { required: false });
19788
19961
  async run() {
@@ -19819,14 +19992,14 @@ function renderListHuman(builtIns2, plugins, resolveEnabled, ansi) {
19819
19992
  const idCol = row.id.padEnd(idWidth);
19820
19993
  const countCol = String(row.names.length).padStart(countWidth);
19821
19994
  lines.push(
19822
- tx(PLUGINS_TEXTS.bundleRow, {
19995
+ tx(PLUGINS_TEXTS.pluginRow, {
19823
19996
  glyph,
19824
19997
  id: idCol,
19825
19998
  count: ` ${countCol}`,
19826
19999
  source: ansi.dim(row.source)
19827
20000
  })
19828
20001
  );
19829
- const indent = PLUGINS_TEXTS.bundleSubIndent;
20002
+ const indent = PLUGINS_TEXTS.pluginSubIndent;
19830
20003
  if (row.reason) {
19831
20004
  lines.push(`${indent}${ansi.dim(row.reason)}`);
19832
20005
  } else if (row.names.length > 0) {
@@ -19890,11 +20063,11 @@ var PluginsShowCommand = class extends SmCommand {
19890
20063
  category: "Plugins",
19891
20064
  description: "Show a single plugin's manifest + loaded extensions.",
19892
20065
  details: `
19893
- Accepts a bundle / plugin id (\`core\`, \`claude\`, \`my-plugin\`)
20066
+ Accepts a plugin id (\`core\`, \`claude\`, \`my-plugin\`)
19894
20067
  or a qualified extension id (\`core/<ext-id>\`,
19895
20068
  \`<plugin>/<ext-id>\`). When given a qualified id, validates the
19896
20069
  extension exists and renders a single-extension detail block.
19897
- The bare form renders the parent bundle's detail with per-extension
20070
+ The bare form renders the parent plugin's detail with per-extension
19898
20071
  status. The same id shapes \`sm plugins enable\` and
19899
20072
  \`sm plugins disable\` accept resolve cleanly here too.
19900
20073
  `
@@ -19911,9 +20084,9 @@ var PluginsShowCommand = class extends SmCommand {
19911
20084
  this.printer.error(lookupResult.error);
19912
20085
  return ExitCode.NotFound;
19913
20086
  }
19914
- const { bundleId, extId } = lookupResult;
19915
- const builtIn = builtIns2.find((b) => b.id === bundleId);
19916
- const match = plugins.find((p) => p.id === bundleId);
20087
+ const { pluginId, extId } = lookupResult;
20088
+ const builtIn = builtIns2.find((b) => b.id === pluginId);
20089
+ const match = plugins.find((p) => p.id === pluginId);
19917
20090
  if (!builtIn && !match) {
19918
20091
  this.printer.error(
19919
20092
  tx(PLUGINS_TEXTS.pluginNotFound, {
@@ -19925,7 +20098,7 @@ var PluginsShowCommand = class extends SmCommand {
19925
20098
  return ExitCode.NotFound;
19926
20099
  }
19927
20100
  if (extId !== void 0) {
19928
- return this.renderExtensionDetail({ extId, bundleId, builtIn, match });
20101
+ return this.renderExtensionDetail({ extId, pluginId, builtIn, match });
19929
20102
  }
19930
20103
  if (this.json) {
19931
20104
  const payload = builtIn ?? match;
@@ -19939,23 +20112,23 @@ var PluginsShowCommand = class extends SmCommand {
19939
20112
  }
19940
20113
  /**
19941
20114
  * Render the single-extension detail block, the path taken when the
19942
- * user supplies a qualified `<bundle>/<ext>` id. `--json` emits the
19943
- * single extension row (no surrounding bundle envelope) so tooling
20115
+ * user supplies a qualified `<plugin>/<ext>` id. `--json` emits the
20116
+ * single extension row (no surrounding plugin envelope) so tooling
19944
20117
  * can pipe straight into `jq`; human mode renders a focused header
19945
20118
  * plus a Kind / Version / Stability / Description / Preconditions /
19946
20119
  * Entry field block.
19947
20120
  */
19948
20121
  renderExtensionDetail(args2) {
19949
- const { extId, bundleId, builtIn, match } = args2;
20122
+ const { extId, pluginId, builtIn, match } = args2;
19950
20123
  const ansi = this.ansiFor("stdout");
19951
20124
  if (builtIn) {
19952
20125
  const ext = builtIn.extensions.find((e) => e.id === extId);
19953
20126
  if (!ext) return ExitCode.NotFound;
19954
20127
  if (this.json) {
19955
- this.printer.data(JSON.stringify({ pluginId: bundleId, ...ext }, omitModule, 2) + "\n");
20128
+ this.printer.data(JSON.stringify({ pluginId, ...ext }, omitModule, 2) + "\n");
19956
20129
  return ExitCode.Ok;
19957
20130
  }
19958
- this.printer.data(renderBuiltInExtensionDetail(bundleId, ext, ansi));
20131
+ this.printer.data(renderBuiltInExtensionDetail(pluginId, ext, ansi));
19959
20132
  return ExitCode.Ok;
19960
20133
  }
19961
20134
  const userExt = match?.extensions?.find((e) => e.id === extId);
@@ -19964,53 +20137,53 @@ var PluginsShowCommand = class extends SmCommand {
19964
20137
  this.printer.data(JSON.stringify(userExt, omitModule, 2) + "\n");
19965
20138
  return ExitCode.Ok;
19966
20139
  }
19967
- this.printer.data(renderUserExtensionDetail(bundleId, userExt, ansi));
20140
+ this.printer.data(renderUserExtensionDetail(pluginId, userExt, ansi));
19968
20141
  return ExitCode.Ok;
19969
20142
  }
19970
20143
  };
19971
20144
  function resolveShowLookupId(id, builtIns2, plugins, ansi) {
19972
- if (!id.includes("/")) return { bundleId: id };
20145
+ if (!id.includes("/")) return { pluginId: id };
19973
20146
  const parsed = parseQualifiedId(id);
19974
20147
  if ("error" in parsed) return { error: malformedQualifiedError(id, ansi) };
19975
- const { bundleId, extId } = parsed;
19976
- const knownExts = collectKnownExtensions(bundleId, builtIns2, plugins);
19977
- if (knownExts === null) return { error: unknownBundleError(bundleId, ansi) };
20148
+ const { pluginId, extId } = parsed;
20149
+ const knownExts = collectKnownExtensions(pluginId, builtIns2, plugins);
20150
+ if (knownExts === null) return { error: unknownPluginError(pluginId, ansi) };
19978
20151
  if (!knownExts.includes(extId)) {
19979
- return { error: unknownExtensionError(id, bundleId, extId, ansi) };
20152
+ return { error: unknownExtensionError(id, pluginId, extId, ansi) };
19980
20153
  }
19981
- return { bundleId, extId };
20154
+ return { pluginId, extId };
19982
20155
  }
19983
20156
  function parseQualifiedId(id) {
19984
- const [bundleId, extId, ...rest] = id.split("/");
19985
- if (!bundleId || !extId || rest.length > 0) return { error: true };
19986
- return { bundleId, extId };
20157
+ const [pluginId, extId, ...rest] = id.split("/");
20158
+ if (!pluginId || !extId || rest.length > 0) return { error: true };
20159
+ return { pluginId, extId };
19987
20160
  }
19988
- function collectKnownExtensions(bundleId, builtIns2, plugins) {
19989
- const builtIn = builtIns2.find((b) => b.id === bundleId);
20161
+ function collectKnownExtensions(pluginId, builtIns2, plugins) {
20162
+ const builtIn = builtIns2.find((b) => b.id === pluginId);
19990
20163
  if (builtIn) return builtIn.extensions.map((e) => e.id);
19991
- const userPlugin = plugins.find((p) => p.id === bundleId);
20164
+ const userPlugin = plugins.find((p) => p.id === pluginId);
19992
20165
  if (userPlugin) return userPlugin.extensions?.map((e) => e.id) ?? [];
19993
20166
  return null;
19994
20167
  }
19995
20168
  function malformedQualifiedError(id, ansi) {
19996
- return tx(PLUGINS_TEXTS.qualifiedIdUnknownBundle, {
20169
+ return tx(PLUGINS_TEXTS.qualifiedIdUnknownPlugin, {
19997
20170
  glyph: ansi.red("\u2715"),
19998
- bundleId: sanitizeForTerminal(id),
19999
- hint: ansi.dim(PLUGINS_TEXTS.qualifiedIdUnknownBundleHint)
20171
+ pluginId: sanitizeForTerminal(id),
20172
+ hint: ansi.dim(PLUGINS_TEXTS.qualifiedIdUnknownPluginHint)
20000
20173
  });
20001
20174
  }
20002
- function unknownBundleError(bundleId, ansi) {
20003
- return tx(PLUGINS_TEXTS.qualifiedIdUnknownBundle, {
20175
+ function unknownPluginError(pluginId, ansi) {
20176
+ return tx(PLUGINS_TEXTS.qualifiedIdUnknownPlugin, {
20004
20177
  glyph: ansi.red("\u2715"),
20005
- bundleId: sanitizeForTerminal(bundleId),
20006
- hint: ansi.dim(PLUGINS_TEXTS.qualifiedIdUnknownBundleHint)
20178
+ pluginId: sanitizeForTerminal(pluginId),
20179
+ hint: ansi.dim(PLUGINS_TEXTS.qualifiedIdUnknownPluginHint)
20007
20180
  });
20008
20181
  }
20009
- function unknownExtensionError(id, bundleId, extId, ansi) {
20182
+ function unknownExtensionError(id, pluginId, extId, ansi) {
20010
20183
  return tx(PLUGINS_TEXTS.qualifiedIdNotFound, {
20011
20184
  glyph: ansi.red("\u2715"),
20012
20185
  id: sanitizeForTerminal(id),
20013
- bundleId: sanitizeForTerminal(bundleId),
20186
+ pluginId: sanitizeForTerminal(pluginId),
20014
20187
  extId: sanitizeForTerminal(extId),
20015
20188
  hint: ansi.dim(PLUGINS_TEXTS.qualifiedIdNotFoundHint)
20016
20189
  });
@@ -20101,18 +20274,18 @@ function renderPluginDetailFields(match) {
20101
20274
  function collectPluginExtensionItems(match, ansi) {
20102
20275
  const enabled = match.status === "enabled";
20103
20276
  if (!enabled || !match.extensions) return [];
20104
- const safeBundleId = sanitizeForTerminal(match.id);
20277
+ const safePluginId = sanitizeForTerminal(match.id);
20105
20278
  const sorted = sortExtensionsCanonical(match.extensions);
20106
20279
  return sorted.map((ext) => {
20107
20280
  const safeExtId = sanitizeForTerminal(ext.id);
20108
20281
  return {
20109
20282
  // User plugins surfaced via `loadAll` already filter on the
20110
20283
  // resolver, so a reachable extension on this surface is enabled
20111
- // by construction. The disabled path goes through the bundle
20284
+ // by construction. The disabled path goes through the plugin
20112
20285
  // status header above (✕ on the row).
20113
20286
  glyph: ansi.green(PLUGINS_TEXTS.rowGlyphOk),
20114
20287
  kind: sanitizeForTerminal(ext.kind),
20115
- name: `${safeBundleId}/${safeExtId}`,
20288
+ name: `${safePluginId}/${safeExtId}`,
20116
20289
  version: sanitizeForTerminal(ext.version)
20117
20290
  };
20118
20291
  });
@@ -20138,11 +20311,11 @@ function renderExtensionItems(items) {
20138
20311
  }
20139
20312
  return out.join("");
20140
20313
  }
20141
- function renderBuiltInExtensionDetail(bundleId, ext, ansi) {
20314
+ function renderBuiltInExtensionDetail(pluginId, ext, ansi) {
20142
20315
  const glyph = ext.enabled ? ansi.green(PLUGINS_TEXTS.rowGlyphOk) : ansi.red(PLUGINS_TEXTS.rowGlyphOff);
20143
20316
  const header = tx(PLUGINS_TEXTS.detailHeaderExtensionBuiltIn, {
20144
20317
  glyph,
20145
- qualifiedId: sanitizeForTerminal(`${bundleId}/${ext.id}`),
20318
+ qualifiedId: sanitizeForTerminal(`${pluginId}/${ext.id}`),
20146
20319
  source: ansi.dim(PLUGINS_TEXTS.sourceBuiltIn)
20147
20320
  });
20148
20321
  const meta = { kind: ext.kind };
@@ -20150,11 +20323,11 @@ function renderBuiltInExtensionDetail(bundleId, ext, ansi) {
20150
20323
  if (ext.entry !== void 0) meta.entry = ext.entry;
20151
20324
  return header + "\n" + renderExtensionFields(meta);
20152
20325
  }
20153
- function renderUserExtensionDetail(bundleId, ext, ansi) {
20326
+ function renderUserExtensionDetail(pluginId, ext, ansi) {
20154
20327
  const glyph = ansi.green(PLUGINS_TEXTS.rowGlyphOk);
20155
20328
  const header = tx(PLUGINS_TEXTS.detailHeaderExtensionUser, {
20156
20329
  glyph,
20157
- qualifiedId: sanitizeForTerminal(`${bundleId}/${ext.id}`),
20330
+ qualifiedId: sanitizeForTerminal(`${pluginId}/${ext.id}`),
20158
20331
  source: ansi.dim(PLUGINS_TEXTS.sourceUser)
20159
20332
  });
20160
20333
  const meta = readInstanceMeta(ext.instance);
@@ -20408,13 +20581,13 @@ function forEachProviderInstance(plugins, callback) {
20408
20581
  forEachUserPluginProvider(plugins, callback);
20409
20582
  }
20410
20583
  function forEachBuiltInProvider(callback) {
20411
- for (const bundle of builtInBundles) {
20412
- for (const ext of bundle.extensions) {
20584
+ for (const plugin of builtInPlugins) {
20585
+ for (const ext of plugin.extensions) {
20413
20586
  if (ext.kind !== "provider") continue;
20414
20587
  const provider = ext;
20415
20588
  callback({
20416
20589
  id: provider.id,
20417
- pluginId: bundle.id,
20590
+ pluginId: plugin.id,
20418
20591
  instance: provider
20419
20592
  });
20420
20593
  }
@@ -20457,15 +20630,15 @@ function collectApplicableKindWarnings(plugins, knownKinds) {
20457
20630
  return out;
20458
20631
  }
20459
20632
  function collectBuiltInApplicableKindWarnings(out, knownKinds) {
20460
- for (const bundle of builtInBundles) {
20461
- for (const ext of bundle.extensions) {
20633
+ for (const plugin of builtInPlugins) {
20634
+ for (const ext of plugin.extensions) {
20462
20635
  if (ext.kind !== "extractor") continue;
20463
20636
  const extractor = ext;
20464
20637
  const kinds = extractor.precondition?.kind;
20465
20638
  if (!kinds || kinds.length === 0) continue;
20466
20639
  appendUnknownKindWarnings(
20467
20640
  out,
20468
- qualifiedExtensionId(bundle.id, extractor.id),
20641
+ qualifiedExtensionId(plugin.id, extractor.id),
20469
20642
  kinds,
20470
20643
  knownKinds
20471
20644
  );
@@ -20508,11 +20681,11 @@ function collectUnknownSlotWarnings(plugins, knownSlots) {
20508
20681
  return out;
20509
20682
  }
20510
20683
  function collectBuiltInUnknownSlotWarnings(out, knownSlots) {
20511
- for (const bundle of builtInBundles) {
20512
- for (const ext of bundle.extensions) {
20684
+ for (const plugin of builtInPlugins) {
20685
+ for (const ext of plugin.extensions) {
20513
20686
  const vc = ext.viewContributions;
20514
20687
  if (!vc) continue;
20515
- appendUnknownSlotWarnings(out, qualifiedExtensionId(bundle.id, ext.id), vc, knownSlots);
20688
+ appendUnknownSlotWarnings(out, qualifiedExtensionId(plugin.id, ext.id), vc, knownSlots);
20516
20689
  }
20517
20690
  }
20518
20691
  }
@@ -20594,7 +20767,7 @@ import { Command as Command25, Option as Option24 } from "clipanion";
20594
20767
  var TogglePluginsBase = class extends SmCommand {
20595
20768
  all = Option24.Boolean("--all", false);
20596
20769
  yes = Option24.Boolean("--yes,-y", false, {
20597
- description: "Skip the interactive confirm when a bare bundle id (or --all) fans the toggle out across multiple extensions."
20770
+ description: "Skip the interactive confirm when a bare plugin id (or --all) fans the toggle out across multiple extensions."
20598
20771
  });
20599
20772
  ids = Option24.Rest({ name: "ids" });
20600
20773
  async toggle(enabled) {
@@ -20603,7 +20776,7 @@ var TogglePluginsBase = class extends SmCommand {
20603
20776
  const argError = this.#validateArgs(stderrAnsi, verb);
20604
20777
  if (argError !== null) return argError;
20605
20778
  const plugins = await loadAll({ pluginDir: void 0 });
20606
- const catalogue = bundleCatalogue(plugins);
20779
+ const catalogue = pluginCatalogue(plugins);
20607
20780
  const targetsResult = this.#pickTargets(catalogue, stderrAnsi);
20608
20781
  if (typeof targetsResult === "number") return targetsResult;
20609
20782
  let targets = targetsResult;
@@ -20661,7 +20834,7 @@ var TogglePluginsBase = class extends SmCommand {
20661
20834
  if (this.all) {
20662
20835
  return catalogue.map((b) => ({
20663
20836
  origin: "bare",
20664
- bundleId: b.id,
20837
+ pluginId: b.id,
20665
20838
  keys: b.extensionIds.map((extId) => qualifiedExtensionId(b.id, extId))
20666
20839
  }));
20667
20840
  }
@@ -20682,8 +20855,8 @@ var TogglePluginsBase = class extends SmCommand {
20682
20855
  }
20683
20856
  /**
20684
20857
  * Macro gate: when the request would fan a single user input out
20685
- * across more than one extension (either `--all` or a bare bundle
20686
- * id whose bundle holds ≥2 extensions), confirm the intent.
20858
+ * across more than one extension (either `--all` or a bare plugin
20859
+ * id whose plugin holds ≥2 extensions), confirm the intent.
20687
20860
  *
20688
20861
  * Resolution order:
20689
20862
  * 1. `--yes` flag: skip the prompt entirely.
@@ -20692,7 +20865,7 @@ var TogglePluginsBase = class extends SmCommand {
20692
20865
  * message that names the extensions and points at `--yes`.
20693
20866
  *
20694
20867
  * Returns `true` when the verb should proceed, `false` when it
20695
- * should abort. Single-extension targets (bare bundle id mapping to
20868
+ * should abort. Single-extension targets (bare plugin id mapping to
20696
20869
  * one child, or qualified ids) skip the gate uniformly.
20697
20870
  */
20698
20871
  // Cyclomatic count comes from the three-stage gate (--yes shortcut,
@@ -20706,11 +20879,11 @@ var TogglePluginsBase = class extends SmCommand {
20706
20879
  if (this.yes) return true;
20707
20880
  const isTty = Boolean(this.context.stdin && "isTTY" in this.context.stdin && this.context.stdin.isTTY);
20708
20881
  for (const target of macroTargets) {
20709
- const bundleLabel = target.origin === "bare" ? target.bundleId ?? "--all" : "--all";
20882
+ const pluginLabel = target.origin === "bare" ? target.pluginId ?? "--all" : "--all";
20710
20883
  this.printer.info(
20711
20884
  tx(PLUGINS_TEXTS.bundleMacroHeader, {
20712
20885
  verb,
20713
- bundleId: sanitizeForTerminal(bundleLabel),
20886
+ pluginId: sanitizeForTerminal(pluginLabel),
20714
20887
  count: target.keys.length
20715
20888
  })
20716
20889
  );
@@ -20766,7 +20939,7 @@ var TogglePluginsBase = class extends SmCommand {
20766
20939
  * Persist every qualified id in `config_plugins`. On disable, also
20767
20940
  * purge the plugin's `scan_contributions` rows immediately (matches
20768
20941
  * the BFF route, see `server/routes/plugins.ts:applyChangeToAdapter`).
20769
- * Every key is `<bundle>/<ext>` shape so the contribution purge can
20942
+ * Every key is `<plugin>/<ext>` shape so the contribution purge can
20770
20943
  * split into `(pluginId, extensionId)` cleanly.
20771
20944
  */
20772
20945
  async #persistKeys(keys, enabled) {
@@ -20829,11 +21002,11 @@ var PluginsEnableCommand = class extends TogglePluginsBase {
20829
21002
  flip; sm config reset plugins.<id>.enabled drops the settings.json
20830
21003
  baseline.
20831
21004
 
20832
- Accepts qualified ids (\`claude/at-directive\`) and bare bundle
21005
+ Accepts qualified ids (\`claude/at-directive\`) and bare plugin
20833
21006
  ids (\`claude\`, which fans the toggle out across every extension
20834
- inside the bundle). Multi-extension bundles need --yes (or an
21007
+ inside the plugin). Multi-extension plugins need --yes (or an
20835
21008
  interactive TTY confirm) to avoid flipping 27 core extensions by
20836
- accident. Single-extension bundles (openai, agent-skills,
21009
+ accident. Single-extension plugins (openai, agent-skills,
20837
21010
  antigravity) apply without prompting.
20838
21011
 
20839
21012
  Batches are all-or-nothing: a single unknown id aborts before
@@ -20856,11 +21029,11 @@ var PluginsDisableCommand = class extends TogglePluginsBase {
20856
21029
  sm plugins list, but with status=disabled; the kernel will not
20857
21030
  run any of its disabled extensions.
20858
21031
 
20859
- Accepts qualified ids (\`core/markdown-link\`) and bare bundle
21032
+ Accepts qualified ids (\`core/markdown-link\`) and bare plugin
20860
21033
  ids (\`core\`, which fans the toggle out across every extension
20861
- inside the bundle). Multi-extension bundles need --yes (or an
21034
+ inside the plugin). Multi-extension plugins need --yes (or an
20862
21035
  interactive TTY confirm) to avoid flipping 27 core extensions by
20863
- accident. Single-extension bundles (openai, agent-skills,
21036
+ accident. Single-extension plugins (openai, agent-skills,
20864
21037
  antigravity) apply without prompting.
20865
21038
 
20866
21039
  Batches are all-or-nothing: a single unknown id aborts before
@@ -20872,12 +21045,12 @@ var PluginsDisableCommand = class extends TogglePluginsBase {
20872
21045
  return this.toggle(false);
20873
21046
  }
20874
21047
  };
20875
- function bundleCatalogue(plugins) {
21048
+ function pluginCatalogue(plugins) {
20876
21049
  const out = [];
20877
- for (const bundle of builtInBundles) {
21050
+ for (const plugin of builtInPlugins) {
20878
21051
  out.push({
20879
- id: bundle.id,
20880
- extensionIds: bundle.extensions.map((e) => e.id)
21052
+ id: plugin.id,
21053
+ extensionIds: plugin.extensions.map((e) => e.id)
20881
21054
  });
20882
21055
  }
20883
21056
  for (const p of plugins) {
@@ -20893,32 +21066,32 @@ function resolveToggleTarget(id, catalogue, ansi) {
20893
21066
  }
20894
21067
  function resolveQualifiedToggle(id, catalogue, ansi) {
20895
21068
  const errGlyph = ansi.red("\u2715");
20896
- const [bundleId, extId, ...rest] = id.split("/");
20897
- if (!bundleId || !extId || rest.length > 0) {
21069
+ const [pluginId, extId, ...rest] = id.split("/");
21070
+ if (!pluginId || !extId || rest.length > 0) {
20898
21071
  return {
20899
- error: tx(PLUGINS_TEXTS.qualifiedIdUnknownBundle, {
21072
+ error: tx(PLUGINS_TEXTS.qualifiedIdUnknownPlugin, {
20900
21073
  glyph: errGlyph,
20901
- bundleId: sanitizeForTerminal(id),
20902
- hint: ansi.dim(PLUGINS_TEXTS.qualifiedIdUnknownBundleHint)
21074
+ pluginId: sanitizeForTerminal(id),
21075
+ hint: ansi.dim(PLUGINS_TEXTS.qualifiedIdUnknownPluginHint)
20903
21076
  })
20904
21077
  };
20905
21078
  }
20906
- const bundle = catalogue.find((b) => b.id === bundleId);
20907
- if (!bundle) {
21079
+ const plugin = catalogue.find((b) => b.id === pluginId);
21080
+ if (!plugin) {
20908
21081
  return {
20909
- error: tx(PLUGINS_TEXTS.qualifiedIdUnknownBundle, {
21082
+ error: tx(PLUGINS_TEXTS.qualifiedIdUnknownPlugin, {
20910
21083
  glyph: errGlyph,
20911
- bundleId: sanitizeForTerminal(bundleId),
20912
- hint: ansi.dim(PLUGINS_TEXTS.qualifiedIdUnknownBundleHint)
21084
+ pluginId: sanitizeForTerminal(pluginId),
21085
+ hint: ansi.dim(PLUGINS_TEXTS.qualifiedIdUnknownPluginHint)
20913
21086
  })
20914
21087
  };
20915
21088
  }
20916
- if (!bundle.extensionIds.includes(extId)) {
21089
+ if (!plugin.extensionIds.includes(extId)) {
20917
21090
  return {
20918
21091
  error: tx(PLUGINS_TEXTS.qualifiedIdNotFound, {
20919
21092
  glyph: errGlyph,
20920
21093
  id: sanitizeForTerminal(id),
20921
- bundleId: sanitizeForTerminal(bundleId),
21094
+ pluginId: sanitizeForTerminal(pluginId),
20922
21095
  extId: sanitizeForTerminal(extId),
20923
21096
  hint: ansi.dim(PLUGINS_TEXTS.qualifiedIdNotFoundHint)
20924
21097
  })
@@ -20926,12 +21099,12 @@ function resolveQualifiedToggle(id, catalogue, ansi) {
20926
21099
  }
20927
21100
  return {
20928
21101
  origin: "qualified",
20929
- keys: [qualifiedExtensionId(bundleId, extId)]
21102
+ keys: [qualifiedExtensionId(pluginId, extId)]
20930
21103
  };
20931
21104
  }
20932
21105
  function resolveBareToggle(id, catalogue) {
20933
- const bundle = catalogue.find((b) => b.id === id);
20934
- if (!bundle) {
21106
+ const plugin = catalogue.find((b) => b.id === id);
21107
+ if (!plugin) {
20935
21108
  return {
20936
21109
  error: tx(PLUGINS_TEXTS.pluginNotFound, {
20937
21110
  glyph: "\u2715",
@@ -20942,8 +21115,8 @@ function resolveBareToggle(id, catalogue) {
20942
21115
  }
20943
21116
  return {
20944
21117
  origin: "bare",
20945
- bundleId: bundle.id,
20946
- keys: bundle.extensionIds.map((extId) => qualifiedExtensionId(bundle.id, extId))
21118
+ pluginId: plugin.id,
21119
+ keys: plugin.extensionIds.map((extId) => qualifiedExtensionId(plugin.id, extId))
20947
21120
  };
20948
21121
  }
20949
21122
 
@@ -20991,20 +21164,10 @@ var PluginsCreateCommand = class extends SmCommand {
20991
21164
  mkdirSync5(join18(targetDir, "extractors", extractorName), { recursive: true });
20992
21165
  const specVersion = installedSpecVersion();
20993
21166
  const manifest = {
20994
- id: this.pluginId,
20995
21167
  version: "0.1.0",
20996
21168
  specCompat: `^${specVersion}`,
20997
21169
  catalogCompat: "^1.0.0",
20998
- description: "Generated by `sm plugins create`. Edit to taste.",
20999
- settings: {
21000
- keywords: {
21001
- type: "string-list",
21002
- label: "Keywords to track",
21003
- description: "Words counted across each scanned node body.",
21004
- default: ["TODO", "FIXME"],
21005
- min: 1
21006
- }
21007
- }
21170
+ description: "Generated by `sm plugins create`. Edit to taste."
21008
21171
  };
21009
21172
  writeFileSync(
21010
21173
  join18(targetDir, "plugin.json"),
@@ -21029,36 +21192,44 @@ function scaffolderExtractorStub(extractorId) {
21029
21192
  * Generated by \`sm plugins create\`. Edit the extract() body.
21030
21193
  *
21031
21194
  * Loader contract: the plugin loader resolves the extension via the
21032
- * MODULE'S DEFAULT EXPORT (\`export default { ... }\`). Renaming or
21033
- * splitting into a named export will surface as \`load-error: default
21034
- * export missing a string \\\`kind\\\` field\`.
21195
+ * MODULE'S DEFAULT EXPORT (\`export default { ... }\`). It must be an
21196
+ * object literal; renaming or splitting into a named export surfaces as
21197
+ * a \`load-error\`.
21035
21198
  *
21036
21199
  * Folder convention: this file lives at
21037
- * \`extractors/${extractorId}/index.js\`. The bundle's plugin.json#/id
21038
- * provides the qualified id \`<plugin-id>/${extractorId}\`; the loader
21039
- * injects \`pluginId\` automatically, do NOT hardcode it here.
21200
+ * \`extractors/${extractorId}/index.js\`. The folder layout is the
21201
+ * source of truth (structure-as-truth): the loader derives \`kind\`
21202
+ * (\`extractor\`) from the parent folder and the id (\`${extractorId}\`)
21203
+ * from the leaf folder, and injects \`pluginId\` from the plugin, so none
21204
+ * of them are declared here. Re-declaring \`kind\` / \`id\` is rejected as
21205
+ * \`invalid-manifest\`.
21040
21206
  *
21041
- * Declared view contributions (in plugin.json):
21207
+ * Declared view contributions (\`ui\`):
21042
21208
  * - 'count' \u2192 slot \`card.footer.left\` (renders as a chip
21043
21209
  * in the left footer of the node card)
21044
21210
  *
21045
- * Declared settings:
21211
+ * Declared settings (\`settings\`):
21046
21212
  * - 'keywords' (string-list) \u2192 exposed as ctx.settings.keywords
21047
21213
  *
21048
21214
  * See: spec/plugin-author-guide.md \xA7View contributions
21049
21215
  * spec/view-slots.md
21050
21216
  */
21051
21217
  export default {
21052
- id: '${extractorId}',
21053
- kind: 'extractor',
21054
21218
  version: '0.1.0',
21055
21219
  description: 'Counts configured keywords per node.',
21056
- stability: 'experimental',
21057
- emitsLinkKinds: [],
21058
- defaultConfidence: 'high',
21059
21220
  scope: 'body',
21060
21221
 
21061
- viewContributions: {
21222
+ settings: {
21223
+ keywords: {
21224
+ type: 'string-list',
21225
+ label: 'Keywords to track',
21226
+ description: 'Words counted across each scanned node body.',
21227
+ default: ['TODO', 'FIXME'],
21228
+ min: 1,
21229
+ },
21230
+ },
21231
+
21232
+ ui: {
21062
21233
  count: {
21063
21234
  slot: 'card.footer.left',
21064
21235
  icon: '\u{1F50D}',
@@ -21084,7 +21255,7 @@ export default {
21084
21255
  function scaffolderReadme(pluginId) {
21085
21256
  return `# ${pluginId}
21086
21257
 
21087
- Generated by \`sm plugins create\`. Edit \`extensions/extractor.js\` to taste.
21258
+ Generated by \`sm plugins create\`. Edit \`extractors/${pluginId}-extractor/index.js\` to taste.
21088
21259
 
21089
21260
  ## Verbs
21090
21261
 
@@ -21604,6 +21775,31 @@ var IntentionalFailCommand = class extends SmCommand {
21604
21775
  // cli/commands/scan.ts
21605
21776
  import { Command as Command31, Option as Option29 } from "clipanion";
21606
21777
 
21778
+ // kernel/util/format-bytes.ts
21779
+ var UNITS = ["B", "KiB", "MiB", "GiB", "TiB", "PiB"];
21780
+ function formatBytes(bytes) {
21781
+ if (!Number.isFinite(bytes) || bytes <= 0) return "0 B";
21782
+ if (bytes < 1024) return `${Math.round(bytes)} B`;
21783
+ let value = bytes;
21784
+ let unitIndex = 0;
21785
+ while (value >= 1024 && unitIndex < UNITS.length - 1) {
21786
+ value /= 1024;
21787
+ unitIndex += 1;
21788
+ }
21789
+ const rounded = Math.round(value * 10) / 10;
21790
+ const text = Number.isInteger(rounded) ? String(rounded) : rounded.toFixed(1);
21791
+ return `${text} ${UNITS[unitIndex]}`;
21792
+ }
21793
+
21794
+ // kernel/util/format-oversized.ts
21795
+ function formatOversizedFilePair(file) {
21796
+ return `${file.path} (${formatBytes(file.bytes)})`;
21797
+ }
21798
+ function formatOversizedFileRows(files) {
21799
+ return files.map((file) => ` - ${formatOversizedFilePair(file)}
21800
+ `);
21801
+ }
21802
+
21607
21803
  // cli/i18n/scan.texts.ts
21608
21804
  var SCAN_TEXTS = {
21609
21805
  // --- scan command ----------------------------------------------------
@@ -21651,6 +21847,20 @@ var SCAN_TEXTS = {
21651
21847
  */
21652
21848
  scanCappedNotice: "{{glyph}} Scan capped at {{limit}} nodes ({{source}}).\n {{hint}}\n",
21653
21849
  scanCappedNoticeHint: "Trim .skillmapignore to exclude noisy paths (preferred), or re-run with --max-nodes <N> to raise the cap. Past the recommended limit the graph is hard to read and analyzer signal drops.",
21850
+ /**
21851
+ * File-size skip notice, printed (WARN, stderr) when the walker
21852
+ * skipped one or more files for exceeding `scan.maxFileSizeBytes`.
21853
+ * `{{glyph}}` is the yellow warning glyph, `{{count}}`/`{{noun}}` the
21854
+ * skipped-file tally, `{{files}}` the pre-rendered list of
21855
+ * `path (size)` rows, `{{hint}}` the dim escape-route line.
21856
+ */
21857
+ scanSkippedFilesNotice: "{{glyph}} Skipped {{count}} {{noun}} over the size limit (scan.maxFileSizeBytes):\n{{files}} {{hint}}\n",
21858
+ // The per-file ` - path (size)\n` rows that fill `{{files}}` are
21859
+ // rendered by `kernel/util/format-oversized.ts:formatOversizedFileRows`,
21860
+ // shared with `sm watch` / `sm serve` so the three surfaces never drift.
21861
+ scanSkippedFileNounSingular: "file",
21862
+ scanSkippedFileNounPlural: "files",
21863
+ scanSkippedFilesNoticeHint: "Raise scan.maxFileSizeBytes to include these, or add them to .skillmapignore to skip them on purpose.",
21654
21864
  /**
21655
21865
  * Validation message for an invalid `--max-nodes` value. Surfaced as a
21656
21866
  * §3.1b two-line block.
@@ -21829,7 +22039,8 @@ function createWatcherRuntime(opts) {
21829
22039
  strict,
21830
22040
  emitter,
21831
22041
  recommendedNodeLimit: cfg.scan.maxNodes,
21832
- overrideMaxNodes: opts.maxNodesOverride ?? null
22042
+ overrideMaxNodes: opts.maxNodesOverride ?? null,
22043
+ maxFileSizeBytes: cfg.scan.maxFileSizeBytes
21833
22044
  };
21834
22045
  if (cfg.scan.referencePaths.length > 0) {
21835
22046
  const walk3 = walkReferencePaths(cfg.scan.referencePaths, cwd);
@@ -22047,7 +22258,20 @@ var WATCH_TEXTS = {
22047
22258
  * §3.1b two-line block. Validation rejection for `--max-nodes`.
22048
22259
  */
22049
22260
  maxNodesInvalid: "{{glyph}} sm watch: --max-nodes must be an integer >= 1 (got {{raw}}).\n {{hint}}\n",
22050
- maxNodesInvalidHint: "Pass a positive integer, e.g. --max-nodes 256."
22261
+ maxNodesInvalidHint: "Pass a positive integer, e.g. --max-nodes 256.",
22262
+ /**
22263
+ * File-size skip WARN, emitted per batch (stderr) when the walker
22264
+ * skipped one or more files for exceeding `scan.maxFileSizeBytes`.
22265
+ * Mirrors `sm scan`'s notice. `{{files}}` is the pre-rendered list of
22266
+ * `path (size)` rows.
22267
+ */
22268
+ skippedFilesNotice: "{{glyph}} Skipped {{count}} {{noun}} over the size limit (scan.maxFileSizeBytes):\n{{files}} {{hint}}\n",
22269
+ // The per-file ` - path (size)\n` rows that fill `{{files}}` are
22270
+ // rendered by `kernel/util/format-oversized.ts:formatOversizedFileRows`,
22271
+ // shared with `sm scan` / `sm serve` so the three surfaces never drift.
22272
+ skippedFileNounSingular: "file",
22273
+ skippedFileNounPlural: "files",
22274
+ skippedFilesNoticeHint: "Raise scan.maxFileSizeBytes to include these, or add them to .skillmapignore to skip them on purpose."
22051
22275
  };
22052
22276
 
22053
22277
  // cli/commands/watch.ts
@@ -22088,6 +22312,21 @@ async function runWatchLoop(opts) {
22088
22312
  })
22089
22313
  );
22090
22314
  }
22315
+ renderOversizedWarning(result);
22316
+ };
22317
+ const renderOversizedWarning = (result) => {
22318
+ const oversized = result.oversizedFiles ?? [];
22319
+ if ((result.stats.filesOversized ?? oversized.length) <= 0) return;
22320
+ const files = formatOversizedFileRows(oversized).join("");
22321
+ context.stderr.write(
22322
+ tx(WATCH_TEXTS.skippedFilesNotice, {
22323
+ glyph: stderrAnsi.yellow("\u26A0"),
22324
+ count: oversized.length,
22325
+ noun: oversized.length === 1 ? WATCH_TEXTS.skippedFileNounSingular : WATCH_TEXTS.skippedFileNounPlural,
22326
+ files,
22327
+ hint: stderrAnsi.dim(WATCH_TEXTS.skippedFilesNoticeHint)
22328
+ })
22329
+ );
22091
22330
  };
22092
22331
  const runtimeOpts = {
22093
22332
  dbPath,
@@ -22414,7 +22653,13 @@ var ScanCommand = class extends SmCommand {
22414
22653
  });
22415
22654
  if (outcome.kind === "ok") {
22416
22655
  setScanExtensions(buildScanExtensionSet(outcome.executedExtensionIds));
22417
- return this.renderOutcome(outcome.result, outcome.persistedTo, outcome.dbPath, outcome.strict);
22656
+ return this.renderOutcome(
22657
+ outcome.result,
22658
+ outcome.persistedTo,
22659
+ outcome.dbPath,
22660
+ outcome.strict,
22661
+ outcome.lensAutoDetected
22662
+ );
22418
22663
  }
22419
22664
  return this.renderFailure(outcome);
22420
22665
  }
@@ -22523,12 +22768,13 @@ var ScanCommand = class extends SmCommand {
22523
22768
  * the exit code. Exit 1 only when at least one issue is at `error`
22524
22769
  * severity (mirrors `sm check`, per spec § Exit codes).
22525
22770
  */
22526
- renderOutcome(result, persistedTo, dbPath, strict) {
22771
+ renderOutcome(result, persistedTo, dbPath, strict, lensAutoDetected) {
22527
22772
  const exitCode2 = result.issues.some((i) => i.severity === "error") ? ExitCode.Issues : ExitCode.Ok;
22528
22773
  if (this.json) {
22529
22774
  return this.#renderJsonOutcome(result, exitCode2, strict);
22530
22775
  }
22531
22776
  const ansi = this.ansiFor("stdout");
22777
+ this.#announceAutoDetectedLens(lensAutoDetected);
22532
22778
  const cwd = defaultRuntimeContext().cwd;
22533
22779
  const hasErrors = exitCode2 === ExitCode.Issues;
22534
22780
  const severityCounts = countBySeverity(result.issues);
@@ -22558,8 +22804,32 @@ var ScanCommand = class extends SmCommand {
22558
22804
  );
22559
22805
  }
22560
22806
  this.maybePrintCapNotice(result, ansi);
22807
+ this.maybePrintSkippedFilesNotice(result, ansi);
22561
22808
  return exitCode2;
22562
22809
  }
22810
+ /**
22811
+ * Surface a WARN when the walker skipped one or more files for
22812
+ * exceeding `scan.maxFileSizeBytes`. Lists every skipped file as
22813
+ * `path (humanSize)` and points the user at the two levers
22814
+ * (`scan.maxFileSizeBytes` to raise the limit, `.skillmapignore` to
22815
+ * exclude the path). Routed through `printer.warn` (stderr) because a
22816
+ * silently dropped file is degraded state the operator should read,
22817
+ * not a mid-flight progress line.
22818
+ */
22819
+ maybePrintSkippedFilesNotice(result, ansi) {
22820
+ const oversized = result.oversizedFiles ?? [];
22821
+ if ((result.stats.filesOversized ?? oversized.length) <= 0) return;
22822
+ const lines = formatOversizedFileRows(oversized).join("");
22823
+ this.printer.warn(
22824
+ tx(SCAN_TEXTS.scanSkippedFilesNotice, {
22825
+ glyph: ansi.yellow("\u26A0"),
22826
+ count: String(oversized.length),
22827
+ noun: oversized.length === 1 ? SCAN_TEXTS.scanSkippedFileNounSingular : SCAN_TEXTS.scanSkippedFileNounPlural,
22828
+ files: lines,
22829
+ hint: ansi.dim(SCAN_TEXTS.scanSkippedFilesNoticeHint)
22830
+ })
22831
+ );
22832
+ }
22563
22833
  /**
22564
22834
  * Surface the §Node cap notice when the walker actually stopped
22565
22835
  * accepting files because of the cap. Derivation: `filesWalked >
@@ -22583,6 +22853,21 @@ var ScanCommand = class extends SmCommand {
22583
22853
  })
22584
22854
  );
22585
22855
  }
22856
+ /**
22857
+ * Print the lens auto-detect line on stdout (the SAME stream as the
22858
+ * scan summary) so the two never interleave on a tty. The bootstrap
22859
+ * deliberately no longer prints this to stderr; the runner threads
22860
+ * `lensAutoDetected` through so the CLI announces it here, in order,
22861
+ * right before the summary. The text ends in a newline, so the
22862
+ * summary lands cleanly on its own line. No-op when the lens came
22863
+ * from config or no marker matched (`null` / `undefined`).
22864
+ */
22865
+ #announceAutoDetectedLens(lensAutoDetected) {
22866
+ if (!lensAutoDetected) return;
22867
+ this.printer.data(
22868
+ tx(SCAN_RUNNER_TEXTS.activeProviderAutodetected, { id: lensAutoDetected })
22869
+ );
22870
+ }
22586
22871
  /**
22587
22872
  * `--json` output path. Under `--strict` (H4) self-validates the
22588
22873
  * ScanResult against `scan-result.schema.json` before emitting it,
@@ -23055,6 +23340,12 @@ var SERVER_TEXTS = {
23055
23340
  // watcher loop continues, a transient FS error must not kill the
23056
23341
  // broadcaster.
23057
23342
  watcherBatchFailed: "skill-map server: watcher batch failed ({{message}}).\n",
23343
+ // Logged on the server pane when a scan batch (initial or follow-up)
23344
+ // skipped one or more files for exceeding `scan.maxFileSizeBytes`.
23345
+ // `{{files}}` is the comma-separated `path (size)` list. The SPA
23346
+ // raises its own banner from the persisted `oversizedFiles`, so this
23347
+ // is log-only (no WS advisory).
23348
+ watcherFilesOversized: "skill-map server: skipped {{count}} file(s) over the size limit (scan.maxFileSizeBytes): {{files}}. Raise the limit or add them to .skillmapignore.\n",
23058
23349
  // Logged once when the pre-1.0 schema-drift check rebuilt the DB on
23059
23350
  // watcher boot (the on-disk cache was written by a different
23060
23351
  // major.minor). The scan that follows repopulates it; .sm sidecars
@@ -23140,19 +23431,19 @@ var SERVER_TEXTS = {
23140
23431
  // 400, cascade route rejects qualified ids: the bare-id PATCH is the
23141
23432
  // bundle macro endpoint. Anything containing `/` needs the dedicated
23142
23433
  // per-extension route below.
23143
- pluginsCascadeRouteQualifiedRejected: 'Plugin id "{{id}}" contains "/"; toggle individual extensions via PATCH /api/plugins/<bundle>/extensions/<extensionId>.',
23434
+ pluginsCascadeRouteQualifiedRejected: 'Plugin id "{{id}}" contains "/"; toggle individual extensions via PATCH /api/plugins/<plugin>/extensions/<extensionId>.',
23144
23435
  // 404, unknown plugin / extension.
23145
23436
  pluginsUnknown: 'No plugin with id "{{id}}".',
23146
- pluginsExtensionUnknown: 'Plugin "{{bundleId}}" has no extension named "{{extensionId}}".',
23437
+ pluginsExtensionUnknown: 'Plugin "{{pluginId}}" has no extension named "{{extensionId}}".',
23147
23438
  // 500, DB missing on a write path. Read paths degrade to empty
23148
23439
  // shapes, but mutations cannot persist without a DB so they fail fast.
23149
23440
  pluginsDbMissing: "Cannot persist plugin override: project DB not found at {{path}}. Run `sm scan` first or pass --db <path>.",
23150
23441
  // 403, host-enforced lock from `src/server/locked-plugins.ts`. The
23151
- // bundle (or qualified extension) is in the hardcoded lock-list and
23442
+ // plugin (or qualified extension) is in the hardcoded lock-list and
23152
23443
  // its enabled state is fixed; the UI mirrors the same rule by
23153
23444
  // disabling the toggle.
23154
23445
  pluginsLocked: 'Plugin "{{id}}" is locked by the host and cannot be toggled.',
23155
- pluginsExtensionLocked: 'Extension "{{bundleId}}/{{extensionId}}" is locked by the host and cannot be toggled.',
23446
+ pluginsExtensionLocked: 'Extension "{{pluginId}}/{{extensionId}}" is locked by the host and cannot be toggled.',
23156
23447
  // 400 envelopes specific to the bulk `PATCH /api/plugins` endpoint.
23157
23448
  // The single-id variants above still apply for per-entry validation
23158
23449
  // (unknown id, granularity mismatch, lock); these cover the
@@ -24219,28 +24510,28 @@ function registerPluginsRoute(app, deps) {
24219
24510
  });
24220
24511
  }
24221
24512
  const body = await parsePatchBody(c.req.raw);
24222
- const childIds = bundleExtensionIds(handle).map((extId) => qualifiedExtensionId(id, extId));
24513
+ const childIds = pluginExtensionIds(handle).map((extId) => qualifiedExtensionId(id, extId));
24223
24514
  const writable = childIds.filter((q) => !isPluginLocked(q));
24224
24515
  return await persistManyAndProject(c, deps, writable, body.enabled);
24225
24516
  });
24226
- app.patch("/api/plugins/:bundleId/extensions/:extensionId", async (c) => {
24227
- const bundleId = c.req.param("bundleId");
24517
+ app.patch("/api/plugins/:pluginId/extensions/:extensionId", async (c) => {
24518
+ const pluginId = c.req.param("pluginId");
24228
24519
  const extensionId = c.req.param("extensionId");
24229
- const handle = findHandle(bundleId, deps);
24520
+ const handle = findHandle(pluginId, deps);
24230
24521
  if (!handle) {
24231
24522
  throw new HTTPException9(404, {
24232
- message: tx(SERVER_TEXTS.pluginsUnknown, { id: bundleId })
24523
+ message: tx(SERVER_TEXTS.pluginsUnknown, { id: pluginId })
24233
24524
  });
24234
24525
  }
24235
24526
  if (!hasExtension(handle, extensionId)) {
24236
24527
  throw new HTTPException9(404, {
24237
- message: tx(SERVER_TEXTS.pluginsExtensionUnknown, { bundleId, extensionId })
24528
+ message: tx(SERVER_TEXTS.pluginsExtensionUnknown, { pluginId, extensionId })
24238
24529
  });
24239
24530
  }
24240
- const qualified = qualifiedExtensionId(bundleId, extensionId);
24241
- if (isPluginLocked(qualified) || isPluginLocked(bundleId)) {
24531
+ const qualified = qualifiedExtensionId(pluginId, extensionId);
24532
+ if (isPluginLocked(qualified) || isPluginLocked(pluginId)) {
24242
24533
  throw new HTTPException9(403, {
24243
- message: tx(SERVER_TEXTS.pluginsExtensionLocked, { bundleId, extensionId })
24534
+ message: tx(SERVER_TEXTS.pluginsExtensionLocked, { pluginId, extensionId })
24244
24535
  });
24245
24536
  }
24246
24537
  const body = await parsePatchBody(c.req.raw);
@@ -24269,11 +24560,11 @@ function listItems(deps, resolveEnabled) {
24269
24560
  ];
24270
24561
  }
24271
24562
  function buildBuiltInItems(resolveEnabled) {
24272
- return sortBundlesForPresentation(builtInBundles).map((bundle) => {
24273
- const bundleLocked = isPluginLocked(bundle.id);
24274
- const extensions = bundle.extensions.map((ext) => {
24275
- const qualified = qualifiedExtensionId(bundle.id, ext.id);
24276
- const extLocked = bundleLocked || isPluginLocked(qualified);
24563
+ return sortPluginsForPresentation(builtInPlugins).map((plugin) => {
24564
+ const pluginLocked = isPluginLocked(plugin.id);
24565
+ const extensions = plugin.extensions.map((ext) => {
24566
+ const qualified = qualifiedExtensionId(plugin.id, ext.id);
24567
+ const extLocked = pluginLocked || isPluginLocked(qualified);
24277
24568
  return {
24278
24569
  id: ext.id,
24279
24570
  kind: ext.kind,
@@ -24283,17 +24574,17 @@ function buildBuiltInItems(resolveEnabled) {
24283
24574
  ...extLocked ? { locked: true } : {}
24284
24575
  };
24285
24576
  });
24286
- const bundleEnabled = extensions.some((e) => e.enabled);
24577
+ const pluginEnabled = extensions.some((e) => e.enabled);
24287
24578
  return {
24288
- id: bundle.id,
24289
- version: firstVersion(bundle.extensions),
24290
- kinds: uniqueKinds(bundle.extensions.map((e) => e.kind)),
24291
- status: bundleEnabled ? "enabled" : "disabled",
24579
+ id: plugin.id,
24580
+ version: firstVersion(plugin.extensions),
24581
+ kinds: uniqueKinds(plugin.extensions.map((e) => e.kind)),
24582
+ status: pluginEnabled ? "enabled" : "disabled",
24292
24583
  reason: null,
24293
24584
  source: "built-in",
24294
- description: bundle.description,
24585
+ description: plugin.description,
24295
24586
  ...extensions.length > 0 ? { extensions } : {},
24296
- ...bundleLocked ? { locked: true } : {}
24587
+ ...pluginLocked ? { locked: true } : {}
24297
24588
  };
24298
24589
  });
24299
24590
  }
@@ -24301,8 +24592,8 @@ function buildDiscoveredItems(discovered, deps, resolveEnabled) {
24301
24592
  return discovered.map((plugin) => buildDiscoveredItem(plugin, deps, resolveEnabled));
24302
24593
  }
24303
24594
  function buildDiscoveredItem(plugin, deps, resolveEnabled) {
24304
- const bundleLocked = isPluginLocked(plugin.id);
24305
- const extensions = projectExtensionRows(plugin, resolveEnabled, bundleLocked);
24595
+ const pluginLocked = isPluginLocked(plugin.id);
24596
+ const extensions = projectExtensionRows(plugin, resolveEnabled, pluginLocked);
24306
24597
  const optional = optionalDiscoveredFields(plugin, extensions);
24307
24598
  return {
24308
24599
  id: plugin.id,
@@ -24312,7 +24603,7 @@ function buildDiscoveredItem(plugin, deps, resolveEnabled) {
24312
24603
  reason: plugin.reason ?? null,
24313
24604
  source: classifyPluginSource(plugin.path, deps),
24314
24605
  ...optional,
24315
- ...bundleLocked ? { locked: true } : {},
24606
+ ...pluginLocked ? { locked: true } : {},
24316
24607
  ...plugin.status === "disabled" ? { startsAsDisabled: true } : {}
24317
24608
  };
24318
24609
  }
@@ -24323,12 +24614,12 @@ function optionalDiscoveredFields(plugin, extensions) {
24323
24614
  if (extensions) out.extensions = extensions;
24324
24615
  return out;
24325
24616
  }
24326
- function projectExtensionRows(plugin, resolveEnabled, bundleLocked) {
24617
+ function projectExtensionRows(plugin, resolveEnabled, pluginLocked) {
24327
24618
  if (!plugin.extensions || plugin.extensions.length === 0) return void 0;
24328
24619
  return plugin.extensions.map((ext) => {
24329
24620
  const description = readInstanceDescription(ext.instance);
24330
24621
  const qualified = qualifiedExtensionId(plugin.id, ext.id);
24331
- const extLocked = bundleLocked || isPluginLocked(qualified);
24622
+ const extLocked = pluginLocked || isPluginLocked(qualified);
24332
24623
  return {
24333
24624
  id: ext.id,
24334
24625
  kind: ext.kind,
@@ -24437,28 +24728,28 @@ function validateBulkChange(change, deps) {
24437
24728
  }
24438
24729
  return null;
24439
24730
  }
24440
- const bundleId = change.id.slice(0, slash);
24731
+ const pluginId = change.id.slice(0, slash);
24441
24732
  const extensionId = change.id.slice(slash + 1);
24442
- const handle = findHandle(bundleId, deps);
24733
+ const handle = findHandle(pluginId, deps);
24443
24734
  if (!handle) {
24444
24735
  return {
24445
24736
  status: 404,
24446
24737
  code: "not-found",
24447
- message: tx(SERVER_TEXTS.pluginsUnknown, { id: bundleId })
24738
+ message: tx(SERVER_TEXTS.pluginsUnknown, { id: pluginId })
24448
24739
  };
24449
24740
  }
24450
24741
  if (!hasExtension(handle, extensionId)) {
24451
24742
  return {
24452
24743
  status: 404,
24453
24744
  code: "not-found",
24454
- message: tx(SERVER_TEXTS.pluginsExtensionUnknown, { bundleId, extensionId })
24745
+ message: tx(SERVER_TEXTS.pluginsExtensionUnknown, { pluginId, extensionId })
24455
24746
  };
24456
24747
  }
24457
- if (isPluginLocked(change.id) || isPluginLocked(bundleId)) {
24748
+ if (isPluginLocked(change.id) || isPluginLocked(pluginId)) {
24458
24749
  return {
24459
24750
  status: 403,
24460
24751
  code: "locked",
24461
- message: tx(SERVER_TEXTS.pluginsExtensionLocked, { bundleId, extensionId })
24752
+ message: tx(SERVER_TEXTS.pluginsExtensionLocked, { pluginId, extensionId })
24462
24753
  };
24463
24754
  }
24464
24755
  return null;
@@ -24482,7 +24773,7 @@ function expandBulkChangeKeys(change, deps) {
24482
24773
  if (change.id.includes("/")) return [change.id];
24483
24774
  const handle = findHandle(change.id, deps);
24484
24775
  if (!handle) return [];
24485
- return bundleExtensionIds(handle).map((extId) => qualifiedExtensionId(change.id, extId)).filter((q) => !isPluginLocked(q));
24776
+ return pluginExtensionIds(handle).map((extId) => qualifiedExtensionId(change.id, extId)).filter((q) => !isPluginLocked(q));
24486
24777
  }
24487
24778
  async function buildFreshResolver2(deps) {
24488
24779
  return buildFreshResolver({
@@ -24495,21 +24786,21 @@ function composeResolver2(deps, overrides) {
24495
24786
  return composeResolver(deps.configService.effective(), overrides);
24496
24787
  }
24497
24788
  function findHandle(id, deps) {
24498
- const builtIn = builtInBundles.find((b) => b.id === id);
24499
- if (builtIn) return { kind: "built-in", bundle: builtIn };
24789
+ const builtIn = builtInPlugins.find((b) => b.id === id);
24790
+ if (builtIn) return { kind: "built-in", plugin: builtIn };
24500
24791
  const discovered = deps.pluginRuntime.discovered.find((p) => p.id === id);
24501
24792
  if (discovered) return { kind: "discovered", plugin: discovered };
24502
24793
  return null;
24503
24794
  }
24504
- function bundleExtensionIds(handle) {
24795
+ function pluginExtensionIds(handle) {
24505
24796
  if (handle.kind === "built-in") {
24506
- return handle.bundle.extensions.map((e) => e.id);
24797
+ return handle.plugin.extensions.map((e) => e.id);
24507
24798
  }
24508
24799
  return (handle.plugin.extensions ?? []).map((e) => e.id);
24509
24800
  }
24510
24801
  function hasExtension(handle, extensionId) {
24511
24802
  if (handle.kind === "built-in") {
24512
- return handle.bundle.extensions.some((e) => e.id === extensionId);
24803
+ return handle.plugin.extensions.some((e) => e.id === extensionId);
24513
24804
  }
24514
24805
  return (handle.plugin.extensions ?? []).some((e) => e.id === extensionId);
24515
24806
  }
@@ -25116,7 +25407,9 @@ function createWatcherService(opts) {
25116
25407
  message: sanitizeForTerminal(outcome.message)
25117
25408
  })
25118
25409
  );
25410
+ return;
25119
25411
  }
25412
+ warnOversizedFiles(outcome.result);
25120
25413
  },
25121
25414
  onWatcherError: (message) => {
25122
25415
  log.warn(
@@ -25179,6 +25472,17 @@ function createWatcherService(opts) {
25179
25472
  }
25180
25473
  };
25181
25474
  }
25475
+ function warnOversizedFiles(result) {
25476
+ const oversized = result.oversizedFiles ?? [];
25477
+ if ((result.stats.filesOversized ?? oversized.length) <= 0) return;
25478
+ const files = oversized.map((f) => formatOversizedFilePair({ path: sanitizeForTerminal(f.path), bytes: f.bytes })).join(", ");
25479
+ log.warn(
25480
+ tx(SERVER_TEXTS.watcherFilesOversized, {
25481
+ count: String(oversized.length),
25482
+ files
25483
+ })
25484
+ );
25485
+ }
25182
25486
  function buildBroadcasterEmitter(broadcaster) {
25183
25487
  return {
25184
25488
  emit(event) {
@@ -26354,8 +26658,8 @@ function assembleKernel(pluginRuntime, noBuiltIns) {
26354
26658
  (c) => `${c.pluginId}/${c.extensionId}/${c.contributionId}`
26355
26659
  )
26356
26660
  );
26357
- for (const bundle of builtInBundles) {
26358
- for (const ext of bundle.extensions) {
26661
+ for (const plugin of builtInPlugins) {
26662
+ for (const ext of plugin.extensions) {
26359
26663
  collectViewContributions(ext.pluginId, ext.id, ext, mergedViewContributions, {
26360
26664
  excludeQualifiedIds: userKey
26361
26665
  });
@@ -26368,8 +26672,8 @@ function assembleKernel(pluginRuntime, noBuiltIns) {
26368
26672
  }
26369
26673
  function collectBuiltInProviders() {
26370
26674
  const out = [];
26371
- for (const bundle of builtInBundles) {
26372
- for (const ext of bundle.extensions) {
26675
+ for (const plugin of builtInPlugins) {
26676
+ for (const ext of plugin.extensions) {
26373
26677
  if (ext.kind === "provider") {
26374
26678
  out.push(ext);
26375
26679
  }
@@ -28615,4 +28919,5 @@ function resolveBareDefault() {
28615
28919
  );
28616
28920
  process.exit(ExitCode.Error);
28617
28921
  }
28618
- //# sourceMappingURL=cli.js.map
28922
+ //# sourceMappingURL=cli.js.map
28923
+ //# debugId=7c131b78-b7ad-5f03-b19c-1f70afdfeefb