@skill-map/cli 0.43.0 → 0.45.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.
- package/dist/cli/tutorial/sm-master/SKILL.md +7 -9
- package/dist/cli/tutorial/sm-master/references/fixture-templates.md +1 -1
- package/dist/cli/tutorial/sm-master/references/tour-plugins.md +10 -9
- package/dist/cli/tutorial/sm-master/references/tour-settings.md +4 -4
- package/dist/cli/tutorial/sm-tutorial/SKILL.md +263 -369
- package/dist/cli.js +945 -256
- package/dist/cli.js.map +1 -1
- package/dist/index.js +21 -15
- package/dist/index.js.map +1 -1
- package/dist/kernel/index.d.ts +43 -6
- package/dist/kernel/index.js +21 -15
- package/dist/kernel/index.js.map +1 -1
- package/dist/ui/chunk-27WQPOXP.js +1 -0
- package/dist/ui/{chunk-DZBSELHN.js → chunk-43S72FTV.js} +1 -1
- package/dist/ui/chunk-555ST76V.js +1 -0
- package/dist/ui/{chunk-JPYAASHN.js → chunk-5AD5ZV4I.js} +3 -3
- package/dist/ui/{chunk-CFJBTDAA.js → chunk-5ITZXW3A.js} +1 -1
- package/dist/ui/chunk-A7PRWMQD.js +1021 -0
- package/dist/ui/chunk-CBI77N5U.js +123 -0
- package/dist/ui/chunk-F7I6KMHX.js +1 -0
- package/dist/ui/{chunk-77J7XU3Y.js → chunk-I5AX4U2N.js} +28 -28
- package/dist/ui/chunk-IUZRAD7S.js +1 -0
- package/dist/ui/{chunk-XOHD5XWA.js → chunk-IYM26L3O.js} +1 -1
- package/dist/ui/{chunk-SBCO7ZSP.js → chunk-LGFABCIA.js} +1 -1
- package/dist/ui/{chunk-IUDL3NDH.js → chunk-MFLFIA7C.js} +1 -1
- package/dist/ui/chunk-MS6B7344.js +315 -0
- package/dist/ui/chunk-P3SNMV4X.js +2 -0
- package/dist/ui/chunk-PZQHB7GS.js +4 -0
- package/dist/ui/chunk-QDUSFOBE.js +1 -0
- package/dist/ui/{chunk-YCR3XCIW.js → chunk-QNTAOR2L.js} +5 -5
- package/dist/ui/{chunk-CR3AANNX.js → chunk-S4S5ZMXJ.js} +1 -1
- package/dist/ui/{chunk-5WJRN3LD.js → chunk-T3IVIRRJ.js} +1 -1
- package/dist/ui/{chunk-HP375T2O.js → chunk-VGPYYAVI.js} +1 -1
- package/dist/ui/chunk-X227ITGS.js +499 -0
- package/dist/ui/index.html +1 -1
- package/dist/ui/main-ERCTR2PR.js +3 -0
- package/package.json +10 -9
- package/dist/ui/chunk-2IF446BS.js +0 -1
- package/dist/ui/chunk-DIKPNZUZ.js +0 -315
- package/dist/ui/chunk-KGCJINI6.js +0 -1
- package/dist/ui/chunk-PPE3P2JD.js +0 -1
- package/dist/ui/chunk-UOCACZLI.js +0 -123
- package/dist/ui/chunk-YL6SWAFJ.js +0 -1024
- package/dist/ui/main-VHFB7Q2D.js +0 -3
- /package/dist/ui/{chunk-C2YUQODZ.js → chunk-4SG4352Z.js} +0 -0
- /package/dist/ui/{chunk-VB56BUGO.js → chunk-WCABR6TI.js} +0 -0
package/dist/cli.js
CHANGED
|
@@ -462,6 +462,11 @@ var claudeProvider = {
|
|
|
462
462
|
// a Claude Code project. Provider-owned (replaces the old central
|
|
463
463
|
// detection table in `src/core/config/active-provider.ts`).
|
|
464
464
|
detect: { markers: [".claude"] },
|
|
465
|
+
// Authoring target for `sm tutorial`: Claude Code discovers skills under
|
|
466
|
+
// `.claude/skills/<name>/SKILL.md`, so a materialised tutorial folder
|
|
467
|
+
// lands there. This is the WRITE side of the territory the `classify`
|
|
468
|
+
// below READS.
|
|
469
|
+
scaffold: { skillDir: ".claude/skills" },
|
|
465
470
|
// Vendor provider: Claude Code only reads its own `.claude/` territory
|
|
466
471
|
// and ignores `.codex/` / Antigravity layouts at runtime. Gating the
|
|
467
472
|
// classifier behind the active lens prevents the walker from inventing
|
|
@@ -912,75 +917,77 @@ var antigravityProvider = {
|
|
|
912
917
|
classify() {
|
|
913
918
|
return null;
|
|
914
919
|
},
|
|
915
|
-
//
|
|
916
|
-
//
|
|
917
|
-
//
|
|
918
|
-
//
|
|
919
|
-
//
|
|
920
|
-
//
|
|
921
|
-
//
|
|
922
|
-
//
|
|
923
|
-
//
|
|
924
|
-
//
|
|
925
|
-
//
|
|
926
|
-
//
|
|
927
|
-
//
|
|
920
|
+
// Built-in slash-command catalog, captured verbatim from `agy /help`
|
|
921
|
+
// (Antigravity CLI v1.0.3). This REPLACES the earlier provisional list
|
|
922
|
+
// that mirrored Gemini CLI's verbs: `agy` ships its own surface. It
|
|
923
|
+
// DROPPED Gemini-only verbs (`vim`, `theme`, `terminal-setup`,
|
|
924
|
+
// `setup-github`, `bashes`, `shells`, `policies`, `extensions`, `about`,
|
|
925
|
+
// `auth`, `bug`, `chat`, `compress`, `docs`, `editor`, `ide`, `init`,
|
|
926
|
+
// `memory`, `restore`, `stats`, `tools`, `upgrade`, `?`, `dir`) and
|
|
927
|
+
// ADDED agent-first ones (`goal`, `grill-me`, `schedule`, `fast`, `btw`,
|
|
928
|
+
// `artifact`, `context`, `diff`, `fork`, `tasks`, `add-dir`, `credits`,
|
|
929
|
+
// `feedback`, `logout`, `open`, `planning`, `rename`, `statusline`,
|
|
930
|
+
// `title`, `usage`). Both the 35 primary verbs and the 8 documented
|
|
931
|
+
// aliases (`new`, `settings`, `quit`, `branch`, `switch`, `conversation`,
|
|
932
|
+
// `undo`, `quota`) are reserved: a user skill named after either is
|
|
933
|
+
// silently shadowed by the built-in once the catalog activates.
|
|
928
934
|
//
|
|
929
|
-
//
|
|
930
|
-
//
|
|
931
|
-
//
|
|
932
|
-
//
|
|
933
|
-
//
|
|
934
|
-
//
|
|
935
|
+
// Declared under the `skill` kind (NOT `command`): Antigravity has no
|
|
936
|
+
// vendor-specific command directory, its user slash-commands are skills
|
|
937
|
+
// (`.agents/skills/<name>/SKILL.md`, owned by the universal `agent-skills`
|
|
938
|
+
// Provider). The catalog is ACTIVE via the LENS SCOPE in
|
|
939
|
+
// `buildReservedNodePaths` (spec/architecture.md §Provider ·
|
|
940
|
+
// reservedNames): when `activeProvider === 'antigravity'` the orchestrator
|
|
941
|
+
// lends this `skill` catalog to `agent-skills` skill nodes, so a user
|
|
942
|
+
// `.agents/skills/goal/SKILL.md` is flagged because `/goal` is built-in.
|
|
935
943
|
//
|
|
936
|
-
// **Reconciliation marker**:
|
|
937
|
-
//
|
|
938
|
-
// reference, replace this comment + array with the official list (and
|
|
939
|
-
// bump the file's leading docblock to cite the new source URL).
|
|
944
|
+
// **Reconciliation marker**: re-capture from `agy /help` on each major
|
|
945
|
+
// Antigravity CLI release and bump the cited version above.
|
|
940
946
|
reservedNames: {
|
|
941
|
-
|
|
942
|
-
"
|
|
943
|
-
"about",
|
|
947
|
+
skill: [
|
|
948
|
+
"add-dir",
|
|
944
949
|
"agents",
|
|
945
|
-
"
|
|
946
|
-
"
|
|
947
|
-
"
|
|
948
|
-
"
|
|
950
|
+
"artifact",
|
|
951
|
+
"branch",
|
|
952
|
+
"btw",
|
|
953
|
+
"changelog",
|
|
949
954
|
"clear",
|
|
950
|
-
"
|
|
951
|
-
"
|
|
955
|
+
"config",
|
|
956
|
+
"context",
|
|
957
|
+
"conversation",
|
|
952
958
|
"copy",
|
|
953
|
-
"
|
|
954
|
-
"
|
|
955
|
-
"docs",
|
|
956
|
-
"editor",
|
|
959
|
+
"credits",
|
|
960
|
+
"diff",
|
|
957
961
|
"exit",
|
|
958
|
-
"
|
|
962
|
+
"fast",
|
|
963
|
+
"feedback",
|
|
964
|
+
"fork",
|
|
965
|
+
"goal",
|
|
966
|
+
"grill-me",
|
|
959
967
|
"help",
|
|
960
968
|
"hooks",
|
|
961
|
-
"
|
|
962
|
-
"
|
|
969
|
+
"keybindings",
|
|
970
|
+
"logout",
|
|
963
971
|
"mcp",
|
|
964
|
-
"memory",
|
|
965
972
|
"model",
|
|
973
|
+
"new",
|
|
974
|
+
"open",
|
|
966
975
|
"permissions",
|
|
967
|
-
"
|
|
968
|
-
"policies",
|
|
969
|
-
"privacy",
|
|
976
|
+
"planning",
|
|
970
977
|
"quit",
|
|
971
|
-
"
|
|
978
|
+
"quota",
|
|
979
|
+
"rename",
|
|
972
980
|
"resume",
|
|
973
981
|
"rewind",
|
|
982
|
+
"schedule",
|
|
974
983
|
"settings",
|
|
975
|
-
"setup-github",
|
|
976
|
-
"shells",
|
|
977
984
|
"skills",
|
|
978
|
-
"
|
|
979
|
-
"
|
|
980
|
-
"
|
|
981
|
-
"
|
|
982
|
-
"
|
|
983
|
-
"
|
|
985
|
+
"statusline",
|
|
986
|
+
"switch",
|
|
987
|
+
"tasks",
|
|
988
|
+
"title",
|
|
989
|
+
"undo",
|
|
990
|
+
"usage"
|
|
984
991
|
]
|
|
985
992
|
}
|
|
986
993
|
};
|
|
@@ -1145,6 +1152,13 @@ var agentSkillsProvider = {
|
|
|
1145
1152
|
// (Antigravity adopted the open standard), so such projects auto-detect
|
|
1146
1153
|
// as this universal lens. Provider-owned.
|
|
1147
1154
|
detect: { markers: [".agents"] },
|
|
1155
|
+
// Authoring target for `sm tutorial`: the open standard discovers skills
|
|
1156
|
+
// under `.agents/skills/<name>/SKILL.md`. The same path is consumed by
|
|
1157
|
+
// Antigravity (adopted the standard rather than a `.gemini/` layout) and
|
|
1158
|
+
// OpenAI Codex (skills mirror the open standard), so `aka` surfaces both
|
|
1159
|
+
// names in the destination prompt to orient testers on those agents.
|
|
1160
|
+
// `aka` is display-only, `--for` still matches the `agent-skills` id.
|
|
1161
|
+
scaffold: { skillDir: ".agents/skills", aka: ["Antigravity", "OpenAI Codex"] },
|
|
1148
1162
|
read: { extensions: [".md"], parser: "frontmatter-yaml" },
|
|
1149
1163
|
kinds: {
|
|
1150
1164
|
skill: {
|
|
@@ -3946,7 +3960,7 @@ var UPDATE_CHECK_TEXTS = {
|
|
|
3946
3960
|
// package.json
|
|
3947
3961
|
var package_default = {
|
|
3948
3962
|
name: "@skill-map/cli",
|
|
3949
|
-
version: "0.
|
|
3963
|
+
version: "0.45.0",
|
|
3950
3964
|
description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
|
|
3951
3965
|
license: "MIT",
|
|
3952
3966
|
type: "module",
|
|
@@ -3997,25 +4011,25 @@ var package_default = {
|
|
|
3997
4011
|
typecheck: "tsc --noEmit",
|
|
3998
4012
|
lint: "eslint .",
|
|
3999
4013
|
"lint:fix": "eslint . --fix",
|
|
4000
|
-
reference: "node scripts/build-reference.js",
|
|
4001
|
-
"reference:check": "node scripts/build-reference.js --check",
|
|
4002
4014
|
"build-built-ins": "node ../scripts/generate-built-ins.js",
|
|
4003
4015
|
"built-ins:check": "node ../scripts/generate-built-ins.js --check",
|
|
4004
4016
|
prebuild: "pnpm build-built-ins",
|
|
4005
4017
|
validate: "pnpm validate:compile && pnpm validate:test",
|
|
4006
|
-
"validate:compile": "pnpm typecheck && pnpm lint && pnpm build && pnpm
|
|
4018
|
+
"validate:compile": "pnpm typecheck && pnpm lint && pnpm build && pnpm built-ins:check",
|
|
4007
4019
|
"validate:test": "pnpm test:ci",
|
|
4008
4020
|
pretest: "tsup",
|
|
4009
4021
|
"pretest:coverage": "tsup",
|
|
4010
4022
|
"pretest:coverage:html": "tsup",
|
|
4011
|
-
test: "tsc --noEmit && node --import tsx --test --test-reporter=
|
|
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:
|
|
4014
|
-
"test:coverage
|
|
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'",
|
|
4015
4028
|
clean: "rm -rf dist coverage"
|
|
4016
4029
|
},
|
|
4017
4030
|
dependencies: {
|
|
4018
4031
|
"@hono/node-server": "2.0.1",
|
|
4032
|
+
"@sentry/node": "10.55.0",
|
|
4019
4033
|
"@skill-map/spec": "workspace:*",
|
|
4020
4034
|
ajv: "8.18.0",
|
|
4021
4035
|
"ajv-formats": "3.0.1",
|
|
@@ -4026,6 +4040,7 @@ var package_default = {
|
|
|
4026
4040
|
"js-tiktoken": "1.0.21",
|
|
4027
4041
|
"js-yaml": "4.1.1",
|
|
4028
4042
|
kysely: "0.28.17",
|
|
4043
|
+
"posthog-node": "5.35.6",
|
|
4029
4044
|
semver: "7.7.4",
|
|
4030
4045
|
"smol-toml": "1.6.1",
|
|
4031
4046
|
typanion: "3.14.0",
|
|
@@ -4096,6 +4111,7 @@ function ansiFor(opts) {
|
|
|
4096
4111
|
}
|
|
4097
4112
|
|
|
4098
4113
|
// cli/util/user-settings-store.ts
|
|
4114
|
+
import { randomUUID } from "crypto";
|
|
4099
4115
|
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync5 } from "fs";
|
|
4100
4116
|
import { homedir } from "os";
|
|
4101
4117
|
import { join as join2 } from "path";
|
|
@@ -4204,13 +4220,13 @@ function userSettingsFilePath() {
|
|
|
4204
4220
|
return join2(homedir(), SKILL_MAP_DIR, FILENAME);
|
|
4205
4221
|
}
|
|
4206
4222
|
function defaultSettings() {
|
|
4207
|
-
return { schemaVersion: SCHEMA_VERSION, updateCheck: {} };
|
|
4223
|
+
return { schemaVersion: SCHEMA_VERSION, updateCheck: {}, telemetry: {} };
|
|
4208
4224
|
}
|
|
4209
4225
|
function readUserSettings() {
|
|
4210
4226
|
const parsed = readParsedFile();
|
|
4211
4227
|
if (parsed === null) return defaultSettings();
|
|
4212
4228
|
const validated = validateOrDefault(parsed);
|
|
4213
|
-
return
|
|
4229
|
+
return backfillSubObjects(validated);
|
|
4214
4230
|
}
|
|
4215
4231
|
function readParsedFile() {
|
|
4216
4232
|
const path = userSettingsFilePath();
|
|
@@ -4232,11 +4248,12 @@ function validateOrDefault(parsed) {
|
|
|
4232
4248
|
if (!result.ok) return defaultSettings();
|
|
4233
4249
|
return result.data;
|
|
4234
4250
|
}
|
|
4235
|
-
function
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4251
|
+
function backfillSubObjects(settings) {
|
|
4252
|
+
return {
|
|
4253
|
+
...settings,
|
|
4254
|
+
updateCheck: settings.updateCheck ?? {},
|
|
4255
|
+
telemetry: settings.telemetry ?? {}
|
|
4256
|
+
};
|
|
4240
4257
|
}
|
|
4241
4258
|
function writeUserSettings(patch) {
|
|
4242
4259
|
const dir = join2(homedir(), SKILL_MAP_DIR);
|
|
@@ -4258,14 +4275,49 @@ function isUpdateCheckEnabled() {
|
|
|
4258
4275
|
const settings = readUserSettings();
|
|
4259
4276
|
return settings.updateCheck?.enabled !== false;
|
|
4260
4277
|
}
|
|
4278
|
+
function isErrorTelemetryEnabled() {
|
|
4279
|
+
const settings = readUserSettings();
|
|
4280
|
+
return settings.telemetry?.errorsEnabled === true;
|
|
4281
|
+
}
|
|
4282
|
+
function isUsageCliTelemetryEnabled() {
|
|
4283
|
+
const settings = readUserSettings();
|
|
4284
|
+
return settings.telemetry?.usageCliEnabled === true;
|
|
4285
|
+
}
|
|
4286
|
+
function isUsageUiTelemetryEnabled() {
|
|
4287
|
+
const settings = readUserSettings();
|
|
4288
|
+
return settings.telemetry?.usageUiEnabled === true;
|
|
4289
|
+
}
|
|
4290
|
+
function readAnonymousId() {
|
|
4291
|
+
const settings = readUserSettings();
|
|
4292
|
+
return settings.telemetry?.anonymousId ?? null;
|
|
4293
|
+
}
|
|
4294
|
+
function ensureAnonymousId(generate = () => randomUUID()) {
|
|
4295
|
+
const existing = readAnonymousId();
|
|
4296
|
+
if (existing !== null && existing !== "") return existing;
|
|
4297
|
+
const id = generate();
|
|
4298
|
+
writeUserSettings({ telemetry: { anonymousId: id } });
|
|
4299
|
+
return id;
|
|
4300
|
+
}
|
|
4301
|
+
function hasTelemetryPromptBeenShown() {
|
|
4302
|
+
const settings = readUserSettings();
|
|
4303
|
+
return typeof settings.telemetry?.promptedAt === "number";
|
|
4304
|
+
}
|
|
4305
|
+
function hasSeenFirstRun() {
|
|
4306
|
+
const settings = readUserSettings();
|
|
4307
|
+
return typeof settings.telemetry?.firstRunAt === "number";
|
|
4308
|
+
}
|
|
4261
4309
|
function mergeSettings(current, patch) {
|
|
4262
4310
|
const merged = {
|
|
4263
4311
|
schemaVersion: SCHEMA_VERSION,
|
|
4264
|
-
updateCheck: { ...current.updateCheck ?? {} }
|
|
4312
|
+
updateCheck: { ...current.updateCheck ?? {} },
|
|
4313
|
+
telemetry: { ...current.telemetry ?? {} }
|
|
4265
4314
|
};
|
|
4266
4315
|
if (patch.updateCheck) {
|
|
4267
4316
|
merged.updateCheck = { ...merged.updateCheck, ...patch.updateCheck };
|
|
4268
4317
|
}
|
|
4318
|
+
if (patch.telemetry) {
|
|
4319
|
+
merged.telemetry = { ...merged.telemetry, ...patch.telemetry };
|
|
4320
|
+
}
|
|
4269
4321
|
return merged;
|
|
4270
4322
|
}
|
|
4271
4323
|
function tryLoadValidators() {
|
|
@@ -4378,40 +4430,40 @@ var updateCheckHook = {
|
|
|
4378
4430
|
};
|
|
4379
4431
|
|
|
4380
4432
|
// plugins/built-ins.ts
|
|
4381
|
-
var claudeProvider2 = { ...claudeProvider, pluginId: "claude", version: "0.
|
|
4382
|
-
var atDirectiveExtractor2 = { ...atDirectiveExtractor, pluginId: "claude", version: "0.
|
|
4383
|
-
var slashCommandExtractor2 = { ...slashCommandExtractor, pluginId: "claude", version: "0.
|
|
4384
|
-
var antigravityProvider2 = { ...antigravityProvider, pluginId: "antigravity", version: "0.
|
|
4385
|
-
var openaiProvider2 = { ...openaiProvider, pluginId: "openai", version: "0.
|
|
4386
|
-
var agentSkillsProvider2 = { ...agentSkillsProvider, pluginId: "agent-skills", version: "0.
|
|
4387
|
-
var coreMarkdownProvider2 = { ...coreMarkdownProvider, pluginId: "core", version: "0.
|
|
4388
|
-
var annotationsExtractor2 = { ...annotationsExtractor, pluginId: "core", version: "0.
|
|
4389
|
-
var externalUrlCounterExtractor2 = { ...externalUrlCounterExtractor, pluginId: "core", version: "0.
|
|
4390
|
-
var markdownLinkExtractor2 = { ...markdownLinkExtractor, pluginId: "core", version: "0.
|
|
4391
|
-
var mcpToolsExtractor2 = { ...mcpToolsExtractor, pluginId: "core", version: "0.
|
|
4392
|
-
var toolsCounterExtractor2 = { ...toolsCounterExtractor, pluginId: "core", version: "0.
|
|
4393
|
-
var annotationFieldUnknownAnalyzer2 = { ...annotationFieldUnknownAnalyzer, pluginId: "core", version: "0.
|
|
4394
|
-
var annotationOrphanAnalyzer2 = { ...annotationOrphanAnalyzer, pluginId: "core", version: "0.
|
|
4395
|
-
var annotationStaleAnalyzer2 = { ...annotationStaleAnalyzer, pluginId: "core", version: "0.
|
|
4396
|
-
var contributionOrphanAnalyzer2 = { ...contributionOrphanAnalyzer, pluginId: "core", version: "0.
|
|
4397
|
-
var issueCounterAnalyzer2 = { ...issueCounterAnalyzer, pluginId: "core", version: "0.
|
|
4398
|
-
var jobFileOrphanAnalyzer2 = { ...jobFileOrphanAnalyzer, pluginId: "core", version: "0.
|
|
4399
|
-
var linkConflictAnalyzer2 = { ...linkConflictAnalyzer, pluginId: "core", version: "0.
|
|
4400
|
-
var linkCounterAnalyzer2 = { ...linkCounterAnalyzer, pluginId: "core", version: "0.
|
|
4401
|
-
var linkSelfLoopAnalyzer2 = { ...linkSelfLoopAnalyzer, pluginId: "core", version: "0.
|
|
4402
|
-
var nameReservedAnalyzer2 = { ...nameReservedAnalyzer, pluginId: "core", version: "0.
|
|
4403
|
-
var nodeStabilityAnalyzer2 = { ...nodeStabilityAnalyzer, pluginId: "core", version: "0.
|
|
4404
|
-
var nodeSupersededAnalyzer2 = { ...nodeSupersededAnalyzer, pluginId: "core", version: "0.
|
|
4405
|
-
var referenceBrokenAnalyzer2 = { ...referenceBrokenAnalyzer, pluginId: "core", version: "0.
|
|
4406
|
-
var referenceRedundantAnalyzer2 = { ...referenceRedundantAnalyzer, pluginId: "core", version: "0.
|
|
4407
|
-
var schemaViolationAnalyzer2 = { ...schemaViolationAnalyzer, pluginId: "core", version: "0.
|
|
4408
|
-
var signalCollisionAnalyzer2 = { ...signalCollisionAnalyzer, pluginId: "core", version: "0.
|
|
4409
|
-
var triggerCollisionAnalyzer2 = { ...triggerCollisionAnalyzer, pluginId: "core", version: "0.
|
|
4410
|
-
var asciiFormatter2 = { ...asciiFormatter, pluginId: "core", version: "0.
|
|
4411
|
-
var jsonFormatter2 = { ...jsonFormatter, pluginId: "core", version: "0.
|
|
4412
|
-
var nodeBumpAction2 = { ...nodeBumpAction, pluginId: "core", version: "0.
|
|
4413
|
-
var nodeSupersedeAction2 = { ...nodeSupersedeAction, pluginId: "core", version: "0.
|
|
4414
|
-
var updateCheckHook2 = { ...updateCheckHook, pluginId: "core", version: "0.
|
|
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" };
|
|
4415
4467
|
var builtInBundles = [
|
|
4416
4468
|
{
|
|
4417
4469
|
id: "claude",
|
|
@@ -4943,6 +4995,341 @@ function defaultRuntimeContext() {
|
|
|
4943
4995
|
return { cwd: process.cwd() };
|
|
4944
4996
|
}
|
|
4945
4997
|
|
|
4998
|
+
// cli/telemetry/first-run-prompt.ts
|
|
4999
|
+
import { createInterface } from "readline/promises";
|
|
5000
|
+
|
|
5001
|
+
// cli/i18n/telemetry.texts.ts
|
|
5002
|
+
var TELEMETRY_PROMPT_TEXTS = {
|
|
5003
|
+
// Header + body of the one-time question (glyph `ℹ` added by the renderer).
|
|
5004
|
+
title: "Anonymous error and usage reporting",
|
|
5005
|
+
intro: [
|
|
5006
|
+
"skill-map can send anonymous reports to help fix bugs and decide what to",
|
|
5007
|
+
"build next. No personal information is ever sent: not your files or their",
|
|
5008
|
+
"contents, not your folder or home paths, not your settings."
|
|
5009
|
+
],
|
|
5010
|
+
question: "Enable anonymous error and usage reporting?",
|
|
5011
|
+
answerYes: "[Y]es",
|
|
5012
|
+
answerNo: "[n]o",
|
|
5013
|
+
answerDetails: "[d]etails",
|
|
5014
|
+
// Disclosure shown on `[d]etails`, then the question is re-asked.
|
|
5015
|
+
detailsSentTitle: "Sent, only if you turn this on",
|
|
5016
|
+
detailsSent: [
|
|
5017
|
+
"crashes: error name, code, message, and a path-stripped stack trace",
|
|
5018
|
+
"usage: the command you ran and its flag names (never their values)",
|
|
5019
|
+
"usage: which built-in extractors ran, and which UI views you opened",
|
|
5020
|
+
"cli version, node major, os, arch, and a random anonymous id"
|
|
5021
|
+
],
|
|
5022
|
+
detailsNeverTitle: "Never sent",
|
|
5023
|
+
detailsNever: [
|
|
5024
|
+
"your files, their contents, frontmatter, annotations",
|
|
5025
|
+
"absolute paths, hostname, your username, ip address",
|
|
5026
|
+
"your settings values or any flag values"
|
|
5027
|
+
],
|
|
5028
|
+
detailsHint: "Turn error reports, CLI usage, and UI usage on or off independently in Settings, or force everything off with SKILL_MAP_TELEMETRY=0.",
|
|
5029
|
+
// Confirmation lines (glyph added by the renderer).
|
|
5030
|
+
enabled: "Telemetry on. Thanks. Turn error reports, CLI usage, and UI usage off independently in Settings.",
|
|
5031
|
+
disabled: "Telemetry off. You can turn any of it on later in Settings."
|
|
5032
|
+
};
|
|
5033
|
+
|
|
5034
|
+
// public-config.ts
|
|
5035
|
+
var SENTRY_DSN_NODE = "https://8b73dbb2563da4b77def12ce5ee46e75@o4511475590037504.ingest.de.sentry.io/4511475708002384";
|
|
5036
|
+
var POSTHOG_KEY_NODE = "phc_vMX3PcNeDsacWNg2hYEbKVXDijSWcjKFzabCkzU7RNEr";
|
|
5037
|
+
var POSTHOG_HOST = "https://eu.i.posthog.com";
|
|
5038
|
+
|
|
5039
|
+
// core/telemetry/scrub.ts
|
|
5040
|
+
var HOME_PLACEHOLDER = "<HOME>";
|
|
5041
|
+
var STRIPPED_ENVELOPE_KEYS = ["server_name", "user"];
|
|
5042
|
+
var HOME_PATTERNS = [
|
|
5043
|
+
// Windows: drive + Users + one user segment (back- or forward-slash).
|
|
5044
|
+
/[A-Za-z]:[\\/]Users[\\/][^\\/\s:*?"<>|]+/g,
|
|
5045
|
+
// POSIX user homes: /home/<user> or macOS /Users/<user>.
|
|
5046
|
+
/\/(?:home|Users)\/[^/\s:]+/g,
|
|
5047
|
+
// Root account home.
|
|
5048
|
+
/\/root(?=\/|\b)/g
|
|
5049
|
+
];
|
|
5050
|
+
function scrubString(value) {
|
|
5051
|
+
let out = value;
|
|
5052
|
+
for (const pattern of HOME_PATTERNS) {
|
|
5053
|
+
out = out.replace(pattern, HOME_PLACEHOLDER);
|
|
5054
|
+
}
|
|
5055
|
+
return out;
|
|
5056
|
+
}
|
|
5057
|
+
function scrubEvent(event) {
|
|
5058
|
+
const walked = walk(event);
|
|
5059
|
+
if (walked !== null && typeof walked === "object" && !Array.isArray(walked)) {
|
|
5060
|
+
const record = walked;
|
|
5061
|
+
for (const key of STRIPPED_ENVELOPE_KEYS) {
|
|
5062
|
+
if (key in record) delete record[key];
|
|
5063
|
+
}
|
|
5064
|
+
}
|
|
5065
|
+
return walked;
|
|
5066
|
+
}
|
|
5067
|
+
function walk(value) {
|
|
5068
|
+
if (typeof value === "string") return scrubString(value);
|
|
5069
|
+
if (Array.isArray(value)) return value.map((item) => walk(item));
|
|
5070
|
+
if (value !== null && typeof value === "object") {
|
|
5071
|
+
const out = {};
|
|
5072
|
+
for (const [key, child] of Object.entries(value)) {
|
|
5073
|
+
out[key] = walk(child);
|
|
5074
|
+
}
|
|
5075
|
+
return out;
|
|
5076
|
+
}
|
|
5077
|
+
return value;
|
|
5078
|
+
}
|
|
5079
|
+
|
|
5080
|
+
// cli/telemetry/telemetry-env.ts
|
|
5081
|
+
var TELEMETRY_ENV_VAR = "SKILL_MAP_TELEMETRY_ENV";
|
|
5082
|
+
function resolveTelemetryEnv() {
|
|
5083
|
+
const raw = process.env[TELEMETRY_ENV_VAR];
|
|
5084
|
+
if (raw === void 0 || raw.trim() === "" || raw === "prod" || raw === "production") {
|
|
5085
|
+
return "prod";
|
|
5086
|
+
}
|
|
5087
|
+
return "dev";
|
|
5088
|
+
}
|
|
5089
|
+
|
|
5090
|
+
// cli/telemetry/sentry-init.ts
|
|
5091
|
+
var KILL_SWITCH_ENV = "SKILL_MAP_TELEMETRY";
|
|
5092
|
+
var sdk = null;
|
|
5093
|
+
function isCliDsnConfigured() {
|
|
5094
|
+
return SENTRY_DSN_NODE !== "";
|
|
5095
|
+
}
|
|
5096
|
+
function isTelemetryForcedOff() {
|
|
5097
|
+
return process.env[KILL_SWITCH_ENV] === "0";
|
|
5098
|
+
}
|
|
5099
|
+
function isTelemetryActive(dsn) {
|
|
5100
|
+
if (isTelemetryForcedOff()) return false;
|
|
5101
|
+
if (dsn === "") return false;
|
|
5102
|
+
return isErrorTelemetryEnabled();
|
|
5103
|
+
}
|
|
5104
|
+
async function initSentryCli(version, loadSdk = () => import("@sentry/node")) {
|
|
5105
|
+
if (sdk) return;
|
|
5106
|
+
if (!isTelemetryActive(SENTRY_DSN_NODE)) return;
|
|
5107
|
+
const Sentry = await loadSdk();
|
|
5108
|
+
Sentry.init({
|
|
5109
|
+
dsn: SENTRY_DSN_NODE,
|
|
5110
|
+
release: `@skill-map/cli@${version}`,
|
|
5111
|
+
environment: resolveTelemetryEnv(),
|
|
5112
|
+
// CLI and BFF share one Sentry project; the `surface` tag tells their
|
|
5113
|
+
// events apart in the shared issue stream.
|
|
5114
|
+
initialScope: { tags: { surface: "cli" } },
|
|
5115
|
+
// Errors only: do NOT register the OpenTelemetry ESM loader hooks. We
|
|
5116
|
+
// run no tracing / auto-instrumentation, and the hook calls the
|
|
5117
|
+
// deprecated `module.register()` (a `DEP0205` warning on Node >= 26 that
|
|
5118
|
+
// would print on every telemetry-on invocation). Disabling it keeps
|
|
5119
|
+
// stderr clean and skips the loader's startup cost.
|
|
5120
|
+
registerEsmLoaderHooks: false,
|
|
5121
|
+
defaultIntegrations: false,
|
|
5122
|
+
integrations: [
|
|
5123
|
+
Sentry.onUncaughtExceptionIntegration(),
|
|
5124
|
+
Sentry.onUnhandledRejectionIntegration()
|
|
5125
|
+
],
|
|
5126
|
+
tracesSampleRate: 0,
|
|
5127
|
+
sendDefaultPii: false,
|
|
5128
|
+
beforeSend: (event) => scrubEvent(event)
|
|
5129
|
+
});
|
|
5130
|
+
sdk = Sentry;
|
|
5131
|
+
}
|
|
5132
|
+
function setTelemetryVerbTag(verb) {
|
|
5133
|
+
if (!sdk || verb === void 0 || verb === "") return;
|
|
5134
|
+
sdk.setTag("verb", verb);
|
|
5135
|
+
}
|
|
5136
|
+
async function closeSentryCli(timeoutMs = 2e3) {
|
|
5137
|
+
if (!sdk) return;
|
|
5138
|
+
try {
|
|
5139
|
+
await sdk.close(timeoutMs);
|
|
5140
|
+
} catch {
|
|
5141
|
+
}
|
|
5142
|
+
}
|
|
5143
|
+
|
|
5144
|
+
// cli/telemetry/usage-collector.ts
|
|
5145
|
+
var BUILT_IN_PLUGIN_IDS = /* @__PURE__ */ new Set([
|
|
5146
|
+
"claude",
|
|
5147
|
+
"antigravity",
|
|
5148
|
+
"openai",
|
|
5149
|
+
"agent-skills",
|
|
5150
|
+
"core"
|
|
5151
|
+
]);
|
|
5152
|
+
var EXTERNAL_PLUGIN_PLACEHOLDER = "external_plugin";
|
|
5153
|
+
function qualifyExtensionForUsage(qualifiedId2) {
|
|
5154
|
+
const slash = qualifiedId2.indexOf("/");
|
|
5155
|
+
if (slash <= 0) return EXTERNAL_PLUGIN_PLACEHOLDER;
|
|
5156
|
+
const pluginId = qualifiedId2.slice(0, slash);
|
|
5157
|
+
return BUILT_IN_PLUGIN_IDS.has(pluginId) ? qualifiedId2 : EXTERNAL_PLUGIN_PLACEHOLDER;
|
|
5158
|
+
}
|
|
5159
|
+
function buildScanExtensionSet(executedExtensionIds) {
|
|
5160
|
+
const out = /* @__PURE__ */ new Set();
|
|
5161
|
+
for (const id of executedExtensionIds) {
|
|
5162
|
+
out.add(qualifyExtensionForUsage(id));
|
|
5163
|
+
}
|
|
5164
|
+
return [...out].sort();
|
|
5165
|
+
}
|
|
5166
|
+
function extractFlagNames(args2) {
|
|
5167
|
+
const out = /* @__PURE__ */ new Set();
|
|
5168
|
+
for (const arg of args2) {
|
|
5169
|
+
if (!arg.startsWith("-")) continue;
|
|
5170
|
+
const name = arg.replace(/^-+/, "").split("=")[0];
|
|
5171
|
+
if (name !== void 0 && name !== "") out.add(name);
|
|
5172
|
+
}
|
|
5173
|
+
return [...out].sort();
|
|
5174
|
+
}
|
|
5175
|
+
function cliVerbEventName(verb, knownVerbs) {
|
|
5176
|
+
return `cli.${knownVerbs.has(verb) ? verb : "unknown"}`;
|
|
5177
|
+
}
|
|
5178
|
+
function buildCliVerbProperties(flagNames, extensions) {
|
|
5179
|
+
const flags = [...new Set(flagNames)].sort();
|
|
5180
|
+
return extensions ? { flags, extensions } : { flags };
|
|
5181
|
+
}
|
|
5182
|
+
function envUsageProps(cliVersion) {
|
|
5183
|
+
return {
|
|
5184
|
+
cli_version: cliVersion,
|
|
5185
|
+
node_major: Number.parseInt(process.versions.node.split(".")[0] ?? "0", 10),
|
|
5186
|
+
os: process.platform,
|
|
5187
|
+
arch: process.arch,
|
|
5188
|
+
environment: resolveTelemetryEnv()
|
|
5189
|
+
};
|
|
5190
|
+
}
|
|
5191
|
+
|
|
5192
|
+
// cli/telemetry/posthog-init.ts
|
|
5193
|
+
var client = null;
|
|
5194
|
+
function isUsageKeyConfigured() {
|
|
5195
|
+
return POSTHOG_KEY_NODE !== "";
|
|
5196
|
+
}
|
|
5197
|
+
function isUsageCliTelemetryActive(key) {
|
|
5198
|
+
if (isTelemetryForcedOff()) return false;
|
|
5199
|
+
if (key === "") return false;
|
|
5200
|
+
return isUsageCliTelemetryEnabled();
|
|
5201
|
+
}
|
|
5202
|
+
function scrubUsageEvent(event) {
|
|
5203
|
+
return event === null ? null : scrubEvent(event);
|
|
5204
|
+
}
|
|
5205
|
+
async function initUsageCli(loadSdk = () => import("posthog-node")) {
|
|
5206
|
+
if (client) return;
|
|
5207
|
+
if (!isUsageCliTelemetryActive(POSTHOG_KEY_NODE)) return;
|
|
5208
|
+
const { PostHog } = await loadSdk();
|
|
5209
|
+
client = new PostHog(POSTHOG_KEY_NODE, {
|
|
5210
|
+
host: POSTHOG_HOST,
|
|
5211
|
+
// Second line of defense behind the project-level IP drop: the client
|
|
5212
|
+
// never attaches an IP or geo, and never autocaptures.
|
|
5213
|
+
disableGeoip: true,
|
|
5214
|
+
before_send: scrubUsageEvent
|
|
5215
|
+
});
|
|
5216
|
+
}
|
|
5217
|
+
function captureUsage(event, properties) {
|
|
5218
|
+
if (client === null) return;
|
|
5219
|
+
const distinctId = readAnonymousId();
|
|
5220
|
+
if (distinctId === null) return;
|
|
5221
|
+
client.capture({
|
|
5222
|
+
distinctId,
|
|
5223
|
+
event,
|
|
5224
|
+
properties: { ...envUsageProps(VERSION), ...properties }
|
|
5225
|
+
});
|
|
5226
|
+
}
|
|
5227
|
+
var pendingScanExtensions = null;
|
|
5228
|
+
function setScanExtensions(extensions) {
|
|
5229
|
+
pendingScanExtensions = extensions;
|
|
5230
|
+
}
|
|
5231
|
+
function captureCliInvocation(verb, flagNames, knownVerbs) {
|
|
5232
|
+
const extensions = pendingScanExtensions;
|
|
5233
|
+
pendingScanExtensions = null;
|
|
5234
|
+
captureUsage(cliVerbEventName(verb, knownVerbs), buildCliVerbProperties(flagNames, extensions));
|
|
5235
|
+
}
|
|
5236
|
+
async function flushUsageCli(timeoutMs = 2e3) {
|
|
5237
|
+
if (client === null) return;
|
|
5238
|
+
try {
|
|
5239
|
+
await client.shutdown(timeoutMs);
|
|
5240
|
+
} catch {
|
|
5241
|
+
}
|
|
5242
|
+
}
|
|
5243
|
+
|
|
5244
|
+
// cli/telemetry/first-run-prompt.ts
|
|
5245
|
+
function interpretConsentAnswer(raw) {
|
|
5246
|
+
const value = raw.trim().toLowerCase();
|
|
5247
|
+
if (value === "n" || value === "no") return "no";
|
|
5248
|
+
if (value === "d" || value === "details") return "details";
|
|
5249
|
+
return "yes";
|
|
5250
|
+
}
|
|
5251
|
+
function isPromptEligible(opts) {
|
|
5252
|
+
return opts.dsnConfigured && opts.isTTY && !opts.isCI && !opts.forcedOff && !opts.alreadyPrompted;
|
|
5253
|
+
}
|
|
5254
|
+
function liveGateInputs(stdout) {
|
|
5255
|
+
return {
|
|
5256
|
+
dsnConfigured: isCliDsnConfigured() || isUsageKeyConfigured(),
|
|
5257
|
+
isTTY: stdout.isTTY === true,
|
|
5258
|
+
isCI: Boolean(process.env["CI"]),
|
|
5259
|
+
forcedOff: isTelemetryForcedOff(),
|
|
5260
|
+
alreadyPrompted: hasTelemetryPromptBeenShown()
|
|
5261
|
+
};
|
|
5262
|
+
}
|
|
5263
|
+
function renderConsent(ansi) {
|
|
5264
|
+
const t = TELEMETRY_PROMPT_TEXTS;
|
|
5265
|
+
const answerLine = ` ${t.question} ${ansi.bold(t.answerYes)} ${t.answerNo} ${ansi.dim(t.answerDetails)} `;
|
|
5266
|
+
return {
|
|
5267
|
+
question: [
|
|
5268
|
+
` ${ansi.cyan("\u2139")} ${ansi.bold(t.title)}`,
|
|
5269
|
+
...t.intro.map((line) => ` ${line}`),
|
|
5270
|
+
"",
|
|
5271
|
+
answerLine
|
|
5272
|
+
].join("\n"),
|
|
5273
|
+
reprompt: answerLine,
|
|
5274
|
+
details: [
|
|
5275
|
+
"",
|
|
5276
|
+
` ${t.detailsSentTitle}`,
|
|
5277
|
+
...t.detailsSent.map((line) => ` ${ansi.dim("\u2192")} ${line}`),
|
|
5278
|
+
` ${t.detailsNeverTitle}`,
|
|
5279
|
+
...t.detailsNever.map((line) => ` ${ansi.red("\u2715")} ${line}`),
|
|
5280
|
+
"",
|
|
5281
|
+
` ${ansi.dim(t.detailsHint)}`,
|
|
5282
|
+
""
|
|
5283
|
+
].join("\n"),
|
|
5284
|
+
enabled: ` ${ansi.green("\u2713")} ${t.enabled}
|
|
5285
|
+
`,
|
|
5286
|
+
disabled: ` ${ansi.cyan("\u2139")} ${t.disabled}
|
|
5287
|
+
`
|
|
5288
|
+
};
|
|
5289
|
+
}
|
|
5290
|
+
async function readConsentDecision(rl, stdout, rendered) {
|
|
5291
|
+
let answer = interpretConsentAnswer(await rl.question(rendered.question));
|
|
5292
|
+
while (answer === "details") {
|
|
5293
|
+
stdout.write(rendered.details);
|
|
5294
|
+
answer = interpretConsentAnswer(await rl.question(rendered.reprompt));
|
|
5295
|
+
}
|
|
5296
|
+
return answer === "yes";
|
|
5297
|
+
}
|
|
5298
|
+
async function runConsentPrompt(stdin, stdout, nowMs) {
|
|
5299
|
+
const rendered = renderConsent(
|
|
5300
|
+
ansiFor({ isTTY: stdout.isTTY === true, noColorFlag: false })
|
|
5301
|
+
);
|
|
5302
|
+
const rl = createInterface({ input: stdin, output: stdout });
|
|
5303
|
+
try {
|
|
5304
|
+
const consented = await readConsentDecision(rl, stdout, rendered);
|
|
5305
|
+
writeUserSettings({
|
|
5306
|
+
telemetry: {
|
|
5307
|
+
errorsEnabled: consented,
|
|
5308
|
+
usageCliEnabled: consented,
|
|
5309
|
+
usageUiEnabled: consented,
|
|
5310
|
+
promptedAt: nowMs
|
|
5311
|
+
}
|
|
5312
|
+
});
|
|
5313
|
+
if (consented) ensureAnonymousId();
|
|
5314
|
+
stdout.write(consented ? rendered.enabled : rendered.disabled);
|
|
5315
|
+
} catch {
|
|
5316
|
+
} finally {
|
|
5317
|
+
rl.close();
|
|
5318
|
+
}
|
|
5319
|
+
}
|
|
5320
|
+
async function maybeRunFirstRunPrompt({
|
|
5321
|
+
stdin = process.stdin,
|
|
5322
|
+
stdout = process.stdout,
|
|
5323
|
+
nowMs = Date.now()
|
|
5324
|
+
} = {}) {
|
|
5325
|
+
if (!isPromptEligible(liveGateInputs(stdout))) return;
|
|
5326
|
+
if (!hasSeenFirstRun()) {
|
|
5327
|
+
writeUserSettings({ telemetry: { firstRunAt: nowMs } });
|
|
5328
|
+
return;
|
|
5329
|
+
}
|
|
5330
|
+
await runConsentPrompt(stdin, stdout, nowMs);
|
|
5331
|
+
}
|
|
5332
|
+
|
|
4946
5333
|
// cli/commands/bump.ts
|
|
4947
5334
|
import { Command as Command2, Option as Option2 } from "clipanion";
|
|
4948
5335
|
|
|
@@ -5625,13 +6012,13 @@ var CONSENT_TEXTS = {
|
|
|
5625
6012
|
};
|
|
5626
6013
|
|
|
5627
6014
|
// cli/util/confirm.ts
|
|
5628
|
-
import { createInterface } from "readline";
|
|
6015
|
+
import { createInterface as createInterface2 } from "readline";
|
|
5629
6016
|
var YES_PATTERN = new RegExp(UTIL_TEXTS.confirmYesPatternSource, "i");
|
|
5630
6017
|
var NO_PATTERN = new RegExp(UTIL_TEXTS.confirmNoPatternSource, "i");
|
|
5631
6018
|
async function confirm(question, streams, opts) {
|
|
5632
6019
|
const defaultAnswer = opts?.defaultAnswer ?? "no";
|
|
5633
6020
|
const suffix = defaultAnswer === "yes" ? UTIL_TEXTS.confirmPromptSuffixDefaultYes : UTIL_TEXTS.confirmPromptSuffix;
|
|
5634
|
-
const rl =
|
|
6021
|
+
const rl = createInterface2({ input: streams.stdin, output: streams.stderr });
|
|
5635
6022
|
try {
|
|
5636
6023
|
const answer = await new Promise(
|
|
5637
6024
|
(resolveP) => rl.question(`${question}${suffix}`, resolveP)
|
|
@@ -7904,7 +8291,7 @@ var SqliteStorageAdapter = class {
|
|
|
7904
8291
|
/**
|
|
7905
8292
|
* Access the underlying Kysely instance.
|
|
7906
8293
|
*
|
|
7907
|
-
* Test-only escape hatch (per
|
|
8294
|
+
* Test-only escape hatch (per context/kernel.md §Kernel boundaries, tests
|
|
7908
8295
|
* are the documented exception). CLI commands MUST consume the
|
|
7909
8296
|
* adapter through the namespaced port surfaces (`port.<namespace>.*`
|
|
7910
8297
|
* or `port.transaction(...)`); reaching for this getter from a
|
|
@@ -10243,8 +10630,8 @@ var DEFAULT_READ_CONFIG = Object.freeze({
|
|
|
10243
10630
|
});
|
|
10244
10631
|
function resolveProviderWalk(provider) {
|
|
10245
10632
|
if (provider.walk) {
|
|
10246
|
-
const
|
|
10247
|
-
return
|
|
10633
|
+
const walk3 = provider.walk.bind(provider);
|
|
10634
|
+
return walk3;
|
|
10248
10635
|
}
|
|
10249
10636
|
const read = provider.read ?? DEFAULT_READ_CONFIG;
|
|
10250
10637
|
return (roots, options) => {
|
|
@@ -13870,8 +14257,7 @@ var HelpCommand = class extends Command15 {
|
|
|
13870
14257
|
|
|
13871
14258
|
Formats:
|
|
13872
14259
|
human (default): pretty terminal output.
|
|
13873
|
-
md : canonical markdown
|
|
13874
|
-
regenerated from this and CI fails on drift.
|
|
14260
|
+
md : canonical markdown for documentation.
|
|
13875
14261
|
json : structured surface dump per spec/cli-contract.md.
|
|
13876
14262
|
`
|
|
13877
14263
|
});
|
|
@@ -15508,11 +15894,11 @@ import { join as join13, relative as relative4, sep as sep4 } from "path";
|
|
|
15508
15894
|
function discoverOrphanSidecars(roots, shouldSkip) {
|
|
15509
15895
|
const out = [];
|
|
15510
15896
|
for (const root of roots) {
|
|
15511
|
-
|
|
15897
|
+
walk2(root, root, shouldSkip ?? (() => false), out);
|
|
15512
15898
|
}
|
|
15513
15899
|
return out;
|
|
15514
15900
|
}
|
|
15515
|
-
function
|
|
15901
|
+
function walk2(root, current, shouldSkip, out) {
|
|
15516
15902
|
let entries;
|
|
15517
15903
|
try {
|
|
15518
15904
|
entries = readdirSync7(current, { withFileTypes: true, encoding: "utf8" });
|
|
@@ -15525,7 +15911,7 @@ function walk(root, current, shouldSkip, out) {
|
|
|
15525
15911
|
if (shouldSkip(rel)) continue;
|
|
15526
15912
|
if (entry.isSymbolicLink()) continue;
|
|
15527
15913
|
if (entry.isDirectory()) {
|
|
15528
|
-
|
|
15914
|
+
walk2(root, full, shouldSkip, out);
|
|
15529
15915
|
continue;
|
|
15530
15916
|
}
|
|
15531
15917
|
if (!entry.isFile()) continue;
|
|
@@ -16149,7 +16535,8 @@ function buildPostWalkTransformCtx(providers, nodes, activeProvider) {
|
|
|
16149
16535
|
const reservedNodePaths = buildReservedNodePaths(
|
|
16150
16536
|
nodes,
|
|
16151
16537
|
kindRegistry,
|
|
16152
|
-
reservedNamesByProviderKind
|
|
16538
|
+
reservedNamesByProviderKind,
|
|
16539
|
+
activeProvider
|
|
16153
16540
|
);
|
|
16154
16541
|
return { kindRegistry, providerResolution, activeProvider, reservedNodePaths };
|
|
16155
16542
|
}
|
|
@@ -16180,19 +16567,23 @@ function indexReservedNames(provider, out) {
|
|
|
16180
16567
|
}
|
|
16181
16568
|
}
|
|
16182
16569
|
}
|
|
16183
|
-
function buildReservedNodePaths(nodes, kindRegistry, reservedNamesByProviderKind) {
|
|
16570
|
+
function buildReservedNodePaths(nodes, kindRegistry, reservedNamesByProviderKind, activeProvider) {
|
|
16184
16571
|
const out = /* @__PURE__ */ new Set();
|
|
16185
16572
|
for (const node of nodes) {
|
|
16186
|
-
const
|
|
16187
|
-
const
|
|
16188
|
-
|
|
16189
|
-
|
|
16190
|
-
|
|
16573
|
+
const selfKey = `${node.provider}/${node.kind}`;
|
|
16574
|
+
const selfReserved = reservedNamesByProviderKind.get(selfKey);
|
|
16575
|
+
const lensReserved = activeProvider && activeProvider !== node.provider ? reservedNamesByProviderKind.get(`${activeProvider}/${node.kind}`) : void 0;
|
|
16576
|
+
if (!hasEntries(selfReserved) && !hasEntries(lensReserved)) continue;
|
|
16577
|
+
const ids = deriveNodeIdentifiers(node, kindRegistry.get(selfKey));
|
|
16578
|
+
if (ids.some((id) => selfReserved?.has(id) === true || lensReserved?.has(id) === true)) {
|
|
16191
16579
|
out.add(node.path);
|
|
16192
16580
|
}
|
|
16193
16581
|
}
|
|
16194
16582
|
return out;
|
|
16195
16583
|
}
|
|
16584
|
+
function hasEntries(set) {
|
|
16585
|
+
return set !== void 0 && set.size > 0;
|
|
16586
|
+
}
|
|
16196
16587
|
function buildScanSetup(options) {
|
|
16197
16588
|
const start = Date.now();
|
|
16198
16589
|
const emitter = options.emitter ?? new InMemoryProgressEmitter();
|
|
@@ -16621,13 +17012,13 @@ var SCAN_RUNNER_TEXTS = {
|
|
|
16621
17012
|
* Active-provider bootstrap: filesystem auto-detect found exactly
|
|
16622
17013
|
* one marker and persisted the detected id to project settings.
|
|
16623
17014
|
*/
|
|
16624
|
-
activeProviderAutodetected: "Auto-detected activeProvider = {{id}} from filesystem markers; persisted to .skill-map/settings.json
|
|
17015
|
+
activeProviderAutodetected: "Auto-detected activeProvider = {{id}} from filesystem markers; persisted to .skill-map/settings.json.\n",
|
|
16625
17016
|
/**
|
|
16626
17017
|
* Active-provider bootstrap: persistence of the auto-detected id
|
|
16627
17018
|
* failed (permission, disk full, etc). Non-fatal; the scan
|
|
16628
17019
|
* continues with the value in memory for this run.
|
|
16629
17020
|
*/
|
|
16630
|
-
activeProviderPersistFailed: "Auto-detected activeProvider = {{id}}, but persisting to .skill-map/settings.json failed: {{message}}. Run `sm config set activeProvider {{id}}` manually to make the choice sticky
|
|
17021
|
+
activeProviderPersistFailed: "Auto-detected activeProvider = {{id}}, but persisting to .skill-map/settings.json failed: {{message}}. Run `sm config set activeProvider {{id}}` manually to make the choice sticky.\n",
|
|
16631
17022
|
/**
|
|
16632
17023
|
* Active-provider bootstrap: ambiguous detection (2+ markers
|
|
16633
17024
|
* present), interactive prompt header. Follows
|
|
@@ -16754,7 +17145,7 @@ function safeStat(path) {
|
|
|
16754
17145
|
}
|
|
16755
17146
|
|
|
16756
17147
|
// core/runtime/active-provider-bootstrap.ts
|
|
16757
|
-
import { createInterface as
|
|
17148
|
+
import { createInterface as createInterface3 } from "readline";
|
|
16758
17149
|
import { isAbsolute as isAbsolute9, join as join16 } from "path";
|
|
16759
17150
|
async function bootstrapActiveProvider(opts) {
|
|
16760
17151
|
const fromCwd = resolveActiveProvider(opts.cwd, opts.providers);
|
|
@@ -16910,7 +17301,7 @@ async function promptForLens(detected, stdin, stderr, warnGlyph) {
|
|
|
16910
17301
|
);
|
|
16911
17302
|
}
|
|
16912
17303
|
stderr.write(lines.join("\n") + "\n");
|
|
16913
|
-
const rl =
|
|
17304
|
+
const rl = createInterface3({ input: stdin, output: stderr });
|
|
16914
17305
|
try {
|
|
16915
17306
|
const answer = await new Promise(
|
|
16916
17307
|
(resolveP) => rl.question(SCAN_RUNNER_TEXTS.activeProviderPromptInput, resolveP)
|
|
@@ -16929,7 +17320,7 @@ async function promptForLens(detected, stdin, stderr, warnGlyph) {
|
|
|
16929
17320
|
|
|
16930
17321
|
// core/sqlite/db-drift-reset.ts
|
|
16931
17322
|
import { existsSync as existsSync23 } from "fs";
|
|
16932
|
-
import { createInterface as
|
|
17323
|
+
import { createInterface as createInterface4 } from "readline";
|
|
16933
17324
|
import { DatabaseSync as DatabaseSync7 } from "node:sqlite";
|
|
16934
17325
|
|
|
16935
17326
|
// core/sqlite/i18n/db-drift.texts.ts
|
|
@@ -16996,7 +17387,7 @@ async function askDriftReset(dbVersion, policy) {
|
|
|
16996
17387
|
hint: dim(DB_DRIFT_TEXTS.driftPromptHint)
|
|
16997
17388
|
})
|
|
16998
17389
|
);
|
|
16999
|
-
const rl =
|
|
17390
|
+
const rl = createInterface4({ input: policy.stdin, output: policy.stderr });
|
|
17000
17391
|
try {
|
|
17001
17392
|
const answer = await new Promise(
|
|
17002
17393
|
(resolveP) => rl.question(DB_DRIFT_TEXTS.driftPromptQuestion, resolveP)
|
|
@@ -17032,9 +17423,9 @@ async function runScanForCommand(opts) {
|
|
|
17032
17423
|
const { cfg, ignoreFilter, strict, effectiveRoots } = scanInputs;
|
|
17033
17424
|
let referenceablePaths;
|
|
17034
17425
|
if (cfg.scan.referencePaths.length > 0) {
|
|
17035
|
-
const
|
|
17036
|
-
referenceablePaths =
|
|
17037
|
-
emitReferenceWalkAdvisory(
|
|
17426
|
+
const walk3 = walkReferencePaths(cfg.scan.referencePaths, ctx.cwd);
|
|
17427
|
+
referenceablePaths = walk3.paths;
|
|
17428
|
+
emitReferenceWalkAdvisory(walk3, opts);
|
|
17038
17429
|
}
|
|
17039
17430
|
const loadPrior = makePriorLoader(opts.noBuiltIns, strict);
|
|
17040
17431
|
const jobsDir = defaultProjectJobsDir(ctx);
|
|
@@ -17096,11 +17487,11 @@ async function resolveActiveLens(opts, ctx, effectiveRoots, pluginRuntime, provi
|
|
|
17096
17487
|
});
|
|
17097
17488
|
return { kind: "ok", activeProvider: bootstrap.activeProvider };
|
|
17098
17489
|
}
|
|
17099
|
-
function emitReferenceWalkAdvisory(
|
|
17100
|
-
if (
|
|
17490
|
+
function emitReferenceWalkAdvisory(walk3, opts) {
|
|
17491
|
+
if (walk3.truncated) {
|
|
17101
17492
|
opts.printer.warn(SCAN_RUNNER_TEXTS.referenceWalkTruncated);
|
|
17102
17493
|
}
|
|
17103
|
-
for (const missing of
|
|
17494
|
+
for (const missing of walk3.missingRoots) {
|
|
17104
17495
|
opts.printer.warn(
|
|
17105
17496
|
tx(SCAN_RUNNER_TEXTS.referenceWalkMissingRoot, { path: missing })
|
|
17106
17497
|
);
|
|
@@ -17279,7 +17670,8 @@ async function runPersistPath(opts, dbPath, jobsDir, strict, loadPrior, runScanW
|
|
|
17279
17670
|
renameOps: outcome.renameOps,
|
|
17280
17671
|
persistedTo: dbPath,
|
|
17281
17672
|
dbPath,
|
|
17282
|
-
strict
|
|
17673
|
+
strict,
|
|
17674
|
+
executedExtensionIds: outcome.extractorRuns.map((run) => run.extractorId)
|
|
17283
17675
|
};
|
|
17284
17676
|
}
|
|
17285
17677
|
async function runEphemeralPath(opts, dbPath, strict, loadPrior, runScanWith) {
|
|
@@ -17297,7 +17689,8 @@ async function runEphemeralPath(opts, dbPath, strict, loadPrior, runScanWith) {
|
|
|
17297
17689
|
renameOps: scanned.renameOps,
|
|
17298
17690
|
persistedTo: null,
|
|
17299
17691
|
dbPath,
|
|
17300
|
-
strict
|
|
17692
|
+
strict,
|
|
17693
|
+
executedExtensionIds: scanned.extractorRuns.map((run) => run.extractorId)
|
|
17301
17694
|
};
|
|
17302
17695
|
} catch (err) {
|
|
17303
17696
|
return { kind: "scan-error", message: formatErrorMessage(err) };
|
|
@@ -17318,7 +17711,7 @@ var INIT_TEXTS = {
|
|
|
17318
17711
|
* scan never inherits stale rows from a pre-current schema.
|
|
17319
17712
|
*/
|
|
17320
17713
|
removedPriorDb: "{{glyph}} Removed prior DB at {{path}} (--force reset)\n",
|
|
17321
|
-
runningFirstScan: "
|
|
17714
|
+
runningFirstScan: "\nRunning first scan...\n",
|
|
17322
17715
|
configLoadFailure: "{{glyph}} sm init: {{message}}\n",
|
|
17323
17716
|
scanFailed: "{{glyph}} sm init: scan failed: {{message}}\n",
|
|
17324
17717
|
firstScanSummary: "{{glyph}} First scan: {{nodes}} node{{nodesPlural}}, {{links}} link{{linksPlural}}, {{issues}} issue{{issuesPlural}}.\n",
|
|
@@ -20221,6 +20614,8 @@ var TogglePluginsBase = class extends SmCommand {
|
|
|
20221
20614
|
targets = lockError;
|
|
20222
20615
|
const keys = expandToKeys(targets);
|
|
20223
20616
|
await this.#persistKeys(keys, enabled);
|
|
20617
|
+
const set = buildScanExtensionSet(keys);
|
|
20618
|
+
captureUsage("plugin.apply", enabled ? { enabled: set, disabled: [] } : { enabled: [], disabled: set });
|
|
20224
20619
|
this.#renderSuccess(keys, enabled);
|
|
20225
20620
|
return ExitCode.Ok;
|
|
20226
20621
|
}
|
|
@@ -21183,6 +21578,29 @@ function stripFrontmatterFence(text) {
|
|
|
21183
21578
|
}
|
|
21184
21579
|
var REFRESH_COMMANDS = [RefreshCommand];
|
|
21185
21580
|
|
|
21581
|
+
// cli/i18n/intentional-fail.texts.ts
|
|
21582
|
+
var INTENTIONAL_FAIL_TEXTS = {
|
|
21583
|
+
triggering: "Triggering an intentional uncaught error to exercise Sentry error reporting...",
|
|
21584
|
+
errorMessage: "skill-map intentional failure (Sentry self-test)"
|
|
21585
|
+
};
|
|
21586
|
+
|
|
21587
|
+
// cli/commands/intentional-fail.ts
|
|
21588
|
+
var IntentionalFailCommand = class extends SmCommand {
|
|
21589
|
+
static paths = [["intentional-fail"]];
|
|
21590
|
+
// No `static usage` on purpose: that is what keeps the verb out of every
|
|
21591
|
+
// help / reference surface Clipanion drives from command definitions.
|
|
21592
|
+
// A self-test crash has no meaningful "done in <...>" line.
|
|
21593
|
+
emitElapsed = false;
|
|
21594
|
+
async run() {
|
|
21595
|
+
this.printer.warn(INTENTIONAL_FAIL_TEXTS.triggering);
|
|
21596
|
+
setTimeout(() => {
|
|
21597
|
+
throw new Error(INTENTIONAL_FAIL_TEXTS.errorMessage);
|
|
21598
|
+
}, 0);
|
|
21599
|
+
await new Promise((resolve40) => setTimeout(resolve40, 5e3));
|
|
21600
|
+
return 1;
|
|
21601
|
+
}
|
|
21602
|
+
};
|
|
21603
|
+
|
|
21186
21604
|
// cli/commands/scan.ts
|
|
21187
21605
|
import { Command as Command31, Option as Option29 } from "clipanion";
|
|
21188
21606
|
|
|
@@ -21414,9 +21832,9 @@ function createWatcherRuntime(opts) {
|
|
|
21414
21832
|
overrideMaxNodes: opts.maxNodesOverride ?? null
|
|
21415
21833
|
};
|
|
21416
21834
|
if (cfg.scan.referencePaths.length > 0) {
|
|
21417
|
-
const
|
|
21418
|
-
if (
|
|
21419
|
-
runOptions.referenceablePaths =
|
|
21835
|
+
const walk3 = walkReferencePaths(cfg.scan.referencePaths, cwd);
|
|
21836
|
+
if (walk3.paths.size > 0) {
|
|
21837
|
+
runOptions.referenceablePaths = walk3.paths;
|
|
21420
21838
|
runOptions.cwd = cwd;
|
|
21421
21839
|
}
|
|
21422
21840
|
}
|
|
@@ -21994,7 +22412,11 @@ var ScanCommand = class extends SmCommand {
|
|
|
21994
22412
|
style,
|
|
21995
22413
|
...parsedMaxNodes.value !== void 0 ? { maxNodes: parsedMaxNodes.value } : {}
|
|
21996
22414
|
});
|
|
21997
|
-
|
|
22415
|
+
if (outcome.kind === "ok") {
|
|
22416
|
+
setScanExtensions(buildScanExtensionSet(outcome.executedExtensionIds));
|
|
22417
|
+
return this.renderOutcome(outcome.result, outcome.persistedTo, outcome.dbPath, outcome.strict);
|
|
22418
|
+
}
|
|
22419
|
+
return this.renderFailure(outcome);
|
|
21998
22420
|
}
|
|
21999
22421
|
/**
|
|
22000
22422
|
* Parse `--max-nodes <N>`. Returns either the integer value (or
|
|
@@ -22512,7 +22934,7 @@ import { WebSocketServer } from "ws";
|
|
|
22512
22934
|
// server/app.ts
|
|
22513
22935
|
import { Hono } from "hono";
|
|
22514
22936
|
import { bodyLimit } from "hono/body-limit";
|
|
22515
|
-
import { HTTPException as
|
|
22937
|
+
import { HTTPException as HTTPException16 } from "hono/http-exception";
|
|
22516
22938
|
|
|
22517
22939
|
// core/config/service.ts
|
|
22518
22940
|
var ConfigService = class {
|
|
@@ -22749,6 +23171,10 @@ var SERVER_TEXTS = {
|
|
|
22749
23171
|
preferencesBodyEmpty: "Request body must contain at least one known preference (e.g. `updateCheck.enabled`).",
|
|
22750
23172
|
preferencesUpdateCheckNotObject: '`updateCheck` must be an object (e.g. `{"updateCheck": {"enabled": false}}`).',
|
|
22751
23173
|
preferencesUpdateCheckEnabledNotBoolean: "`updateCheck.enabled` must be a boolean.",
|
|
23174
|
+
preferencesTelemetryNotObject: '`telemetry` must be an object (e.g. `{"telemetry": {"errorsEnabled": true}}`).',
|
|
23175
|
+
preferencesTelemetryErrorsEnabledNotBoolean: "`telemetry.errorsEnabled` must be a boolean.",
|
|
23176
|
+
preferencesTelemetryUsageCliEnabledNotBoolean: "`telemetry.usageCliEnabled` must be a boolean.",
|
|
23177
|
+
preferencesTelemetryUsageUiEnabledNotBoolean: "`telemetry.usageUiEnabled` must be a boolean.",
|
|
22752
23178
|
preferencesPersistFailed: "Could not persist preferences: {{message}}",
|
|
22753
23179
|
// ---- project-preferences route (routes/project-preferences.ts) ----------
|
|
22754
23180
|
//
|
|
@@ -22908,6 +23334,53 @@ function createSecurityHeaders() {
|
|
|
22908
23334
|
};
|
|
22909
23335
|
}
|
|
22910
23336
|
|
|
23337
|
+
// server/telemetry/sentry.ts
|
|
23338
|
+
import { HTTPException } from "hono/http-exception";
|
|
23339
|
+
var sdk2 = null;
|
|
23340
|
+
async function initSentryBff(version, loadSdk = () => import("@sentry/node")) {
|
|
23341
|
+
if (sdk2) return;
|
|
23342
|
+
if (!isTelemetryActive(SENTRY_DSN_NODE)) return;
|
|
23343
|
+
const Sentry = await loadSdk();
|
|
23344
|
+
Sentry.init({
|
|
23345
|
+
dsn: SENTRY_DSN_NODE,
|
|
23346
|
+
release: `@skill-map/cli@${version}`,
|
|
23347
|
+
environment: resolveTelemetryEnv(),
|
|
23348
|
+
// Shared project with the CLI; the `surface` tag separates the two.
|
|
23349
|
+
initialScope: { tags: { surface: "bff" } },
|
|
23350
|
+
// Errors only: skip the OpenTelemetry ESM loader hooks (we run no
|
|
23351
|
+
// tracing). They call the deprecated `module.register()` (a Node >= 26
|
|
23352
|
+
// DEP0205 warning) and add startup cost for nothing here.
|
|
23353
|
+
registerEsmLoaderHooks: false,
|
|
23354
|
+
defaultIntegrations: false,
|
|
23355
|
+
integrations: [],
|
|
23356
|
+
tracesSampleRate: 0,
|
|
23357
|
+
sendDefaultPii: false,
|
|
23358
|
+
beforeSend: (event) => scrubEvent(event)
|
|
23359
|
+
});
|
|
23360
|
+
sdk2 = Sentry;
|
|
23361
|
+
}
|
|
23362
|
+
function shouldCaptureError(err) {
|
|
23363
|
+
if (err instanceof HTTPException) return err.status >= 500;
|
|
23364
|
+
return true;
|
|
23365
|
+
}
|
|
23366
|
+
function createSentryRequestCapture() {
|
|
23367
|
+
return async function sentryRequestCapture(c, next) {
|
|
23368
|
+
try {
|
|
23369
|
+
await next();
|
|
23370
|
+
} catch (err) {
|
|
23371
|
+
const client2 = sdk2;
|
|
23372
|
+
if (client2 && shouldCaptureError(err)) {
|
|
23373
|
+
client2.withScope((scope) => {
|
|
23374
|
+
scope.setTag("route", c.req.routePath ?? c.req.path);
|
|
23375
|
+
scope.setTag("method", c.req.method);
|
|
23376
|
+
client2.captureException(err);
|
|
23377
|
+
});
|
|
23378
|
+
}
|
|
23379
|
+
throw err;
|
|
23380
|
+
}
|
|
23381
|
+
};
|
|
23382
|
+
}
|
|
23383
|
+
|
|
22911
23384
|
// server/routes/annotations.ts
|
|
22912
23385
|
var ENVELOPE_KIND = "annotations.registered";
|
|
22913
23386
|
function registerAnnotationsRoute(app, deps) {
|
|
@@ -22924,10 +23397,10 @@ function registerAnnotationsRoute(app, deps) {
|
|
|
22924
23397
|
}
|
|
22925
23398
|
|
|
22926
23399
|
// server/routes/contributions.ts
|
|
22927
|
-
import { HTTPException as
|
|
23400
|
+
import { HTTPException as HTTPException3 } from "hono/http-exception";
|
|
22928
23401
|
|
|
22929
23402
|
// server/util/parse-query.ts
|
|
22930
|
-
import { HTTPException } from "hono/http-exception";
|
|
23403
|
+
import { HTTPException as HTTPException2 } from "hono/http-exception";
|
|
22931
23404
|
function parseCsv(value) {
|
|
22932
23405
|
if (value === void 0) return [];
|
|
22933
23406
|
return value.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
@@ -22936,7 +23409,7 @@ function parsePagination(query, defaults) {
|
|
|
22936
23409
|
const offset = parseNonNegativeInt(query.offset, "offset", 0);
|
|
22937
23410
|
const limit = parseNonNegativeInt(query.limit, "limit", defaults.limit);
|
|
22938
23411
|
if (limit > defaults.max) {
|
|
22939
|
-
throw new
|
|
23412
|
+
throw new HTTPException2(400, {
|
|
22940
23413
|
message: tx(SERVER_TEXTS.paginationLimitTooLarge, {
|
|
22941
23414
|
value: limit,
|
|
22942
23415
|
max: defaults.max
|
|
@@ -22950,7 +23423,7 @@ function parseBooleanFlag(value) {
|
|
|
22950
23423
|
}
|
|
22951
23424
|
function parseRequiredString(value, name) {
|
|
22952
23425
|
if (typeof value !== "string" || value.length === 0) {
|
|
22953
|
-
throw new
|
|
23426
|
+
throw new HTTPException2(400, {
|
|
22954
23427
|
message: tx(SERVER_TEXTS.queryRequiredString, { name })
|
|
22955
23428
|
});
|
|
22956
23429
|
}
|
|
@@ -22961,7 +23434,7 @@ function parseNonNegativeInt(raw, name, fallback) {
|
|
|
22961
23434
|
const trimmed = raw.trim();
|
|
22962
23435
|
const parsed = Number.parseInt(trimmed, 10);
|
|
22963
23436
|
if (!Number.isInteger(parsed) || parsed < 0 || String(parsed) !== trimmed) {
|
|
22964
|
-
throw new
|
|
23437
|
+
throw new HTTPException2(400, {
|
|
22965
23438
|
message: tx(SERVER_TEXTS.paginationInvalidInteger, { name, value: raw })
|
|
22966
23439
|
});
|
|
22967
23440
|
}
|
|
@@ -22972,7 +23445,7 @@ function parseNonNegativeInt(raw, name, fallback) {
|
|
|
22972
23445
|
var QUALIFIED_ID_SEGMENT = /^[A-Za-z0-9._-]+$/;
|
|
22973
23446
|
function parseQualifiedIdSegment(value, name) {
|
|
22974
23447
|
if (!QUALIFIED_ID_SEGMENT.test(value)) {
|
|
22975
|
-
throw new
|
|
23448
|
+
throw new HTTPException3(400, {
|
|
22976
23449
|
message: tx(SERVER_TEXTS.qualifiedIdMalformed, {
|
|
22977
23450
|
name,
|
|
22978
23451
|
value: sanitizeForTerminal(value)
|
|
@@ -23002,7 +23475,7 @@ function registerContributionsRoutes(app, deps) {
|
|
|
23002
23475
|
(e) => e.pluginId === pluginId && e.extensionId === extensionId && e.contributionId === contributionId
|
|
23003
23476
|
);
|
|
23004
23477
|
if (!catalogEntry) {
|
|
23005
|
-
throw new
|
|
23478
|
+
throw new HTTPException3(404, {
|
|
23006
23479
|
message: tx(SERVER_TEXTS.contributionUnknown, {
|
|
23007
23480
|
pluginId: sanitizeForTerminal(pluginId),
|
|
23008
23481
|
extensionId: sanitizeForTerminal(extensionId),
|
|
@@ -23033,7 +23506,7 @@ function registerContributionsRoutes(app, deps) {
|
|
|
23033
23506
|
}
|
|
23034
23507
|
|
|
23035
23508
|
// server/routes/config.ts
|
|
23036
|
-
import { HTTPException as
|
|
23509
|
+
import { HTTPException as HTTPException4 } from "hono/http-exception";
|
|
23037
23510
|
|
|
23038
23511
|
// server/envelope.ts
|
|
23039
23512
|
var REST_ENVELOPE_SCHEMA_VERSION = "1";
|
|
@@ -23072,7 +23545,7 @@ function registerConfigRoute(app, deps) {
|
|
|
23072
23545
|
try {
|
|
23073
23546
|
loaded = deps.configService.get();
|
|
23074
23547
|
} catch (err) {
|
|
23075
|
-
throw new
|
|
23548
|
+
throw new HTTPException4(500, { message: formatErrorMessage(err) });
|
|
23076
23549
|
}
|
|
23077
23550
|
for (const warn of loaded.warnings) {
|
|
23078
23551
|
log.warn(sanitizeForTerminal(warn));
|
|
@@ -23090,7 +23563,7 @@ function registerConfigRoute(app, deps) {
|
|
|
23090
23563
|
}
|
|
23091
23564
|
|
|
23092
23565
|
// server/routes/favorites.ts
|
|
23093
|
-
import { HTTPException as
|
|
23566
|
+
import { HTTPException as HTTPException5 } from "hono/http-exception";
|
|
23094
23567
|
|
|
23095
23568
|
// server/path-codec.ts
|
|
23096
23569
|
var PathCodecError = class extends Error {
|
|
@@ -23130,7 +23603,7 @@ function registerFavoritesRoutes(app, deps) {
|
|
|
23130
23603
|
}
|
|
23131
23604
|
);
|
|
23132
23605
|
if (!result || !result.found) {
|
|
23133
|
-
throw new
|
|
23606
|
+
throw new HTTPException5(404, {
|
|
23134
23607
|
message: tx(SERVER_TEXTS.nodeNotFound, { path: sanitizeForTerminal(nodePath) })
|
|
23135
23608
|
});
|
|
23136
23609
|
}
|
|
@@ -23150,14 +23623,14 @@ function decodePath(pathB64) {
|
|
|
23150
23623
|
return decodeNodePath(pathB64);
|
|
23151
23624
|
} catch (err) {
|
|
23152
23625
|
if (err instanceof PathCodecError) {
|
|
23153
|
-
throw new
|
|
23626
|
+
throw new HTTPException5(404, { message: SERVER_TEXTS.pathB64Malformed });
|
|
23154
23627
|
}
|
|
23155
23628
|
throw err;
|
|
23156
23629
|
}
|
|
23157
23630
|
}
|
|
23158
23631
|
|
|
23159
23632
|
// server/routes/graph.ts
|
|
23160
|
-
import { HTTPException as
|
|
23633
|
+
import { HTTPException as HTTPException6 } from "hono/http-exception";
|
|
23161
23634
|
var DEFAULT_FORMAT2 = "ascii";
|
|
23162
23635
|
var FORMAT_ID_PATTERN = /^[a-z0-9-]+$/;
|
|
23163
23636
|
var FORMAT_ID_MAX = 32;
|
|
@@ -23165,7 +23638,7 @@ function registerGraphRoute(app, deps) {
|
|
|
23165
23638
|
app.get("/api/graph", async (c) => {
|
|
23166
23639
|
const format = c.req.query("format") ?? DEFAULT_FORMAT2;
|
|
23167
23640
|
if (format.length > FORMAT_ID_MAX || !FORMAT_ID_PATTERN.test(format)) {
|
|
23168
|
-
throw new
|
|
23641
|
+
throw new HTTPException6(400, {
|
|
23169
23642
|
// Sanitize defensively, the regex above already rejects ANSI
|
|
23170
23643
|
// and control bytes, but the message interpolates user input
|
|
23171
23644
|
// and the BFF mirrors error envelopes into the server log.
|
|
@@ -23181,7 +23654,7 @@ function registerGraphRoute(app, deps) {
|
|
|
23181
23654
|
const formatter = formatters.find((f) => f.formatId === format);
|
|
23182
23655
|
if (!formatter) {
|
|
23183
23656
|
const available = formatters.map((f) => f.formatId).sort().join(", ");
|
|
23184
|
-
throw new
|
|
23657
|
+
throw new HTTPException6(400, {
|
|
23185
23658
|
message: tx(SERVER_TEXTS.graphUnknownFormat, {
|
|
23186
23659
|
format,
|
|
23187
23660
|
available: available || "(none)"
|
|
@@ -23354,7 +23827,7 @@ function registerLinksRoute(app, deps) {
|
|
|
23354
23827
|
}
|
|
23355
23828
|
|
|
23356
23829
|
// server/routes/nodes.ts
|
|
23357
|
-
import { HTTPException as
|
|
23830
|
+
import { HTTPException as HTTPException7 } from "hono/http-exception";
|
|
23358
23831
|
|
|
23359
23832
|
// server/node-body.ts
|
|
23360
23833
|
import { constants as fsConstants2 } from "fs";
|
|
@@ -23465,7 +23938,7 @@ function registerNodesRoutes(app, deps) {
|
|
|
23465
23938
|
nodePath = decodeNodePath(pathB64);
|
|
23466
23939
|
} catch (err) {
|
|
23467
23940
|
if (err instanceof PathCodecError) {
|
|
23468
|
-
throw new
|
|
23941
|
+
throw new HTTPException7(404, { message: SERVER_TEXTS.pathB64Malformed });
|
|
23469
23942
|
}
|
|
23470
23943
|
throw err;
|
|
23471
23944
|
}
|
|
@@ -23497,7 +23970,7 @@ function registerNodesRoutes(app, deps) {
|
|
|
23497
23970
|
const contributions = result?.contributions ?? [];
|
|
23498
23971
|
const tags = result?.tags ?? [];
|
|
23499
23972
|
if (!bundle) {
|
|
23500
|
-
throw new
|
|
23973
|
+
throw new HTTPException7(404, {
|
|
23501
23974
|
message: tx(SERVER_TEXTS.nodeNotFound, { path: nodePath })
|
|
23502
23975
|
});
|
|
23503
23976
|
}
|
|
@@ -23607,11 +24080,11 @@ async function groupContributionsByPath(rows) {
|
|
|
23607
24080
|
}
|
|
23608
24081
|
|
|
23609
24082
|
// server/routes/plugins.ts
|
|
23610
|
-
import { HTTPException as
|
|
24083
|
+
import { HTTPException as HTTPException9 } from "hono/http-exception";
|
|
23611
24084
|
|
|
23612
24085
|
// server/util/parse-body.ts
|
|
23613
24086
|
import { Ajv2020 as Ajv20207 } from "ajv/dist/2020.js";
|
|
23614
|
-
import { HTTPException as
|
|
24087
|
+
import { HTTPException as HTTPException8 } from "hono/http-exception";
|
|
23615
24088
|
function makeBodyValidator(schema, messages) {
|
|
23616
24089
|
const ajv = new Ajv20207({ strict: false, allErrors: false });
|
|
23617
24090
|
const validate = ajv.compile(schema);
|
|
@@ -23620,16 +24093,16 @@ function makeBodyValidator(schema, messages) {
|
|
|
23620
24093
|
try {
|
|
23621
24094
|
raw = await req.json();
|
|
23622
24095
|
} catch {
|
|
23623
|
-
throw new
|
|
24096
|
+
throw new HTTPException8(400, { message: messages.notJson });
|
|
23624
24097
|
}
|
|
23625
24098
|
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
|
|
23626
|
-
throw new
|
|
24099
|
+
throw new HTTPException8(400, { message: messages.notObject });
|
|
23627
24100
|
}
|
|
23628
24101
|
if (validate(raw)) {
|
|
23629
24102
|
return raw;
|
|
23630
24103
|
}
|
|
23631
24104
|
const message = resolveErrorMessage(validate.errors, messages);
|
|
23632
|
-
throw new
|
|
24105
|
+
throw new HTTPException8(400, { message });
|
|
23633
24106
|
};
|
|
23634
24107
|
}
|
|
23635
24108
|
function resolveErrorMessage(errors, messages) {
|
|
@@ -23730,18 +24203,18 @@ function registerPluginsRoute(app, deps) {
|
|
|
23730
24203
|
app.patch("/api/plugins/:id", async (c) => {
|
|
23731
24204
|
const id = c.req.param("id");
|
|
23732
24205
|
if (id.includes("/")) {
|
|
23733
|
-
throw new
|
|
24206
|
+
throw new HTTPException9(400, {
|
|
23734
24207
|
message: tx(SERVER_TEXTS.pluginsCascadeRouteQualifiedRejected, { id })
|
|
23735
24208
|
});
|
|
23736
24209
|
}
|
|
23737
24210
|
const handle = findHandle(id, deps);
|
|
23738
24211
|
if (!handle) {
|
|
23739
|
-
throw new
|
|
24212
|
+
throw new HTTPException9(404, {
|
|
23740
24213
|
message: tx(SERVER_TEXTS.pluginsUnknown, { id })
|
|
23741
24214
|
});
|
|
23742
24215
|
}
|
|
23743
24216
|
if (isPluginLocked(id)) {
|
|
23744
|
-
throw new
|
|
24217
|
+
throw new HTTPException9(403, {
|
|
23745
24218
|
message: tx(SERVER_TEXTS.pluginsLocked, { id })
|
|
23746
24219
|
});
|
|
23747
24220
|
}
|
|
@@ -23755,18 +24228,18 @@ function registerPluginsRoute(app, deps) {
|
|
|
23755
24228
|
const extensionId = c.req.param("extensionId");
|
|
23756
24229
|
const handle = findHandle(bundleId, deps);
|
|
23757
24230
|
if (!handle) {
|
|
23758
|
-
throw new
|
|
24231
|
+
throw new HTTPException9(404, {
|
|
23759
24232
|
message: tx(SERVER_TEXTS.pluginsUnknown, { id: bundleId })
|
|
23760
24233
|
});
|
|
23761
24234
|
}
|
|
23762
24235
|
if (!hasExtension(handle, extensionId)) {
|
|
23763
|
-
throw new
|
|
24236
|
+
throw new HTTPException9(404, {
|
|
23764
24237
|
message: tx(SERVER_TEXTS.pluginsExtensionUnknown, { bundleId, extensionId })
|
|
23765
24238
|
});
|
|
23766
24239
|
}
|
|
23767
24240
|
const qualified = qualifiedExtensionId(bundleId, extensionId);
|
|
23768
24241
|
if (isPluginLocked(qualified) || isPluginLocked(bundleId)) {
|
|
23769
|
-
throw new
|
|
24242
|
+
throw new HTTPException9(403, {
|
|
23770
24243
|
message: tx(SERVER_TEXTS.pluginsExtensionLocked, { bundleId, extensionId })
|
|
23771
24244
|
});
|
|
23772
24245
|
}
|
|
@@ -24042,7 +24515,7 @@ function hasExtension(handle, extensionId) {
|
|
|
24042
24515
|
}
|
|
24043
24516
|
|
|
24044
24517
|
// server/routes/preferences.ts
|
|
24045
|
-
import { HTTPException as
|
|
24518
|
+
import { HTTPException as HTTPException10 } from "hono/http-exception";
|
|
24046
24519
|
function registerPreferencesRoute(app, _deps) {
|
|
24047
24520
|
app.get("/api/preferences", (c) => {
|
|
24048
24521
|
return c.json(buildEnvelope());
|
|
@@ -24055,20 +24528,43 @@ function registerPreferencesRoute(app, _deps) {
|
|
|
24055
24528
|
}
|
|
24056
24529
|
function buildEnvelope() {
|
|
24057
24530
|
return {
|
|
24058
|
-
updateCheck: { enabled: isUpdateCheckEnabled() }
|
|
24531
|
+
updateCheck: { enabled: isUpdateCheckEnabled() },
|
|
24532
|
+
telemetry: {
|
|
24533
|
+
errorsEnabled: isErrorTelemetryEnabled(),
|
|
24534
|
+
usageCliEnabled: isUsageCliTelemetryEnabled(),
|
|
24535
|
+
usageUiEnabled: isUsageUiTelemetryEnabled(),
|
|
24536
|
+
anonymousId: readAnonymousId(),
|
|
24537
|
+
environment: resolveTelemetryEnv()
|
|
24538
|
+
}
|
|
24059
24539
|
};
|
|
24060
24540
|
}
|
|
24061
24541
|
function applyPatch(body) {
|
|
24062
|
-
|
|
24063
|
-
|
|
24542
|
+
try {
|
|
24543
|
+
if (body.updateCheck && typeof body.updateCheck.enabled === "boolean") {
|
|
24064
24544
|
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
24545
|
}
|
|
24546
|
+
if (body.telemetry) {
|
|
24547
|
+
applyTelemetryPatch(body.telemetry);
|
|
24548
|
+
}
|
|
24549
|
+
} catch (err) {
|
|
24550
|
+
throw new HTTPException10(400, {
|
|
24551
|
+
message: tx(SERVER_TEXTS.preferencesPersistFailed, {
|
|
24552
|
+
message: formatErrorMessage(err)
|
|
24553
|
+
})
|
|
24554
|
+
});
|
|
24555
|
+
}
|
|
24556
|
+
}
|
|
24557
|
+
function applyTelemetryPatch(t) {
|
|
24558
|
+
if (typeof t.errorsEnabled === "boolean") {
|
|
24559
|
+
writeUserSettings({ telemetry: { errorsEnabled: t.errorsEnabled } });
|
|
24560
|
+
}
|
|
24561
|
+
if (typeof t.usageCliEnabled === "boolean") {
|
|
24562
|
+
writeUserSettings({ telemetry: { usageCliEnabled: t.usageCliEnabled } });
|
|
24563
|
+
if (t.usageCliEnabled) ensureAnonymousId();
|
|
24564
|
+
}
|
|
24565
|
+
if (typeof t.usageUiEnabled === "boolean") {
|
|
24566
|
+
writeUserSettings({ telemetry: { usageUiEnabled: t.usageUiEnabled } });
|
|
24567
|
+
if (t.usageUiEnabled) ensureAnonymousId();
|
|
24072
24568
|
}
|
|
24073
24569
|
}
|
|
24074
24570
|
var PATCH_BODY_SCHEMA = {
|
|
@@ -24082,6 +24578,15 @@ var PATCH_BODY_SCHEMA = {
|
|
|
24082
24578
|
properties: {
|
|
24083
24579
|
enabled: { type: "boolean" }
|
|
24084
24580
|
}
|
|
24581
|
+
},
|
|
24582
|
+
telemetry: {
|
|
24583
|
+
type: "object",
|
|
24584
|
+
additionalProperties: false,
|
|
24585
|
+
properties: {
|
|
24586
|
+
errorsEnabled: { type: "boolean" },
|
|
24587
|
+
usageCliEnabled: { type: "boolean" },
|
|
24588
|
+
usageUiEnabled: { type: "boolean" }
|
|
24589
|
+
}
|
|
24085
24590
|
}
|
|
24086
24591
|
}
|
|
24087
24592
|
};
|
|
@@ -24092,12 +24597,16 @@ var parsePatchBody2 = makeBodyValidator(PATCH_BODY_SCHEMA, {
|
|
|
24092
24597
|
mapping: {
|
|
24093
24598
|
":minProperties": SERVER_TEXTS.preferencesBodyEmpty,
|
|
24094
24599
|
"/updateCheck:type:object": SERVER_TEXTS.preferencesUpdateCheckNotObject,
|
|
24095
|
-
"/updateCheck/enabled:type:boolean": SERVER_TEXTS.preferencesUpdateCheckEnabledNotBoolean
|
|
24600
|
+
"/updateCheck/enabled:type:boolean": SERVER_TEXTS.preferencesUpdateCheckEnabledNotBoolean,
|
|
24601
|
+
"/telemetry:type:object": SERVER_TEXTS.preferencesTelemetryNotObject,
|
|
24602
|
+
"/telemetry/errorsEnabled:type:boolean": SERVER_TEXTS.preferencesTelemetryErrorsEnabledNotBoolean,
|
|
24603
|
+
"/telemetry/usageCliEnabled:type:boolean": SERVER_TEXTS.preferencesTelemetryUsageCliEnabledNotBoolean,
|
|
24604
|
+
"/telemetry/usageUiEnabled:type:boolean": SERVER_TEXTS.preferencesTelemetryUsageUiEnabledNotBoolean
|
|
24096
24605
|
}
|
|
24097
24606
|
});
|
|
24098
24607
|
|
|
24099
24608
|
// server/routes/project-ignore.ts
|
|
24100
|
-
import { HTTPException as
|
|
24609
|
+
import { HTTPException as HTTPException11 } from "hono/http-exception";
|
|
24101
24610
|
|
|
24102
24611
|
// server/util/skillmapignore-io.ts
|
|
24103
24612
|
import { existsSync as existsSync26, readFileSync as readFileSync17, writeFileSync as writeFileSync2 } from "fs";
|
|
@@ -24184,12 +24693,12 @@ async function applyPatch2(deps, body) {
|
|
|
24184
24693
|
for (const raw of body.patterns) {
|
|
24185
24694
|
const t = raw.trim();
|
|
24186
24695
|
if (t.length === 0) {
|
|
24187
|
-
throw new
|
|
24696
|
+
throw new HTTPException11(400, {
|
|
24188
24697
|
message: SERVER_TEXTS.projectIgnorePatternEmpty
|
|
24189
24698
|
});
|
|
24190
24699
|
}
|
|
24191
24700
|
if (seen.has(t)) {
|
|
24192
|
-
throw new
|
|
24701
|
+
throw new HTTPException11(400, {
|
|
24193
24702
|
message: tx(SERVER_TEXTS.projectIgnorePatternDuplicate, { pattern: t })
|
|
24194
24703
|
});
|
|
24195
24704
|
}
|
|
@@ -24200,7 +24709,7 @@ async function applyPatch2(deps, body) {
|
|
|
24200
24709
|
try {
|
|
24201
24710
|
writePatterns(cwd, trimmed);
|
|
24202
24711
|
} catch (err) {
|
|
24203
|
-
throw new
|
|
24712
|
+
throw new HTTPException11(400, {
|
|
24204
24713
|
message: tx(SERVER_TEXTS.projectIgnorePersistFailed, {
|
|
24205
24714
|
message: formatErrorMessage(err)
|
|
24206
24715
|
})
|
|
@@ -24278,7 +24787,7 @@ var parsePatchBody3 = makeBodyValidator(PATCH_BODY_SCHEMA2, {
|
|
|
24278
24787
|
|
|
24279
24788
|
// server/routes/project-preferences.ts
|
|
24280
24789
|
import { statSync as statSync9 } from "fs";
|
|
24281
|
-
import { HTTPException as
|
|
24790
|
+
import { HTTPException as HTTPException12 } from "hono/http-exception";
|
|
24282
24791
|
function registerProjectPreferencesRoute(app, deps) {
|
|
24283
24792
|
app.get("/api/project-preferences", (c) => {
|
|
24284
24793
|
return c.json(buildEnvelope3(deps));
|
|
@@ -24306,7 +24815,7 @@ async function applyPatch3(deps, body) {
|
|
|
24306
24815
|
const cwd = deps.runtimeContext.cwd;
|
|
24307
24816
|
const missingPaths = collectMissingPaths(writes, cwd);
|
|
24308
24817
|
if (missingPaths.length > 0) {
|
|
24309
|
-
throw new
|
|
24818
|
+
throw new HTTPException12(400, {
|
|
24310
24819
|
message: tx(SERVER_TEXTS.projectPrefsPathNotFound, {
|
|
24311
24820
|
paths: missingPaths.join(", ")
|
|
24312
24821
|
})
|
|
@@ -24315,7 +24824,7 @@ async function applyPatch3(deps, body) {
|
|
|
24315
24824
|
const exposures = writes.map((w) => projectPathExposure({ key: w.key, value: w.value, cwd })).filter((e) => e.expandsSurface);
|
|
24316
24825
|
if (exposures.length > 0 && body.confirm !== true) {
|
|
24317
24826
|
const exposed = exposures.flatMap((e) => e.exposedPaths);
|
|
24318
|
-
throw new
|
|
24827
|
+
throw new HTTPException12(412, {
|
|
24319
24828
|
message: tx(SERVER_TEXTS.projectPrefsConfirmRequired, {
|
|
24320
24829
|
paths: exposed.join(", ")
|
|
24321
24830
|
})
|
|
@@ -24367,7 +24876,7 @@ function runWrite(w, cwd) {
|
|
|
24367
24876
|
try {
|
|
24368
24877
|
writeConfigValue(w.key, w.value, { target: "project-local", cwd });
|
|
24369
24878
|
} catch (err) {
|
|
24370
|
-
throw new
|
|
24879
|
+
throw new HTTPException12(400, {
|
|
24371
24880
|
message: tx(SERVER_TEXTS.projectPrefsPersistFailed, {
|
|
24372
24881
|
key: w.key,
|
|
24373
24882
|
message: formatErrorMessage(err)
|
|
@@ -24467,7 +24976,7 @@ var parsePatchBody4 = makeBodyValidator(PATCH_BODY_SCHEMA3, {
|
|
|
24467
24976
|
|
|
24468
24977
|
// server/routes/active-provider.ts
|
|
24469
24978
|
import { existsSync as existsSync27 } from "fs";
|
|
24470
|
-
import { HTTPException as
|
|
24979
|
+
import { HTTPException as HTTPException13 } from "hono/http-exception";
|
|
24471
24980
|
function registerActiveProviderRoute(app, deps) {
|
|
24472
24981
|
app.get("/api/active-provider", (c) => {
|
|
24473
24982
|
return c.json(buildEnvelope4(deps));
|
|
@@ -24492,7 +25001,7 @@ function applyLensSwitch(deps, newValue) {
|
|
|
24492
25001
|
try {
|
|
24493
25002
|
writeConfigValue("activeProvider", newValue, { target: "project", cwd });
|
|
24494
25003
|
} catch (err) {
|
|
24495
|
-
throw new
|
|
25004
|
+
throw new HTTPException13(400, {
|
|
24496
25005
|
message: tx(SERVER_TEXTS.activeProviderPersistFailed, {
|
|
24497
25006
|
message: formatErrorMessage(err)
|
|
24498
25007
|
})
|
|
@@ -24528,7 +25037,7 @@ var parsePatchBody5 = makeBodyValidator(PATCH_BODY_SCHEMA4, {
|
|
|
24528
25037
|
});
|
|
24529
25038
|
|
|
24530
25039
|
// server/routes/scan.ts
|
|
24531
|
-
import { HTTPException as
|
|
25040
|
+
import { HTTPException as HTTPException14 } from "hono/http-exception";
|
|
24532
25041
|
|
|
24533
25042
|
// server/scan-mutex.ts
|
|
24534
25043
|
var inFlight = null;
|
|
@@ -24694,7 +25203,7 @@ function registerScanRoute(app, deps) {
|
|
|
24694
25203
|
}
|
|
24695
25204
|
async function runPersistedScan(c, deps) {
|
|
24696
25205
|
if (deps.options.noBuiltIns || deps.options.noPlugins) {
|
|
24697
|
-
throw new
|
|
25206
|
+
throw new HTTPException14(400, { message: SERVER_TEXTS.scanPostRequiresFullPipeline });
|
|
24698
25207
|
}
|
|
24699
25208
|
const dbExists = await tryWithSqlite(
|
|
24700
25209
|
{ databasePath: deps.options.dbPath, autoBackup: false },
|
|
@@ -24731,7 +25240,7 @@ async function runPersistedScan(c, deps) {
|
|
|
24731
25240
|
...deps.options.maxNodes !== void 0 ? { maxNodes: deps.options.maxNodes } : {}
|
|
24732
25241
|
});
|
|
24733
25242
|
if (outcome.kind !== "ok") {
|
|
24734
|
-
throw new
|
|
25243
|
+
throw new HTTPException14(500, {
|
|
24735
25244
|
message: outcome.kind === "guard-trip" ? tx(SERVER_TEXTS.scanGuardTrip, { existing: outcome.existing }) : outcome.message
|
|
24736
25245
|
});
|
|
24737
25246
|
}
|
|
@@ -24739,7 +25248,7 @@ async function runPersistedScan(c, deps) {
|
|
|
24739
25248
|
});
|
|
24740
25249
|
} catch (err) {
|
|
24741
25250
|
if (err instanceof ScanBusyError) {
|
|
24742
|
-
throw new
|
|
25251
|
+
throw new HTTPException14(409, { message: SERVER_TEXTS.scanPostBusy });
|
|
24743
25252
|
}
|
|
24744
25253
|
throw err;
|
|
24745
25254
|
}
|
|
@@ -24800,7 +25309,7 @@ function groupTagsByPath2(rows) {
|
|
|
24800
25309
|
}
|
|
24801
25310
|
async function runFreshScan(deps) {
|
|
24802
25311
|
if (deps.options.noBuiltIns || deps.options.noPlugins) {
|
|
24803
|
-
throw new
|
|
25312
|
+
throw new HTTPException14(400, { message: SERVER_TEXTS.freshScanRequiresPipeline });
|
|
24804
25313
|
}
|
|
24805
25314
|
const resolveEnabledOverride = await buildBffResolverOverride(deps);
|
|
24806
25315
|
const outcome = await runScanForCommand({
|
|
@@ -24835,7 +25344,7 @@ async function runFreshScan(deps) {
|
|
|
24835
25344
|
...deps.options.maxNodes !== void 0 ? { maxNodes: deps.options.maxNodes } : {}
|
|
24836
25345
|
});
|
|
24837
25346
|
if (outcome.kind !== "ok") {
|
|
24838
|
-
throw new
|
|
25347
|
+
throw new HTTPException14(500, {
|
|
24839
25348
|
message: outcome.kind === "guard-trip" ? tx(SERVER_TEXTS.freshScanGuardTrip, { existing: outcome.existing }) : outcome.message
|
|
24840
25349
|
});
|
|
24841
25350
|
}
|
|
@@ -24875,7 +25384,7 @@ function emptyScanResult() {
|
|
|
24875
25384
|
}
|
|
24876
25385
|
|
|
24877
25386
|
// server/routes/sidecar.ts
|
|
24878
|
-
import { HTTPException as
|
|
25387
|
+
import { HTTPException as HTTPException15 } from "hono/http-exception";
|
|
24879
25388
|
import { resolve as resolve36 } from "path";
|
|
24880
25389
|
var STATUS_FRESH = "fresh";
|
|
24881
25390
|
var ENVELOPE_KIND2 = "sidecar.bumped";
|
|
@@ -24912,11 +25421,11 @@ function registerSidecarRoutes(app, deps) {
|
|
|
24912
25421
|
assertContained(deps.runtimeContext.cwd, node.path);
|
|
24913
25422
|
absPath = resolve36(deps.runtimeContext.cwd, node.path);
|
|
24914
25423
|
} catch (err) {
|
|
24915
|
-
throw new
|
|
25424
|
+
throw new HTTPException15(400, { message: formatErrorMessage(err) });
|
|
24916
25425
|
}
|
|
24917
25426
|
const result = invokeBump2(node, absPath, body);
|
|
24918
25427
|
if (result.report.ok === false && result.report.reason === "fresh") {
|
|
24919
|
-
throw new
|
|
25428
|
+
throw new HTTPException15(409, { message: SERVER_TEXTS.sidecarFreshRefusal });
|
|
24920
25429
|
}
|
|
24921
25430
|
if (result.report.ok === true && result.report.noop === true) {
|
|
24922
25431
|
const envelope2 = {
|
|
@@ -24943,7 +25452,7 @@ function registerSidecarRoutes(app, deps) {
|
|
|
24943
25452
|
}
|
|
24944
25453
|
} catch (err) {
|
|
24945
25454
|
if (err instanceof EConsentRequiredError) throw err;
|
|
24946
|
-
throw new
|
|
25455
|
+
throw new HTTPException15(500, { message: formatErrorMessage(err) });
|
|
24947
25456
|
}
|
|
24948
25457
|
if (body.confirm === true) {
|
|
24949
25458
|
deps.configService.reload();
|
|
@@ -24980,7 +25489,7 @@ async function loadNode(deps, nodePath) {
|
|
|
24980
25489
|
);
|
|
24981
25490
|
const node = persisted?.nodes.find((n) => n.path === nodePath);
|
|
24982
25491
|
if (!node) {
|
|
24983
|
-
throw new
|
|
25492
|
+
throw new HTTPException15(404, {
|
|
24984
25493
|
message: tx(SERVER_TEXTS.nodeNotFound, { path: sanitizeForTerminal(nodePath) })
|
|
24985
25494
|
});
|
|
24986
25495
|
}
|
|
@@ -24988,7 +25497,7 @@ async function loadNode(deps, nodePath) {
|
|
|
24988
25497
|
}
|
|
24989
25498
|
function invokeBump2(node, absPath, body) {
|
|
24990
25499
|
if (!nodeBumpAction.invoke) {
|
|
24991
|
-
throw new
|
|
25500
|
+
throw new HTTPException15(500, { message: SERVER_TEXTS.sidecarBumpInvokeMissing });
|
|
24992
25501
|
}
|
|
24993
25502
|
const input = {};
|
|
24994
25503
|
if (body.force === true) input.force = true;
|
|
@@ -25175,13 +25684,13 @@ function attachBroadcasterRoute(app, broadcaster) {
|
|
|
25175
25684
|
|
|
25176
25685
|
// server/app.ts
|
|
25177
25686
|
var BODY_LIMIT_BYTES = 1024 * 1024;
|
|
25178
|
-
var DbMissingError = class extends
|
|
25687
|
+
var DbMissingError = class extends HTTPException16 {
|
|
25179
25688
|
constructor(message) {
|
|
25180
25689
|
super(500, { message });
|
|
25181
25690
|
this.name = "DbMissingError";
|
|
25182
25691
|
}
|
|
25183
25692
|
};
|
|
25184
|
-
var BulkValidationError = class extends
|
|
25693
|
+
var BulkValidationError = class extends HTTPException16 {
|
|
25185
25694
|
id;
|
|
25186
25695
|
code;
|
|
25187
25696
|
constructor(init) {
|
|
@@ -25191,7 +25700,7 @@ var BulkValidationError = class extends HTTPException15 {
|
|
|
25191
25700
|
this.code = init.code;
|
|
25192
25701
|
}
|
|
25193
25702
|
};
|
|
25194
|
-
var LoopbackGateError = class extends
|
|
25703
|
+
var LoopbackGateError = class extends HTTPException16 {
|
|
25195
25704
|
code;
|
|
25196
25705
|
constructor(init) {
|
|
25197
25706
|
super(403, { message: init.message });
|
|
@@ -25204,6 +25713,7 @@ function createApp(deps) {
|
|
|
25204
25713
|
const configService = new ConfigService({
|
|
25205
25714
|
cwd: deps.runtimeContext.cwd
|
|
25206
25715
|
});
|
|
25716
|
+
app.use("*", createSentryRequestCapture());
|
|
25207
25717
|
app.use("*", createLoopbackGate({ port: deps.options.port }));
|
|
25208
25718
|
app.use("*", createSecurityHeaders());
|
|
25209
25719
|
app.use(
|
|
@@ -25211,7 +25721,7 @@ function createApp(deps) {
|
|
|
25211
25721
|
bodyLimit({
|
|
25212
25722
|
maxSize: BODY_LIMIT_BYTES,
|
|
25213
25723
|
onError: () => {
|
|
25214
|
-
throw new
|
|
25724
|
+
throw new HTTPException16(413, { message: tx(SERVER_TEXTS.bodyTooLarge, { maxBytes: String(BODY_LIMIT_BYTES) }) });
|
|
25215
25725
|
}
|
|
25216
25726
|
})
|
|
25217
25727
|
);
|
|
@@ -25257,7 +25767,7 @@ function createApp(deps) {
|
|
|
25257
25767
|
registerActiveProviderRoute(app, routeDeps);
|
|
25258
25768
|
registerProjectIgnoreRoute(app, routeDeps);
|
|
25259
25769
|
app.all("/api/*", (c) => {
|
|
25260
|
-
throw new
|
|
25770
|
+
throw new HTTPException16(404, {
|
|
25261
25771
|
message: tx(SERVER_TEXTS.unknownApiEndpoint, { path: sanitizeForTerminal(c.req.path) })
|
|
25262
25772
|
});
|
|
25263
25773
|
});
|
|
@@ -25265,7 +25775,7 @@ function createApp(deps) {
|
|
|
25265
25775
|
app.use("*", createStaticHandler({ uiDist: deps.options.uiDist, noUi: deps.options.noUi }));
|
|
25266
25776
|
app.get("*", createSpaFallback({ uiDist: deps.options.uiDist, noUi: deps.options.noUi }));
|
|
25267
25777
|
app.notFound((c) => {
|
|
25268
|
-
throw new
|
|
25778
|
+
throw new HTTPException16(404, {
|
|
25269
25779
|
message: tx(SERVER_TEXTS.unknownPath, { path: sanitizeForTerminal(c.req.path) })
|
|
25270
25780
|
});
|
|
25271
25781
|
});
|
|
@@ -25320,7 +25830,7 @@ function formatError2(err, c) {
|
|
|
25320
25830
|
};
|
|
25321
25831
|
return c.json(envelope, 403);
|
|
25322
25832
|
}
|
|
25323
|
-
if (err instanceof
|
|
25833
|
+
if (err instanceof HTTPException16) {
|
|
25324
25834
|
const status = err.status;
|
|
25325
25835
|
const envelope = {
|
|
25326
25836
|
ok: false,
|
|
@@ -25419,7 +25929,7 @@ var WsBroadcaster = class {
|
|
|
25419
25929
|
* stop the rest from receiving the event. A failing socket is closed
|
|
25420
25930
|
* + unregistered so the next broadcast doesn't waste cycles on it.
|
|
25421
25931
|
*
|
|
25422
|
-
* Backpressure check (per
|
|
25932
|
+
* Backpressure check (per context/kernel.md §Kernel boundaries): if a
|
|
25423
25933
|
* client's `bufferedAmount` exceeds `MAX_BUFFERED_BYTES`, it's evicted
|
|
25424
25934
|
* with close code 1009. The check runs BEFORE `send` so the threshold
|
|
25425
25935
|
* acts as an admission gate, not a post-mortem.
|
|
@@ -25439,8 +25949,8 @@ var WsBroadcaster = class {
|
|
|
25439
25949
|
return;
|
|
25440
25950
|
}
|
|
25441
25951
|
const snapshot = Array.from(this.#clients);
|
|
25442
|
-
for (const
|
|
25443
|
-
this.#deliver(
|
|
25952
|
+
for (const client2 of snapshot) {
|
|
25953
|
+
this.#deliver(client2, payload);
|
|
25444
25954
|
}
|
|
25445
25955
|
}
|
|
25446
25956
|
/**
|
|
@@ -25454,9 +25964,9 @@ var WsBroadcaster = class {
|
|
|
25454
25964
|
this.#shutDown = true;
|
|
25455
25965
|
const snapshot = Array.from(this.#clients);
|
|
25456
25966
|
this.#clients.clear();
|
|
25457
|
-
for (const
|
|
25967
|
+
for (const client2 of snapshot) {
|
|
25458
25968
|
try {
|
|
25459
|
-
|
|
25969
|
+
client2.close(CLOSE_CODE_GOING_AWAY, "server shutdown");
|
|
25460
25970
|
} catch {
|
|
25461
25971
|
}
|
|
25462
25972
|
}
|
|
@@ -25465,31 +25975,31 @@ var WsBroadcaster = class {
|
|
|
25465
25975
|
* Per-client delivery: backpressure check, then `send()`. Eviction +
|
|
25466
25976
|
* unregistration on either failure mode.
|
|
25467
25977
|
*/
|
|
25468
|
-
#deliver(
|
|
25469
|
-
if (
|
|
25470
|
-
this.#clients.delete(
|
|
25978
|
+
#deliver(client2, payload) {
|
|
25979
|
+
if (client2.readyState !== READY_STATE_OPEN) {
|
|
25980
|
+
this.#clients.delete(client2);
|
|
25471
25981
|
return;
|
|
25472
25982
|
}
|
|
25473
|
-
if (
|
|
25474
|
-
this.#clients.delete(
|
|
25983
|
+
if (client2.bufferedAmount > MAX_BUFFERED_BYTES) {
|
|
25984
|
+
this.#clients.delete(client2);
|
|
25475
25985
|
try {
|
|
25476
|
-
|
|
25986
|
+
client2.close(CLOSE_CODE_MESSAGE_TOO_BIG, "backpressure exceeded");
|
|
25477
25987
|
} catch {
|
|
25478
25988
|
}
|
|
25479
25989
|
log.warn(
|
|
25480
25990
|
tx(SERVER_TEXTS.wsBackpressureEvicted, {
|
|
25481
|
-
buffered: String(
|
|
25991
|
+
buffered: String(client2.bufferedAmount),
|
|
25482
25992
|
threshold: String(MAX_BUFFERED_BYTES)
|
|
25483
25993
|
})
|
|
25484
25994
|
);
|
|
25485
25995
|
return;
|
|
25486
25996
|
}
|
|
25487
25997
|
try {
|
|
25488
|
-
|
|
25998
|
+
client2.send(payload);
|
|
25489
25999
|
} catch (err) {
|
|
25490
|
-
this.#clients.delete(
|
|
26000
|
+
this.#clients.delete(client2);
|
|
25491
26001
|
try {
|
|
25492
|
-
|
|
26002
|
+
client2.close();
|
|
25493
26003
|
} catch {
|
|
25494
26004
|
}
|
|
25495
26005
|
const message = formatErrorMessage(err);
|
|
@@ -26250,7 +26760,7 @@ var ServeCommand = class extends SmCommand {
|
|
|
26250
26760
|
emitElapsed = false;
|
|
26251
26761
|
// CLI orchestrator with multi-flag handling, each `if (this.flag)`
|
|
26252
26762
|
// branch is one cyclomatic point. Splitting per branch scatters the
|
|
26253
|
-
// validation away from the flag it gates. Per
|
|
26763
|
+
// validation away from the flag it gates. Per context/lint.md
|
|
26254
26764
|
// category 1 ("CLI orchestrators with multi-flag handling").
|
|
26255
26765
|
// eslint-disable-next-line complexity
|
|
26256
26766
|
async run() {
|
|
@@ -26353,6 +26863,7 @@ var ServeCommand = class extends SmCommand {
|
|
|
26353
26863
|
this.printer.info(formatValidationError(validation.error, stderrAnsi));
|
|
26354
26864
|
return ExitCode.Error;
|
|
26355
26865
|
}
|
|
26866
|
+
await initSentryBff(VERSION);
|
|
26356
26867
|
let handle;
|
|
26357
26868
|
try {
|
|
26358
26869
|
handle = await createServer(validation.options);
|
|
@@ -27536,31 +28047,54 @@ var STUB_COMMANDS = [
|
|
|
27536
28047
|
];
|
|
27537
28048
|
|
|
27538
28049
|
// cli/commands/tutorial.ts
|
|
27539
|
-
import { cpSync as cpSync2, existsSync as existsSync31, mkdirSync as mkdirSync6, rmSync as rmSync2, statSync as statSync11 } from "fs";
|
|
28050
|
+
import { cpSync as cpSync2, existsSync as existsSync31, mkdirSync as mkdirSync6, readdirSync as readdirSync10, rmSync as rmSync2, statSync as statSync11 } from "fs";
|
|
27540
28051
|
import { dirname as dirname19, join as join21, resolve as resolve39 } from "path";
|
|
28052
|
+
import { createInterface as createInterface5 } from "readline";
|
|
27541
28053
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
27542
28054
|
import { Command as Command37, Option as Option35 } from "clipanion";
|
|
27543
28055
|
|
|
27544
28056
|
// cli/i18n/tutorial.texts.ts
|
|
27545
28057
|
var TUTORIAL_TEXTS = {
|
|
27546
28058
|
// Success, written to stdout after `<cwd>/{{target}}` is created.
|
|
27547
|
-
// The skill
|
|
28059
|
+
// The skill lives at `<skillDir>/<slug>/`; the tester's agent
|
|
27548
28060
|
// auto-discovers it on the next boot, so the tester invokes by
|
|
27549
28061
|
// speaking a trigger phrase rather than referencing the file path.
|
|
27550
|
-
//
|
|
27551
|
-
//
|
|
27552
|
-
//
|
|
27553
|
-
|
|
28062
|
+
// Message stays host-agnostic ("your coding agent") because the
|
|
28063
|
+
// destination Provider varies. English / Spanish triggers are
|
|
28064
|
+
// surfaced side by side and the first phrase the tester types sets
|
|
28065
|
+
// the tutorial language for the rest of the session.
|
|
28066
|
+
written: " {{glyph}} Skill `{{slug}}` materialized at {{target}} (under {{cwd}}, for {{provider}})\n\n Open your coding agent in this directory. The skill is auto-\n discovered; invoke it with one of its trigger phrases. The\n first message you type sets the tutorial language for the\n rest of the session:\n\n {{enLabel}} {{enTrigger}}\n {{esLabel}} {{esTrigger}}\n",
|
|
27554
28067
|
writtenLabelEn: "English",
|
|
27555
28068
|
writtenLabelEs: "Espa\xF1ol",
|
|
27556
|
-
//
|
|
27557
|
-
//
|
|
27558
|
-
//
|
|
27559
|
-
//
|
|
27560
|
-
|
|
27561
|
-
|
|
28069
|
+
// Destination-provider prompt (interactive stdin, no `--for`). Header
|
|
28070
|
+
// uses a yellow `?` glyph; options are a numbered list of provider
|
|
28071
|
+
// label (with any `aka` agents in parentheses) + skill directory, with
|
|
28072
|
+
// a `(default)` marker on the first option (Claude). The input line
|
|
28073
|
+
// accepts a number, a provider id, or an empty answer (which takes the
|
|
28074
|
+
// default).
|
|
28075
|
+
promptHeader: "{{glyph}} Which agent should host the tutorial skill?",
|
|
28076
|
+
promptOption: " {{index}}) {{label}}: {{skillDir}}{{marker}}",
|
|
28077
|
+
promptDefaultMarker: " (default)",
|
|
28078
|
+
promptInput: " Enter the number or provider id [default {{index}}]: ",
|
|
28079
|
+
// Prompt answer matched neither an index nor an id. Goes to stderr,
|
|
28080
|
+
// exit code 2. Mirrors the error shape: glyph + headline + dim hint.
|
|
28081
|
+
promptInvalid: "{{glyph}} sm tutorial: that is not one of the listed providers\n {{hint}}\n",
|
|
28082
|
+
// `--for` named a provider that does not exist or declares no
|
|
28083
|
+
// `scaffold.skillDir`. Goes to stderr, exit code 2.
|
|
28084
|
+
forUnknown: "{{glyph}} sm tutorial: unknown provider '{{provider}}' for --for\n {{hint}}\n",
|
|
28085
|
+
forUnknownHint: "Valid providers: {{ids}}.",
|
|
28086
|
+
// Defensive: no built-in provider declares a scaffold target. Should
|
|
28087
|
+
// never happen (claude always does). Goes to stderr, exit code 2.
|
|
28088
|
+
noTargets: "{{glyph}} sm tutorial: no provider declares a skill scaffold target.\n",
|
|
28089
|
+
// Refusal, the cwd is not empty and `--force` was not set. Goes to
|
|
28090
|
+
// stderr, exit code 2 (operational error per spec § Exit codes). The
|
|
28091
|
+
// tutorial seeds a self-contained scenario into the cwd, so it needs
|
|
28092
|
+
// an empty directory; the hint spells the two ways forward. Mirrors
|
|
28093
|
+
// the error shape: glyph + headline + dim hint.
|
|
28094
|
+
notEmpty: "{{glyph}} sm tutorial: the current directory is not empty (found {{entries}})\n {{hint}}\n",
|
|
28095
|
+
notEmptyHint: "sm tutorial seeds a self-contained scenario; run it in a fresh empty directory, or pass `--force` to use this one anyway.",
|
|
27562
28096
|
// Invalid `variant` positional argument. Goes to stderr, exit code 2.
|
|
27563
|
-
// Mirrors
|
|
28097
|
+
// Mirrors the error shape: glyph + headline + dim hint enumerating the
|
|
27564
28098
|
// valid values.
|
|
27565
28099
|
invalidVariant: "{{glyph}} sm tutorial: unknown variant '{{variant}}'\n {{hint}}\n",
|
|
27566
28100
|
invalidVariantHint: "Valid values: tutorial (default), master.",
|
|
@@ -27612,6 +28146,13 @@ var TutorialCommand = class extends SmCommand {
|
|
|
27612
28146
|
]
|
|
27613
28147
|
});
|
|
27614
28148
|
variant = Option35.String({ required: false });
|
|
28149
|
+
// Named `forProvider`, NOT `for` (reserved word). The CLI surface stays
|
|
28150
|
+
// `--for`; selects the destination Provider whose `scaffold.skillDir`
|
|
28151
|
+
// the skill is materialised under, skipping the interactive prompt.
|
|
28152
|
+
forProvider = Option35.String("--for", {
|
|
28153
|
+
required: false,
|
|
28154
|
+
description: "Destination provider id (e.g. claude, agent-skills). Skips the prompt."
|
|
28155
|
+
});
|
|
27615
28156
|
force = Option35.Boolean("--force", false, {
|
|
27616
28157
|
description: "Overwrite an existing target directory without prompting."
|
|
27617
28158
|
});
|
|
@@ -27620,32 +28161,24 @@ var TutorialCommand = class extends SmCommand {
|
|
|
27620
28161
|
const stderr = this.context.stderr;
|
|
27621
28162
|
const stderrAnsi = this.ansiFor("stderr");
|
|
27622
28163
|
const errGlyph = stderrAnsi.red("\u2715");
|
|
27623
|
-
const
|
|
27624
|
-
if (
|
|
27625
|
-
this.printer.error(
|
|
27626
|
-
tx(TUTORIAL_TEXTS.invalidVariant, {
|
|
27627
|
-
glyph: errGlyph,
|
|
27628
|
-
variant: rawVariant,
|
|
27629
|
-
hint: stderrAnsi.dim(TUTORIAL_TEXTS.invalidVariantHint)
|
|
27630
|
-
})
|
|
27631
|
-
);
|
|
27632
|
-
return ExitCode.Error;
|
|
27633
|
-
}
|
|
27634
|
-
const variant = rawVariant ?? DEFAULT_VARIANT;
|
|
28164
|
+
const variant = this.resolveVariantArg(errGlyph, stderrAnsi);
|
|
28165
|
+
if (variant === null) return ExitCode.Error;
|
|
27635
28166
|
const spec = VARIANT_SPECS[variant];
|
|
27636
|
-
|
|
27637
|
-
const targetDisplay = `.claude/skills/${spec.slug}/`;
|
|
27638
|
-
if (existsSync31(targetDir) && !this.force) {
|
|
28167
|
+
if (!this.force && !isDirEmpty(ctx.cwd)) {
|
|
27639
28168
|
this.printer.error(
|
|
27640
|
-
tx(TUTORIAL_TEXTS.
|
|
28169
|
+
tx(TUTORIAL_TEXTS.notEmpty, {
|
|
27641
28170
|
glyph: errGlyph,
|
|
27642
|
-
|
|
27643
|
-
|
|
27644
|
-
hint: stderrAnsi.dim(TUTORIAL_TEXTS.alreadyExistsHint)
|
|
28171
|
+
entries: listCwdEntries(ctx.cwd),
|
|
28172
|
+
hint: stderrAnsi.dim(TUTORIAL_TEXTS.notEmptyHint)
|
|
27645
28173
|
})
|
|
27646
28174
|
);
|
|
27647
28175
|
return ExitCode.Error;
|
|
27648
28176
|
}
|
|
28177
|
+
const targets = listScaffoldTargets();
|
|
28178
|
+
const target = await this.resolveScaffoldTarget(targets, stderrAnsi, errGlyph);
|
|
28179
|
+
if (target === null) return ExitCode.Error;
|
|
28180
|
+
const targetDir = join21(ctx.cwd, target.skillDir, spec.slug);
|
|
28181
|
+
const targetDisplay = `${target.skillDir}/${spec.slug}/`;
|
|
27649
28182
|
let sourceDir;
|
|
27650
28183
|
try {
|
|
27651
28184
|
sourceDir = resolveSkillSourceDir(variant);
|
|
@@ -27685,6 +28218,7 @@ var TutorialCommand = class extends SmCommand {
|
|
|
27685
28218
|
glyph: ansi.green("\u2713"),
|
|
27686
28219
|
slug: spec.slug,
|
|
27687
28220
|
target: targetDisplay,
|
|
28221
|
+
provider: ansi.dim(target.label),
|
|
27688
28222
|
cwd: ansi.dim(displayCwd(ctx.cwd)),
|
|
27689
28223
|
enLabel: ansi.dim(TUTORIAL_TEXTS.writtenLabelEn),
|
|
27690
28224
|
esLabel: ansi.dim(TUTORIAL_TEXTS.writtenLabelEs),
|
|
@@ -27694,15 +28228,154 @@ var TutorialCommand = class extends SmCommand {
|
|
|
27694
28228
|
);
|
|
27695
28229
|
return ExitCode.Ok;
|
|
27696
28230
|
}
|
|
28231
|
+
/**
|
|
28232
|
+
* Validate the positional `variant` arg against the closed catalog.
|
|
28233
|
+
* Returns the resolved variant, or `null` after printing the
|
|
28234
|
+
* `invalidVariant` error (caller exits non-zero). Extracted from
|
|
28235
|
+
* `run()` to keep its cyclomatic complexity within the lint budget.
|
|
28236
|
+
*/
|
|
28237
|
+
resolveVariantArg(errGlyph, stderrAnsi) {
|
|
28238
|
+
const rawVariant = this.variant;
|
|
28239
|
+
if (rawVariant !== void 0 && !isTutorialVariant(rawVariant)) {
|
|
28240
|
+
this.printer.error(
|
|
28241
|
+
tx(TUTORIAL_TEXTS.invalidVariant, {
|
|
28242
|
+
glyph: errGlyph,
|
|
28243
|
+
variant: rawVariant,
|
|
28244
|
+
hint: stderrAnsi.dim(TUTORIAL_TEXTS.invalidVariantHint)
|
|
28245
|
+
})
|
|
28246
|
+
);
|
|
28247
|
+
return null;
|
|
28248
|
+
}
|
|
28249
|
+
return rawVariant ?? DEFAULT_VARIANT;
|
|
28250
|
+
}
|
|
28251
|
+
/**
|
|
28252
|
+
* Resolve the destination Provider. Precedence:
|
|
28253
|
+
* 1. `--for <id>` (validated against the scaffold-capable catalog).
|
|
28254
|
+
* 2. Interactive stdin → numbered prompt defaulting to Claude (the
|
|
28255
|
+
* first entry); an empty answer accepts it.
|
|
28256
|
+
* 3. Non-interactive stdin → Claude (the first entry), so the verb
|
|
28257
|
+
* stays scriptable.
|
|
28258
|
+
* The verb requires an empty cwd, so there is no marker to detect: the
|
|
28259
|
+
* default is always the first scaffold-capable Provider (Claude).
|
|
28260
|
+
* Returns `null` after printing an error (caller exits non-zero).
|
|
28261
|
+
*/
|
|
28262
|
+
async resolveScaffoldTarget(targets, stderrAnsi, errGlyph) {
|
|
28263
|
+
if (targets.length === 0) {
|
|
28264
|
+
this.printer.error(tx(TUTORIAL_TEXTS.noTargets, { glyph: errGlyph }));
|
|
28265
|
+
return null;
|
|
28266
|
+
}
|
|
28267
|
+
const requested = this.forProvider;
|
|
28268
|
+
if (requested !== void 0) {
|
|
28269
|
+
const found = targets.find((t) => t.id === requested);
|
|
28270
|
+
if (found === void 0) {
|
|
28271
|
+
this.printer.error(
|
|
28272
|
+
tx(TUTORIAL_TEXTS.forUnknown, {
|
|
28273
|
+
glyph: errGlyph,
|
|
28274
|
+
provider: requested,
|
|
28275
|
+
hint: stderrAnsi.dim(
|
|
28276
|
+
tx(TUTORIAL_TEXTS.forUnknownHint, { ids: targets.map((t) => t.id).join(", ") })
|
|
28277
|
+
)
|
|
28278
|
+
})
|
|
28279
|
+
);
|
|
28280
|
+
return null;
|
|
28281
|
+
}
|
|
28282
|
+
return found;
|
|
28283
|
+
}
|
|
28284
|
+
const defaultIndex = 0;
|
|
28285
|
+
const def = targets[defaultIndex];
|
|
28286
|
+
const stdin = this.context.stdin;
|
|
28287
|
+
if (targets.length === 1 || stdin.isTTY !== true) return def;
|
|
28288
|
+
const stderr = this.context.stderr;
|
|
28289
|
+
const picked = await promptForTarget(
|
|
28290
|
+
targets,
|
|
28291
|
+
defaultIndex,
|
|
28292
|
+
stdin,
|
|
28293
|
+
stderr,
|
|
28294
|
+
stderrAnsi.yellow("?")
|
|
28295
|
+
);
|
|
28296
|
+
if (picked === null) {
|
|
28297
|
+
this.printer.error(
|
|
28298
|
+
tx(TUTORIAL_TEXTS.promptInvalid, {
|
|
28299
|
+
glyph: errGlyph,
|
|
28300
|
+
hint: stderrAnsi.dim(
|
|
28301
|
+
tx(TUTORIAL_TEXTS.forUnknownHint, { ids: targets.map((t) => t.id).join(", ") })
|
|
28302
|
+
)
|
|
28303
|
+
})
|
|
28304
|
+
);
|
|
28305
|
+
return null;
|
|
28306
|
+
}
|
|
28307
|
+
return picked;
|
|
28308
|
+
}
|
|
27697
28309
|
};
|
|
27698
28310
|
function isTutorialVariant(value) {
|
|
27699
28311
|
return VALID_VARIANTS.includes(value);
|
|
27700
28312
|
}
|
|
28313
|
+
function toScaffoldTarget(provider) {
|
|
28314
|
+
const scaffold = provider.scaffold;
|
|
28315
|
+
if (!scaffold || !scaffold.skillDir) return null;
|
|
28316
|
+
return {
|
|
28317
|
+
id: provider.id,
|
|
28318
|
+
label: provider.presentation.label,
|
|
28319
|
+
skillDir: scaffold.skillDir,
|
|
28320
|
+
aka: scaffold.aka ?? []
|
|
28321
|
+
};
|
|
28322
|
+
}
|
|
28323
|
+
function listScaffoldTargets() {
|
|
28324
|
+
const out = [];
|
|
28325
|
+
for (const provider of builtIns().providers) {
|
|
28326
|
+
const target = toScaffoldTarget(provider);
|
|
28327
|
+
if (target !== null) out.push(target);
|
|
28328
|
+
}
|
|
28329
|
+
return out;
|
|
28330
|
+
}
|
|
28331
|
+
function labelWithAka(target) {
|
|
28332
|
+
return target.aka.length > 0 ? `${target.label} (${target.aka.join(", ")})` : target.label;
|
|
28333
|
+
}
|
|
28334
|
+
async function promptForTarget(targets, defaultIndex, stdin, stderr, glyph) {
|
|
28335
|
+
const lines = [tx(TUTORIAL_TEXTS.promptHeader, { glyph })];
|
|
28336
|
+
for (let i = 0; i < targets.length; i += 1) {
|
|
28337
|
+
const t = targets[i];
|
|
28338
|
+
lines.push(
|
|
28339
|
+
tx(TUTORIAL_TEXTS.promptOption, {
|
|
28340
|
+
index: i + 1,
|
|
28341
|
+
label: labelWithAka(t),
|
|
28342
|
+
skillDir: `${t.skillDir}/`,
|
|
28343
|
+
marker: i === defaultIndex ? TUTORIAL_TEXTS.promptDefaultMarker : ""
|
|
28344
|
+
})
|
|
28345
|
+
);
|
|
28346
|
+
}
|
|
28347
|
+
stderr.write(lines.join("\n") + "\n");
|
|
28348
|
+
const rl = createInterface5({ input: stdin, output: stderr });
|
|
28349
|
+
try {
|
|
28350
|
+
const answer = await new Promise(
|
|
28351
|
+
(resolveP) => rl.question(tx(TUTORIAL_TEXTS.promptInput, { index: defaultIndex + 1 }), resolveP)
|
|
28352
|
+
);
|
|
28353
|
+
const trimmed = answer.trim();
|
|
28354
|
+
if (trimmed === "") return targets[defaultIndex];
|
|
28355
|
+
const asNumber = Number.parseInt(trimmed, 10);
|
|
28356
|
+
if (!Number.isNaN(asNumber) && asNumber >= 1 && asNumber <= targets.length) {
|
|
28357
|
+
return targets[asNumber - 1];
|
|
28358
|
+
}
|
|
28359
|
+
const byId = targets.find((t) => t.id.toLowerCase() === trimmed.toLowerCase());
|
|
28360
|
+
return byId ?? null;
|
|
28361
|
+
} finally {
|
|
28362
|
+
rl.close();
|
|
28363
|
+
}
|
|
28364
|
+
}
|
|
27701
28365
|
function displayCwd(cwd) {
|
|
27702
28366
|
const segments = cwd.split("/").filter((s) => s.length > 0);
|
|
27703
28367
|
if (segments.length === 0) return "./";
|
|
27704
28368
|
return `./${segments[segments.length - 1]}/`;
|
|
27705
28369
|
}
|
|
28370
|
+
function isDirEmpty(dir) {
|
|
28371
|
+
return readdirSync10(dir).length === 0;
|
|
28372
|
+
}
|
|
28373
|
+
function listCwdEntries(dir) {
|
|
28374
|
+
const entries = readdirSync10(dir).sort();
|
|
28375
|
+
const shown = entries.slice(0, 5);
|
|
28376
|
+
const more = entries.length > shown.length ? ", ..." : "";
|
|
28377
|
+
return shown.join(", ") + more;
|
|
28378
|
+
}
|
|
27706
28379
|
var cachedSourceDirs = /* @__PURE__ */ new Map();
|
|
27707
28380
|
function resolveSkillSourceDir(variant) {
|
|
27708
28381
|
const cached = cachedSourceDirs.get(variant);
|
|
@@ -27822,6 +28495,7 @@ cli.register(RootHelpCommand);
|
|
|
27822
28495
|
cli.register(HelpCommand);
|
|
27823
28496
|
cli.register(InitCommand);
|
|
27824
28497
|
cli.register(TutorialCommand);
|
|
28498
|
+
cli.register(IntentionalFailCommand);
|
|
27825
28499
|
cli.register(ScanCommand);
|
|
27826
28500
|
cli.register(ScanCompareCommand);
|
|
27827
28501
|
cli.register(ServeCommand);
|
|
@@ -27855,6 +28529,13 @@ var logLevel = resolveLogLevel({
|
|
|
27855
28529
|
configureLogger(new Logger({ level: logLevel, stream: process.stderr }));
|
|
27856
28530
|
var bareArgs = resolveBareInvocation(args);
|
|
27857
28531
|
var routedArgs = routeHelpArgs(bareArgs ?? args, cli);
|
|
28532
|
+
var telemetryVerb = routedArgs[0];
|
|
28533
|
+
await maybeRunFirstRunPrompt();
|
|
28534
|
+
if (telemetryVerb !== "serve") {
|
|
28535
|
+
await initSentryCli(VERSION);
|
|
28536
|
+
setTelemetryVerbTag(telemetryVerb);
|
|
28537
|
+
await initUsageCli();
|
|
28538
|
+
}
|
|
27858
28539
|
var lifecycleDispatcher = makeHookDispatcher(
|
|
27859
28540
|
builtIns().hooks ?? [],
|
|
27860
28541
|
new InMemoryProgressEmitter()
|
|
@@ -27891,10 +28572,18 @@ var exitCode = await cli.run(routedArgs, {
|
|
|
27891
28572
|
stdout: process.stdout,
|
|
27892
28573
|
stderr: process.stderr
|
|
27893
28574
|
});
|
|
28575
|
+
if (telemetryVerb !== void 0 && telemetryVerb !== "") {
|
|
28576
|
+
const knownVerbs = new Set(
|
|
28577
|
+
registeredVerbPaths(cli).map((path) => path[0]).filter((token) => token !== void 0)
|
|
28578
|
+
);
|
|
28579
|
+
captureCliInvocation(telemetryVerb, extractFlagNames(routedArgs.slice(1)), knownVerbs);
|
|
28580
|
+
}
|
|
27894
28581
|
await lifecycleDispatcher.dispatch(
|
|
27895
28582
|
"shutdown",
|
|
27896
28583
|
makeEvent("shutdown", { exitCode })
|
|
27897
28584
|
);
|
|
28585
|
+
await closeSentryCli();
|
|
28586
|
+
await flushUsageCli();
|
|
27898
28587
|
process.exit(exitCode);
|
|
27899
28588
|
function resolveBareInvocation(rawArgs) {
|
|
27900
28589
|
if (rawArgs.length === 0) return resolveBareDefault();
|