@skill-map/cli 0.43.0 → 0.44.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 (46) hide show
  1. package/dist/cli/tutorial/sm-master/SKILL.md +7 -9
  2. package/dist/cli/tutorial/sm-master/references/fixture-templates.md +1 -1
  3. package/dist/cli/tutorial/sm-master/references/tour-plugins.md +10 -9
  4. package/dist/cli/tutorial/sm-master/references/tour-settings.md +4 -4
  5. package/dist/cli/tutorial/sm-tutorial/SKILL.md +216 -299
  6. package/dist/cli.js +736 -221
  7. package/dist/cli.js.map +1 -1
  8. package/dist/index.js +21 -15
  9. package/dist/index.js.map +1 -1
  10. package/dist/kernel/index.d.ts +8 -6
  11. package/dist/kernel/index.js +21 -15
  12. package/dist/kernel/index.js.map +1 -1
  13. package/dist/ui/chunk-27WQPOXP.js +1 -0
  14. package/dist/ui/{chunk-DZBSELHN.js → chunk-43S72FTV.js} +1 -1
  15. package/dist/ui/chunk-555ST76V.js +1 -0
  16. package/dist/ui/{chunk-JPYAASHN.js → chunk-5AD5ZV4I.js} +3 -3
  17. package/dist/ui/{chunk-CFJBTDAA.js → chunk-5ITZXW3A.js} +1 -1
  18. package/dist/ui/chunk-A7PRWMQD.js +1021 -0
  19. package/dist/ui/chunk-DL5EA245.js +123 -0
  20. package/dist/ui/chunk-F7I6KMHX.js +1 -0
  21. package/dist/ui/{chunk-77J7XU3Y.js → chunk-I5AX4U2N.js} +28 -28
  22. package/dist/ui/chunk-IUZRAD7S.js +1 -0
  23. package/dist/ui/{chunk-XOHD5XWA.js → chunk-IYM26L3O.js} +1 -1
  24. package/dist/ui/{chunk-SBCO7ZSP.js → chunk-LGFABCIA.js} +1 -1
  25. package/dist/ui/{chunk-IUDL3NDH.js → chunk-MFLFIA7C.js} +1 -1
  26. package/dist/ui/chunk-MS6B7344.js +315 -0
  27. package/dist/ui/chunk-P3SNMV4X.js +2 -0
  28. package/dist/ui/chunk-PZQHB7GS.js +4 -0
  29. package/dist/ui/chunk-QDUSFOBE.js +1 -0
  30. package/dist/ui/{chunk-YCR3XCIW.js → chunk-QNTAOR2L.js} +5 -5
  31. package/dist/ui/{chunk-CR3AANNX.js → chunk-S4S5ZMXJ.js} +1 -1
  32. package/dist/ui/{chunk-5WJRN3LD.js → chunk-T3IVIRRJ.js} +1 -1
  33. package/dist/ui/{chunk-HP375T2O.js → chunk-VGPYYAVI.js} +1 -1
  34. package/dist/ui/chunk-X227ITGS.js +499 -0
  35. package/dist/ui/index.html +1 -1
  36. package/dist/ui/main-O3CWFYKV.js +3 -0
  37. package/package.json +10 -9
  38. package/dist/ui/chunk-2IF446BS.js +0 -1
  39. package/dist/ui/chunk-DIKPNZUZ.js +0 -315
  40. package/dist/ui/chunk-KGCJINI6.js +0 -1
  41. package/dist/ui/chunk-PPE3P2JD.js +0 -1
  42. package/dist/ui/chunk-UOCACZLI.js +0 -123
  43. package/dist/ui/chunk-YL6SWAFJ.js +0 -1024
  44. package/dist/ui/main-VHFB7Q2D.js +0 -3
  45. /package/dist/ui/{chunk-C2YUQODZ.js → chunk-4SG4352Z.js} +0 -0
  46. /package/dist/ui/{chunk-VB56BUGO.js → chunk-WCABR6TI.js} +0 -0
package/dist/cli.js CHANGED
@@ -912,75 +912,77 @@ var antigravityProvider = {
912
912
  classify() {
913
913
  return null;
914
914
  },
915
- // Seed catalog, PROVISIONAL, derived from the Gemini CLI slash-command
916
- // surface. The Google Developers Blog post that announced the Antigravity
917
- // CLI rollout on 2026-05-19 states verbatim: "The Antigravity CLI fully
918
- // replaces the Gemini CLI ... preserves the most critical Gemini CLI
919
- // features: Agent Skills, Hooks, Subagents, and Extensions (now rebranded
920
- // as Antigravity plugins) ... shares the same agent harness as Antigravity
921
- // 2.0, the new Antigravity desktop application." Since the four feature
922
- // pillars carry over 1:1 and the agent harness is shared, the operator's
923
- // built-in slash-command vocabulary is overwhelmingly likely to be
924
- // Gemini CLI's. We mirror the full 38-verb Gemini CLI catalog (plus its
925
- // four documented aliases: `dir`, `?`, `exit`, `bashes`) so a user file
926
- // that names a skill / command `help`, `clear`, `mcp`, etc. is flagged
927
- // immediately by `core/name-reserved` once the lens activates the catalog.
915
+ // Built-in slash-command catalog, captured verbatim from `agy /help`
916
+ // (Antigravity CLI v1.0.3). This REPLACES the earlier provisional list
917
+ // that mirrored Gemini CLI's verbs: `agy` ships its own surface. It
918
+ // DROPPED Gemini-only verbs (`vim`, `theme`, `terminal-setup`,
919
+ // `setup-github`, `bashes`, `shells`, `policies`, `extensions`, `about`,
920
+ // `auth`, `bug`, `chat`, `compress`, `docs`, `editor`, `ide`, `init`,
921
+ // `memory`, `restore`, `stats`, `tools`, `upgrade`, `?`, `dir`) and
922
+ // ADDED agent-first ones (`goal`, `grill-me`, `schedule`, `fast`, `btw`,
923
+ // `artifact`, `context`, `diff`, `fork`, `tasks`, `add-dir`, `credits`,
924
+ // `feedback`, `logout`, `open`, `planning`, `rename`, `statusline`,
925
+ // `title`, `usage`). Both the 35 primary verbs and the 8 documented
926
+ // aliases (`new`, `settings`, `quit`, `branch`, `switch`, `conversation`,
927
+ // `undo`, `quota`) are reserved: a user skill named after either is
928
+ // silently shadowed by the built-in once the catalog activates.
928
929
  //
929
- // The catalog is INACTIVE today: the analyzer keys on `node.provider`
930
- // and this Provider's `classify()` returns `null` for every path, so
931
- // no node carries `provider: 'antigravity'`. The seed lives here so
932
- // the day Antigravity grows its own on-disk kind (e.g. a vendor-specific
933
- // `.antigravity/commands/` directory beyond the open-standard
934
- // `.agents/skills/`) the catalog is already in place with no migration.
930
+ // Declared under the `skill` kind (NOT `command`): Antigravity has no
931
+ // vendor-specific command directory, its user slash-commands are skills
932
+ // (`.agents/skills/<name>/SKILL.md`, owned by the universal `agent-skills`
933
+ // Provider). The catalog is ACTIVE via the LENS SCOPE in
934
+ // `buildReservedNodePaths` (spec/architecture.md §Provider ·
935
+ // reservedNames): when `activeProvider === 'antigravity'` the orchestrator
936
+ // lends this `skill` catalog to `agent-skills` skill nodes, so a user
937
+ // `.agents/skills/goal/SKILL.md` is flagged because `/goal` is built-in.
935
938
  //
936
- // **Reconciliation marker**: the day Google's docs at
937
- // antigravity.google/docs publishes the authoritative slash-command
938
- // reference, replace this comment + array with the official list (and
939
- // bump the file's leading docblock to cite the new source URL).
939
+ // **Reconciliation marker**: re-capture from `agy /help` on each major
940
+ // Antigravity CLI release and bump the cited version above.
940
941
  reservedNames: {
941
- command: [
942
- "?",
943
- "about",
942
+ skill: [
943
+ "add-dir",
944
944
  "agents",
945
- "auth",
946
- "bashes",
947
- "bug",
948
- "chat",
945
+ "artifact",
946
+ "branch",
947
+ "btw",
948
+ "changelog",
949
949
  "clear",
950
- "commands",
951
- "compress",
950
+ "config",
951
+ "context",
952
+ "conversation",
952
953
  "copy",
953
- "dir",
954
- "directory",
955
- "docs",
956
- "editor",
954
+ "credits",
955
+ "diff",
957
956
  "exit",
958
- "extensions",
957
+ "fast",
958
+ "feedback",
959
+ "fork",
960
+ "goal",
961
+ "grill-me",
959
962
  "help",
960
963
  "hooks",
961
- "ide",
962
- "init",
964
+ "keybindings",
965
+ "logout",
963
966
  "mcp",
964
- "memory",
965
967
  "model",
968
+ "new",
969
+ "open",
966
970
  "permissions",
967
- "plan",
968
- "policies",
969
- "privacy",
971
+ "planning",
970
972
  "quit",
971
- "restore",
973
+ "quota",
974
+ "rename",
972
975
  "resume",
973
976
  "rewind",
977
+ "schedule",
974
978
  "settings",
975
- "setup-github",
976
- "shells",
977
979
  "skills",
978
- "stats",
979
- "terminal-setup",
980
- "theme",
981
- "tools",
982
- "upgrade",
983
- "vim"
980
+ "statusline",
981
+ "switch",
982
+ "tasks",
983
+ "title",
984
+ "undo",
985
+ "usage"
984
986
  ]
985
987
  }
986
988
  };
@@ -3946,7 +3948,7 @@ var UPDATE_CHECK_TEXTS = {
3946
3948
  // package.json
3947
3949
  var package_default = {
3948
3950
  name: "@skill-map/cli",
3949
- version: "0.43.0",
3951
+ version: "0.44.0",
3950
3952
  description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
3951
3953
  license: "MIT",
3952
3954
  type: "module",
@@ -3997,25 +3999,25 @@ var package_default = {
3997
3999
  typecheck: "tsc --noEmit",
3998
4000
  lint: "eslint .",
3999
4001
  "lint:fix": "eslint . --fix",
4000
- reference: "node scripts/build-reference.js",
4001
- "reference:check": "node scripts/build-reference.js --check",
4002
4002
  "build-built-ins": "node ../scripts/generate-built-ins.js",
4003
4003
  "built-ins:check": "node ../scripts/generate-built-ins.js --check",
4004
4004
  prebuild: "pnpm build-built-ins",
4005
4005
  validate: "pnpm validate:compile && pnpm validate:test",
4006
- "validate:compile": "pnpm typecheck && pnpm lint && pnpm build && pnpm reference:check && pnpm built-ins:check",
4006
+ "validate:compile": "pnpm typecheck && pnpm lint && pnpm build && pnpm built-ins:check",
4007
4007
  "validate:test": "pnpm test:ci",
4008
4008
  pretest: "tsup",
4009
4009
  "pretest:coverage": "tsup",
4010
4010
  "pretest:coverage:html": "tsup",
4011
- test: "tsc --noEmit && 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'",
4012
- "test:ci": "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'",
4013
- "test:coverage": "tsc --noEmit && 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'",
4014
- "test:coverage:html": "tsc --noEmit && 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'",
4011
+ 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'",
4012
+ "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'",
4013
+ "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'",
4014
+ "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'",
4015
+ "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'",
4015
4016
  clean: "rm -rf dist coverage"
4016
4017
  },
4017
4018
  dependencies: {
4018
4019
  "@hono/node-server": "2.0.1",
4020
+ "@sentry/node": "10.55.0",
4019
4021
  "@skill-map/spec": "workspace:*",
4020
4022
  ajv: "8.18.0",
4021
4023
  "ajv-formats": "3.0.1",
@@ -4026,6 +4028,7 @@ var package_default = {
4026
4028
  "js-tiktoken": "1.0.21",
4027
4029
  "js-yaml": "4.1.1",
4028
4030
  kysely: "0.28.17",
4031
+ "posthog-node": "5.35.6",
4029
4032
  semver: "7.7.4",
4030
4033
  "smol-toml": "1.6.1",
4031
4034
  typanion: "3.14.0",
@@ -4096,6 +4099,7 @@ function ansiFor(opts) {
4096
4099
  }
4097
4100
 
4098
4101
  // cli/util/user-settings-store.ts
4102
+ import { randomUUID } from "crypto";
4099
4103
  import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync5 } from "fs";
4100
4104
  import { homedir } from "os";
4101
4105
  import { join as join2 } from "path";
@@ -4204,13 +4208,13 @@ function userSettingsFilePath() {
4204
4208
  return join2(homedir(), SKILL_MAP_DIR, FILENAME);
4205
4209
  }
4206
4210
  function defaultSettings() {
4207
- return { schemaVersion: SCHEMA_VERSION, updateCheck: {} };
4211
+ return { schemaVersion: SCHEMA_VERSION, updateCheck: {}, telemetry: {} };
4208
4212
  }
4209
4213
  function readUserSettings() {
4210
4214
  const parsed = readParsedFile();
4211
4215
  if (parsed === null) return defaultSettings();
4212
4216
  const validated = validateOrDefault(parsed);
4213
- return backfillUpdateCheck(validated);
4217
+ return backfillSubObjects(validated);
4214
4218
  }
4215
4219
  function readParsedFile() {
4216
4220
  const path = userSettingsFilePath();
@@ -4232,11 +4236,12 @@ function validateOrDefault(parsed) {
4232
4236
  if (!result.ok) return defaultSettings();
4233
4237
  return result.data;
4234
4238
  }
4235
- function backfillUpdateCheck(settings) {
4236
- if (settings.updateCheck === void 0) {
4237
- return { ...settings, updateCheck: {} };
4238
- }
4239
- return settings;
4239
+ function backfillSubObjects(settings) {
4240
+ return {
4241
+ ...settings,
4242
+ updateCheck: settings.updateCheck ?? {},
4243
+ telemetry: settings.telemetry ?? {}
4244
+ };
4240
4245
  }
4241
4246
  function writeUserSettings(patch) {
4242
4247
  const dir = join2(homedir(), SKILL_MAP_DIR);
@@ -4258,14 +4263,49 @@ function isUpdateCheckEnabled() {
4258
4263
  const settings = readUserSettings();
4259
4264
  return settings.updateCheck?.enabled !== false;
4260
4265
  }
4266
+ function isErrorTelemetryEnabled() {
4267
+ const settings = readUserSettings();
4268
+ return settings.telemetry?.errorsEnabled === true;
4269
+ }
4270
+ function isUsageCliTelemetryEnabled() {
4271
+ const settings = readUserSettings();
4272
+ return settings.telemetry?.usageCliEnabled === true;
4273
+ }
4274
+ function isUsageUiTelemetryEnabled() {
4275
+ const settings = readUserSettings();
4276
+ return settings.telemetry?.usageUiEnabled === true;
4277
+ }
4278
+ function readAnonymousId() {
4279
+ const settings = readUserSettings();
4280
+ return settings.telemetry?.anonymousId ?? null;
4281
+ }
4282
+ function ensureAnonymousId(generate = () => randomUUID()) {
4283
+ const existing = readAnonymousId();
4284
+ if (existing !== null && existing !== "") return existing;
4285
+ const id = generate();
4286
+ writeUserSettings({ telemetry: { anonymousId: id } });
4287
+ return id;
4288
+ }
4289
+ function hasTelemetryPromptBeenShown() {
4290
+ const settings = readUserSettings();
4291
+ return typeof settings.telemetry?.promptedAt === "number";
4292
+ }
4293
+ function hasSeenFirstRun() {
4294
+ const settings = readUserSettings();
4295
+ return typeof settings.telemetry?.firstRunAt === "number";
4296
+ }
4261
4297
  function mergeSettings(current, patch) {
4262
4298
  const merged = {
4263
4299
  schemaVersion: SCHEMA_VERSION,
4264
- updateCheck: { ...current.updateCheck ?? {} }
4300
+ updateCheck: { ...current.updateCheck ?? {} },
4301
+ telemetry: { ...current.telemetry ?? {} }
4265
4302
  };
4266
4303
  if (patch.updateCheck) {
4267
4304
  merged.updateCheck = { ...merged.updateCheck, ...patch.updateCheck };
4268
4305
  }
4306
+ if (patch.telemetry) {
4307
+ merged.telemetry = { ...merged.telemetry, ...patch.telemetry };
4308
+ }
4269
4309
  return merged;
4270
4310
  }
4271
4311
  function tryLoadValidators() {
@@ -4378,40 +4418,40 @@ var updateCheckHook = {
4378
4418
  };
4379
4419
 
4380
4420
  // plugins/built-ins.ts
4381
- var claudeProvider2 = { ...claudeProvider, pluginId: "claude", version: "0.43.0" };
4382
- var atDirectiveExtractor2 = { ...atDirectiveExtractor, pluginId: "claude", version: "0.43.0" };
4383
- var slashCommandExtractor2 = { ...slashCommandExtractor, pluginId: "claude", version: "0.43.0" };
4384
- var antigravityProvider2 = { ...antigravityProvider, pluginId: "antigravity", version: "0.43.0" };
4385
- var openaiProvider2 = { ...openaiProvider, pluginId: "openai", version: "0.43.0" };
4386
- var agentSkillsProvider2 = { ...agentSkillsProvider, pluginId: "agent-skills", version: "0.43.0" };
4387
- var coreMarkdownProvider2 = { ...coreMarkdownProvider, pluginId: "core", version: "0.43.0" };
4388
- var annotationsExtractor2 = { ...annotationsExtractor, pluginId: "core", version: "0.43.0" };
4389
- var externalUrlCounterExtractor2 = { ...externalUrlCounterExtractor, pluginId: "core", version: "0.43.0" };
4390
- var markdownLinkExtractor2 = { ...markdownLinkExtractor, pluginId: "core", version: "0.43.0" };
4391
- var mcpToolsExtractor2 = { ...mcpToolsExtractor, pluginId: "core", version: "0.43.0" };
4392
- var toolsCounterExtractor2 = { ...toolsCounterExtractor, pluginId: "core", version: "0.43.0" };
4393
- var annotationFieldUnknownAnalyzer2 = { ...annotationFieldUnknownAnalyzer, pluginId: "core", version: "0.43.0" };
4394
- var annotationOrphanAnalyzer2 = { ...annotationOrphanAnalyzer, pluginId: "core", version: "0.43.0" };
4395
- var annotationStaleAnalyzer2 = { ...annotationStaleAnalyzer, pluginId: "core", version: "0.43.0" };
4396
- var contributionOrphanAnalyzer2 = { ...contributionOrphanAnalyzer, pluginId: "core", version: "0.43.0" };
4397
- var issueCounterAnalyzer2 = { ...issueCounterAnalyzer, pluginId: "core", version: "0.43.0" };
4398
- var jobFileOrphanAnalyzer2 = { ...jobFileOrphanAnalyzer, pluginId: "core", version: "0.43.0" };
4399
- var linkConflictAnalyzer2 = { ...linkConflictAnalyzer, pluginId: "core", version: "0.43.0" };
4400
- var linkCounterAnalyzer2 = { ...linkCounterAnalyzer, pluginId: "core", version: "0.43.0" };
4401
- var linkSelfLoopAnalyzer2 = { ...linkSelfLoopAnalyzer, pluginId: "core", version: "0.43.0" };
4402
- var nameReservedAnalyzer2 = { ...nameReservedAnalyzer, pluginId: "core", version: "0.43.0" };
4403
- var nodeStabilityAnalyzer2 = { ...nodeStabilityAnalyzer, pluginId: "core", version: "0.43.0" };
4404
- var nodeSupersededAnalyzer2 = { ...nodeSupersededAnalyzer, pluginId: "core", version: "0.43.0" };
4405
- var referenceBrokenAnalyzer2 = { ...referenceBrokenAnalyzer, pluginId: "core", version: "0.43.0" };
4406
- var referenceRedundantAnalyzer2 = { ...referenceRedundantAnalyzer, pluginId: "core", version: "0.43.0" };
4407
- var schemaViolationAnalyzer2 = { ...schemaViolationAnalyzer, pluginId: "core", version: "0.43.0" };
4408
- var signalCollisionAnalyzer2 = { ...signalCollisionAnalyzer, pluginId: "core", version: "0.43.0" };
4409
- var triggerCollisionAnalyzer2 = { ...triggerCollisionAnalyzer, pluginId: "core", version: "0.43.0" };
4410
- var asciiFormatter2 = { ...asciiFormatter, pluginId: "core", version: "0.43.0" };
4411
- var jsonFormatter2 = { ...jsonFormatter, pluginId: "core", version: "0.43.0" };
4412
- var nodeBumpAction2 = { ...nodeBumpAction, pluginId: "core", version: "0.43.0" };
4413
- var nodeSupersedeAction2 = { ...nodeSupersedeAction, pluginId: "core", version: "0.43.0" };
4414
- var updateCheckHook2 = { ...updateCheckHook, pluginId: "core", version: "0.43.0" };
4421
+ var claudeProvider2 = { ...claudeProvider, pluginId: "claude", version: "0.44.0" };
4422
+ var atDirectiveExtractor2 = { ...atDirectiveExtractor, pluginId: "claude", version: "0.44.0" };
4423
+ var slashCommandExtractor2 = { ...slashCommandExtractor, pluginId: "claude", version: "0.44.0" };
4424
+ var antigravityProvider2 = { ...antigravityProvider, pluginId: "antigravity", version: "0.44.0" };
4425
+ var openaiProvider2 = { ...openaiProvider, pluginId: "openai", version: "0.44.0" };
4426
+ var agentSkillsProvider2 = { ...agentSkillsProvider, pluginId: "agent-skills", version: "0.44.0" };
4427
+ var coreMarkdownProvider2 = { ...coreMarkdownProvider, pluginId: "core", version: "0.44.0" };
4428
+ var annotationsExtractor2 = { ...annotationsExtractor, pluginId: "core", version: "0.44.0" };
4429
+ var externalUrlCounterExtractor2 = { ...externalUrlCounterExtractor, pluginId: "core", version: "0.44.0" };
4430
+ var markdownLinkExtractor2 = { ...markdownLinkExtractor, pluginId: "core", version: "0.44.0" };
4431
+ var mcpToolsExtractor2 = { ...mcpToolsExtractor, pluginId: "core", version: "0.44.0" };
4432
+ var toolsCounterExtractor2 = { ...toolsCounterExtractor, pluginId: "core", version: "0.44.0" };
4433
+ var annotationFieldUnknownAnalyzer2 = { ...annotationFieldUnknownAnalyzer, pluginId: "core", version: "0.44.0" };
4434
+ var annotationOrphanAnalyzer2 = { ...annotationOrphanAnalyzer, pluginId: "core", version: "0.44.0" };
4435
+ var annotationStaleAnalyzer2 = { ...annotationStaleAnalyzer, pluginId: "core", version: "0.44.0" };
4436
+ var contributionOrphanAnalyzer2 = { ...contributionOrphanAnalyzer, pluginId: "core", version: "0.44.0" };
4437
+ var issueCounterAnalyzer2 = { ...issueCounterAnalyzer, pluginId: "core", version: "0.44.0" };
4438
+ var jobFileOrphanAnalyzer2 = { ...jobFileOrphanAnalyzer, pluginId: "core", version: "0.44.0" };
4439
+ var linkConflictAnalyzer2 = { ...linkConflictAnalyzer, pluginId: "core", version: "0.44.0" };
4440
+ var linkCounterAnalyzer2 = { ...linkCounterAnalyzer, pluginId: "core", version: "0.44.0" };
4441
+ var linkSelfLoopAnalyzer2 = { ...linkSelfLoopAnalyzer, pluginId: "core", version: "0.44.0" };
4442
+ var nameReservedAnalyzer2 = { ...nameReservedAnalyzer, pluginId: "core", version: "0.44.0" };
4443
+ var nodeStabilityAnalyzer2 = { ...nodeStabilityAnalyzer, pluginId: "core", version: "0.44.0" };
4444
+ var nodeSupersededAnalyzer2 = { ...nodeSupersededAnalyzer, pluginId: "core", version: "0.44.0" };
4445
+ var referenceBrokenAnalyzer2 = { ...referenceBrokenAnalyzer, pluginId: "core", version: "0.44.0" };
4446
+ var referenceRedundantAnalyzer2 = { ...referenceRedundantAnalyzer, pluginId: "core", version: "0.44.0" };
4447
+ var schemaViolationAnalyzer2 = { ...schemaViolationAnalyzer, pluginId: "core", version: "0.44.0" };
4448
+ var signalCollisionAnalyzer2 = { ...signalCollisionAnalyzer, pluginId: "core", version: "0.44.0" };
4449
+ var triggerCollisionAnalyzer2 = { ...triggerCollisionAnalyzer, pluginId: "core", version: "0.44.0" };
4450
+ var asciiFormatter2 = { ...asciiFormatter, pluginId: "core", version: "0.44.0" };
4451
+ var jsonFormatter2 = { ...jsonFormatter, pluginId: "core", version: "0.44.0" };
4452
+ var nodeBumpAction2 = { ...nodeBumpAction, pluginId: "core", version: "0.44.0" };
4453
+ var nodeSupersedeAction2 = { ...nodeSupersedeAction, pluginId: "core", version: "0.44.0" };
4454
+ var updateCheckHook2 = { ...updateCheckHook, pluginId: "core", version: "0.44.0" };
4415
4455
  var builtInBundles = [
4416
4456
  {
4417
4457
  id: "claude",
@@ -4943,6 +4983,341 @@ function defaultRuntimeContext() {
4943
4983
  return { cwd: process.cwd() };
4944
4984
  }
4945
4985
 
4986
+ // cli/telemetry/first-run-prompt.ts
4987
+ import { createInterface } from "readline/promises";
4988
+
4989
+ // cli/i18n/telemetry.texts.ts
4990
+ var TELEMETRY_PROMPT_TEXTS = {
4991
+ // Header + body of the one-time question (glyph `ℹ` added by the renderer).
4992
+ title: "Anonymous error and usage reporting",
4993
+ intro: [
4994
+ "skill-map can send anonymous reports to help fix bugs and decide what to",
4995
+ "build next. No personal information is ever sent: not your files or their",
4996
+ "contents, not your folder or home paths, not your settings."
4997
+ ],
4998
+ question: "Enable anonymous error and usage reporting?",
4999
+ answerYes: "[Y]es",
5000
+ answerNo: "[n]o",
5001
+ answerDetails: "[d]etails",
5002
+ // Disclosure shown on `[d]etails`, then the question is re-asked.
5003
+ detailsSentTitle: "Sent, only if you turn this on",
5004
+ detailsSent: [
5005
+ "crashes: error name, code, message, and a path-stripped stack trace",
5006
+ "usage: the command you ran and its flag names (never their values)",
5007
+ "usage: which built-in extractors ran, and which UI views you opened",
5008
+ "cli version, node major, os, arch, and a random anonymous id"
5009
+ ],
5010
+ detailsNeverTitle: "Never sent",
5011
+ detailsNever: [
5012
+ "your files, their contents, frontmatter, annotations",
5013
+ "absolute paths, hostname, your username, ip address",
5014
+ "your settings values or any flag values"
5015
+ ],
5016
+ detailsHint: "Turn error reports, CLI usage, and UI usage on or off independently in Settings, or force everything off with SKILL_MAP_TELEMETRY=0.",
5017
+ // Confirmation lines (glyph added by the renderer).
5018
+ enabled: "Telemetry on. Thanks. Turn error reports, CLI usage, and UI usage off independently in Settings.",
5019
+ disabled: "Telemetry off. You can turn any of it on later in Settings."
5020
+ };
5021
+
5022
+ // public-config.ts
5023
+ var SENTRY_DSN_NODE = "https://8b73dbb2563da4b77def12ce5ee46e75@o4511475590037504.ingest.de.sentry.io/4511475708002384";
5024
+ var POSTHOG_KEY_NODE = "phc_vMX3PcNeDsacWNg2hYEbKVXDijSWcjKFzabCkzU7RNEr";
5025
+ var POSTHOG_HOST = "https://eu.i.posthog.com";
5026
+
5027
+ // core/telemetry/scrub.ts
5028
+ var HOME_PLACEHOLDER = "<HOME>";
5029
+ var STRIPPED_ENVELOPE_KEYS = ["server_name", "user"];
5030
+ var HOME_PATTERNS = [
5031
+ // Windows: drive + Users + one user segment (back- or forward-slash).
5032
+ /[A-Za-z]:[\\/]Users[\\/][^\\/\s:*?"<>|]+/g,
5033
+ // POSIX user homes: /home/<user> or macOS /Users/<user>.
5034
+ /\/(?:home|Users)\/[^/\s:]+/g,
5035
+ // Root account home.
5036
+ /\/root(?=\/|\b)/g
5037
+ ];
5038
+ function scrubString(value) {
5039
+ let out = value;
5040
+ for (const pattern of HOME_PATTERNS) {
5041
+ out = out.replace(pattern, HOME_PLACEHOLDER);
5042
+ }
5043
+ return out;
5044
+ }
5045
+ function scrubEvent(event) {
5046
+ const walked = walk(event);
5047
+ if (walked !== null && typeof walked === "object" && !Array.isArray(walked)) {
5048
+ const record = walked;
5049
+ for (const key of STRIPPED_ENVELOPE_KEYS) {
5050
+ if (key in record) delete record[key];
5051
+ }
5052
+ }
5053
+ return walked;
5054
+ }
5055
+ function walk(value) {
5056
+ if (typeof value === "string") return scrubString(value);
5057
+ if (Array.isArray(value)) return value.map((item) => walk(item));
5058
+ if (value !== null && typeof value === "object") {
5059
+ const out = {};
5060
+ for (const [key, child] of Object.entries(value)) {
5061
+ out[key] = walk(child);
5062
+ }
5063
+ return out;
5064
+ }
5065
+ return value;
5066
+ }
5067
+
5068
+ // cli/telemetry/telemetry-env.ts
5069
+ var TELEMETRY_ENV_VAR = "SKILL_MAP_TELEMETRY_ENV";
5070
+ function resolveTelemetryEnv() {
5071
+ const raw = process.env[TELEMETRY_ENV_VAR];
5072
+ if (raw === void 0 || raw.trim() === "" || raw === "prod" || raw === "production") {
5073
+ return "prod";
5074
+ }
5075
+ return "dev";
5076
+ }
5077
+
5078
+ // cli/telemetry/sentry-init.ts
5079
+ var KILL_SWITCH_ENV = "SKILL_MAP_TELEMETRY";
5080
+ var sdk = null;
5081
+ function isCliDsnConfigured() {
5082
+ return SENTRY_DSN_NODE !== "";
5083
+ }
5084
+ function isTelemetryForcedOff() {
5085
+ return process.env[KILL_SWITCH_ENV] === "0";
5086
+ }
5087
+ function isTelemetryActive(dsn) {
5088
+ if (isTelemetryForcedOff()) return false;
5089
+ if (dsn === "") return false;
5090
+ return isErrorTelemetryEnabled();
5091
+ }
5092
+ async function initSentryCli(version, loadSdk = () => import("@sentry/node")) {
5093
+ if (sdk) return;
5094
+ if (!isTelemetryActive(SENTRY_DSN_NODE)) return;
5095
+ const Sentry = await loadSdk();
5096
+ Sentry.init({
5097
+ dsn: SENTRY_DSN_NODE,
5098
+ release: `@skill-map/cli@${version}`,
5099
+ environment: resolveTelemetryEnv(),
5100
+ // CLI and BFF share one Sentry project; the `surface` tag tells their
5101
+ // events apart in the shared issue stream.
5102
+ initialScope: { tags: { surface: "cli" } },
5103
+ // Errors only: do NOT register the OpenTelemetry ESM loader hooks. We
5104
+ // run no tracing / auto-instrumentation, and the hook calls the
5105
+ // deprecated `module.register()` (a `DEP0205` warning on Node >= 26 that
5106
+ // would print on every telemetry-on invocation). Disabling it keeps
5107
+ // stderr clean and skips the loader's startup cost.
5108
+ registerEsmLoaderHooks: false,
5109
+ defaultIntegrations: false,
5110
+ integrations: [
5111
+ Sentry.onUncaughtExceptionIntegration(),
5112
+ Sentry.onUnhandledRejectionIntegration()
5113
+ ],
5114
+ tracesSampleRate: 0,
5115
+ sendDefaultPii: false,
5116
+ beforeSend: (event) => scrubEvent(event)
5117
+ });
5118
+ sdk = Sentry;
5119
+ }
5120
+ function setTelemetryVerbTag(verb) {
5121
+ if (!sdk || verb === void 0 || verb === "") return;
5122
+ sdk.setTag("verb", verb);
5123
+ }
5124
+ async function closeSentryCli(timeoutMs = 2e3) {
5125
+ if (!sdk) return;
5126
+ try {
5127
+ await sdk.close(timeoutMs);
5128
+ } catch {
5129
+ }
5130
+ }
5131
+
5132
+ // cli/telemetry/usage-collector.ts
5133
+ var BUILT_IN_PLUGIN_IDS = /* @__PURE__ */ new Set([
5134
+ "claude",
5135
+ "antigravity",
5136
+ "openai",
5137
+ "agent-skills",
5138
+ "core"
5139
+ ]);
5140
+ var EXTERNAL_PLUGIN_PLACEHOLDER = "external_plugin";
5141
+ function qualifyExtensionForUsage(qualifiedId2) {
5142
+ const slash = qualifiedId2.indexOf("/");
5143
+ if (slash <= 0) return EXTERNAL_PLUGIN_PLACEHOLDER;
5144
+ const pluginId = qualifiedId2.slice(0, slash);
5145
+ return BUILT_IN_PLUGIN_IDS.has(pluginId) ? qualifiedId2 : EXTERNAL_PLUGIN_PLACEHOLDER;
5146
+ }
5147
+ function buildScanExtensionSet(executedExtensionIds) {
5148
+ const out = /* @__PURE__ */ new Set();
5149
+ for (const id of executedExtensionIds) {
5150
+ out.add(qualifyExtensionForUsage(id));
5151
+ }
5152
+ return [...out].sort();
5153
+ }
5154
+ function extractFlagNames(args2) {
5155
+ const out = /* @__PURE__ */ new Set();
5156
+ for (const arg of args2) {
5157
+ if (!arg.startsWith("-")) continue;
5158
+ const name = arg.replace(/^-+/, "").split("=")[0];
5159
+ if (name !== void 0 && name !== "") out.add(name);
5160
+ }
5161
+ return [...out].sort();
5162
+ }
5163
+ function cliVerbEventName(verb, knownVerbs) {
5164
+ return `cli.${knownVerbs.has(verb) ? verb : "unknown"}`;
5165
+ }
5166
+ function buildCliVerbProperties(flagNames, extensions) {
5167
+ const flags = [...new Set(flagNames)].sort();
5168
+ return extensions ? { flags, extensions } : { flags };
5169
+ }
5170
+ function envUsageProps(cliVersion) {
5171
+ return {
5172
+ cli_version: cliVersion,
5173
+ node_major: Number.parseInt(process.versions.node.split(".")[0] ?? "0", 10),
5174
+ os: process.platform,
5175
+ arch: process.arch,
5176
+ environment: resolveTelemetryEnv()
5177
+ };
5178
+ }
5179
+
5180
+ // cli/telemetry/posthog-init.ts
5181
+ var client = null;
5182
+ function isUsageKeyConfigured() {
5183
+ return POSTHOG_KEY_NODE !== "";
5184
+ }
5185
+ function isUsageCliTelemetryActive(key) {
5186
+ if (isTelemetryForcedOff()) return false;
5187
+ if (key === "") return false;
5188
+ return isUsageCliTelemetryEnabled();
5189
+ }
5190
+ function scrubUsageEvent(event) {
5191
+ return event === null ? null : scrubEvent(event);
5192
+ }
5193
+ async function initUsageCli(loadSdk = () => import("posthog-node")) {
5194
+ if (client) return;
5195
+ if (!isUsageCliTelemetryActive(POSTHOG_KEY_NODE)) return;
5196
+ const { PostHog } = await loadSdk();
5197
+ client = new PostHog(POSTHOG_KEY_NODE, {
5198
+ host: POSTHOG_HOST,
5199
+ // Second line of defense behind the project-level IP drop: the client
5200
+ // never attaches an IP or geo, and never autocaptures.
5201
+ disableGeoip: true,
5202
+ before_send: scrubUsageEvent
5203
+ });
5204
+ }
5205
+ function captureUsage(event, properties) {
5206
+ if (client === null) return;
5207
+ const distinctId = readAnonymousId();
5208
+ if (distinctId === null) return;
5209
+ client.capture({
5210
+ distinctId,
5211
+ event,
5212
+ properties: { ...envUsageProps(VERSION), ...properties }
5213
+ });
5214
+ }
5215
+ var pendingScanExtensions = null;
5216
+ function setScanExtensions(extensions) {
5217
+ pendingScanExtensions = extensions;
5218
+ }
5219
+ function captureCliInvocation(verb, flagNames, knownVerbs) {
5220
+ const extensions = pendingScanExtensions;
5221
+ pendingScanExtensions = null;
5222
+ captureUsage(cliVerbEventName(verb, knownVerbs), buildCliVerbProperties(flagNames, extensions));
5223
+ }
5224
+ async function flushUsageCli(timeoutMs = 2e3) {
5225
+ if (client === null) return;
5226
+ try {
5227
+ await client.shutdown(timeoutMs);
5228
+ } catch {
5229
+ }
5230
+ }
5231
+
5232
+ // cli/telemetry/first-run-prompt.ts
5233
+ function interpretConsentAnswer(raw) {
5234
+ const value = raw.trim().toLowerCase();
5235
+ if (value === "n" || value === "no") return "no";
5236
+ if (value === "d" || value === "details") return "details";
5237
+ return "yes";
5238
+ }
5239
+ function isPromptEligible(opts) {
5240
+ return opts.dsnConfigured && opts.isTTY && !opts.isCI && !opts.forcedOff && !opts.alreadyPrompted;
5241
+ }
5242
+ function liveGateInputs(stdout) {
5243
+ return {
5244
+ dsnConfigured: isCliDsnConfigured() || isUsageKeyConfigured(),
5245
+ isTTY: stdout.isTTY === true,
5246
+ isCI: Boolean(process.env["CI"]),
5247
+ forcedOff: isTelemetryForcedOff(),
5248
+ alreadyPrompted: hasTelemetryPromptBeenShown()
5249
+ };
5250
+ }
5251
+ function renderConsent(ansi) {
5252
+ const t = TELEMETRY_PROMPT_TEXTS;
5253
+ const answerLine = ` ${t.question} ${ansi.bold(t.answerYes)} ${t.answerNo} ${ansi.dim(t.answerDetails)} `;
5254
+ return {
5255
+ question: [
5256
+ ` ${ansi.cyan("\u2139")} ${ansi.bold(t.title)}`,
5257
+ ...t.intro.map((line) => ` ${line}`),
5258
+ "",
5259
+ answerLine
5260
+ ].join("\n"),
5261
+ reprompt: answerLine,
5262
+ details: [
5263
+ "",
5264
+ ` ${t.detailsSentTitle}`,
5265
+ ...t.detailsSent.map((line) => ` ${ansi.dim("\u2192")} ${line}`),
5266
+ ` ${t.detailsNeverTitle}`,
5267
+ ...t.detailsNever.map((line) => ` ${ansi.red("\u2715")} ${line}`),
5268
+ "",
5269
+ ` ${ansi.dim(t.detailsHint)}`,
5270
+ ""
5271
+ ].join("\n"),
5272
+ enabled: ` ${ansi.green("\u2713")} ${t.enabled}
5273
+ `,
5274
+ disabled: ` ${ansi.cyan("\u2139")} ${t.disabled}
5275
+ `
5276
+ };
5277
+ }
5278
+ async function readConsentDecision(rl, stdout, rendered) {
5279
+ let answer = interpretConsentAnswer(await rl.question(rendered.question));
5280
+ while (answer === "details") {
5281
+ stdout.write(rendered.details);
5282
+ answer = interpretConsentAnswer(await rl.question(rendered.reprompt));
5283
+ }
5284
+ return answer === "yes";
5285
+ }
5286
+ async function runConsentPrompt(stdin, stdout, nowMs) {
5287
+ const rendered = renderConsent(
5288
+ ansiFor({ isTTY: stdout.isTTY === true, noColorFlag: false })
5289
+ );
5290
+ const rl = createInterface({ input: stdin, output: stdout });
5291
+ try {
5292
+ const consented = await readConsentDecision(rl, stdout, rendered);
5293
+ writeUserSettings({
5294
+ telemetry: {
5295
+ errorsEnabled: consented,
5296
+ usageCliEnabled: consented,
5297
+ usageUiEnabled: consented,
5298
+ promptedAt: nowMs
5299
+ }
5300
+ });
5301
+ if (consented) ensureAnonymousId();
5302
+ stdout.write(consented ? rendered.enabled : rendered.disabled);
5303
+ } catch {
5304
+ } finally {
5305
+ rl.close();
5306
+ }
5307
+ }
5308
+ async function maybeRunFirstRunPrompt({
5309
+ stdin = process.stdin,
5310
+ stdout = process.stdout,
5311
+ nowMs = Date.now()
5312
+ } = {}) {
5313
+ if (!isPromptEligible(liveGateInputs(stdout))) return;
5314
+ if (!hasSeenFirstRun()) {
5315
+ writeUserSettings({ telemetry: { firstRunAt: nowMs } });
5316
+ return;
5317
+ }
5318
+ await runConsentPrompt(stdin, stdout, nowMs);
5319
+ }
5320
+
4946
5321
  // cli/commands/bump.ts
4947
5322
  import { Command as Command2, Option as Option2 } from "clipanion";
4948
5323
 
@@ -5625,13 +6000,13 @@ var CONSENT_TEXTS = {
5625
6000
  };
5626
6001
 
5627
6002
  // cli/util/confirm.ts
5628
- import { createInterface } from "readline";
6003
+ import { createInterface as createInterface2 } from "readline";
5629
6004
  var YES_PATTERN = new RegExp(UTIL_TEXTS.confirmYesPatternSource, "i");
5630
6005
  var NO_PATTERN = new RegExp(UTIL_TEXTS.confirmNoPatternSource, "i");
5631
6006
  async function confirm(question, streams, opts) {
5632
6007
  const defaultAnswer = opts?.defaultAnswer ?? "no";
5633
6008
  const suffix = defaultAnswer === "yes" ? UTIL_TEXTS.confirmPromptSuffixDefaultYes : UTIL_TEXTS.confirmPromptSuffix;
5634
- const rl = createInterface({ input: streams.stdin, output: streams.stderr });
6009
+ const rl = createInterface2({ input: streams.stdin, output: streams.stderr });
5635
6010
  try {
5636
6011
  const answer = await new Promise(
5637
6012
  (resolveP) => rl.question(`${question}${suffix}`, resolveP)
@@ -7904,7 +8279,7 @@ var SqliteStorageAdapter = class {
7904
8279
  /**
7905
8280
  * Access the underlying Kysely instance.
7906
8281
  *
7907
- * Test-only escape hatch (per AGENTS.md § Kernel boundaries, tests
8282
+ * Test-only escape hatch (per context/kernel.md §Kernel boundaries, tests
7908
8283
  * are the documented exception). CLI commands MUST consume the
7909
8284
  * adapter through the namespaced port surfaces (`port.<namespace>.*`
7910
8285
  * or `port.transaction(...)`); reaching for this getter from a
@@ -10243,8 +10618,8 @@ var DEFAULT_READ_CONFIG = Object.freeze({
10243
10618
  });
10244
10619
  function resolveProviderWalk(provider) {
10245
10620
  if (provider.walk) {
10246
- const walk2 = provider.walk.bind(provider);
10247
- return walk2;
10621
+ const walk3 = provider.walk.bind(provider);
10622
+ return walk3;
10248
10623
  }
10249
10624
  const read = provider.read ?? DEFAULT_READ_CONFIG;
10250
10625
  return (roots, options) => {
@@ -13870,8 +14245,7 @@ var HelpCommand = class extends Command15 {
13870
14245
 
13871
14246
  Formats:
13872
14247
  human (default): pretty terminal output.
13873
- md : canonical markdown. context/cli-reference.md is
13874
- regenerated from this and CI fails on drift.
14248
+ md : canonical markdown for documentation.
13875
14249
  json : structured surface dump per spec/cli-contract.md.
13876
14250
  `
13877
14251
  });
@@ -15508,11 +15882,11 @@ import { join as join13, relative as relative4, sep as sep4 } from "path";
15508
15882
  function discoverOrphanSidecars(roots, shouldSkip) {
15509
15883
  const out = [];
15510
15884
  for (const root of roots) {
15511
- walk(root, root, shouldSkip ?? (() => false), out);
15885
+ walk2(root, root, shouldSkip ?? (() => false), out);
15512
15886
  }
15513
15887
  return out;
15514
15888
  }
15515
- function walk(root, current, shouldSkip, out) {
15889
+ function walk2(root, current, shouldSkip, out) {
15516
15890
  let entries;
15517
15891
  try {
15518
15892
  entries = readdirSync7(current, { withFileTypes: true, encoding: "utf8" });
@@ -15525,7 +15899,7 @@ function walk(root, current, shouldSkip, out) {
15525
15899
  if (shouldSkip(rel)) continue;
15526
15900
  if (entry.isSymbolicLink()) continue;
15527
15901
  if (entry.isDirectory()) {
15528
- walk(root, full, shouldSkip, out);
15902
+ walk2(root, full, shouldSkip, out);
15529
15903
  continue;
15530
15904
  }
15531
15905
  if (!entry.isFile()) continue;
@@ -16149,7 +16523,8 @@ function buildPostWalkTransformCtx(providers, nodes, activeProvider) {
16149
16523
  const reservedNodePaths = buildReservedNodePaths(
16150
16524
  nodes,
16151
16525
  kindRegistry,
16152
- reservedNamesByProviderKind
16526
+ reservedNamesByProviderKind,
16527
+ activeProvider
16153
16528
  );
16154
16529
  return { kindRegistry, providerResolution, activeProvider, reservedNodePaths };
16155
16530
  }
@@ -16180,19 +16555,23 @@ function indexReservedNames(provider, out) {
16180
16555
  }
16181
16556
  }
16182
16557
  }
16183
- function buildReservedNodePaths(nodes, kindRegistry, reservedNamesByProviderKind) {
16558
+ function buildReservedNodePaths(nodes, kindRegistry, reservedNamesByProviderKind, activeProvider) {
16184
16559
  const out = /* @__PURE__ */ new Set();
16185
16560
  for (const node of nodes) {
16186
- const key = `${node.provider}/${node.kind}`;
16187
- const reservedSet = reservedNamesByProviderKind.get(key);
16188
- if (!reservedSet || reservedSet.size === 0) continue;
16189
- const ids = deriveNodeIdentifiers(node, kindRegistry.get(key));
16190
- if (ids.some((id) => reservedSet.has(id))) {
16561
+ const selfKey = `${node.provider}/${node.kind}`;
16562
+ const selfReserved = reservedNamesByProviderKind.get(selfKey);
16563
+ const lensReserved = activeProvider && activeProvider !== node.provider ? reservedNamesByProviderKind.get(`${activeProvider}/${node.kind}`) : void 0;
16564
+ if (!hasEntries(selfReserved) && !hasEntries(lensReserved)) continue;
16565
+ const ids = deriveNodeIdentifiers(node, kindRegistry.get(selfKey));
16566
+ if (ids.some((id) => selfReserved?.has(id) === true || lensReserved?.has(id) === true)) {
16191
16567
  out.add(node.path);
16192
16568
  }
16193
16569
  }
16194
16570
  return out;
16195
16571
  }
16572
+ function hasEntries(set) {
16573
+ return set !== void 0 && set.size > 0;
16574
+ }
16196
16575
  function buildScanSetup(options) {
16197
16576
  const start = Date.now();
16198
16577
  const emitter = options.emitter ?? new InMemoryProgressEmitter();
@@ -16754,7 +17133,7 @@ function safeStat(path) {
16754
17133
  }
16755
17134
 
16756
17135
  // core/runtime/active-provider-bootstrap.ts
16757
- import { createInterface as createInterface2 } from "readline";
17136
+ import { createInterface as createInterface3 } from "readline";
16758
17137
  import { isAbsolute as isAbsolute9, join as join16 } from "path";
16759
17138
  async function bootstrapActiveProvider(opts) {
16760
17139
  const fromCwd = resolveActiveProvider(opts.cwd, opts.providers);
@@ -16910,7 +17289,7 @@ async function promptForLens(detected, stdin, stderr, warnGlyph) {
16910
17289
  );
16911
17290
  }
16912
17291
  stderr.write(lines.join("\n") + "\n");
16913
- const rl = createInterface2({ input: stdin, output: stderr });
17292
+ const rl = createInterface3({ input: stdin, output: stderr });
16914
17293
  try {
16915
17294
  const answer = await new Promise(
16916
17295
  (resolveP) => rl.question(SCAN_RUNNER_TEXTS.activeProviderPromptInput, resolveP)
@@ -16929,7 +17308,7 @@ async function promptForLens(detected, stdin, stderr, warnGlyph) {
16929
17308
 
16930
17309
  // core/sqlite/db-drift-reset.ts
16931
17310
  import { existsSync as existsSync23 } from "fs";
16932
- import { createInterface as createInterface3 } from "readline";
17311
+ import { createInterface as createInterface4 } from "readline";
16933
17312
  import { DatabaseSync as DatabaseSync7 } from "node:sqlite";
16934
17313
 
16935
17314
  // core/sqlite/i18n/db-drift.texts.ts
@@ -16996,7 +17375,7 @@ async function askDriftReset(dbVersion, policy) {
16996
17375
  hint: dim(DB_DRIFT_TEXTS.driftPromptHint)
16997
17376
  })
16998
17377
  );
16999
- const rl = createInterface3({ input: policy.stdin, output: policy.stderr });
17378
+ const rl = createInterface4({ input: policy.stdin, output: policy.stderr });
17000
17379
  try {
17001
17380
  const answer = await new Promise(
17002
17381
  (resolveP) => rl.question(DB_DRIFT_TEXTS.driftPromptQuestion, resolveP)
@@ -17032,9 +17411,9 @@ async function runScanForCommand(opts) {
17032
17411
  const { cfg, ignoreFilter, strict, effectiveRoots } = scanInputs;
17033
17412
  let referenceablePaths;
17034
17413
  if (cfg.scan.referencePaths.length > 0) {
17035
- const walk2 = walkReferencePaths(cfg.scan.referencePaths, ctx.cwd);
17036
- referenceablePaths = walk2.paths;
17037
- emitReferenceWalkAdvisory(walk2, opts);
17414
+ const walk3 = walkReferencePaths(cfg.scan.referencePaths, ctx.cwd);
17415
+ referenceablePaths = walk3.paths;
17416
+ emitReferenceWalkAdvisory(walk3, opts);
17038
17417
  }
17039
17418
  const loadPrior = makePriorLoader(opts.noBuiltIns, strict);
17040
17419
  const jobsDir = defaultProjectJobsDir(ctx);
@@ -17096,11 +17475,11 @@ async function resolveActiveLens(opts, ctx, effectiveRoots, pluginRuntime, provi
17096
17475
  });
17097
17476
  return { kind: "ok", activeProvider: bootstrap.activeProvider };
17098
17477
  }
17099
- function emitReferenceWalkAdvisory(walk2, opts) {
17100
- if (walk2.truncated) {
17478
+ function emitReferenceWalkAdvisory(walk3, opts) {
17479
+ if (walk3.truncated) {
17101
17480
  opts.printer.warn(SCAN_RUNNER_TEXTS.referenceWalkTruncated);
17102
17481
  }
17103
- for (const missing of walk2.missingRoots) {
17482
+ for (const missing of walk3.missingRoots) {
17104
17483
  opts.printer.warn(
17105
17484
  tx(SCAN_RUNNER_TEXTS.referenceWalkMissingRoot, { path: missing })
17106
17485
  );
@@ -17279,7 +17658,8 @@ async function runPersistPath(opts, dbPath, jobsDir, strict, loadPrior, runScanW
17279
17658
  renameOps: outcome.renameOps,
17280
17659
  persistedTo: dbPath,
17281
17660
  dbPath,
17282
- strict
17661
+ strict,
17662
+ executedExtensionIds: outcome.extractorRuns.map((run) => run.extractorId)
17283
17663
  };
17284
17664
  }
17285
17665
  async function runEphemeralPath(opts, dbPath, strict, loadPrior, runScanWith) {
@@ -17297,7 +17677,8 @@ async function runEphemeralPath(opts, dbPath, strict, loadPrior, runScanWith) {
17297
17677
  renameOps: scanned.renameOps,
17298
17678
  persistedTo: null,
17299
17679
  dbPath,
17300
- strict
17680
+ strict,
17681
+ executedExtensionIds: scanned.extractorRuns.map((run) => run.extractorId)
17301
17682
  };
17302
17683
  } catch (err) {
17303
17684
  return { kind: "scan-error", message: formatErrorMessage(err) };
@@ -20221,6 +20602,8 @@ var TogglePluginsBase = class extends SmCommand {
20221
20602
  targets = lockError;
20222
20603
  const keys = expandToKeys(targets);
20223
20604
  await this.#persistKeys(keys, enabled);
20605
+ const set = buildScanExtensionSet(keys);
20606
+ captureUsage("plugin.apply", enabled ? { enabled: set, disabled: [] } : { enabled: [], disabled: set });
20224
20607
  this.#renderSuccess(keys, enabled);
20225
20608
  return ExitCode.Ok;
20226
20609
  }
@@ -21183,6 +21566,29 @@ function stripFrontmatterFence(text) {
21183
21566
  }
21184
21567
  var REFRESH_COMMANDS = [RefreshCommand];
21185
21568
 
21569
+ // cli/i18n/intentional-fail.texts.ts
21570
+ var INTENTIONAL_FAIL_TEXTS = {
21571
+ triggering: "Triggering an intentional uncaught error to exercise Sentry error reporting...",
21572
+ errorMessage: "skill-map intentional failure (Sentry self-test)"
21573
+ };
21574
+
21575
+ // cli/commands/intentional-fail.ts
21576
+ var IntentionalFailCommand = class extends SmCommand {
21577
+ static paths = [["intentional-fail"]];
21578
+ // No `static usage` on purpose: that is what keeps the verb out of every
21579
+ // help / reference surface Clipanion drives from command definitions.
21580
+ // A self-test crash has no meaningful "done in <...>" line.
21581
+ emitElapsed = false;
21582
+ async run() {
21583
+ this.printer.warn(INTENTIONAL_FAIL_TEXTS.triggering);
21584
+ setTimeout(() => {
21585
+ throw new Error(INTENTIONAL_FAIL_TEXTS.errorMessage);
21586
+ }, 0);
21587
+ await new Promise((resolve40) => setTimeout(resolve40, 5e3));
21588
+ return 1;
21589
+ }
21590
+ };
21591
+
21186
21592
  // cli/commands/scan.ts
21187
21593
  import { Command as Command31, Option as Option29 } from "clipanion";
21188
21594
 
@@ -21414,9 +21820,9 @@ function createWatcherRuntime(opts) {
21414
21820
  overrideMaxNodes: opts.maxNodesOverride ?? null
21415
21821
  };
21416
21822
  if (cfg.scan.referencePaths.length > 0) {
21417
- const walk2 = walkReferencePaths(cfg.scan.referencePaths, cwd);
21418
- if (walk2.paths.size > 0) {
21419
- runOptions.referenceablePaths = walk2.paths;
21823
+ const walk3 = walkReferencePaths(cfg.scan.referencePaths, cwd);
21824
+ if (walk3.paths.size > 0) {
21825
+ runOptions.referenceablePaths = walk3.paths;
21420
21826
  runOptions.cwd = cwd;
21421
21827
  }
21422
21828
  }
@@ -21994,7 +22400,11 @@ var ScanCommand = class extends SmCommand {
21994
22400
  style,
21995
22401
  ...parsedMaxNodes.value !== void 0 ? { maxNodes: parsedMaxNodes.value } : {}
21996
22402
  });
21997
- return outcome.kind === "ok" ? this.renderOutcome(outcome.result, outcome.persistedTo, outcome.dbPath, outcome.strict) : this.renderFailure(outcome);
22403
+ if (outcome.kind === "ok") {
22404
+ setScanExtensions(buildScanExtensionSet(outcome.executedExtensionIds));
22405
+ return this.renderOutcome(outcome.result, outcome.persistedTo, outcome.dbPath, outcome.strict);
22406
+ }
22407
+ return this.renderFailure(outcome);
21998
22408
  }
21999
22409
  /**
22000
22410
  * Parse `--max-nodes <N>`. Returns either the integer value (or
@@ -22512,7 +22922,7 @@ import { WebSocketServer } from "ws";
22512
22922
  // server/app.ts
22513
22923
  import { Hono } from "hono";
22514
22924
  import { bodyLimit } from "hono/body-limit";
22515
- import { HTTPException as HTTPException15 } from "hono/http-exception";
22925
+ import { HTTPException as HTTPException16 } from "hono/http-exception";
22516
22926
 
22517
22927
  // core/config/service.ts
22518
22928
  var ConfigService = class {
@@ -22749,6 +23159,10 @@ var SERVER_TEXTS = {
22749
23159
  preferencesBodyEmpty: "Request body must contain at least one known preference (e.g. `updateCheck.enabled`).",
22750
23160
  preferencesUpdateCheckNotObject: '`updateCheck` must be an object (e.g. `{"updateCheck": {"enabled": false}}`).',
22751
23161
  preferencesUpdateCheckEnabledNotBoolean: "`updateCheck.enabled` must be a boolean.",
23162
+ preferencesTelemetryNotObject: '`telemetry` must be an object (e.g. `{"telemetry": {"errorsEnabled": true}}`).',
23163
+ preferencesTelemetryErrorsEnabledNotBoolean: "`telemetry.errorsEnabled` must be a boolean.",
23164
+ preferencesTelemetryUsageCliEnabledNotBoolean: "`telemetry.usageCliEnabled` must be a boolean.",
23165
+ preferencesTelemetryUsageUiEnabledNotBoolean: "`telemetry.usageUiEnabled` must be a boolean.",
22752
23166
  preferencesPersistFailed: "Could not persist preferences: {{message}}",
22753
23167
  // ---- project-preferences route (routes/project-preferences.ts) ----------
22754
23168
  //
@@ -22908,6 +23322,53 @@ function createSecurityHeaders() {
22908
23322
  };
22909
23323
  }
22910
23324
 
23325
+ // server/telemetry/sentry.ts
23326
+ import { HTTPException } from "hono/http-exception";
23327
+ var sdk2 = null;
23328
+ async function initSentryBff(version, loadSdk = () => import("@sentry/node")) {
23329
+ if (sdk2) return;
23330
+ if (!isTelemetryActive(SENTRY_DSN_NODE)) return;
23331
+ const Sentry = await loadSdk();
23332
+ Sentry.init({
23333
+ dsn: SENTRY_DSN_NODE,
23334
+ release: `@skill-map/cli@${version}`,
23335
+ environment: resolveTelemetryEnv(),
23336
+ // Shared project with the CLI; the `surface` tag separates the two.
23337
+ initialScope: { tags: { surface: "bff" } },
23338
+ // Errors only: skip the OpenTelemetry ESM loader hooks (we run no
23339
+ // tracing). They call the deprecated `module.register()` (a Node >= 26
23340
+ // DEP0205 warning) and add startup cost for nothing here.
23341
+ registerEsmLoaderHooks: false,
23342
+ defaultIntegrations: false,
23343
+ integrations: [],
23344
+ tracesSampleRate: 0,
23345
+ sendDefaultPii: false,
23346
+ beforeSend: (event) => scrubEvent(event)
23347
+ });
23348
+ sdk2 = Sentry;
23349
+ }
23350
+ function shouldCaptureError(err) {
23351
+ if (err instanceof HTTPException) return err.status >= 500;
23352
+ return true;
23353
+ }
23354
+ function createSentryRequestCapture() {
23355
+ return async function sentryRequestCapture(c, next) {
23356
+ try {
23357
+ await next();
23358
+ } catch (err) {
23359
+ const client2 = sdk2;
23360
+ if (client2 && shouldCaptureError(err)) {
23361
+ client2.withScope((scope) => {
23362
+ scope.setTag("route", c.req.routePath ?? c.req.path);
23363
+ scope.setTag("method", c.req.method);
23364
+ client2.captureException(err);
23365
+ });
23366
+ }
23367
+ throw err;
23368
+ }
23369
+ };
23370
+ }
23371
+
22911
23372
  // server/routes/annotations.ts
22912
23373
  var ENVELOPE_KIND = "annotations.registered";
22913
23374
  function registerAnnotationsRoute(app, deps) {
@@ -22924,10 +23385,10 @@ function registerAnnotationsRoute(app, deps) {
22924
23385
  }
22925
23386
 
22926
23387
  // server/routes/contributions.ts
22927
- import { HTTPException as HTTPException2 } from "hono/http-exception";
23388
+ import { HTTPException as HTTPException3 } from "hono/http-exception";
22928
23389
 
22929
23390
  // server/util/parse-query.ts
22930
- import { HTTPException } from "hono/http-exception";
23391
+ import { HTTPException as HTTPException2 } from "hono/http-exception";
22931
23392
  function parseCsv(value) {
22932
23393
  if (value === void 0) return [];
22933
23394
  return value.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
@@ -22936,7 +23397,7 @@ function parsePagination(query, defaults) {
22936
23397
  const offset = parseNonNegativeInt(query.offset, "offset", 0);
22937
23398
  const limit = parseNonNegativeInt(query.limit, "limit", defaults.limit);
22938
23399
  if (limit > defaults.max) {
22939
- throw new HTTPException(400, {
23400
+ throw new HTTPException2(400, {
22940
23401
  message: tx(SERVER_TEXTS.paginationLimitTooLarge, {
22941
23402
  value: limit,
22942
23403
  max: defaults.max
@@ -22950,7 +23411,7 @@ function parseBooleanFlag(value) {
22950
23411
  }
22951
23412
  function parseRequiredString(value, name) {
22952
23413
  if (typeof value !== "string" || value.length === 0) {
22953
- throw new HTTPException(400, {
23414
+ throw new HTTPException2(400, {
22954
23415
  message: tx(SERVER_TEXTS.queryRequiredString, { name })
22955
23416
  });
22956
23417
  }
@@ -22961,7 +23422,7 @@ function parseNonNegativeInt(raw, name, fallback) {
22961
23422
  const trimmed = raw.trim();
22962
23423
  const parsed = Number.parseInt(trimmed, 10);
22963
23424
  if (!Number.isInteger(parsed) || parsed < 0 || String(parsed) !== trimmed) {
22964
- throw new HTTPException(400, {
23425
+ throw new HTTPException2(400, {
22965
23426
  message: tx(SERVER_TEXTS.paginationInvalidInteger, { name, value: raw })
22966
23427
  });
22967
23428
  }
@@ -22972,7 +23433,7 @@ function parseNonNegativeInt(raw, name, fallback) {
22972
23433
  var QUALIFIED_ID_SEGMENT = /^[A-Za-z0-9._-]+$/;
22973
23434
  function parseQualifiedIdSegment(value, name) {
22974
23435
  if (!QUALIFIED_ID_SEGMENT.test(value)) {
22975
- throw new HTTPException2(400, {
23436
+ throw new HTTPException3(400, {
22976
23437
  message: tx(SERVER_TEXTS.qualifiedIdMalformed, {
22977
23438
  name,
22978
23439
  value: sanitizeForTerminal(value)
@@ -23002,7 +23463,7 @@ function registerContributionsRoutes(app, deps) {
23002
23463
  (e) => e.pluginId === pluginId && e.extensionId === extensionId && e.contributionId === contributionId
23003
23464
  );
23004
23465
  if (!catalogEntry) {
23005
- throw new HTTPException2(404, {
23466
+ throw new HTTPException3(404, {
23006
23467
  message: tx(SERVER_TEXTS.contributionUnknown, {
23007
23468
  pluginId: sanitizeForTerminal(pluginId),
23008
23469
  extensionId: sanitizeForTerminal(extensionId),
@@ -23033,7 +23494,7 @@ function registerContributionsRoutes(app, deps) {
23033
23494
  }
23034
23495
 
23035
23496
  // server/routes/config.ts
23036
- import { HTTPException as HTTPException3 } from "hono/http-exception";
23497
+ import { HTTPException as HTTPException4 } from "hono/http-exception";
23037
23498
 
23038
23499
  // server/envelope.ts
23039
23500
  var REST_ENVELOPE_SCHEMA_VERSION = "1";
@@ -23072,7 +23533,7 @@ function registerConfigRoute(app, deps) {
23072
23533
  try {
23073
23534
  loaded = deps.configService.get();
23074
23535
  } catch (err) {
23075
- throw new HTTPException3(500, { message: formatErrorMessage(err) });
23536
+ throw new HTTPException4(500, { message: formatErrorMessage(err) });
23076
23537
  }
23077
23538
  for (const warn of loaded.warnings) {
23078
23539
  log.warn(sanitizeForTerminal(warn));
@@ -23090,7 +23551,7 @@ function registerConfigRoute(app, deps) {
23090
23551
  }
23091
23552
 
23092
23553
  // server/routes/favorites.ts
23093
- import { HTTPException as HTTPException4 } from "hono/http-exception";
23554
+ import { HTTPException as HTTPException5 } from "hono/http-exception";
23094
23555
 
23095
23556
  // server/path-codec.ts
23096
23557
  var PathCodecError = class extends Error {
@@ -23130,7 +23591,7 @@ function registerFavoritesRoutes(app, deps) {
23130
23591
  }
23131
23592
  );
23132
23593
  if (!result || !result.found) {
23133
- throw new HTTPException4(404, {
23594
+ throw new HTTPException5(404, {
23134
23595
  message: tx(SERVER_TEXTS.nodeNotFound, { path: sanitizeForTerminal(nodePath) })
23135
23596
  });
23136
23597
  }
@@ -23150,14 +23611,14 @@ function decodePath(pathB64) {
23150
23611
  return decodeNodePath(pathB64);
23151
23612
  } catch (err) {
23152
23613
  if (err instanceof PathCodecError) {
23153
- throw new HTTPException4(404, { message: SERVER_TEXTS.pathB64Malformed });
23614
+ throw new HTTPException5(404, { message: SERVER_TEXTS.pathB64Malformed });
23154
23615
  }
23155
23616
  throw err;
23156
23617
  }
23157
23618
  }
23158
23619
 
23159
23620
  // server/routes/graph.ts
23160
- import { HTTPException as HTTPException5 } from "hono/http-exception";
23621
+ import { HTTPException as HTTPException6 } from "hono/http-exception";
23161
23622
  var DEFAULT_FORMAT2 = "ascii";
23162
23623
  var FORMAT_ID_PATTERN = /^[a-z0-9-]+$/;
23163
23624
  var FORMAT_ID_MAX = 32;
@@ -23165,7 +23626,7 @@ function registerGraphRoute(app, deps) {
23165
23626
  app.get("/api/graph", async (c) => {
23166
23627
  const format = c.req.query("format") ?? DEFAULT_FORMAT2;
23167
23628
  if (format.length > FORMAT_ID_MAX || !FORMAT_ID_PATTERN.test(format)) {
23168
- throw new HTTPException5(400, {
23629
+ throw new HTTPException6(400, {
23169
23630
  // Sanitize defensively, the regex above already rejects ANSI
23170
23631
  // and control bytes, but the message interpolates user input
23171
23632
  // and the BFF mirrors error envelopes into the server log.
@@ -23181,7 +23642,7 @@ function registerGraphRoute(app, deps) {
23181
23642
  const formatter = formatters.find((f) => f.formatId === format);
23182
23643
  if (!formatter) {
23183
23644
  const available = formatters.map((f) => f.formatId).sort().join(", ");
23184
- throw new HTTPException5(400, {
23645
+ throw new HTTPException6(400, {
23185
23646
  message: tx(SERVER_TEXTS.graphUnknownFormat, {
23186
23647
  format,
23187
23648
  available: available || "(none)"
@@ -23354,7 +23815,7 @@ function registerLinksRoute(app, deps) {
23354
23815
  }
23355
23816
 
23356
23817
  // server/routes/nodes.ts
23357
- import { HTTPException as HTTPException6 } from "hono/http-exception";
23818
+ import { HTTPException as HTTPException7 } from "hono/http-exception";
23358
23819
 
23359
23820
  // server/node-body.ts
23360
23821
  import { constants as fsConstants2 } from "fs";
@@ -23465,7 +23926,7 @@ function registerNodesRoutes(app, deps) {
23465
23926
  nodePath = decodeNodePath(pathB64);
23466
23927
  } catch (err) {
23467
23928
  if (err instanceof PathCodecError) {
23468
- throw new HTTPException6(404, { message: SERVER_TEXTS.pathB64Malformed });
23929
+ throw new HTTPException7(404, { message: SERVER_TEXTS.pathB64Malformed });
23469
23930
  }
23470
23931
  throw err;
23471
23932
  }
@@ -23497,7 +23958,7 @@ function registerNodesRoutes(app, deps) {
23497
23958
  const contributions = result?.contributions ?? [];
23498
23959
  const tags = result?.tags ?? [];
23499
23960
  if (!bundle) {
23500
- throw new HTTPException6(404, {
23961
+ throw new HTTPException7(404, {
23501
23962
  message: tx(SERVER_TEXTS.nodeNotFound, { path: nodePath })
23502
23963
  });
23503
23964
  }
@@ -23607,11 +24068,11 @@ async function groupContributionsByPath(rows) {
23607
24068
  }
23608
24069
 
23609
24070
  // server/routes/plugins.ts
23610
- import { HTTPException as HTTPException8 } from "hono/http-exception";
24071
+ import { HTTPException as HTTPException9 } from "hono/http-exception";
23611
24072
 
23612
24073
  // server/util/parse-body.ts
23613
24074
  import { Ajv2020 as Ajv20207 } from "ajv/dist/2020.js";
23614
- import { HTTPException as HTTPException7 } from "hono/http-exception";
24075
+ import { HTTPException as HTTPException8 } from "hono/http-exception";
23615
24076
  function makeBodyValidator(schema, messages) {
23616
24077
  const ajv = new Ajv20207({ strict: false, allErrors: false });
23617
24078
  const validate = ajv.compile(schema);
@@ -23620,16 +24081,16 @@ function makeBodyValidator(schema, messages) {
23620
24081
  try {
23621
24082
  raw = await req.json();
23622
24083
  } catch {
23623
- throw new HTTPException7(400, { message: messages.notJson });
24084
+ throw new HTTPException8(400, { message: messages.notJson });
23624
24085
  }
23625
24086
  if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
23626
- throw new HTTPException7(400, { message: messages.notObject });
24087
+ throw new HTTPException8(400, { message: messages.notObject });
23627
24088
  }
23628
24089
  if (validate(raw)) {
23629
24090
  return raw;
23630
24091
  }
23631
24092
  const message = resolveErrorMessage(validate.errors, messages);
23632
- throw new HTTPException7(400, { message });
24093
+ throw new HTTPException8(400, { message });
23633
24094
  };
23634
24095
  }
23635
24096
  function resolveErrorMessage(errors, messages) {
@@ -23730,18 +24191,18 @@ function registerPluginsRoute(app, deps) {
23730
24191
  app.patch("/api/plugins/:id", async (c) => {
23731
24192
  const id = c.req.param("id");
23732
24193
  if (id.includes("/")) {
23733
- throw new HTTPException8(400, {
24194
+ throw new HTTPException9(400, {
23734
24195
  message: tx(SERVER_TEXTS.pluginsCascadeRouteQualifiedRejected, { id })
23735
24196
  });
23736
24197
  }
23737
24198
  const handle = findHandle(id, deps);
23738
24199
  if (!handle) {
23739
- throw new HTTPException8(404, {
24200
+ throw new HTTPException9(404, {
23740
24201
  message: tx(SERVER_TEXTS.pluginsUnknown, { id })
23741
24202
  });
23742
24203
  }
23743
24204
  if (isPluginLocked(id)) {
23744
- throw new HTTPException8(403, {
24205
+ throw new HTTPException9(403, {
23745
24206
  message: tx(SERVER_TEXTS.pluginsLocked, { id })
23746
24207
  });
23747
24208
  }
@@ -23755,18 +24216,18 @@ function registerPluginsRoute(app, deps) {
23755
24216
  const extensionId = c.req.param("extensionId");
23756
24217
  const handle = findHandle(bundleId, deps);
23757
24218
  if (!handle) {
23758
- throw new HTTPException8(404, {
24219
+ throw new HTTPException9(404, {
23759
24220
  message: tx(SERVER_TEXTS.pluginsUnknown, { id: bundleId })
23760
24221
  });
23761
24222
  }
23762
24223
  if (!hasExtension(handle, extensionId)) {
23763
- throw new HTTPException8(404, {
24224
+ throw new HTTPException9(404, {
23764
24225
  message: tx(SERVER_TEXTS.pluginsExtensionUnknown, { bundleId, extensionId })
23765
24226
  });
23766
24227
  }
23767
24228
  const qualified = qualifiedExtensionId(bundleId, extensionId);
23768
24229
  if (isPluginLocked(qualified) || isPluginLocked(bundleId)) {
23769
- throw new HTTPException8(403, {
24230
+ throw new HTTPException9(403, {
23770
24231
  message: tx(SERVER_TEXTS.pluginsExtensionLocked, { bundleId, extensionId })
23771
24232
  });
23772
24233
  }
@@ -24042,7 +24503,7 @@ function hasExtension(handle, extensionId) {
24042
24503
  }
24043
24504
 
24044
24505
  // server/routes/preferences.ts
24045
- import { HTTPException as HTTPException9 } from "hono/http-exception";
24506
+ import { HTTPException as HTTPException10 } from "hono/http-exception";
24046
24507
  function registerPreferencesRoute(app, _deps) {
24047
24508
  app.get("/api/preferences", (c) => {
24048
24509
  return c.json(buildEnvelope());
@@ -24055,20 +24516,43 @@ function registerPreferencesRoute(app, _deps) {
24055
24516
  }
24056
24517
  function buildEnvelope() {
24057
24518
  return {
24058
- updateCheck: { enabled: isUpdateCheckEnabled() }
24519
+ updateCheck: { enabled: isUpdateCheckEnabled() },
24520
+ telemetry: {
24521
+ errorsEnabled: isErrorTelemetryEnabled(),
24522
+ usageCliEnabled: isUsageCliTelemetryEnabled(),
24523
+ usageUiEnabled: isUsageUiTelemetryEnabled(),
24524
+ anonymousId: readAnonymousId(),
24525
+ environment: resolveTelemetryEnv()
24526
+ }
24059
24527
  };
24060
24528
  }
24061
24529
  function applyPatch(body) {
24062
- if (body.updateCheck && typeof body.updateCheck.enabled === "boolean") {
24063
- try {
24530
+ try {
24531
+ if (body.updateCheck && typeof body.updateCheck.enabled === "boolean") {
24064
24532
  writeUserSettings({ updateCheck: { enabled: body.updateCheck.enabled } });
24065
- } catch (err) {
24066
- throw new HTTPException9(400, {
24067
- message: tx(SERVER_TEXTS.preferencesPersistFailed, {
24068
- message: formatErrorMessage(err)
24069
- })
24070
- });
24071
24533
  }
24534
+ if (body.telemetry) {
24535
+ applyTelemetryPatch(body.telemetry);
24536
+ }
24537
+ } catch (err) {
24538
+ throw new HTTPException10(400, {
24539
+ message: tx(SERVER_TEXTS.preferencesPersistFailed, {
24540
+ message: formatErrorMessage(err)
24541
+ })
24542
+ });
24543
+ }
24544
+ }
24545
+ function applyTelemetryPatch(t) {
24546
+ if (typeof t.errorsEnabled === "boolean") {
24547
+ writeUserSettings({ telemetry: { errorsEnabled: t.errorsEnabled } });
24548
+ }
24549
+ if (typeof t.usageCliEnabled === "boolean") {
24550
+ writeUserSettings({ telemetry: { usageCliEnabled: t.usageCliEnabled } });
24551
+ if (t.usageCliEnabled) ensureAnonymousId();
24552
+ }
24553
+ if (typeof t.usageUiEnabled === "boolean") {
24554
+ writeUserSettings({ telemetry: { usageUiEnabled: t.usageUiEnabled } });
24555
+ if (t.usageUiEnabled) ensureAnonymousId();
24072
24556
  }
24073
24557
  }
24074
24558
  var PATCH_BODY_SCHEMA = {
@@ -24082,6 +24566,15 @@ var PATCH_BODY_SCHEMA = {
24082
24566
  properties: {
24083
24567
  enabled: { type: "boolean" }
24084
24568
  }
24569
+ },
24570
+ telemetry: {
24571
+ type: "object",
24572
+ additionalProperties: false,
24573
+ properties: {
24574
+ errorsEnabled: { type: "boolean" },
24575
+ usageCliEnabled: { type: "boolean" },
24576
+ usageUiEnabled: { type: "boolean" }
24577
+ }
24085
24578
  }
24086
24579
  }
24087
24580
  };
@@ -24092,12 +24585,16 @@ var parsePatchBody2 = makeBodyValidator(PATCH_BODY_SCHEMA, {
24092
24585
  mapping: {
24093
24586
  ":minProperties": SERVER_TEXTS.preferencesBodyEmpty,
24094
24587
  "/updateCheck:type:object": SERVER_TEXTS.preferencesUpdateCheckNotObject,
24095
- "/updateCheck/enabled:type:boolean": SERVER_TEXTS.preferencesUpdateCheckEnabledNotBoolean
24588
+ "/updateCheck/enabled:type:boolean": SERVER_TEXTS.preferencesUpdateCheckEnabledNotBoolean,
24589
+ "/telemetry:type:object": SERVER_TEXTS.preferencesTelemetryNotObject,
24590
+ "/telemetry/errorsEnabled:type:boolean": SERVER_TEXTS.preferencesTelemetryErrorsEnabledNotBoolean,
24591
+ "/telemetry/usageCliEnabled:type:boolean": SERVER_TEXTS.preferencesTelemetryUsageCliEnabledNotBoolean,
24592
+ "/telemetry/usageUiEnabled:type:boolean": SERVER_TEXTS.preferencesTelemetryUsageUiEnabledNotBoolean
24096
24593
  }
24097
24594
  });
24098
24595
 
24099
24596
  // server/routes/project-ignore.ts
24100
- import { HTTPException as HTTPException10 } from "hono/http-exception";
24597
+ import { HTTPException as HTTPException11 } from "hono/http-exception";
24101
24598
 
24102
24599
  // server/util/skillmapignore-io.ts
24103
24600
  import { existsSync as existsSync26, readFileSync as readFileSync17, writeFileSync as writeFileSync2 } from "fs";
@@ -24184,12 +24681,12 @@ async function applyPatch2(deps, body) {
24184
24681
  for (const raw of body.patterns) {
24185
24682
  const t = raw.trim();
24186
24683
  if (t.length === 0) {
24187
- throw new HTTPException10(400, {
24684
+ throw new HTTPException11(400, {
24188
24685
  message: SERVER_TEXTS.projectIgnorePatternEmpty
24189
24686
  });
24190
24687
  }
24191
24688
  if (seen.has(t)) {
24192
- throw new HTTPException10(400, {
24689
+ throw new HTTPException11(400, {
24193
24690
  message: tx(SERVER_TEXTS.projectIgnorePatternDuplicate, { pattern: t })
24194
24691
  });
24195
24692
  }
@@ -24200,7 +24697,7 @@ async function applyPatch2(deps, body) {
24200
24697
  try {
24201
24698
  writePatterns(cwd, trimmed);
24202
24699
  } catch (err) {
24203
- throw new HTTPException10(400, {
24700
+ throw new HTTPException11(400, {
24204
24701
  message: tx(SERVER_TEXTS.projectIgnorePersistFailed, {
24205
24702
  message: formatErrorMessage(err)
24206
24703
  })
@@ -24278,7 +24775,7 @@ var parsePatchBody3 = makeBodyValidator(PATCH_BODY_SCHEMA2, {
24278
24775
 
24279
24776
  // server/routes/project-preferences.ts
24280
24777
  import { statSync as statSync9 } from "fs";
24281
- import { HTTPException as HTTPException11 } from "hono/http-exception";
24778
+ import { HTTPException as HTTPException12 } from "hono/http-exception";
24282
24779
  function registerProjectPreferencesRoute(app, deps) {
24283
24780
  app.get("/api/project-preferences", (c) => {
24284
24781
  return c.json(buildEnvelope3(deps));
@@ -24306,7 +24803,7 @@ async function applyPatch3(deps, body) {
24306
24803
  const cwd = deps.runtimeContext.cwd;
24307
24804
  const missingPaths = collectMissingPaths(writes, cwd);
24308
24805
  if (missingPaths.length > 0) {
24309
- throw new HTTPException11(400, {
24806
+ throw new HTTPException12(400, {
24310
24807
  message: tx(SERVER_TEXTS.projectPrefsPathNotFound, {
24311
24808
  paths: missingPaths.join(", ")
24312
24809
  })
@@ -24315,7 +24812,7 @@ async function applyPatch3(deps, body) {
24315
24812
  const exposures = writes.map((w) => projectPathExposure({ key: w.key, value: w.value, cwd })).filter((e) => e.expandsSurface);
24316
24813
  if (exposures.length > 0 && body.confirm !== true) {
24317
24814
  const exposed = exposures.flatMap((e) => e.exposedPaths);
24318
- throw new HTTPException11(412, {
24815
+ throw new HTTPException12(412, {
24319
24816
  message: tx(SERVER_TEXTS.projectPrefsConfirmRequired, {
24320
24817
  paths: exposed.join(", ")
24321
24818
  })
@@ -24367,7 +24864,7 @@ function runWrite(w, cwd) {
24367
24864
  try {
24368
24865
  writeConfigValue(w.key, w.value, { target: "project-local", cwd });
24369
24866
  } catch (err) {
24370
- throw new HTTPException11(400, {
24867
+ throw new HTTPException12(400, {
24371
24868
  message: tx(SERVER_TEXTS.projectPrefsPersistFailed, {
24372
24869
  key: w.key,
24373
24870
  message: formatErrorMessage(err)
@@ -24467,7 +24964,7 @@ var parsePatchBody4 = makeBodyValidator(PATCH_BODY_SCHEMA3, {
24467
24964
 
24468
24965
  // server/routes/active-provider.ts
24469
24966
  import { existsSync as existsSync27 } from "fs";
24470
- import { HTTPException as HTTPException12 } from "hono/http-exception";
24967
+ import { HTTPException as HTTPException13 } from "hono/http-exception";
24471
24968
  function registerActiveProviderRoute(app, deps) {
24472
24969
  app.get("/api/active-provider", (c) => {
24473
24970
  return c.json(buildEnvelope4(deps));
@@ -24492,7 +24989,7 @@ function applyLensSwitch(deps, newValue) {
24492
24989
  try {
24493
24990
  writeConfigValue("activeProvider", newValue, { target: "project", cwd });
24494
24991
  } catch (err) {
24495
- throw new HTTPException12(400, {
24992
+ throw new HTTPException13(400, {
24496
24993
  message: tx(SERVER_TEXTS.activeProviderPersistFailed, {
24497
24994
  message: formatErrorMessage(err)
24498
24995
  })
@@ -24528,7 +25025,7 @@ var parsePatchBody5 = makeBodyValidator(PATCH_BODY_SCHEMA4, {
24528
25025
  });
24529
25026
 
24530
25027
  // server/routes/scan.ts
24531
- import { HTTPException as HTTPException13 } from "hono/http-exception";
25028
+ import { HTTPException as HTTPException14 } from "hono/http-exception";
24532
25029
 
24533
25030
  // server/scan-mutex.ts
24534
25031
  var inFlight = null;
@@ -24694,7 +25191,7 @@ function registerScanRoute(app, deps) {
24694
25191
  }
24695
25192
  async function runPersistedScan(c, deps) {
24696
25193
  if (deps.options.noBuiltIns || deps.options.noPlugins) {
24697
- throw new HTTPException13(400, { message: SERVER_TEXTS.scanPostRequiresFullPipeline });
25194
+ throw new HTTPException14(400, { message: SERVER_TEXTS.scanPostRequiresFullPipeline });
24698
25195
  }
24699
25196
  const dbExists = await tryWithSqlite(
24700
25197
  { databasePath: deps.options.dbPath, autoBackup: false },
@@ -24731,7 +25228,7 @@ async function runPersistedScan(c, deps) {
24731
25228
  ...deps.options.maxNodes !== void 0 ? { maxNodes: deps.options.maxNodes } : {}
24732
25229
  });
24733
25230
  if (outcome.kind !== "ok") {
24734
- throw new HTTPException13(500, {
25231
+ throw new HTTPException14(500, {
24735
25232
  message: outcome.kind === "guard-trip" ? tx(SERVER_TEXTS.scanGuardTrip, { existing: outcome.existing }) : outcome.message
24736
25233
  });
24737
25234
  }
@@ -24739,7 +25236,7 @@ async function runPersistedScan(c, deps) {
24739
25236
  });
24740
25237
  } catch (err) {
24741
25238
  if (err instanceof ScanBusyError) {
24742
- throw new HTTPException13(409, { message: SERVER_TEXTS.scanPostBusy });
25239
+ throw new HTTPException14(409, { message: SERVER_TEXTS.scanPostBusy });
24743
25240
  }
24744
25241
  throw err;
24745
25242
  }
@@ -24800,7 +25297,7 @@ function groupTagsByPath2(rows) {
24800
25297
  }
24801
25298
  async function runFreshScan(deps) {
24802
25299
  if (deps.options.noBuiltIns || deps.options.noPlugins) {
24803
- throw new HTTPException13(400, { message: SERVER_TEXTS.freshScanRequiresPipeline });
25300
+ throw new HTTPException14(400, { message: SERVER_TEXTS.freshScanRequiresPipeline });
24804
25301
  }
24805
25302
  const resolveEnabledOverride = await buildBffResolverOverride(deps);
24806
25303
  const outcome = await runScanForCommand({
@@ -24835,7 +25332,7 @@ async function runFreshScan(deps) {
24835
25332
  ...deps.options.maxNodes !== void 0 ? { maxNodes: deps.options.maxNodes } : {}
24836
25333
  });
24837
25334
  if (outcome.kind !== "ok") {
24838
- throw new HTTPException13(500, {
25335
+ throw new HTTPException14(500, {
24839
25336
  message: outcome.kind === "guard-trip" ? tx(SERVER_TEXTS.freshScanGuardTrip, { existing: outcome.existing }) : outcome.message
24840
25337
  });
24841
25338
  }
@@ -24875,7 +25372,7 @@ function emptyScanResult() {
24875
25372
  }
24876
25373
 
24877
25374
  // server/routes/sidecar.ts
24878
- import { HTTPException as HTTPException14 } from "hono/http-exception";
25375
+ import { HTTPException as HTTPException15 } from "hono/http-exception";
24879
25376
  import { resolve as resolve36 } from "path";
24880
25377
  var STATUS_FRESH = "fresh";
24881
25378
  var ENVELOPE_KIND2 = "sidecar.bumped";
@@ -24912,11 +25409,11 @@ function registerSidecarRoutes(app, deps) {
24912
25409
  assertContained(deps.runtimeContext.cwd, node.path);
24913
25410
  absPath = resolve36(deps.runtimeContext.cwd, node.path);
24914
25411
  } catch (err) {
24915
- throw new HTTPException14(400, { message: formatErrorMessage(err) });
25412
+ throw new HTTPException15(400, { message: formatErrorMessage(err) });
24916
25413
  }
24917
25414
  const result = invokeBump2(node, absPath, body);
24918
25415
  if (result.report.ok === false && result.report.reason === "fresh") {
24919
- throw new HTTPException14(409, { message: SERVER_TEXTS.sidecarFreshRefusal });
25416
+ throw new HTTPException15(409, { message: SERVER_TEXTS.sidecarFreshRefusal });
24920
25417
  }
24921
25418
  if (result.report.ok === true && result.report.noop === true) {
24922
25419
  const envelope2 = {
@@ -24943,7 +25440,7 @@ function registerSidecarRoutes(app, deps) {
24943
25440
  }
24944
25441
  } catch (err) {
24945
25442
  if (err instanceof EConsentRequiredError) throw err;
24946
- throw new HTTPException14(500, { message: formatErrorMessage(err) });
25443
+ throw new HTTPException15(500, { message: formatErrorMessage(err) });
24947
25444
  }
24948
25445
  if (body.confirm === true) {
24949
25446
  deps.configService.reload();
@@ -24980,7 +25477,7 @@ async function loadNode(deps, nodePath) {
24980
25477
  );
24981
25478
  const node = persisted?.nodes.find((n) => n.path === nodePath);
24982
25479
  if (!node) {
24983
- throw new HTTPException14(404, {
25480
+ throw new HTTPException15(404, {
24984
25481
  message: tx(SERVER_TEXTS.nodeNotFound, { path: sanitizeForTerminal(nodePath) })
24985
25482
  });
24986
25483
  }
@@ -24988,7 +25485,7 @@ async function loadNode(deps, nodePath) {
24988
25485
  }
24989
25486
  function invokeBump2(node, absPath, body) {
24990
25487
  if (!nodeBumpAction.invoke) {
24991
- throw new HTTPException14(500, { message: SERVER_TEXTS.sidecarBumpInvokeMissing });
25488
+ throw new HTTPException15(500, { message: SERVER_TEXTS.sidecarBumpInvokeMissing });
24992
25489
  }
24993
25490
  const input = {};
24994
25491
  if (body.force === true) input.force = true;
@@ -25175,13 +25672,13 @@ function attachBroadcasterRoute(app, broadcaster) {
25175
25672
 
25176
25673
  // server/app.ts
25177
25674
  var BODY_LIMIT_BYTES = 1024 * 1024;
25178
- var DbMissingError = class extends HTTPException15 {
25675
+ var DbMissingError = class extends HTTPException16 {
25179
25676
  constructor(message) {
25180
25677
  super(500, { message });
25181
25678
  this.name = "DbMissingError";
25182
25679
  }
25183
25680
  };
25184
- var BulkValidationError = class extends HTTPException15 {
25681
+ var BulkValidationError = class extends HTTPException16 {
25185
25682
  id;
25186
25683
  code;
25187
25684
  constructor(init) {
@@ -25191,7 +25688,7 @@ var BulkValidationError = class extends HTTPException15 {
25191
25688
  this.code = init.code;
25192
25689
  }
25193
25690
  };
25194
- var LoopbackGateError = class extends HTTPException15 {
25691
+ var LoopbackGateError = class extends HTTPException16 {
25195
25692
  code;
25196
25693
  constructor(init) {
25197
25694
  super(403, { message: init.message });
@@ -25204,6 +25701,7 @@ function createApp(deps) {
25204
25701
  const configService = new ConfigService({
25205
25702
  cwd: deps.runtimeContext.cwd
25206
25703
  });
25704
+ app.use("*", createSentryRequestCapture());
25207
25705
  app.use("*", createLoopbackGate({ port: deps.options.port }));
25208
25706
  app.use("*", createSecurityHeaders());
25209
25707
  app.use(
@@ -25211,7 +25709,7 @@ function createApp(deps) {
25211
25709
  bodyLimit({
25212
25710
  maxSize: BODY_LIMIT_BYTES,
25213
25711
  onError: () => {
25214
- throw new HTTPException15(413, { message: tx(SERVER_TEXTS.bodyTooLarge, { maxBytes: String(BODY_LIMIT_BYTES) }) });
25712
+ throw new HTTPException16(413, { message: tx(SERVER_TEXTS.bodyTooLarge, { maxBytes: String(BODY_LIMIT_BYTES) }) });
25215
25713
  }
25216
25714
  })
25217
25715
  );
@@ -25257,7 +25755,7 @@ function createApp(deps) {
25257
25755
  registerActiveProviderRoute(app, routeDeps);
25258
25756
  registerProjectIgnoreRoute(app, routeDeps);
25259
25757
  app.all("/api/*", (c) => {
25260
- throw new HTTPException15(404, {
25758
+ throw new HTTPException16(404, {
25261
25759
  message: tx(SERVER_TEXTS.unknownApiEndpoint, { path: sanitizeForTerminal(c.req.path) })
25262
25760
  });
25263
25761
  });
@@ -25265,7 +25763,7 @@ function createApp(deps) {
25265
25763
  app.use("*", createStaticHandler({ uiDist: deps.options.uiDist, noUi: deps.options.noUi }));
25266
25764
  app.get("*", createSpaFallback({ uiDist: deps.options.uiDist, noUi: deps.options.noUi }));
25267
25765
  app.notFound((c) => {
25268
- throw new HTTPException15(404, {
25766
+ throw new HTTPException16(404, {
25269
25767
  message: tx(SERVER_TEXTS.unknownPath, { path: sanitizeForTerminal(c.req.path) })
25270
25768
  });
25271
25769
  });
@@ -25320,7 +25818,7 @@ function formatError2(err, c) {
25320
25818
  };
25321
25819
  return c.json(envelope, 403);
25322
25820
  }
25323
- if (err instanceof HTTPException15) {
25821
+ if (err instanceof HTTPException16) {
25324
25822
  const status = err.status;
25325
25823
  const envelope = {
25326
25824
  ok: false,
@@ -25419,7 +25917,7 @@ var WsBroadcaster = class {
25419
25917
  * stop the rest from receiving the event. A failing socket is closed
25420
25918
  * + unregistered so the next broadcast doesn't waste cycles on it.
25421
25919
  *
25422
- * Backpressure check (per AGENTS.md §Watcher integration): if a
25920
+ * Backpressure check (per context/kernel.md §Kernel boundaries): if a
25423
25921
  * client's `bufferedAmount` exceeds `MAX_BUFFERED_BYTES`, it's evicted
25424
25922
  * with close code 1009. The check runs BEFORE `send` so the threshold
25425
25923
  * acts as an admission gate, not a post-mortem.
@@ -25439,8 +25937,8 @@ var WsBroadcaster = class {
25439
25937
  return;
25440
25938
  }
25441
25939
  const snapshot = Array.from(this.#clients);
25442
- for (const client of snapshot) {
25443
- this.#deliver(client, payload);
25940
+ for (const client2 of snapshot) {
25941
+ this.#deliver(client2, payload);
25444
25942
  }
25445
25943
  }
25446
25944
  /**
@@ -25454,9 +25952,9 @@ var WsBroadcaster = class {
25454
25952
  this.#shutDown = true;
25455
25953
  const snapshot = Array.from(this.#clients);
25456
25954
  this.#clients.clear();
25457
- for (const client of snapshot) {
25955
+ for (const client2 of snapshot) {
25458
25956
  try {
25459
- client.close(CLOSE_CODE_GOING_AWAY, "server shutdown");
25957
+ client2.close(CLOSE_CODE_GOING_AWAY, "server shutdown");
25460
25958
  } catch {
25461
25959
  }
25462
25960
  }
@@ -25465,31 +25963,31 @@ var WsBroadcaster = class {
25465
25963
  * Per-client delivery: backpressure check, then `send()`. Eviction +
25466
25964
  * unregistration on either failure mode.
25467
25965
  */
25468
- #deliver(client, payload) {
25469
- if (client.readyState !== READY_STATE_OPEN) {
25470
- this.#clients.delete(client);
25966
+ #deliver(client2, payload) {
25967
+ if (client2.readyState !== READY_STATE_OPEN) {
25968
+ this.#clients.delete(client2);
25471
25969
  return;
25472
25970
  }
25473
- if (client.bufferedAmount > MAX_BUFFERED_BYTES) {
25474
- this.#clients.delete(client);
25971
+ if (client2.bufferedAmount > MAX_BUFFERED_BYTES) {
25972
+ this.#clients.delete(client2);
25475
25973
  try {
25476
- client.close(CLOSE_CODE_MESSAGE_TOO_BIG, "backpressure exceeded");
25974
+ client2.close(CLOSE_CODE_MESSAGE_TOO_BIG, "backpressure exceeded");
25477
25975
  } catch {
25478
25976
  }
25479
25977
  log.warn(
25480
25978
  tx(SERVER_TEXTS.wsBackpressureEvicted, {
25481
- buffered: String(client.bufferedAmount),
25979
+ buffered: String(client2.bufferedAmount),
25482
25980
  threshold: String(MAX_BUFFERED_BYTES)
25483
25981
  })
25484
25982
  );
25485
25983
  return;
25486
25984
  }
25487
25985
  try {
25488
- client.send(payload);
25986
+ client2.send(payload);
25489
25987
  } catch (err) {
25490
- this.#clients.delete(client);
25988
+ this.#clients.delete(client2);
25491
25989
  try {
25492
- client.close();
25990
+ client2.close();
25493
25991
  } catch {
25494
25992
  }
25495
25993
  const message = formatErrorMessage(err);
@@ -26250,7 +26748,7 @@ var ServeCommand = class extends SmCommand {
26250
26748
  emitElapsed = false;
26251
26749
  // CLI orchestrator with multi-flag handling, each `if (this.flag)`
26252
26750
  // branch is one cyclomatic point. Splitting per branch scatters the
26253
- // validation away from the flag it gates. Per AGENTS.md §Linting
26751
+ // validation away from the flag it gates. Per context/lint.md
26254
26752
  // category 1 ("CLI orchestrators with multi-flag handling").
26255
26753
  // eslint-disable-next-line complexity
26256
26754
  async run() {
@@ -26353,6 +26851,7 @@ var ServeCommand = class extends SmCommand {
26353
26851
  this.printer.info(formatValidationError(validation.error, stderrAnsi));
26354
26852
  return ExitCode.Error;
26355
26853
  }
26854
+ await initSentryBff(VERSION);
26356
26855
  let handle;
26357
26856
  try {
26358
26857
  handle = await createServer(validation.options);
@@ -27822,6 +28321,7 @@ cli.register(RootHelpCommand);
27822
28321
  cli.register(HelpCommand);
27823
28322
  cli.register(InitCommand);
27824
28323
  cli.register(TutorialCommand);
28324
+ cli.register(IntentionalFailCommand);
27825
28325
  cli.register(ScanCommand);
27826
28326
  cli.register(ScanCompareCommand);
27827
28327
  cli.register(ServeCommand);
@@ -27855,6 +28355,13 @@ var logLevel = resolveLogLevel({
27855
28355
  configureLogger(new Logger({ level: logLevel, stream: process.stderr }));
27856
28356
  var bareArgs = resolveBareInvocation(args);
27857
28357
  var routedArgs = routeHelpArgs(bareArgs ?? args, cli);
28358
+ var telemetryVerb = routedArgs[0];
28359
+ await maybeRunFirstRunPrompt();
28360
+ if (telemetryVerb !== "serve") {
28361
+ await initSentryCli(VERSION);
28362
+ setTelemetryVerbTag(telemetryVerb);
28363
+ await initUsageCli();
28364
+ }
27858
28365
  var lifecycleDispatcher = makeHookDispatcher(
27859
28366
  builtIns().hooks ?? [],
27860
28367
  new InMemoryProgressEmitter()
@@ -27891,10 +28398,18 @@ var exitCode = await cli.run(routedArgs, {
27891
28398
  stdout: process.stdout,
27892
28399
  stderr: process.stderr
27893
28400
  });
28401
+ if (telemetryVerb !== void 0 && telemetryVerb !== "") {
28402
+ const knownVerbs = new Set(
28403
+ registeredVerbPaths(cli).map((path) => path[0]).filter((token) => token !== void 0)
28404
+ );
28405
+ captureCliInvocation(telemetryVerb, extractFlagNames(routedArgs.slice(1)), knownVerbs);
28406
+ }
27894
28407
  await lifecycleDispatcher.dispatch(
27895
28408
  "shutdown",
27896
28409
  makeEvent("shutdown", { exitCode })
27897
28410
  );
28411
+ await closeSentryCli();
28412
+ await flushUsageCli();
27898
28413
  process.exit(exitCode);
27899
28414
  function resolveBareInvocation(rawArgs) {
27900
28415
  if (rawArgs.length === 0) return resolveBareDefault();