@skill-map/cli 0.42.0 → 0.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +216 -299
- package/dist/cli.js +1015 -376
- package/dist/cli.js.map +1 -1
- package/dist/index.js +24 -18
- package/dist/index.js.map +1 -1
- package/dist/kernel/index.d.ts +24 -30
- package/dist/kernel/index.js +24 -18
- package/dist/kernel/index.js.map +1 -1
- package/dist/migrations/001_initial.sql +6 -11
- 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-DL5EA245.js +123 -0
- package/dist/ui/chunk-F7I6KMHX.js +1 -0
- package/dist/ui/{chunk-5GD2GBPS.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-HEJCH7BA.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-O3CWFYKV.js +3 -0
- package/migrations/001_initial.sql +6 -11
- package/package.json +11 -10
- package/dist/ui/chunk-HFPA56IM.js +0 -1
- package/dist/ui/chunk-HHPSCDLM.js +0 -315
- package/dist/ui/chunk-HWP3HM55.js +0 -123
- package/dist/ui/chunk-PZ6Q5AOT.js +0 -1
- package/dist/ui/chunk-XJL4DZ4M.js +0 -1
- package/dist/ui/chunk-YL6SWAFJ.js +0 -1024
- package/dist/ui/main-7VYTTJP7.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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// cli/entry.ts
|
|
2
|
-
import { existsSync as
|
|
2
|
+
import { existsSync as existsSync32 } from "fs";
|
|
3
3
|
import { Builtins, Cli as Cli2 } from "clipanion";
|
|
4
4
|
|
|
5
5
|
// kernel/adapters/in-memory-progress.ts
|
|
@@ -244,11 +244,11 @@ function bucketByKind(kind, instance, bag) {
|
|
|
244
244
|
// plugins/claude/providers/claude/schemas/skill.schema.json
|
|
245
245
|
var skill_schema_default = {
|
|
246
246
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
247
|
-
$id: "https://skill-map.
|
|
247
|
+
$id: "https://skill-map.ai/providers/claude/v1/frontmatter/skill.schema.json",
|
|
248
248
|
title: "FrontmatterSkill",
|
|
249
249
|
description: "Frontmatter shape for nodes classified as `skill` by the Claude Provider. Today identical to `command` \u2014 both extend the shared `skill-base.schema.json` per Anthropic's documented merger (https://code.claude.com/docs/en/skills.md \u2014 \"Custom commands have been merged into skills\"). The two are kept SPLIT (not aliased) because skill-map's registry differentiates them in `IProviderKind.ui` (separate label / icon / color) and `defaultRefreshAction`. Splitting also reserves room for future divergence when one kind diverges from the other. No skill-only fields today; `additionalProperties: true` so the file is ready for them.",
|
|
250
250
|
allOf: [
|
|
251
|
-
{ $ref: "https://skill-map.
|
|
251
|
+
{ $ref: "https://skill-map.ai/providers/claude/v1/frontmatter/skill-base.schema.json" }
|
|
252
252
|
],
|
|
253
253
|
type: "object",
|
|
254
254
|
additionalProperties: true,
|
|
@@ -258,11 +258,11 @@ var skill_schema_default = {
|
|
|
258
258
|
// plugins/claude/providers/claude/schemas/skill-base.schema.json
|
|
259
259
|
var skill_base_schema_default = {
|
|
260
260
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
261
|
-
$id: "https://skill-map.
|
|
261
|
+
$id: "https://skill-map.ai/providers/claude/v1/frontmatter/skill-base.schema.json",
|
|
262
262
|
title: "FrontmatterSkillBase",
|
|
263
263
|
description: 'Shared frontmatter base for `skill` and `command` nodes per Anthropic\'s documented merger (https://code.claude.com/docs/en/skills.md \u2014 "Custom commands have been merged into skills"). Both kinds carry the same vendor frontmatter today; skill-map keeps them as distinct `IProviderKind`s in the registry (different UI presentation, different `defaultRefreshAction`) but extends the same base via `allOf` + `$ref` so the field catalog is single-sourced. Field naming is reproduced verbatim from Anthropic \u2014 a deliberate mix of kebab-case (`argument-hint`, `disable-model-invocation`, `user-invocable`, `allowed-tools`), snake_case (`when_to_use`), and camelCase. `additionalProperties: true` so future Anthropic additions flow through unchanged until this schema catches up.',
|
|
264
264
|
allOf: [
|
|
265
|
-
{ $ref: "https://skill-map.
|
|
265
|
+
{ $ref: "https://skill-map.ai/spec/v0/frontmatter/base.schema.json" }
|
|
266
266
|
],
|
|
267
267
|
type: "object",
|
|
268
268
|
additionalProperties: true,
|
|
@@ -297,6 +297,13 @@ var skill_base_schema_default = {
|
|
|
297
297
|
],
|
|
298
298
|
description: "Tools pre-approved for this skill (no per-use permission prompt). Argument-scoped patterns supported (`Bash(git add *)`)."
|
|
299
299
|
},
|
|
300
|
+
"disallowed-tools": {
|
|
301
|
+
oneOf: [
|
|
302
|
+
{ type: "string" },
|
|
303
|
+
{ type: "array", items: { type: "string" } }
|
|
304
|
+
],
|
|
305
|
+
description: "Denylist sibling of `allowed-tools`: tools removed from the available pool while this skill / command is active (the restriction clears on the next user message). Accepts a space- or comma-separated string or a YAML list. Source: https://code.claude.com/docs/en/skills.md."
|
|
306
|
+
},
|
|
300
307
|
model: {
|
|
301
308
|
type: "string",
|
|
302
309
|
description: "Model alias (`sonnet`, `opus`, `haiku`), full Claude id, or the literal `inherit` to defer to the parent session's model."
|
|
@@ -337,11 +344,11 @@ var skill_base_schema_default = {
|
|
|
337
344
|
// plugins/claude/providers/claude/schemas/agent.schema.json
|
|
338
345
|
var agent_schema_default = {
|
|
339
346
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
340
|
-
$id: "https://skill-map.
|
|
347
|
+
$id: "https://skill-map.ai/providers/claude/v1/frontmatter/agent.schema.json",
|
|
341
348
|
title: "FrontmatterAgent",
|
|
342
|
-
description: "Frontmatter shape for nodes classified as `agent` by the Claude Provider. Mirrors Anthropic's documented agent frontmatter verbatim (https://code.claude.com/docs/en/agents.md): `name` and `description` come from the spec base; this schema adds the 14 vendor-specific fields. skill-map AGGREGATES the vendor spec, it does not curate it \u2014 keys are reproduced exactly as Anthropic publishes them (mix of camelCase and snake_case). `additionalProperties: true` so future Anthropic additions flow through unchanged until this schema catches up.",
|
|
349
|
+
description: "Frontmatter shape for nodes classified as `agent` by the Claude Provider. Mirrors Anthropic's documented agent frontmatter verbatim (https://code.claude.com/docs/en/sub-agents.md): `name` and `description` come from the spec base; this schema adds the 14 vendor-specific fields. skill-map AGGREGATES the vendor spec, it does not curate it \u2014 keys are reproduced exactly as Anthropic publishes them (mix of camelCase and snake_case). `additionalProperties: true` so future Anthropic additions flow through unchanged until this schema catches up.",
|
|
343
350
|
allOf: [
|
|
344
|
-
{ $ref: "https://skill-map.
|
|
351
|
+
{ $ref: "https://skill-map.ai/spec/v0/frontmatter/base.schema.json" }
|
|
345
352
|
],
|
|
346
353
|
type: "object",
|
|
347
354
|
additionalProperties: true,
|
|
@@ -363,7 +370,7 @@ var agent_schema_default = {
|
|
|
363
370
|
permissionMode: {
|
|
364
371
|
type: "string",
|
|
365
372
|
enum: ["default", "acceptEdits", "auto", "dontAsk", "bypassPermissions", "plan"],
|
|
366
|
-
description: "How the agent handles permission prompts. See https://code.claude.com/docs/en/agents.md."
|
|
373
|
+
description: "How the agent handles permission prompts. See https://code.claude.com/docs/en/sub-agents.md."
|
|
367
374
|
},
|
|
368
375
|
maxTurns: {
|
|
369
376
|
type: "integer",
|
|
@@ -418,11 +425,11 @@ var agent_schema_default = {
|
|
|
418
425
|
// plugins/claude/providers/claude/schemas/command.schema.json
|
|
419
426
|
var command_schema_default = {
|
|
420
427
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
421
|
-
$id: "https://skill-map.
|
|
428
|
+
$id: "https://skill-map.ai/providers/claude/v1/frontmatter/command.schema.json",
|
|
422
429
|
title: "FrontmatterCommand",
|
|
423
430
|
description: "Frontmatter shape for nodes classified as `command` by the Claude Provider. Today identical to `skill` per Anthropic's documented merger (https://code.claude.com/docs/en/skills.md \u2014 \"Custom commands have been merged into skills\"). Both extend the shared `skill-base.schema.json` via the same `allOf` pattern. The two are kept SPLIT (not aliased) because skill-map's registry differentiates them in `IProviderKind.ui` (separate label / icon / color) and `defaultRefreshAction`. Splitting also reserves room for future divergence. No command-only fields today; `additionalProperties: true` so the file is ready for them.",
|
|
424
431
|
allOf: [
|
|
425
|
-
{ $ref: "https://skill-map.
|
|
432
|
+
{ $ref: "https://skill-map.ai/providers/claude/v1/frontmatter/skill-base.schema.json" }
|
|
426
433
|
],
|
|
427
434
|
type: "object",
|
|
428
435
|
additionalProperties: true,
|
|
@@ -498,7 +505,7 @@ var claudeProvider = {
|
|
|
498
505
|
icon: { kind: "pi", id: "pi-user" }
|
|
499
506
|
},
|
|
500
507
|
// `frontmatter.name` is the documented canonical identifier
|
|
501
|
-
// (https://code.claude.com/docs/en/agents.md); `filename-basename`
|
|
508
|
+
// (https://code.claude.com/docs/en/sub-agents.md); `filename-basename`
|
|
502
509
|
// is a graceful fallback for agents authored without an explicit
|
|
503
510
|
// `name:` field, the file at `.claude/agents/<id>.md` resolves
|
|
504
511
|
// `@<id>` even when frontmatter is partial.
|
|
@@ -905,75 +912,77 @@ var antigravityProvider = {
|
|
|
905
912
|
classify() {
|
|
906
913
|
return null;
|
|
907
914
|
},
|
|
908
|
-
//
|
|
909
|
-
//
|
|
910
|
-
//
|
|
911
|
-
//
|
|
912
|
-
//
|
|
913
|
-
//
|
|
914
|
-
//
|
|
915
|
-
//
|
|
916
|
-
//
|
|
917
|
-
//
|
|
918
|
-
//
|
|
919
|
-
//
|
|
920
|
-
//
|
|
915
|
+
// Built-in slash-command catalog, captured verbatim from `agy /help`
|
|
916
|
+
// (Antigravity CLI v1.0.3). This REPLACES the earlier provisional list
|
|
917
|
+
// that mirrored Gemini CLI's verbs: `agy` ships its own surface. It
|
|
918
|
+
// DROPPED Gemini-only verbs (`vim`, `theme`, `terminal-setup`,
|
|
919
|
+
// `setup-github`, `bashes`, `shells`, `policies`, `extensions`, `about`,
|
|
920
|
+
// `auth`, `bug`, `chat`, `compress`, `docs`, `editor`, `ide`, `init`,
|
|
921
|
+
// `memory`, `restore`, `stats`, `tools`, `upgrade`, `?`, `dir`) and
|
|
922
|
+
// ADDED agent-first ones (`goal`, `grill-me`, `schedule`, `fast`, `btw`,
|
|
923
|
+
// `artifact`, `context`, `diff`, `fork`, `tasks`, `add-dir`, `credits`,
|
|
924
|
+
// `feedback`, `logout`, `open`, `planning`, `rename`, `statusline`,
|
|
925
|
+
// `title`, `usage`). Both the 35 primary verbs and the 8 documented
|
|
926
|
+
// aliases (`new`, `settings`, `quit`, `branch`, `switch`, `conversation`,
|
|
927
|
+
// `undo`, `quota`) are reserved: a user skill named after either is
|
|
928
|
+
// silently shadowed by the built-in once the catalog activates.
|
|
921
929
|
//
|
|
922
|
-
//
|
|
923
|
-
//
|
|
924
|
-
//
|
|
925
|
-
//
|
|
926
|
-
//
|
|
927
|
-
//
|
|
930
|
+
// Declared under the `skill` kind (NOT `command`): Antigravity has no
|
|
931
|
+
// vendor-specific command directory, its user slash-commands are skills
|
|
932
|
+
// (`.agents/skills/<name>/SKILL.md`, owned by the universal `agent-skills`
|
|
933
|
+
// Provider). The catalog is ACTIVE via the LENS SCOPE in
|
|
934
|
+
// `buildReservedNodePaths` (spec/architecture.md §Provider ·
|
|
935
|
+
// reservedNames): when `activeProvider === 'antigravity'` the orchestrator
|
|
936
|
+
// lends this `skill` catalog to `agent-skills` skill nodes, so a user
|
|
937
|
+
// `.agents/skills/goal/SKILL.md` is flagged because `/goal` is built-in.
|
|
928
938
|
//
|
|
929
|
-
// **Reconciliation marker**:
|
|
930
|
-
//
|
|
931
|
-
// reference, replace this comment + array with the official list (and
|
|
932
|
-
// bump the file's leading docblock to cite the new source URL).
|
|
939
|
+
// **Reconciliation marker**: re-capture from `agy /help` on each major
|
|
940
|
+
// Antigravity CLI release and bump the cited version above.
|
|
933
941
|
reservedNames: {
|
|
934
|
-
|
|
935
|
-
"
|
|
936
|
-
"about",
|
|
942
|
+
skill: [
|
|
943
|
+
"add-dir",
|
|
937
944
|
"agents",
|
|
938
|
-
"
|
|
939
|
-
"
|
|
940
|
-
"
|
|
941
|
-
"
|
|
945
|
+
"artifact",
|
|
946
|
+
"branch",
|
|
947
|
+
"btw",
|
|
948
|
+
"changelog",
|
|
942
949
|
"clear",
|
|
943
|
-
"
|
|
944
|
-
"
|
|
950
|
+
"config",
|
|
951
|
+
"context",
|
|
952
|
+
"conversation",
|
|
945
953
|
"copy",
|
|
946
|
-
"
|
|
947
|
-
"
|
|
948
|
-
"docs",
|
|
949
|
-
"editor",
|
|
954
|
+
"credits",
|
|
955
|
+
"diff",
|
|
950
956
|
"exit",
|
|
951
|
-
"
|
|
957
|
+
"fast",
|
|
958
|
+
"feedback",
|
|
959
|
+
"fork",
|
|
960
|
+
"goal",
|
|
961
|
+
"grill-me",
|
|
952
962
|
"help",
|
|
953
963
|
"hooks",
|
|
954
|
-
"
|
|
955
|
-
"
|
|
964
|
+
"keybindings",
|
|
965
|
+
"logout",
|
|
956
966
|
"mcp",
|
|
957
|
-
"memory",
|
|
958
967
|
"model",
|
|
968
|
+
"new",
|
|
969
|
+
"open",
|
|
959
970
|
"permissions",
|
|
960
|
-
"
|
|
961
|
-
"policies",
|
|
962
|
-
"privacy",
|
|
971
|
+
"planning",
|
|
963
972
|
"quit",
|
|
964
|
-
"
|
|
973
|
+
"quota",
|
|
974
|
+
"rename",
|
|
965
975
|
"resume",
|
|
966
976
|
"rewind",
|
|
977
|
+
"schedule",
|
|
967
978
|
"settings",
|
|
968
|
-
"setup-github",
|
|
969
|
-
"shells",
|
|
970
979
|
"skills",
|
|
971
|
-
"
|
|
972
|
-
"
|
|
973
|
-
"
|
|
974
|
-
"
|
|
975
|
-
"
|
|
976
|
-
"
|
|
980
|
+
"statusline",
|
|
981
|
+
"switch",
|
|
982
|
+
"tasks",
|
|
983
|
+
"title",
|
|
984
|
+
"undo",
|
|
985
|
+
"usage"
|
|
977
986
|
]
|
|
978
987
|
}
|
|
979
988
|
};
|
|
@@ -981,11 +990,11 @@ var antigravityProvider = {
|
|
|
981
990
|
// plugins/openai/providers/openai/schemas/agent.schema.json
|
|
982
991
|
var agent_schema_default2 = {
|
|
983
992
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
984
|
-
$id: "https://skill-map.
|
|
993
|
+
$id: "https://skill-map.ai/providers/openai/v1/frontmatter/agent.schema.json",
|
|
985
994
|
title: "FrontmatterCodexAgent",
|
|
986
995
|
description: "Frontmatter shape for nodes classified as `agent` by the OpenAI Codex Provider. Codex sub-agents live as TOML files under `.codex/agents/<name>.toml`; the entire file IS the agent definition (no markdown body). The TOML parser feeds the parsed root object into `frontmatter`, so this schema validates the same shape skill-map's other providers carry on per-kind frontmatter. Mirrors Codex's documented sub-agent fields (https://github.com/openai/codex) with `additionalProperties: true` so future additions flow through unchanged.",
|
|
987
996
|
allOf: [
|
|
988
|
-
{ $ref: "https://skill-map.
|
|
997
|
+
{ $ref: "https://skill-map.ai/spec/v0/frontmatter/base.schema.json" }
|
|
989
998
|
],
|
|
990
999
|
type: "object",
|
|
991
1000
|
additionalProperties: true,
|
|
@@ -1088,15 +1097,34 @@ var openaiProvider = {
|
|
|
1088
1097
|
// plugins/agent-skills/providers/agent-skills/schemas/skill.schema.json
|
|
1089
1098
|
var skill_schema_default2 = {
|
|
1090
1099
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
1091
|
-
$id: "https://skill-map.
|
|
1100
|
+
$id: "https://skill-map.ai/providers/agent-skills/v1/frontmatter/skill.schema.json",
|
|
1092
1101
|
title: "FrontmatterAgentSkillsSkill",
|
|
1093
|
-
description: "Frontmatter shape for nodes classified as `skill` by the neutral `agent-skills` Provider
|
|
1102
|
+
description: "Frontmatter shape for nodes classified as `skill` by the neutral `agent-skills` Provider, Agent Skills delivered as `SKILL.md` files at the open-standard path `.agents/skills/<name>/SKILL.md`. Jointly adopted by Anthropic, OpenAI (Codex), and Google (Gemini); the path is vendor-neutral so no single Provider should own it. Required fields are `name` and `description` (from spec base). The standard's optional frontmatter fields are declared below, mirrored verbatim from https://agentskills.io/specification: `license`, `compatibility`, `metadata`, and the experimental `allowed-tools`. `additionalProperties: true` so any future standard field flows through unchanged until this schema catches up.",
|
|
1094
1103
|
allOf: [
|
|
1095
|
-
{ $ref: "https://skill-map.
|
|
1104
|
+
{ $ref: "https://skill-map.ai/spec/v0/frontmatter/base.schema.json" }
|
|
1096
1105
|
],
|
|
1097
1106
|
type: "object",
|
|
1098
1107
|
additionalProperties: true,
|
|
1099
|
-
properties: {
|
|
1108
|
+
properties: {
|
|
1109
|
+
license: {
|
|
1110
|
+
type: "string",
|
|
1111
|
+
description: "License applied to the skill: a license name (e.g. `Apache-2.0`) or a reference to a bundled license file. Source: https://agentskills.io/specification."
|
|
1112
|
+
},
|
|
1113
|
+
compatibility: {
|
|
1114
|
+
type: "string",
|
|
1115
|
+
maxLength: 500,
|
|
1116
|
+
description: "Environment requirements (intended product, required system packages, network access, etc.). Most skills omit it. Max 500 characters. Source: https://agentskills.io/specification."
|
|
1117
|
+
},
|
|
1118
|
+
metadata: {
|
|
1119
|
+
type: "object",
|
|
1120
|
+
additionalProperties: { type: "string" },
|
|
1121
|
+
description: "Arbitrary string-keyed, string-valued map for client-defined metadata not covered by the standard. Source: https://agentskills.io/specification."
|
|
1122
|
+
},
|
|
1123
|
+
"allowed-tools": {
|
|
1124
|
+
type: "string",
|
|
1125
|
+
description: "Space-separated list of pre-approved tools the skill may run (e.g. `Bash(git:*) Read`). Experimental in the open standard; support varies between agent implementations. Source: https://agentskills.io/specification."
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1100
1128
|
};
|
|
1101
1129
|
|
|
1102
1130
|
// plugins/agent-skills/providers/agent-skills/index.ts
|
|
@@ -1150,11 +1178,11 @@ var agentSkillsProvider = {
|
|
|
1150
1178
|
// plugins/core/providers/core-markdown/schemas/markdown.schema.json
|
|
1151
1179
|
var markdown_schema_default = {
|
|
1152
1180
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
1153
|
-
$id: "https://skill-map.
|
|
1181
|
+
$id: "https://skill-map.ai/providers/core/v1/frontmatter/markdown.schema.json",
|
|
1154
1182
|
title: "FrontmatterMarkdown",
|
|
1155
1183
|
description: "Frontmatter shape for nodes classified as `markdown` by the built-in `core/markdown` Provider, the universal fallback for any markdown file no vendor-specific Provider claims (Claude, OpenAI Codex, agent-skills, plus the metadata-only Antigravity bundle). The kind is named after the format because the file is a generic fallback; format-named kinds apply only as the generic fallback, a TOML file that IS a Codex agent still classifies as `agent`, not `toml`. Extends the spec's universal `frontmatter/base.schema.json` via $ref-by-$id with no additional fields. Ownership relocated from the Claude Provider in spec 0.18.0, markdown is provider-agnostic and lives under `core` so adding new vendor Providers does not require choosing which one owns the universal fallback.",
|
|
1156
1184
|
allOf: [
|
|
1157
|
-
{ $ref: "https://skill-map.
|
|
1185
|
+
{ $ref: "https://skill-map.ai/spec/v0/frontmatter/base.schema.json" }
|
|
1158
1186
|
],
|
|
1159
1187
|
type: "object",
|
|
1160
1188
|
additionalProperties: true
|
|
@@ -2935,10 +2963,10 @@ function buildSchemaValidators() {
|
|
|
2935
2963
|
hook: "extension-hook"
|
|
2936
2964
|
};
|
|
2937
2965
|
const pluginManifestValidator = ajv.compile({
|
|
2938
|
-
$ref: "https://skill-map.
|
|
2966
|
+
$ref: "https://skill-map.ai/spec/v0/plugins-registry.schema.json#/$defs/PluginManifest"
|
|
2939
2967
|
});
|
|
2940
2968
|
const contributionValidators = /* @__PURE__ */ new Map();
|
|
2941
|
-
const VIEW_SLOTS_ID = "https://skill-map.
|
|
2969
|
+
const VIEW_SLOTS_ID = "https://skill-map.ai/spec/v0/view-slots.schema.json";
|
|
2942
2970
|
function getContributionValidator(slot) {
|
|
2943
2971
|
if (!KNOWN_SLOT_NAMES.has(slot)) return null;
|
|
2944
2972
|
const existing = contributionValidators.get(slot);
|
|
@@ -3920,11 +3948,11 @@ var UPDATE_CHECK_TEXTS = {
|
|
|
3920
3948
|
// package.json
|
|
3921
3949
|
var package_default = {
|
|
3922
3950
|
name: "@skill-map/cli",
|
|
3923
|
-
version: "0.
|
|
3951
|
+
version: "0.44.0",
|
|
3924
3952
|
description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
|
|
3925
3953
|
license: "MIT",
|
|
3926
3954
|
type: "module",
|
|
3927
|
-
homepage: "https://skill-map.
|
|
3955
|
+
homepage: "https://skill-map.ai",
|
|
3928
3956
|
repository: {
|
|
3929
3957
|
type: "git",
|
|
3930
3958
|
url: "git+https://github.com/crystian/skill-map.git",
|
|
@@ -3971,25 +3999,25 @@ var package_default = {
|
|
|
3971
3999
|
typecheck: "tsc --noEmit",
|
|
3972
4000
|
lint: "eslint .",
|
|
3973
4001
|
"lint:fix": "eslint . --fix",
|
|
3974
|
-
reference: "node scripts/build-reference.js",
|
|
3975
|
-
"reference:check": "node scripts/build-reference.js --check",
|
|
3976
4002
|
"build-built-ins": "node ../scripts/generate-built-ins.js",
|
|
3977
4003
|
"built-ins:check": "node ../scripts/generate-built-ins.js --check",
|
|
3978
4004
|
prebuild: "pnpm build-built-ins",
|
|
3979
4005
|
validate: "pnpm validate:compile && pnpm validate:test",
|
|
3980
|
-
"validate:compile": "pnpm typecheck && pnpm lint && pnpm build && pnpm
|
|
4006
|
+
"validate:compile": "pnpm typecheck && pnpm lint && pnpm build && pnpm built-ins:check",
|
|
3981
4007
|
"validate:test": "pnpm test:ci",
|
|
3982
4008
|
pretest: "tsup",
|
|
3983
4009
|
"pretest:coverage": "tsup",
|
|
3984
4010
|
"pretest:coverage:html": "tsup",
|
|
3985
|
-
test: "tsc --noEmit && node --import tsx --test --test-reporter=
|
|
3986
|
-
"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'",
|
|
3987
|
-
"test:
|
|
3988
|
-
"test:coverage
|
|
4011
|
+
test: "tsc --noEmit && SKILL_MAP_TELEMETRY=0 node --import tsx --test --test-reporter=./scripts/test-reporter.js --test-reporter-destination=stdout '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
|
|
4012
|
+
"test:ci": "FORCE_COLOR=1 SKILL_MAP_TELEMETRY=0 node --import tsx --test --test-reporter=./scripts/test-reporter.js --test-reporter-destination=stdout '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
|
|
4013
|
+
"test:spec": "SKILL_MAP_TELEMETRY=0 node --import tsx --test --test-reporter=spec '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
|
|
4014
|
+
"test:coverage": "tsc --noEmit && SKILL_MAP_TELEMETRY=0 SKILL_MAP_SKIP_BENCHMARK=1 node --experimental-default-config-file --import tsx --test --experimental-test-coverage '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
|
|
4015
|
+
"test:coverage:html": "tsc --noEmit && SKILL_MAP_TELEMETRY=0 SKILL_MAP_SKIP_BENCHMARK=1 c8 node --import tsx --test '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
|
|
3989
4016
|
clean: "rm -rf dist coverage"
|
|
3990
4017
|
},
|
|
3991
4018
|
dependencies: {
|
|
3992
4019
|
"@hono/node-server": "2.0.1",
|
|
4020
|
+
"@sentry/node": "10.55.0",
|
|
3993
4021
|
"@skill-map/spec": "workspace:*",
|
|
3994
4022
|
ajv: "8.18.0",
|
|
3995
4023
|
"ajv-formats": "3.0.1",
|
|
@@ -4000,6 +4028,7 @@ var package_default = {
|
|
|
4000
4028
|
"js-tiktoken": "1.0.21",
|
|
4001
4029
|
"js-yaml": "4.1.1",
|
|
4002
4030
|
kysely: "0.28.17",
|
|
4031
|
+
"posthog-node": "5.35.6",
|
|
4003
4032
|
semver: "7.7.4",
|
|
4004
4033
|
"smol-toml": "1.6.1",
|
|
4005
4034
|
typanion: "3.14.0",
|
|
@@ -4070,6 +4099,7 @@ function ansiFor(opts) {
|
|
|
4070
4099
|
}
|
|
4071
4100
|
|
|
4072
4101
|
// cli/util/user-settings-store.ts
|
|
4102
|
+
import { randomUUID } from "crypto";
|
|
4073
4103
|
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync5 } from "fs";
|
|
4074
4104
|
import { homedir } from "os";
|
|
4075
4105
|
import { join as join2 } from "path";
|
|
@@ -4178,13 +4208,13 @@ function userSettingsFilePath() {
|
|
|
4178
4208
|
return join2(homedir(), SKILL_MAP_DIR, FILENAME);
|
|
4179
4209
|
}
|
|
4180
4210
|
function defaultSettings() {
|
|
4181
|
-
return { schemaVersion: SCHEMA_VERSION, updateCheck: {} };
|
|
4211
|
+
return { schemaVersion: SCHEMA_VERSION, updateCheck: {}, telemetry: {} };
|
|
4182
4212
|
}
|
|
4183
4213
|
function readUserSettings() {
|
|
4184
4214
|
const parsed = readParsedFile();
|
|
4185
4215
|
if (parsed === null) return defaultSettings();
|
|
4186
4216
|
const validated = validateOrDefault(parsed);
|
|
4187
|
-
return
|
|
4217
|
+
return backfillSubObjects(validated);
|
|
4188
4218
|
}
|
|
4189
4219
|
function readParsedFile() {
|
|
4190
4220
|
const path = userSettingsFilePath();
|
|
@@ -4206,11 +4236,12 @@ function validateOrDefault(parsed) {
|
|
|
4206
4236
|
if (!result.ok) return defaultSettings();
|
|
4207
4237
|
return result.data;
|
|
4208
4238
|
}
|
|
4209
|
-
function
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4239
|
+
function backfillSubObjects(settings) {
|
|
4240
|
+
return {
|
|
4241
|
+
...settings,
|
|
4242
|
+
updateCheck: settings.updateCheck ?? {},
|
|
4243
|
+
telemetry: settings.telemetry ?? {}
|
|
4244
|
+
};
|
|
4214
4245
|
}
|
|
4215
4246
|
function writeUserSettings(patch) {
|
|
4216
4247
|
const dir = join2(homedir(), SKILL_MAP_DIR);
|
|
@@ -4232,14 +4263,49 @@ function isUpdateCheckEnabled() {
|
|
|
4232
4263
|
const settings = readUserSettings();
|
|
4233
4264
|
return settings.updateCheck?.enabled !== false;
|
|
4234
4265
|
}
|
|
4266
|
+
function isErrorTelemetryEnabled() {
|
|
4267
|
+
const settings = readUserSettings();
|
|
4268
|
+
return settings.telemetry?.errorsEnabled === true;
|
|
4269
|
+
}
|
|
4270
|
+
function isUsageCliTelemetryEnabled() {
|
|
4271
|
+
const settings = readUserSettings();
|
|
4272
|
+
return settings.telemetry?.usageCliEnabled === true;
|
|
4273
|
+
}
|
|
4274
|
+
function isUsageUiTelemetryEnabled() {
|
|
4275
|
+
const settings = readUserSettings();
|
|
4276
|
+
return settings.telemetry?.usageUiEnabled === true;
|
|
4277
|
+
}
|
|
4278
|
+
function readAnonymousId() {
|
|
4279
|
+
const settings = readUserSettings();
|
|
4280
|
+
return settings.telemetry?.anonymousId ?? null;
|
|
4281
|
+
}
|
|
4282
|
+
function ensureAnonymousId(generate = () => randomUUID()) {
|
|
4283
|
+
const existing = readAnonymousId();
|
|
4284
|
+
if (existing !== null && existing !== "") return existing;
|
|
4285
|
+
const id = generate();
|
|
4286
|
+
writeUserSettings({ telemetry: { anonymousId: id } });
|
|
4287
|
+
return id;
|
|
4288
|
+
}
|
|
4289
|
+
function hasTelemetryPromptBeenShown() {
|
|
4290
|
+
const settings = readUserSettings();
|
|
4291
|
+
return typeof settings.telemetry?.promptedAt === "number";
|
|
4292
|
+
}
|
|
4293
|
+
function hasSeenFirstRun() {
|
|
4294
|
+
const settings = readUserSettings();
|
|
4295
|
+
return typeof settings.telemetry?.firstRunAt === "number";
|
|
4296
|
+
}
|
|
4235
4297
|
function mergeSettings(current, patch) {
|
|
4236
4298
|
const merged = {
|
|
4237
4299
|
schemaVersion: SCHEMA_VERSION,
|
|
4238
|
-
updateCheck: { ...current.updateCheck ?? {} }
|
|
4300
|
+
updateCheck: { ...current.updateCheck ?? {} },
|
|
4301
|
+
telemetry: { ...current.telemetry ?? {} }
|
|
4239
4302
|
};
|
|
4240
4303
|
if (patch.updateCheck) {
|
|
4241
4304
|
merged.updateCheck = { ...merged.updateCheck, ...patch.updateCheck };
|
|
4242
4305
|
}
|
|
4306
|
+
if (patch.telemetry) {
|
|
4307
|
+
merged.telemetry = { ...merged.telemetry, ...patch.telemetry };
|
|
4308
|
+
}
|
|
4243
4309
|
return merged;
|
|
4244
4310
|
}
|
|
4245
4311
|
function tryLoadValidators() {
|
|
@@ -4352,40 +4418,40 @@ var updateCheckHook = {
|
|
|
4352
4418
|
};
|
|
4353
4419
|
|
|
4354
4420
|
// plugins/built-ins.ts
|
|
4355
|
-
var claudeProvider2 = { ...claudeProvider, pluginId: "claude", version: "0.
|
|
4356
|
-
var atDirectiveExtractor2 = { ...atDirectiveExtractor, pluginId: "claude", version: "0.
|
|
4357
|
-
var slashCommandExtractor2 = { ...slashCommandExtractor, pluginId: "claude", version: "0.
|
|
4358
|
-
var antigravityProvider2 = { ...antigravityProvider, pluginId: "antigravity", version: "0.
|
|
4359
|
-
var openaiProvider2 = { ...openaiProvider, pluginId: "openai", version: "0.
|
|
4360
|
-
var agentSkillsProvider2 = { ...agentSkillsProvider, pluginId: "agent-skills", version: "0.
|
|
4361
|
-
var coreMarkdownProvider2 = { ...coreMarkdownProvider, pluginId: "core", version: "0.
|
|
4362
|
-
var annotationsExtractor2 = { ...annotationsExtractor, pluginId: "core", version: "0.
|
|
4363
|
-
var externalUrlCounterExtractor2 = { ...externalUrlCounterExtractor, pluginId: "core", version: "0.
|
|
4364
|
-
var markdownLinkExtractor2 = { ...markdownLinkExtractor, pluginId: "core", version: "0.
|
|
4365
|
-
var mcpToolsExtractor2 = { ...mcpToolsExtractor, pluginId: "core", version: "0.
|
|
4366
|
-
var toolsCounterExtractor2 = { ...toolsCounterExtractor, pluginId: "core", version: "0.
|
|
4367
|
-
var annotationFieldUnknownAnalyzer2 = { ...annotationFieldUnknownAnalyzer, pluginId: "core", version: "0.
|
|
4368
|
-
var annotationOrphanAnalyzer2 = { ...annotationOrphanAnalyzer, pluginId: "core", version: "0.
|
|
4369
|
-
var annotationStaleAnalyzer2 = { ...annotationStaleAnalyzer, pluginId: "core", version: "0.
|
|
4370
|
-
var contributionOrphanAnalyzer2 = { ...contributionOrphanAnalyzer, pluginId: "core", version: "0.
|
|
4371
|
-
var issueCounterAnalyzer2 = { ...issueCounterAnalyzer, pluginId: "core", version: "0.
|
|
4372
|
-
var jobFileOrphanAnalyzer2 = { ...jobFileOrphanAnalyzer, pluginId: "core", version: "0.
|
|
4373
|
-
var linkConflictAnalyzer2 = { ...linkConflictAnalyzer, pluginId: "core", version: "0.
|
|
4374
|
-
var linkCounterAnalyzer2 = { ...linkCounterAnalyzer, pluginId: "core", version: "0.
|
|
4375
|
-
var linkSelfLoopAnalyzer2 = { ...linkSelfLoopAnalyzer, pluginId: "core", version: "0.
|
|
4376
|
-
var nameReservedAnalyzer2 = { ...nameReservedAnalyzer, pluginId: "core", version: "0.
|
|
4377
|
-
var nodeStabilityAnalyzer2 = { ...nodeStabilityAnalyzer, pluginId: "core", version: "0.
|
|
4378
|
-
var nodeSupersededAnalyzer2 = { ...nodeSupersededAnalyzer, pluginId: "core", version: "0.
|
|
4379
|
-
var referenceBrokenAnalyzer2 = { ...referenceBrokenAnalyzer, pluginId: "core", version: "0.
|
|
4380
|
-
var referenceRedundantAnalyzer2 = { ...referenceRedundantAnalyzer, pluginId: "core", version: "0.
|
|
4381
|
-
var schemaViolationAnalyzer2 = { ...schemaViolationAnalyzer, pluginId: "core", version: "0.
|
|
4382
|
-
var signalCollisionAnalyzer2 = { ...signalCollisionAnalyzer, pluginId: "core", version: "0.
|
|
4383
|
-
var triggerCollisionAnalyzer2 = { ...triggerCollisionAnalyzer, pluginId: "core", version: "0.
|
|
4384
|
-
var asciiFormatter2 = { ...asciiFormatter, pluginId: "core", version: "0.
|
|
4385
|
-
var jsonFormatter2 = { ...jsonFormatter, pluginId: "core", version: "0.
|
|
4386
|
-
var nodeBumpAction2 = { ...nodeBumpAction, pluginId: "core", version: "0.
|
|
4387
|
-
var nodeSupersedeAction2 = { ...nodeSupersedeAction, pluginId: "core", version: "0.
|
|
4388
|
-
var updateCheckHook2 = { ...updateCheckHook, pluginId: "core", version: "0.
|
|
4421
|
+
var claudeProvider2 = { ...claudeProvider, pluginId: "claude", version: "0.44.0" };
|
|
4422
|
+
var atDirectiveExtractor2 = { ...atDirectiveExtractor, pluginId: "claude", version: "0.44.0" };
|
|
4423
|
+
var slashCommandExtractor2 = { ...slashCommandExtractor, pluginId: "claude", version: "0.44.0" };
|
|
4424
|
+
var antigravityProvider2 = { ...antigravityProvider, pluginId: "antigravity", version: "0.44.0" };
|
|
4425
|
+
var openaiProvider2 = { ...openaiProvider, pluginId: "openai", version: "0.44.0" };
|
|
4426
|
+
var agentSkillsProvider2 = { ...agentSkillsProvider, pluginId: "agent-skills", version: "0.44.0" };
|
|
4427
|
+
var coreMarkdownProvider2 = { ...coreMarkdownProvider, pluginId: "core", version: "0.44.0" };
|
|
4428
|
+
var annotationsExtractor2 = { ...annotationsExtractor, pluginId: "core", version: "0.44.0" };
|
|
4429
|
+
var externalUrlCounterExtractor2 = { ...externalUrlCounterExtractor, pluginId: "core", version: "0.44.0" };
|
|
4430
|
+
var markdownLinkExtractor2 = { ...markdownLinkExtractor, pluginId: "core", version: "0.44.0" };
|
|
4431
|
+
var mcpToolsExtractor2 = { ...mcpToolsExtractor, pluginId: "core", version: "0.44.0" };
|
|
4432
|
+
var toolsCounterExtractor2 = { ...toolsCounterExtractor, pluginId: "core", version: "0.44.0" };
|
|
4433
|
+
var annotationFieldUnknownAnalyzer2 = { ...annotationFieldUnknownAnalyzer, pluginId: "core", version: "0.44.0" };
|
|
4434
|
+
var annotationOrphanAnalyzer2 = { ...annotationOrphanAnalyzer, pluginId: "core", version: "0.44.0" };
|
|
4435
|
+
var annotationStaleAnalyzer2 = { ...annotationStaleAnalyzer, pluginId: "core", version: "0.44.0" };
|
|
4436
|
+
var contributionOrphanAnalyzer2 = { ...contributionOrphanAnalyzer, pluginId: "core", version: "0.44.0" };
|
|
4437
|
+
var issueCounterAnalyzer2 = { ...issueCounterAnalyzer, pluginId: "core", version: "0.44.0" };
|
|
4438
|
+
var jobFileOrphanAnalyzer2 = { ...jobFileOrphanAnalyzer, pluginId: "core", version: "0.44.0" };
|
|
4439
|
+
var linkConflictAnalyzer2 = { ...linkConflictAnalyzer, pluginId: "core", version: "0.44.0" };
|
|
4440
|
+
var linkCounterAnalyzer2 = { ...linkCounterAnalyzer, pluginId: "core", version: "0.44.0" };
|
|
4441
|
+
var linkSelfLoopAnalyzer2 = { ...linkSelfLoopAnalyzer, pluginId: "core", version: "0.44.0" };
|
|
4442
|
+
var nameReservedAnalyzer2 = { ...nameReservedAnalyzer, pluginId: "core", version: "0.44.0" };
|
|
4443
|
+
var nodeStabilityAnalyzer2 = { ...nodeStabilityAnalyzer, pluginId: "core", version: "0.44.0" };
|
|
4444
|
+
var nodeSupersededAnalyzer2 = { ...nodeSupersededAnalyzer, pluginId: "core", version: "0.44.0" };
|
|
4445
|
+
var referenceBrokenAnalyzer2 = { ...referenceBrokenAnalyzer, pluginId: "core", version: "0.44.0" };
|
|
4446
|
+
var referenceRedundantAnalyzer2 = { ...referenceRedundantAnalyzer, pluginId: "core", version: "0.44.0" };
|
|
4447
|
+
var schemaViolationAnalyzer2 = { ...schemaViolationAnalyzer, pluginId: "core", version: "0.44.0" };
|
|
4448
|
+
var signalCollisionAnalyzer2 = { ...signalCollisionAnalyzer, pluginId: "core", version: "0.44.0" };
|
|
4449
|
+
var triggerCollisionAnalyzer2 = { ...triggerCollisionAnalyzer, pluginId: "core", version: "0.44.0" };
|
|
4450
|
+
var asciiFormatter2 = { ...asciiFormatter, pluginId: "core", version: "0.44.0" };
|
|
4451
|
+
var jsonFormatter2 = { ...jsonFormatter, pluginId: "core", version: "0.44.0" };
|
|
4452
|
+
var nodeBumpAction2 = { ...nodeBumpAction, pluginId: "core", version: "0.44.0" };
|
|
4453
|
+
var nodeSupersedeAction2 = { ...nodeSupersedeAction, pluginId: "core", version: "0.44.0" };
|
|
4454
|
+
var updateCheckHook2 = { ...updateCheckHook, pluginId: "core", version: "0.44.0" };
|
|
4389
4455
|
var builtInBundles = [
|
|
4390
4456
|
{
|
|
4391
4457
|
id: "claude",
|
|
@@ -4917,6 +4983,341 @@ function defaultRuntimeContext() {
|
|
|
4917
4983
|
return { cwd: process.cwd() };
|
|
4918
4984
|
}
|
|
4919
4985
|
|
|
4986
|
+
// cli/telemetry/first-run-prompt.ts
|
|
4987
|
+
import { createInterface } from "readline/promises";
|
|
4988
|
+
|
|
4989
|
+
// cli/i18n/telemetry.texts.ts
|
|
4990
|
+
var TELEMETRY_PROMPT_TEXTS = {
|
|
4991
|
+
// Header + body of the one-time question (glyph `ℹ` added by the renderer).
|
|
4992
|
+
title: "Anonymous error and usage reporting",
|
|
4993
|
+
intro: [
|
|
4994
|
+
"skill-map can send anonymous reports to help fix bugs and decide what to",
|
|
4995
|
+
"build next. No personal information is ever sent: not your files or their",
|
|
4996
|
+
"contents, not your folder or home paths, not your settings."
|
|
4997
|
+
],
|
|
4998
|
+
question: "Enable anonymous error and usage reporting?",
|
|
4999
|
+
answerYes: "[Y]es",
|
|
5000
|
+
answerNo: "[n]o",
|
|
5001
|
+
answerDetails: "[d]etails",
|
|
5002
|
+
// Disclosure shown on `[d]etails`, then the question is re-asked.
|
|
5003
|
+
detailsSentTitle: "Sent, only if you turn this on",
|
|
5004
|
+
detailsSent: [
|
|
5005
|
+
"crashes: error name, code, message, and a path-stripped stack trace",
|
|
5006
|
+
"usage: the command you ran and its flag names (never their values)",
|
|
5007
|
+
"usage: which built-in extractors ran, and which UI views you opened",
|
|
5008
|
+
"cli version, node major, os, arch, and a random anonymous id"
|
|
5009
|
+
],
|
|
5010
|
+
detailsNeverTitle: "Never sent",
|
|
5011
|
+
detailsNever: [
|
|
5012
|
+
"your files, their contents, frontmatter, annotations",
|
|
5013
|
+
"absolute paths, hostname, your username, ip address",
|
|
5014
|
+
"your settings values or any flag values"
|
|
5015
|
+
],
|
|
5016
|
+
detailsHint: "Turn error reports, CLI usage, and UI usage on or off independently in Settings, or force everything off with SKILL_MAP_TELEMETRY=0.",
|
|
5017
|
+
// Confirmation lines (glyph added by the renderer).
|
|
5018
|
+
enabled: "Telemetry on. Thanks. Turn error reports, CLI usage, and UI usage off independently in Settings.",
|
|
5019
|
+
disabled: "Telemetry off. You can turn any of it on later in Settings."
|
|
5020
|
+
};
|
|
5021
|
+
|
|
5022
|
+
// public-config.ts
|
|
5023
|
+
var SENTRY_DSN_NODE = "https://8b73dbb2563da4b77def12ce5ee46e75@o4511475590037504.ingest.de.sentry.io/4511475708002384";
|
|
5024
|
+
var POSTHOG_KEY_NODE = "phc_vMX3PcNeDsacWNg2hYEbKVXDijSWcjKFzabCkzU7RNEr";
|
|
5025
|
+
var POSTHOG_HOST = "https://eu.i.posthog.com";
|
|
5026
|
+
|
|
5027
|
+
// core/telemetry/scrub.ts
|
|
5028
|
+
var HOME_PLACEHOLDER = "<HOME>";
|
|
5029
|
+
var STRIPPED_ENVELOPE_KEYS = ["server_name", "user"];
|
|
5030
|
+
var HOME_PATTERNS = [
|
|
5031
|
+
// Windows: drive + Users + one user segment (back- or forward-slash).
|
|
5032
|
+
/[A-Za-z]:[\\/]Users[\\/][^\\/\s:*?"<>|]+/g,
|
|
5033
|
+
// POSIX user homes: /home/<user> or macOS /Users/<user>.
|
|
5034
|
+
/\/(?:home|Users)\/[^/\s:]+/g,
|
|
5035
|
+
// Root account home.
|
|
5036
|
+
/\/root(?=\/|\b)/g
|
|
5037
|
+
];
|
|
5038
|
+
function scrubString(value) {
|
|
5039
|
+
let out = value;
|
|
5040
|
+
for (const pattern of HOME_PATTERNS) {
|
|
5041
|
+
out = out.replace(pattern, HOME_PLACEHOLDER);
|
|
5042
|
+
}
|
|
5043
|
+
return out;
|
|
5044
|
+
}
|
|
5045
|
+
function scrubEvent(event) {
|
|
5046
|
+
const walked = walk(event);
|
|
5047
|
+
if (walked !== null && typeof walked === "object" && !Array.isArray(walked)) {
|
|
5048
|
+
const record = walked;
|
|
5049
|
+
for (const key of STRIPPED_ENVELOPE_KEYS) {
|
|
5050
|
+
if (key in record) delete record[key];
|
|
5051
|
+
}
|
|
5052
|
+
}
|
|
5053
|
+
return walked;
|
|
5054
|
+
}
|
|
5055
|
+
function walk(value) {
|
|
5056
|
+
if (typeof value === "string") return scrubString(value);
|
|
5057
|
+
if (Array.isArray(value)) return value.map((item) => walk(item));
|
|
5058
|
+
if (value !== null && typeof value === "object") {
|
|
5059
|
+
const out = {};
|
|
5060
|
+
for (const [key, child] of Object.entries(value)) {
|
|
5061
|
+
out[key] = walk(child);
|
|
5062
|
+
}
|
|
5063
|
+
return out;
|
|
5064
|
+
}
|
|
5065
|
+
return value;
|
|
5066
|
+
}
|
|
5067
|
+
|
|
5068
|
+
// cli/telemetry/telemetry-env.ts
|
|
5069
|
+
var TELEMETRY_ENV_VAR = "SKILL_MAP_TELEMETRY_ENV";
|
|
5070
|
+
function resolveTelemetryEnv() {
|
|
5071
|
+
const raw = process.env[TELEMETRY_ENV_VAR];
|
|
5072
|
+
if (raw === void 0 || raw.trim() === "" || raw === "prod" || raw === "production") {
|
|
5073
|
+
return "prod";
|
|
5074
|
+
}
|
|
5075
|
+
return "dev";
|
|
5076
|
+
}
|
|
5077
|
+
|
|
5078
|
+
// cli/telemetry/sentry-init.ts
|
|
5079
|
+
var KILL_SWITCH_ENV = "SKILL_MAP_TELEMETRY";
|
|
5080
|
+
var sdk = null;
|
|
5081
|
+
function isCliDsnConfigured() {
|
|
5082
|
+
return SENTRY_DSN_NODE !== "";
|
|
5083
|
+
}
|
|
5084
|
+
function isTelemetryForcedOff() {
|
|
5085
|
+
return process.env[KILL_SWITCH_ENV] === "0";
|
|
5086
|
+
}
|
|
5087
|
+
function isTelemetryActive(dsn) {
|
|
5088
|
+
if (isTelemetryForcedOff()) return false;
|
|
5089
|
+
if (dsn === "") return false;
|
|
5090
|
+
return isErrorTelemetryEnabled();
|
|
5091
|
+
}
|
|
5092
|
+
async function initSentryCli(version, loadSdk = () => import("@sentry/node")) {
|
|
5093
|
+
if (sdk) return;
|
|
5094
|
+
if (!isTelemetryActive(SENTRY_DSN_NODE)) return;
|
|
5095
|
+
const Sentry = await loadSdk();
|
|
5096
|
+
Sentry.init({
|
|
5097
|
+
dsn: SENTRY_DSN_NODE,
|
|
5098
|
+
release: `@skill-map/cli@${version}`,
|
|
5099
|
+
environment: resolveTelemetryEnv(),
|
|
5100
|
+
// CLI and BFF share one Sentry project; the `surface` tag tells their
|
|
5101
|
+
// events apart in the shared issue stream.
|
|
5102
|
+
initialScope: { tags: { surface: "cli" } },
|
|
5103
|
+
// Errors only: do NOT register the OpenTelemetry ESM loader hooks. We
|
|
5104
|
+
// run no tracing / auto-instrumentation, and the hook calls the
|
|
5105
|
+
// deprecated `module.register()` (a `DEP0205` warning on Node >= 26 that
|
|
5106
|
+
// would print on every telemetry-on invocation). Disabling it keeps
|
|
5107
|
+
// stderr clean and skips the loader's startup cost.
|
|
5108
|
+
registerEsmLoaderHooks: false,
|
|
5109
|
+
defaultIntegrations: false,
|
|
5110
|
+
integrations: [
|
|
5111
|
+
Sentry.onUncaughtExceptionIntegration(),
|
|
5112
|
+
Sentry.onUnhandledRejectionIntegration()
|
|
5113
|
+
],
|
|
5114
|
+
tracesSampleRate: 0,
|
|
5115
|
+
sendDefaultPii: false,
|
|
5116
|
+
beforeSend: (event) => scrubEvent(event)
|
|
5117
|
+
});
|
|
5118
|
+
sdk = Sentry;
|
|
5119
|
+
}
|
|
5120
|
+
function setTelemetryVerbTag(verb) {
|
|
5121
|
+
if (!sdk || verb === void 0 || verb === "") return;
|
|
5122
|
+
sdk.setTag("verb", verb);
|
|
5123
|
+
}
|
|
5124
|
+
async function closeSentryCli(timeoutMs = 2e3) {
|
|
5125
|
+
if (!sdk) return;
|
|
5126
|
+
try {
|
|
5127
|
+
await sdk.close(timeoutMs);
|
|
5128
|
+
} catch {
|
|
5129
|
+
}
|
|
5130
|
+
}
|
|
5131
|
+
|
|
5132
|
+
// cli/telemetry/usage-collector.ts
|
|
5133
|
+
var BUILT_IN_PLUGIN_IDS = /* @__PURE__ */ new Set([
|
|
5134
|
+
"claude",
|
|
5135
|
+
"antigravity",
|
|
5136
|
+
"openai",
|
|
5137
|
+
"agent-skills",
|
|
5138
|
+
"core"
|
|
5139
|
+
]);
|
|
5140
|
+
var EXTERNAL_PLUGIN_PLACEHOLDER = "external_plugin";
|
|
5141
|
+
function qualifyExtensionForUsage(qualifiedId2) {
|
|
5142
|
+
const slash = qualifiedId2.indexOf("/");
|
|
5143
|
+
if (slash <= 0) return EXTERNAL_PLUGIN_PLACEHOLDER;
|
|
5144
|
+
const pluginId = qualifiedId2.slice(0, slash);
|
|
5145
|
+
return BUILT_IN_PLUGIN_IDS.has(pluginId) ? qualifiedId2 : EXTERNAL_PLUGIN_PLACEHOLDER;
|
|
5146
|
+
}
|
|
5147
|
+
function buildScanExtensionSet(executedExtensionIds) {
|
|
5148
|
+
const out = /* @__PURE__ */ new Set();
|
|
5149
|
+
for (const id of executedExtensionIds) {
|
|
5150
|
+
out.add(qualifyExtensionForUsage(id));
|
|
5151
|
+
}
|
|
5152
|
+
return [...out].sort();
|
|
5153
|
+
}
|
|
5154
|
+
function extractFlagNames(args2) {
|
|
5155
|
+
const out = /* @__PURE__ */ new Set();
|
|
5156
|
+
for (const arg of args2) {
|
|
5157
|
+
if (!arg.startsWith("-")) continue;
|
|
5158
|
+
const name = arg.replace(/^-+/, "").split("=")[0];
|
|
5159
|
+
if (name !== void 0 && name !== "") out.add(name);
|
|
5160
|
+
}
|
|
5161
|
+
return [...out].sort();
|
|
5162
|
+
}
|
|
5163
|
+
function cliVerbEventName(verb, knownVerbs) {
|
|
5164
|
+
return `cli.${knownVerbs.has(verb) ? verb : "unknown"}`;
|
|
5165
|
+
}
|
|
5166
|
+
function buildCliVerbProperties(flagNames, extensions) {
|
|
5167
|
+
const flags = [...new Set(flagNames)].sort();
|
|
5168
|
+
return extensions ? { flags, extensions } : { flags };
|
|
5169
|
+
}
|
|
5170
|
+
function envUsageProps(cliVersion) {
|
|
5171
|
+
return {
|
|
5172
|
+
cli_version: cliVersion,
|
|
5173
|
+
node_major: Number.parseInt(process.versions.node.split(".")[0] ?? "0", 10),
|
|
5174
|
+
os: process.platform,
|
|
5175
|
+
arch: process.arch,
|
|
5176
|
+
environment: resolveTelemetryEnv()
|
|
5177
|
+
};
|
|
5178
|
+
}
|
|
5179
|
+
|
|
5180
|
+
// cli/telemetry/posthog-init.ts
|
|
5181
|
+
var client = null;
|
|
5182
|
+
function isUsageKeyConfigured() {
|
|
5183
|
+
return POSTHOG_KEY_NODE !== "";
|
|
5184
|
+
}
|
|
5185
|
+
function isUsageCliTelemetryActive(key) {
|
|
5186
|
+
if (isTelemetryForcedOff()) return false;
|
|
5187
|
+
if (key === "") return false;
|
|
5188
|
+
return isUsageCliTelemetryEnabled();
|
|
5189
|
+
}
|
|
5190
|
+
function scrubUsageEvent(event) {
|
|
5191
|
+
return event === null ? null : scrubEvent(event);
|
|
5192
|
+
}
|
|
5193
|
+
async function initUsageCli(loadSdk = () => import("posthog-node")) {
|
|
5194
|
+
if (client) return;
|
|
5195
|
+
if (!isUsageCliTelemetryActive(POSTHOG_KEY_NODE)) return;
|
|
5196
|
+
const { PostHog } = await loadSdk();
|
|
5197
|
+
client = new PostHog(POSTHOG_KEY_NODE, {
|
|
5198
|
+
host: POSTHOG_HOST,
|
|
5199
|
+
// Second line of defense behind the project-level IP drop: the client
|
|
5200
|
+
// never attaches an IP or geo, and never autocaptures.
|
|
5201
|
+
disableGeoip: true,
|
|
5202
|
+
before_send: scrubUsageEvent
|
|
5203
|
+
});
|
|
5204
|
+
}
|
|
5205
|
+
function captureUsage(event, properties) {
|
|
5206
|
+
if (client === null) return;
|
|
5207
|
+
const distinctId = readAnonymousId();
|
|
5208
|
+
if (distinctId === null) return;
|
|
5209
|
+
client.capture({
|
|
5210
|
+
distinctId,
|
|
5211
|
+
event,
|
|
5212
|
+
properties: { ...envUsageProps(VERSION), ...properties }
|
|
5213
|
+
});
|
|
5214
|
+
}
|
|
5215
|
+
var pendingScanExtensions = null;
|
|
5216
|
+
function setScanExtensions(extensions) {
|
|
5217
|
+
pendingScanExtensions = extensions;
|
|
5218
|
+
}
|
|
5219
|
+
function captureCliInvocation(verb, flagNames, knownVerbs) {
|
|
5220
|
+
const extensions = pendingScanExtensions;
|
|
5221
|
+
pendingScanExtensions = null;
|
|
5222
|
+
captureUsage(cliVerbEventName(verb, knownVerbs), buildCliVerbProperties(flagNames, extensions));
|
|
5223
|
+
}
|
|
5224
|
+
async function flushUsageCli(timeoutMs = 2e3) {
|
|
5225
|
+
if (client === null) return;
|
|
5226
|
+
try {
|
|
5227
|
+
await client.shutdown(timeoutMs);
|
|
5228
|
+
} catch {
|
|
5229
|
+
}
|
|
5230
|
+
}
|
|
5231
|
+
|
|
5232
|
+
// cli/telemetry/first-run-prompt.ts
|
|
5233
|
+
function interpretConsentAnswer(raw) {
|
|
5234
|
+
const value = raw.trim().toLowerCase();
|
|
5235
|
+
if (value === "n" || value === "no") return "no";
|
|
5236
|
+
if (value === "d" || value === "details") return "details";
|
|
5237
|
+
return "yes";
|
|
5238
|
+
}
|
|
5239
|
+
function isPromptEligible(opts) {
|
|
5240
|
+
return opts.dsnConfigured && opts.isTTY && !opts.isCI && !opts.forcedOff && !opts.alreadyPrompted;
|
|
5241
|
+
}
|
|
5242
|
+
function liveGateInputs(stdout) {
|
|
5243
|
+
return {
|
|
5244
|
+
dsnConfigured: isCliDsnConfigured() || isUsageKeyConfigured(),
|
|
5245
|
+
isTTY: stdout.isTTY === true,
|
|
5246
|
+
isCI: Boolean(process.env["CI"]),
|
|
5247
|
+
forcedOff: isTelemetryForcedOff(),
|
|
5248
|
+
alreadyPrompted: hasTelemetryPromptBeenShown()
|
|
5249
|
+
};
|
|
5250
|
+
}
|
|
5251
|
+
function renderConsent(ansi) {
|
|
5252
|
+
const t = TELEMETRY_PROMPT_TEXTS;
|
|
5253
|
+
const answerLine = ` ${t.question} ${ansi.bold(t.answerYes)} ${t.answerNo} ${ansi.dim(t.answerDetails)} `;
|
|
5254
|
+
return {
|
|
5255
|
+
question: [
|
|
5256
|
+
` ${ansi.cyan("\u2139")} ${ansi.bold(t.title)}`,
|
|
5257
|
+
...t.intro.map((line) => ` ${line}`),
|
|
5258
|
+
"",
|
|
5259
|
+
answerLine
|
|
5260
|
+
].join("\n"),
|
|
5261
|
+
reprompt: answerLine,
|
|
5262
|
+
details: [
|
|
5263
|
+
"",
|
|
5264
|
+
` ${t.detailsSentTitle}`,
|
|
5265
|
+
...t.detailsSent.map((line) => ` ${ansi.dim("\u2192")} ${line}`),
|
|
5266
|
+
` ${t.detailsNeverTitle}`,
|
|
5267
|
+
...t.detailsNever.map((line) => ` ${ansi.red("\u2715")} ${line}`),
|
|
5268
|
+
"",
|
|
5269
|
+
` ${ansi.dim(t.detailsHint)}`,
|
|
5270
|
+
""
|
|
5271
|
+
].join("\n"),
|
|
5272
|
+
enabled: ` ${ansi.green("\u2713")} ${t.enabled}
|
|
5273
|
+
`,
|
|
5274
|
+
disabled: ` ${ansi.cyan("\u2139")} ${t.disabled}
|
|
5275
|
+
`
|
|
5276
|
+
};
|
|
5277
|
+
}
|
|
5278
|
+
async function readConsentDecision(rl, stdout, rendered) {
|
|
5279
|
+
let answer = interpretConsentAnswer(await rl.question(rendered.question));
|
|
5280
|
+
while (answer === "details") {
|
|
5281
|
+
stdout.write(rendered.details);
|
|
5282
|
+
answer = interpretConsentAnswer(await rl.question(rendered.reprompt));
|
|
5283
|
+
}
|
|
5284
|
+
return answer === "yes";
|
|
5285
|
+
}
|
|
5286
|
+
async function runConsentPrompt(stdin, stdout, nowMs) {
|
|
5287
|
+
const rendered = renderConsent(
|
|
5288
|
+
ansiFor({ isTTY: stdout.isTTY === true, noColorFlag: false })
|
|
5289
|
+
);
|
|
5290
|
+
const rl = createInterface({ input: stdin, output: stdout });
|
|
5291
|
+
try {
|
|
5292
|
+
const consented = await readConsentDecision(rl, stdout, rendered);
|
|
5293
|
+
writeUserSettings({
|
|
5294
|
+
telemetry: {
|
|
5295
|
+
errorsEnabled: consented,
|
|
5296
|
+
usageCliEnabled: consented,
|
|
5297
|
+
usageUiEnabled: consented,
|
|
5298
|
+
promptedAt: nowMs
|
|
5299
|
+
}
|
|
5300
|
+
});
|
|
5301
|
+
if (consented) ensureAnonymousId();
|
|
5302
|
+
stdout.write(consented ? rendered.enabled : rendered.disabled);
|
|
5303
|
+
} catch {
|
|
5304
|
+
} finally {
|
|
5305
|
+
rl.close();
|
|
5306
|
+
}
|
|
5307
|
+
}
|
|
5308
|
+
async function maybeRunFirstRunPrompt({
|
|
5309
|
+
stdin = process.stdin,
|
|
5310
|
+
stdout = process.stdout,
|
|
5311
|
+
nowMs = Date.now()
|
|
5312
|
+
} = {}) {
|
|
5313
|
+
if (!isPromptEligible(liveGateInputs(stdout))) return;
|
|
5314
|
+
if (!hasSeenFirstRun()) {
|
|
5315
|
+
writeUserSettings({ telemetry: { firstRunAt: nowMs } });
|
|
5316
|
+
return;
|
|
5317
|
+
}
|
|
5318
|
+
await runConsentPrompt(stdin, stdout, nowMs);
|
|
5319
|
+
}
|
|
5320
|
+
|
|
4920
5321
|
// cli/commands/bump.ts
|
|
4921
5322
|
import { Command as Command2, Option as Option2 } from "clipanion";
|
|
4922
5323
|
|
|
@@ -5599,13 +6000,13 @@ var CONSENT_TEXTS = {
|
|
|
5599
6000
|
};
|
|
5600
6001
|
|
|
5601
6002
|
// cli/util/confirm.ts
|
|
5602
|
-
import { createInterface } from "readline";
|
|
6003
|
+
import { createInterface as createInterface2 } from "readline";
|
|
5603
6004
|
var YES_PATTERN = new RegExp(UTIL_TEXTS.confirmYesPatternSource, "i");
|
|
5604
6005
|
var NO_PATTERN = new RegExp(UTIL_TEXTS.confirmNoPatternSource, "i");
|
|
5605
6006
|
async function confirm(question, streams, opts) {
|
|
5606
6007
|
const defaultAnswer = opts?.defaultAnswer ?? "no";
|
|
5607
6008
|
const suffix = defaultAnswer === "yes" ? UTIL_TEXTS.confirmPromptSuffixDefaultYes : UTIL_TEXTS.confirmPromptSuffix;
|
|
5608
|
-
const rl =
|
|
6009
|
+
const rl = createInterface2({ input: streams.stdin, output: streams.stderr });
|
|
5609
6010
|
try {
|
|
5610
6011
|
const answer = await new Promise(
|
|
5611
6012
|
(resolveP) => rl.question(`${question}${suffix}`, resolveP)
|
|
@@ -7444,8 +7845,7 @@ async function replaceAllScanTags(trx, records, livePaths = /* @__PURE__ */ new
|
|
|
7444
7845
|
if (records.length === 0) return;
|
|
7445
7846
|
const rows = records.map((r) => ({
|
|
7446
7847
|
nodePath: r.nodePath,
|
|
7447
|
-
tag: r.tag
|
|
7448
|
-
source: r.source
|
|
7848
|
+
tag: r.tag
|
|
7449
7849
|
}));
|
|
7450
7850
|
const BATCH = 300;
|
|
7451
7851
|
for (let i = 0; i < rows.length; i += BATCH) {
|
|
@@ -7453,18 +7853,16 @@ async function replaceAllScanTags(trx, records, livePaths = /* @__PURE__ */ new
|
|
|
7453
7853
|
}
|
|
7454
7854
|
}
|
|
7455
7855
|
async function loadTagsForNode(db, nodePath) {
|
|
7456
|
-
const rows = await db.selectFrom("scan_node_tags").select(["nodePath", "tag"
|
|
7457
|
-
return rows.map((r) => ({ nodePath: r.nodePath, tag: r.tag
|
|
7856
|
+
const rows = await db.selectFrom("scan_node_tags").select(["nodePath", "tag"]).where("nodePath", "=", nodePath).orderBy("tag", "asc").execute();
|
|
7857
|
+
return rows.map((r) => ({ nodePath: r.nodePath, tag: r.tag }));
|
|
7458
7858
|
}
|
|
7459
7859
|
async function loadTagsForPaths(db, nodePaths) {
|
|
7460
7860
|
if (nodePaths.length === 0) return [];
|
|
7461
|
-
const rows = await db.selectFrom("scan_node_tags").select(["nodePath", "tag"
|
|
7462
|
-
return rows.map((r) => ({ nodePath: r.nodePath, tag: r.tag
|
|
7861
|
+
const rows = await db.selectFrom("scan_node_tags").select(["nodePath", "tag"]).where("nodePath", "in", [...nodePaths]).orderBy("tag", "asc").execute();
|
|
7862
|
+
return rows.map((r) => ({ nodePath: r.nodePath, tag: r.tag }));
|
|
7463
7863
|
}
|
|
7464
|
-
async function findNodesByTag(db, tag
|
|
7465
|
-
|
|
7466
|
-
if (source !== void 0) q = q.where("source", "=", source);
|
|
7467
|
-
const rows = await q.distinct().orderBy("nodePath", "asc").execute();
|
|
7864
|
+
async function findNodesByTag(db, tag) {
|
|
7865
|
+
const rows = await db.selectFrom("scan_node_tags").select("nodePath").where("tag", "=", tag).distinct().orderBy("nodePath", "asc").execute();
|
|
7468
7866
|
return rows.map((r) => r.nodePath);
|
|
7469
7867
|
}
|
|
7470
7868
|
|
|
@@ -7662,19 +8060,18 @@ function pickIntegerVersion(v) {
|
|
|
7662
8060
|
function nodesToTagRecords(nodes) {
|
|
7663
8061
|
const records = [];
|
|
7664
8062
|
for (const node of nodes) {
|
|
7665
|
-
pushTagRecords(records, node.path, node.
|
|
7666
|
-
pushTagRecords(records, node.path, node.sidecar?.annotations?.["tags"], "user");
|
|
8063
|
+
pushTagRecords(records, node.path, node.sidecar?.annotations?.["tags"]);
|
|
7667
8064
|
}
|
|
7668
8065
|
return records;
|
|
7669
8066
|
}
|
|
7670
|
-
function pushTagRecords(out, nodePath, raw
|
|
8067
|
+
function pushTagRecords(out, nodePath, raw) {
|
|
7671
8068
|
if (!Array.isArray(raw)) return;
|
|
7672
8069
|
const seen = /* @__PURE__ */ new Set();
|
|
7673
8070
|
for (const item of raw) {
|
|
7674
8071
|
if (typeof item !== "string" || item.length === 0) continue;
|
|
7675
8072
|
if (seen.has(item)) continue;
|
|
7676
8073
|
seen.add(item);
|
|
7677
|
-
out.push({ nodePath, tag: item
|
|
8074
|
+
out.push({ nodePath, tag: item });
|
|
7678
8075
|
}
|
|
7679
8076
|
}
|
|
7680
8077
|
function linkToRow(link) {
|
|
@@ -7882,7 +8279,7 @@ var SqliteStorageAdapter = class {
|
|
|
7882
8279
|
/**
|
|
7883
8280
|
* Access the underlying Kysely instance.
|
|
7884
8281
|
*
|
|
7885
|
-
* Test-only escape hatch (per
|
|
8282
|
+
* Test-only escape hatch (per context/kernel.md §Kernel boundaries, tests
|
|
7886
8283
|
* are the documented exception). CLI commands MUST consume the
|
|
7887
8284
|
* adapter through the namespaced port surfaces (`port.<namespace>.*`
|
|
7888
8285
|
* or `port.transaction(...)`); reaching for this getter from a
|
|
@@ -7915,7 +8312,7 @@ var SqliteStorageAdapter = class {
|
|
|
7915
8312
|
this.tags = {
|
|
7916
8313
|
listForNode: (nodePath) => loadTagsForNode(this.db, nodePath),
|
|
7917
8314
|
listForPaths: (paths) => loadTagsForPaths(this.db, paths),
|
|
7918
|
-
findNodes: (tag
|
|
8315
|
+
findNodes: (tag) => findNodesByTag(this.db, tag)
|
|
7919
8316
|
};
|
|
7920
8317
|
this.issues = {
|
|
7921
8318
|
listAll: () => listAllIssues(this.db),
|
|
@@ -10221,8 +10618,8 @@ var DEFAULT_READ_CONFIG = Object.freeze({
|
|
|
10221
10618
|
});
|
|
10222
10619
|
function resolveProviderWalk(provider) {
|
|
10223
10620
|
if (provider.walk) {
|
|
10224
|
-
const
|
|
10225
|
-
return
|
|
10621
|
+
const walk3 = provider.walk.bind(provider);
|
|
10622
|
+
return walk3;
|
|
10226
10623
|
}
|
|
10227
10624
|
const read = provider.read ?? DEFAULT_READ_CONFIG;
|
|
10228
10625
|
return (roots, options) => {
|
|
@@ -12618,9 +13015,22 @@ var DbRestoreCommand = class extends SmCommand {
|
|
|
12618
13015
|
};
|
|
12619
13016
|
|
|
12620
13017
|
// cli/commands/db/reset.ts
|
|
12621
|
-
import { rm as rm2 } from "fs/promises";
|
|
12622
13018
|
import { DatabaseSync as DatabaseSync5 } from "node:sqlite";
|
|
12623
13019
|
import { Command as Command8, Option as Option8 } from "clipanion";
|
|
13020
|
+
|
|
13021
|
+
// core/sqlite/db-files.ts
|
|
13022
|
+
import { existsSync as existsSync19 } from "fs";
|
|
13023
|
+
import { rm as rm2 } from "fs/promises";
|
|
13024
|
+
var DB_FILE_SUFFIXES = ["", "-wal", "-shm"];
|
|
13025
|
+
async function removeDbFiles(dbPath) {
|
|
13026
|
+
if (dbPath === ":memory:") return;
|
|
13027
|
+
for (const suffix of DB_FILE_SUFFIXES) {
|
|
13028
|
+
const p = `${dbPath}${suffix}`;
|
|
13029
|
+
if (existsSync19(p)) await rm2(p);
|
|
13030
|
+
}
|
|
13031
|
+
}
|
|
13032
|
+
|
|
13033
|
+
// cli/commands/db/reset.ts
|
|
12624
13034
|
var DbResetCommand = class extends SmCommand {
|
|
12625
13035
|
static paths = [["db", "reset"]];
|
|
12626
13036
|
static usage = Command8.Usage({
|
|
@@ -12680,10 +13090,7 @@ var DbResetCommand = class extends SmCommand {
|
|
|
12680
13090
|
return ExitCode.Error;
|
|
12681
13091
|
}
|
|
12682
13092
|
}
|
|
12683
|
-
|
|
12684
|
-
const p = `${path}${suffix}`;
|
|
12685
|
-
if (await pathExists(p)) await rm2(p);
|
|
12686
|
-
}
|
|
13093
|
+
await removeDbFiles(path);
|
|
12687
13094
|
const ansiHard = this.ansiFor("stdout");
|
|
12688
13095
|
this.printer.data(
|
|
12689
13096
|
tx(DB_TEXTS.resetHardDeleted, {
|
|
@@ -13838,8 +14245,7 @@ var HelpCommand = class extends Command15 {
|
|
|
13838
14245
|
|
|
13839
14246
|
Formats:
|
|
13840
14247
|
human (default): pretty terminal output.
|
|
13841
|
-
md : canonical markdown
|
|
13842
|
-
regenerated from this and CI fails on drift.
|
|
14248
|
+
md : canonical markdown for documentation.
|
|
13843
14249
|
json : structured surface dump per spec/cli-contract.md.
|
|
13844
14250
|
`
|
|
13845
14251
|
});
|
|
@@ -14424,7 +14830,7 @@ import { join as join17 } from "path";
|
|
|
14424
14830
|
import { Command as Command17, Option as Option16 } from "clipanion";
|
|
14425
14831
|
|
|
14426
14832
|
// kernel/orchestrator/index.ts
|
|
14427
|
-
import { existsSync as
|
|
14833
|
+
import { existsSync as existsSync22, statSync as statSync6 } from "fs";
|
|
14428
14834
|
import { isAbsolute as isAbsolute7, resolve as resolve28 } from "path";
|
|
14429
14835
|
import { Tiktoken as Tiktoken2 } from "js-tiktoken/lite";
|
|
14430
14836
|
import cl100k_base from "js-tiktoken/ranks/cl100k_base";
|
|
@@ -15471,16 +15877,16 @@ function computeDriftStatus(args2) {
|
|
|
15471
15877
|
}
|
|
15472
15878
|
|
|
15473
15879
|
// kernel/sidecar/discover-orphans.ts
|
|
15474
|
-
import { existsSync as
|
|
15880
|
+
import { existsSync as existsSync20, readdirSync as readdirSync7, statSync as statSync5 } from "fs";
|
|
15475
15881
|
import { join as join13, relative as relative4, sep as sep4 } from "path";
|
|
15476
15882
|
function discoverOrphanSidecars(roots, shouldSkip) {
|
|
15477
15883
|
const out = [];
|
|
15478
15884
|
for (const root of roots) {
|
|
15479
|
-
|
|
15885
|
+
walk2(root, root, shouldSkip ?? (() => false), out);
|
|
15480
15886
|
}
|
|
15481
15887
|
return out;
|
|
15482
15888
|
}
|
|
15483
|
-
function
|
|
15889
|
+
function walk2(root, current, shouldSkip, out) {
|
|
15484
15890
|
let entries;
|
|
15485
15891
|
try {
|
|
15486
15892
|
entries = readdirSync7(current, { withFileTypes: true, encoding: "utf8" });
|
|
@@ -15493,13 +15899,13 @@ function walk(root, current, shouldSkip, out) {
|
|
|
15493
15899
|
if (shouldSkip(rel)) continue;
|
|
15494
15900
|
if (entry.isSymbolicLink()) continue;
|
|
15495
15901
|
if (entry.isDirectory()) {
|
|
15496
|
-
|
|
15902
|
+
walk2(root, full, shouldSkip, out);
|
|
15497
15903
|
continue;
|
|
15498
15904
|
}
|
|
15499
15905
|
if (!entry.isFile()) continue;
|
|
15500
15906
|
if (!entry.name.endsWith(".sm")) continue;
|
|
15501
15907
|
const expectedMd = `${full.slice(0, -".sm".length)}.md`;
|
|
15502
|
-
if (
|
|
15908
|
+
if (existsSync20(expectedMd) && safeIsFile(expectedMd)) continue;
|
|
15503
15909
|
out.push({ sidecarPath: full, relativePath: rel, expectedMdPath: expectedMd });
|
|
15504
15910
|
}
|
|
15505
15911
|
}
|
|
@@ -15513,7 +15919,7 @@ function safeIsFile(path) {
|
|
|
15513
15919
|
|
|
15514
15920
|
// kernel/orchestrator/node-build.ts
|
|
15515
15921
|
import { createHash } from "crypto";
|
|
15516
|
-
import { existsSync as
|
|
15922
|
+
import { existsSync as existsSync21 } from "fs";
|
|
15517
15923
|
import { isAbsolute as isAbsolute6, resolve as resolvePath } from "path";
|
|
15518
15924
|
import "js-tiktoken/lite";
|
|
15519
15925
|
import yaml4 from "js-yaml";
|
|
@@ -15677,11 +16083,11 @@ function resolveSidecarOverlay(relativePath2, nodePathForIssue, roots, liveBodyH
|
|
|
15677
16083
|
}
|
|
15678
16084
|
function resolveAbsoluteMdPath(relativePath2, roots) {
|
|
15679
16085
|
if (isAbsolute6(relativePath2)) {
|
|
15680
|
-
return
|
|
16086
|
+
return existsSync21(relativePath2) ? relativePath2 : null;
|
|
15681
16087
|
}
|
|
15682
16088
|
for (const root of roots) {
|
|
15683
16089
|
const candidate = resolvePath(root, relativePath2);
|
|
15684
|
-
if (
|
|
16090
|
+
if (existsSync21(candidate)) return candidate;
|
|
15685
16091
|
}
|
|
15686
16092
|
return null;
|
|
15687
16093
|
}
|
|
@@ -16117,7 +16523,8 @@ function buildPostWalkTransformCtx(providers, nodes, activeProvider) {
|
|
|
16117
16523
|
const reservedNodePaths = buildReservedNodePaths(
|
|
16118
16524
|
nodes,
|
|
16119
16525
|
kindRegistry,
|
|
16120
|
-
reservedNamesByProviderKind
|
|
16526
|
+
reservedNamesByProviderKind,
|
|
16527
|
+
activeProvider
|
|
16121
16528
|
);
|
|
16122
16529
|
return { kindRegistry, providerResolution, activeProvider, reservedNodePaths };
|
|
16123
16530
|
}
|
|
@@ -16148,19 +16555,23 @@ function indexReservedNames(provider, out) {
|
|
|
16148
16555
|
}
|
|
16149
16556
|
}
|
|
16150
16557
|
}
|
|
16151
|
-
function buildReservedNodePaths(nodes, kindRegistry, reservedNamesByProviderKind) {
|
|
16558
|
+
function buildReservedNodePaths(nodes, kindRegistry, reservedNamesByProviderKind, activeProvider) {
|
|
16152
16559
|
const out = /* @__PURE__ */ new Set();
|
|
16153
16560
|
for (const node of nodes) {
|
|
16154
|
-
const
|
|
16155
|
-
const
|
|
16156
|
-
|
|
16157
|
-
|
|
16158
|
-
|
|
16561
|
+
const selfKey = `${node.provider}/${node.kind}`;
|
|
16562
|
+
const selfReserved = reservedNamesByProviderKind.get(selfKey);
|
|
16563
|
+
const lensReserved = activeProvider && activeProvider !== node.provider ? reservedNamesByProviderKind.get(`${activeProvider}/${node.kind}`) : void 0;
|
|
16564
|
+
if (!hasEntries(selfReserved) && !hasEntries(lensReserved)) continue;
|
|
16565
|
+
const ids = deriveNodeIdentifiers(node, kindRegistry.get(selfKey));
|
|
16566
|
+
if (ids.some((id) => selfReserved?.has(id) === true || lensReserved?.has(id) === true)) {
|
|
16159
16567
|
out.add(node.path);
|
|
16160
16568
|
}
|
|
16161
16569
|
}
|
|
16162
16570
|
return out;
|
|
16163
16571
|
}
|
|
16572
|
+
function hasEntries(set) {
|
|
16573
|
+
return set !== void 0 && set.size > 0;
|
|
16574
|
+
}
|
|
16164
16575
|
function buildScanSetup(options) {
|
|
16165
16576
|
const start = Date.now();
|
|
16166
16577
|
const emitter = options.emitter ?? new InMemoryProgressEmitter();
|
|
@@ -16246,7 +16657,7 @@ function validateRoots(roots) {
|
|
|
16246
16657
|
throw new Error(ORCHESTRATOR_TEXTS.runScanRootEmptyArray);
|
|
16247
16658
|
}
|
|
16248
16659
|
for (const root of roots) {
|
|
16249
|
-
if (!
|
|
16660
|
+
if (!existsSync22(root) || !statSync6(root).isDirectory()) {
|
|
16250
16661
|
throw new Error(tx(ORCHESTRATOR_TEXTS.runScanRootMissing, { root }));
|
|
16251
16662
|
}
|
|
16252
16663
|
}
|
|
@@ -16255,7 +16666,7 @@ function resolveActiveProviderOption(optionValue, roots, providers) {
|
|
|
16255
16666
|
if (optionValue !== void 0) return optionValue;
|
|
16256
16667
|
for (const root of roots) {
|
|
16257
16668
|
const absRoot = isAbsolute7(root) ? root : resolve28(root);
|
|
16258
|
-
if (!
|
|
16669
|
+
if (!existsSync22(absRoot)) continue;
|
|
16259
16670
|
const detected = resolveActiveProvider(absRoot, providers).resolved;
|
|
16260
16671
|
if (detected !== null) return detected;
|
|
16261
16672
|
}
|
|
@@ -16722,7 +17133,7 @@ function safeStat(path) {
|
|
|
16722
17133
|
}
|
|
16723
17134
|
|
|
16724
17135
|
// core/runtime/active-provider-bootstrap.ts
|
|
16725
|
-
import { createInterface as
|
|
17136
|
+
import { createInterface as createInterface3 } from "readline";
|
|
16726
17137
|
import { isAbsolute as isAbsolute9, join as join16 } from "path";
|
|
16727
17138
|
async function bootstrapActiveProvider(opts) {
|
|
16728
17139
|
const fromCwd = resolveActiveProvider(opts.cwd, opts.providers);
|
|
@@ -16878,7 +17289,7 @@ async function promptForLens(detected, stdin, stderr, warnGlyph) {
|
|
|
16878
17289
|
);
|
|
16879
17290
|
}
|
|
16880
17291
|
stderr.write(lines.join("\n") + "\n");
|
|
16881
|
-
const rl =
|
|
17292
|
+
const rl = createInterface3({ input: stdin, output: stderr });
|
|
16882
17293
|
try {
|
|
16883
17294
|
const answer = await new Promise(
|
|
16884
17295
|
(resolveP) => rl.question(SCAN_RUNNER_TEXTS.activeProviderPromptInput, resolveP)
|
|
@@ -16895,6 +17306,99 @@ async function promptForLens(detected, stdin, stderr, warnGlyph) {
|
|
|
16895
17306
|
}
|
|
16896
17307
|
}
|
|
16897
17308
|
|
|
17309
|
+
// core/sqlite/db-drift-reset.ts
|
|
17310
|
+
import { existsSync as existsSync23 } from "fs";
|
|
17311
|
+
import { createInterface as createInterface4 } from "readline";
|
|
17312
|
+
import { DatabaseSync as DatabaseSync7 } from "node:sqlite";
|
|
17313
|
+
|
|
17314
|
+
// core/sqlite/i18n/db-drift.texts.ts
|
|
17315
|
+
var DB_DRIFT_TEXTS = {
|
|
17316
|
+
// Interactive confirm (TTY `sm scan`, no `--yes`). The block is
|
|
17317
|
+
// written to stderr, then the question line drives `readline`.
|
|
17318
|
+
driftPrompt: "{{glyph}} The local cache was built by skill-map {{dbVersion}} and you are on {{currentVersion}}.\n {{hint}}\n",
|
|
17319
|
+
driftPromptHint: "Pre-1.0 the DB is a derived cache (your .sm sidecars hold the real data); it cannot be carried across a version change and has to be rebuilt.",
|
|
17320
|
+
driftPromptQuestion: "Delete the local cache and rebuild it on this scan? [y/N] ",
|
|
17321
|
+
// Receipt after the rebuild (printed by the scan / refresh path).
|
|
17322
|
+
driftReset: "{{glyph}} Local cache rebuilt: it was written by skill-map {{dbVersion}}, you are on {{currentVersion}}.\n {{hint}}\n",
|
|
17323
|
+
driftResetHint: "The DB was deleted and is being regenerated by this scan; .sm sidecars were not touched.",
|
|
17324
|
+
// Abort headline when the operator declines (wrapped by the caller's
|
|
17325
|
+
// `sm scan: {message}` shell, so it carries no glyph / verb prefix).
|
|
17326
|
+
driftAborted: "cache rebuild declined: the {{dbVersion}} cache cannot be reused on {{currentVersion}}. {{hint}}",
|
|
17327
|
+
driftAbortedHint: "Re-run with --yes, or run `sm db reset --hard` then `sm scan`."
|
|
17328
|
+
};
|
|
17329
|
+
|
|
17330
|
+
// core/sqlite/db-drift-reset.ts
|
|
17331
|
+
async function maybeResetOnDrift(dbPath, policy) {
|
|
17332
|
+
const dbVersion = readScannedByVersion(dbPath);
|
|
17333
|
+
if (dbVersion === null) return { kind: "no-drift" };
|
|
17334
|
+
const skew = classifyVersionSkew(dbVersion, policy.currentVersion);
|
|
17335
|
+
if (skew.kind === "ok" || skew.kind === "no-meta") return { kind: "no-drift" };
|
|
17336
|
+
const confirmed = await confirmDriftReset(dbVersion, policy);
|
|
17337
|
+
if (!confirmed) {
|
|
17338
|
+
return { kind: "aborted", dbVersion, currentVersion: policy.currentVersion };
|
|
17339
|
+
}
|
|
17340
|
+
await removeDbFiles(dbPath);
|
|
17341
|
+
renderResetReceipt(dbVersion, policy);
|
|
17342
|
+
return { kind: "reset", dbVersion, currentVersion: policy.currentVersion };
|
|
17343
|
+
}
|
|
17344
|
+
function readScannedByVersion(dbPath) {
|
|
17345
|
+
if (dbPath === ":memory:" || !existsSync23(dbPath)) return null;
|
|
17346
|
+
let raw = null;
|
|
17347
|
+
try {
|
|
17348
|
+
raw = new DatabaseSync7(dbPath, { readOnly: true });
|
|
17349
|
+
const row = raw.prepare("SELECT scanned_by_version AS v FROM scan_meta LIMIT 1").get();
|
|
17350
|
+
const v = row?.v;
|
|
17351
|
+
return typeof v === "string" && v.length > 0 ? v : null;
|
|
17352
|
+
} catch {
|
|
17353
|
+
return null;
|
|
17354
|
+
} finally {
|
|
17355
|
+
raw?.close();
|
|
17356
|
+
}
|
|
17357
|
+
}
|
|
17358
|
+
async function confirmDriftReset(dbVersion, policy) {
|
|
17359
|
+
if (!shouldPromptForReset(policy)) return true;
|
|
17360
|
+
return askDriftReset(dbVersion, policy);
|
|
17361
|
+
}
|
|
17362
|
+
function shouldPromptForReset(policy) {
|
|
17363
|
+
if (policy.assumeYes) return false;
|
|
17364
|
+
if (!policy.stdin || !policy.stderr) return false;
|
|
17365
|
+
return policy.stdin.isTTY === true;
|
|
17366
|
+
}
|
|
17367
|
+
async function askDriftReset(dbVersion, policy) {
|
|
17368
|
+
const warnGlyph = policy.style?.warnGlyph ?? "\u26A0";
|
|
17369
|
+
const dim = policy.style?.dim ?? ((s) => s);
|
|
17370
|
+
policy.stderr.write(
|
|
17371
|
+
tx(DB_DRIFT_TEXTS.driftPrompt, {
|
|
17372
|
+
glyph: warnGlyph,
|
|
17373
|
+
dbVersion,
|
|
17374
|
+
currentVersion: policy.currentVersion,
|
|
17375
|
+
hint: dim(DB_DRIFT_TEXTS.driftPromptHint)
|
|
17376
|
+
})
|
|
17377
|
+
);
|
|
17378
|
+
const rl = createInterface4({ input: policy.stdin, output: policy.stderr });
|
|
17379
|
+
try {
|
|
17380
|
+
const answer = await new Promise(
|
|
17381
|
+
(resolveP) => rl.question(DB_DRIFT_TEXTS.driftPromptQuestion, resolveP)
|
|
17382
|
+
);
|
|
17383
|
+
return /^y(es)?$/i.test(answer.trim());
|
|
17384
|
+
} finally {
|
|
17385
|
+
rl.close();
|
|
17386
|
+
}
|
|
17387
|
+
}
|
|
17388
|
+
function renderResetReceipt(dbVersion, policy) {
|
|
17389
|
+
if (!policy.printer) return;
|
|
17390
|
+
const warnGlyph = policy.style?.warnGlyph ?? "\u26A0";
|
|
17391
|
+
const dim = policy.style?.dim ?? ((s) => s);
|
|
17392
|
+
policy.printer.warn(
|
|
17393
|
+
tx(DB_DRIFT_TEXTS.driftReset, {
|
|
17394
|
+
glyph: warnGlyph,
|
|
17395
|
+
dbVersion,
|
|
17396
|
+
currentVersion: policy.currentVersion,
|
|
17397
|
+
hint: dim(DB_DRIFT_TEXTS.driftResetHint)
|
|
17398
|
+
})
|
|
17399
|
+
);
|
|
17400
|
+
}
|
|
17401
|
+
|
|
16898
17402
|
// core/runtime/scan-runner.ts
|
|
16899
17403
|
async function runScanForCommand(opts) {
|
|
16900
17404
|
const ctx = opts.ctx ?? defaultRuntimeContext();
|
|
@@ -16907,9 +17411,9 @@ async function runScanForCommand(opts) {
|
|
|
16907
17411
|
const { cfg, ignoreFilter, strict, effectiveRoots } = scanInputs;
|
|
16908
17412
|
let referenceablePaths;
|
|
16909
17413
|
if (cfg.scan.referencePaths.length > 0) {
|
|
16910
|
-
const
|
|
16911
|
-
referenceablePaths =
|
|
16912
|
-
emitReferenceWalkAdvisory(
|
|
17414
|
+
const walk3 = walkReferencePaths(cfg.scan.referencePaths, ctx.cwd);
|
|
17415
|
+
referenceablePaths = walk3.paths;
|
|
17416
|
+
emitReferenceWalkAdvisory(walk3, opts);
|
|
16913
17417
|
}
|
|
16914
17418
|
const loadPrior = makePriorLoader(opts.noBuiltIns, strict);
|
|
16915
17419
|
const jobsDir = defaultProjectJobsDir(ctx);
|
|
@@ -16971,11 +17475,11 @@ async function resolveActiveLens(opts, ctx, effectiveRoots, pluginRuntime, provi
|
|
|
16971
17475
|
});
|
|
16972
17476
|
return { kind: "ok", activeProvider: bootstrap.activeProvider };
|
|
16973
17477
|
}
|
|
16974
|
-
function emitReferenceWalkAdvisory(
|
|
16975
|
-
if (
|
|
17478
|
+
function emitReferenceWalkAdvisory(walk3, opts) {
|
|
17479
|
+
if (walk3.truncated) {
|
|
16976
17480
|
opts.printer.warn(SCAN_RUNNER_TEXTS.referenceWalkTruncated);
|
|
16977
17481
|
}
|
|
16978
|
-
for (const missing of
|
|
17482
|
+
for (const missing of walk3.missingRoots) {
|
|
16979
17483
|
opts.printer.warn(
|
|
16980
17484
|
tx(SCAN_RUNNER_TEXTS.referenceWalkMissingRoot, { path: missing })
|
|
16981
17485
|
);
|
|
@@ -17092,7 +17596,29 @@ function buildRunScanEmitter(opts) {
|
|
|
17092
17596
|
colorEnabled: opts.colorEnabled === true
|
|
17093
17597
|
});
|
|
17094
17598
|
}
|
|
17599
|
+
async function rebuildOnDrift(opts, dbPath) {
|
|
17600
|
+
const drift = await maybeResetOnDrift(dbPath, {
|
|
17601
|
+
currentVersion: VERSION,
|
|
17602
|
+
assumeYes: opts.yes ?? false,
|
|
17603
|
+
stdin: opts.stdin ?? process.stdin,
|
|
17604
|
+
stderr: opts.stderr,
|
|
17605
|
+
printer: opts.printer,
|
|
17606
|
+
...opts.style ? { style: opts.style } : {}
|
|
17607
|
+
});
|
|
17608
|
+
if (drift.kind !== "aborted") return null;
|
|
17609
|
+
const dim = opts.style?.dim ?? ((s) => s);
|
|
17610
|
+
return {
|
|
17611
|
+
kind: "scan-error",
|
|
17612
|
+
message: tx(DB_DRIFT_TEXTS.driftAborted, {
|
|
17613
|
+
dbVersion: drift.dbVersion,
|
|
17614
|
+
currentVersion: drift.currentVersion,
|
|
17615
|
+
hint: dim(DB_DRIFT_TEXTS.driftAbortedHint)
|
|
17616
|
+
})
|
|
17617
|
+
};
|
|
17618
|
+
}
|
|
17095
17619
|
async function runPersistPath(opts, dbPath, jobsDir, strict, loadPrior, runScanWith, extensions) {
|
|
17620
|
+
const driftError = await rebuildOnDrift(opts, dbPath);
|
|
17621
|
+
if (driftError) return driftError;
|
|
17096
17622
|
let outcome;
|
|
17097
17623
|
try {
|
|
17098
17624
|
outcome = await withSqlite({ databasePath: dbPath }, async (adapter) => {
|
|
@@ -17132,7 +17658,8 @@ async function runPersistPath(opts, dbPath, jobsDir, strict, loadPrior, runScanW
|
|
|
17132
17658
|
renameOps: outcome.renameOps,
|
|
17133
17659
|
persistedTo: dbPath,
|
|
17134
17660
|
dbPath,
|
|
17135
|
-
strict
|
|
17661
|
+
strict,
|
|
17662
|
+
executedExtensionIds: outcome.extractorRuns.map((run) => run.extractorId)
|
|
17136
17663
|
};
|
|
17137
17664
|
}
|
|
17138
17665
|
async function runEphemeralPath(opts, dbPath, strict, loadPrior, runScanWith) {
|
|
@@ -17150,7 +17677,8 @@ async function runEphemeralPath(opts, dbPath, strict, loadPrior, runScanWith) {
|
|
|
17150
17677
|
renameOps: scanned.renameOps,
|
|
17151
17678
|
persistedTo: null,
|
|
17152
17679
|
dbPath,
|
|
17153
|
-
strict
|
|
17680
|
+
strict,
|
|
17681
|
+
executedExtensionIds: scanned.extractorRuns.map((run) => run.extractorId)
|
|
17154
17682
|
};
|
|
17155
17683
|
} catch (err) {
|
|
17156
17684
|
return { kind: "scan-error", message: formatErrorMessage(err) };
|
|
@@ -18182,19 +18710,6 @@ import { Command as Command20, Option as Option19 } from "clipanion";
|
|
|
18182
18710
|
var LIST_TEXTS = {
|
|
18183
18711
|
invalidSortBy: '{{glyph}} --sort-by: invalid sort field "{{value}}".\n {{hint}}\n',
|
|
18184
18712
|
invalidSortByHint: "Allowed: {{allowed}}.",
|
|
18185
|
-
/**
|
|
18186
|
-
* §3.1b two-line block. Closed enum: hint enumerates the two valid
|
|
18187
|
-
* values so the operator does not need to re-read `--help`.
|
|
18188
|
-
*/
|
|
18189
|
-
invalidTagSource: '{{glyph}} --tag-source: expected "author" or "user", got "{{value}}".\n {{hint}}\n',
|
|
18190
|
-
invalidTagSourceHint: "Allowed: author, user.",
|
|
18191
|
-
/**
|
|
18192
|
-
* §3.1b two-line block. `--tag-source` is a narrowing filter on
|
|
18193
|
-
* `--tag`; the hint repeats the dependency in operator-actionable
|
|
18194
|
-
* form.
|
|
18195
|
-
*/
|
|
18196
|
-
tagSourceWithoutTag: "{{glyph}} --tag-source requires --tag <name>.\n {{hint}}\n",
|
|
18197
|
-
tagSourceWithoutTagHint: "The source filter narrows tag matches, it does not stand alone. Pass --tag <name> alongside --tag-source.",
|
|
18198
18713
|
noNodesFound: "No nodes found.\n",
|
|
18199
18714
|
// --- renderTable column headers ----------------------------------------
|
|
18200
18715
|
tableHeaderPath: "PATH",
|
|
@@ -18231,9 +18746,8 @@ var ListCommand = class extends SmCommand {
|
|
|
18231
18746
|
Reads from the persisted scan snapshot (scan_nodes). Filters:
|
|
18232
18747
|
--kind <k> restricts to one node kind; --issue keeps only nodes
|
|
18233
18748
|
that touch at least one current issue; --tag <name> keeps only
|
|
18234
|
-
nodes carrying that tag
|
|
18235
|
-
|
|
18236
|
-
narrows to one side).
|
|
18749
|
+
nodes carrying that tag in their \`.sm\` sidecar
|
|
18750
|
+
(\`annotations.tags\`).
|
|
18237
18751
|
|
|
18238
18752
|
--sort-by accepts: path, kind, tokens_total, links_out_count,
|
|
18239
18753
|
links_in_count, external_refs_count. Default: path. --limit N caps
|
|
@@ -18246,8 +18760,7 @@ var ListCommand = class extends SmCommand {
|
|
|
18246
18760
|
["List only agents", "$0 list --kind agent"],
|
|
18247
18761
|
["Top 5 by total tokens", "$0 list --sort-by tokens_total --limit 5"],
|
|
18248
18762
|
["Only nodes with issues, machine-readable", "$0 list --issue --json"],
|
|
18249
|
-
["Filter by tag
|
|
18250
|
-
["Filter by user-only tag", "$0 list --tag wip --tag-source user"]
|
|
18763
|
+
["Filter by tag", "$0 list --tag urgent"]
|
|
18251
18764
|
]
|
|
18252
18765
|
});
|
|
18253
18766
|
kind = Option19.String("--kind", { required: false });
|
|
@@ -18255,7 +18768,6 @@ var ListCommand = class extends SmCommand {
|
|
|
18255
18768
|
sortBy = Option19.String("--sort-by", { required: false });
|
|
18256
18769
|
limit = Option19.String("--limit", { required: false });
|
|
18257
18770
|
tag = Option19.String("--tag", { required: false });
|
|
18258
|
-
tagSource = Option19.String("--tag-source", { required: false });
|
|
18259
18771
|
async run() {
|
|
18260
18772
|
const stderrAnsi = this.ansiFor("stderr");
|
|
18261
18773
|
const flags = this.#parseFlags(stderrAnsi);
|
|
@@ -18274,7 +18786,6 @@ var ListCommand = class extends SmCommand {
|
|
|
18274
18786
|
* resolved values or a precomputed exit code (already printed
|
|
18275
18787
|
* directed errors before returning).
|
|
18276
18788
|
*/
|
|
18277
|
-
// eslint-disable-next-line complexity
|
|
18278
18789
|
#parseFlags(stderrAnsi) {
|
|
18279
18790
|
let sortColumn = "path";
|
|
18280
18791
|
let sortDirection = "asc";
|
|
@@ -18301,30 +18812,7 @@ var ListCommand = class extends SmCommand {
|
|
|
18301
18812
|
if (parsed === null) return { ok: false, exit: ExitCode.Error };
|
|
18302
18813
|
limitValue = parsed;
|
|
18303
18814
|
}
|
|
18304
|
-
|
|
18305
|
-
if (this.tagSource !== void 0) {
|
|
18306
|
-
if (this.tag === void 0) {
|
|
18307
|
-
this.printer.error(
|
|
18308
|
-
tx(LIST_TEXTS.tagSourceWithoutTag, {
|
|
18309
|
-
glyph: stderrAnsi.red("\u2715"),
|
|
18310
|
-
hint: stderrAnsi.dim(LIST_TEXTS.tagSourceWithoutTagHint)
|
|
18311
|
-
})
|
|
18312
|
-
);
|
|
18313
|
-
return { ok: false, exit: ExitCode.Error };
|
|
18314
|
-
}
|
|
18315
|
-
if (this.tagSource !== "author" && this.tagSource !== "user") {
|
|
18316
|
-
this.printer.error(
|
|
18317
|
-
tx(LIST_TEXTS.invalidTagSource, {
|
|
18318
|
-
glyph: stderrAnsi.red("\u2715"),
|
|
18319
|
-
value: this.tagSource,
|
|
18320
|
-
hint: stderrAnsi.dim(LIST_TEXTS.invalidTagSourceHint)
|
|
18321
|
-
})
|
|
18322
|
-
);
|
|
18323
|
-
return { ok: false, exit: ExitCode.Error };
|
|
18324
|
-
}
|
|
18325
|
-
tagSourceValue = this.tagSource;
|
|
18326
|
-
}
|
|
18327
|
-
return { ok: true, sortColumn, sortDirection, limitValue, tagSourceValue };
|
|
18815
|
+
return { ok: true, sortColumn, sortDirection, limitValue };
|
|
18328
18816
|
}
|
|
18329
18817
|
/**
|
|
18330
18818
|
* Issue the DB queries: optional `--tag` allow-list narrowing, the
|
|
@@ -18332,7 +18820,7 @@ var ListCommand = class extends SmCommand {
|
|
|
18332
18820
|
* out so `run()` reads as a thin orchestrator.
|
|
18333
18821
|
*/
|
|
18334
18822
|
async #runQuery(adapter, flags) {
|
|
18335
|
-
const tagAllowList = await this.#resolveTagAllowList(adapter
|
|
18823
|
+
const tagAllowList = await this.#resolveTagAllowList(adapter);
|
|
18336
18824
|
if (tagAllowList === "no-match") return this.#renderEmpty();
|
|
18337
18825
|
const filter = this.#buildFindNodesFilter(flags);
|
|
18338
18826
|
const allMatchingNodes = await adapter.scans.findNodes(filter);
|
|
@@ -18348,16 +18836,15 @@ var ListCommand = class extends SmCommand {
|
|
|
18348
18836
|
return ExitCode.Ok;
|
|
18349
18837
|
}
|
|
18350
18838
|
/**
|
|
18351
|
-
* Resolve `--tag`
|
|
18352
|
-
* path allow-list. Returns:
|
|
18839
|
+
* Resolve `--tag` into a path allow-list. Returns:
|
|
18353
18840
|
* - `null` when `--tag` was not supplied (no narrowing).
|
|
18354
18841
|
* - `'no-match'` when the tag matched zero nodes (caller renders
|
|
18355
18842
|
* the empty surface and exits).
|
|
18356
18843
|
* - a Set of paths otherwise.
|
|
18357
18844
|
*/
|
|
18358
|
-
async #resolveTagAllowList(adapter
|
|
18845
|
+
async #resolveTagAllowList(adapter) {
|
|
18359
18846
|
if (this.tag === void 0) return null;
|
|
18360
|
-
const matchingPaths = await adapter.tags.findNodes(this.tag
|
|
18847
|
+
const matchingPaths = await adapter.tags.findNodes(this.tag);
|
|
18361
18848
|
if (matchingPaths.length === 0) return "no-match";
|
|
18362
18849
|
return new Set(matchingPaths);
|
|
18363
18850
|
}
|
|
@@ -20115,6 +20602,8 @@ var TogglePluginsBase = class extends SmCommand {
|
|
|
20115
20602
|
targets = lockError;
|
|
20116
20603
|
const keys = expandToKeys(targets);
|
|
20117
20604
|
await this.#persistKeys(keys, enabled);
|
|
20605
|
+
const set = buildScanExtensionSet(keys);
|
|
20606
|
+
captureUsage("plugin.apply", enabled ? { enabled: set, disabled: [] } : { enabled: [], disabled: set });
|
|
20118
20607
|
this.#renderSuccess(keys, enabled);
|
|
20119
20608
|
return ExitCode.Ok;
|
|
20120
20609
|
}
|
|
@@ -20447,7 +20936,7 @@ function resolveBareToggle(id, catalogue) {
|
|
|
20447
20936
|
}
|
|
20448
20937
|
|
|
20449
20938
|
// cli/commands/plugins/create.ts
|
|
20450
|
-
import { existsSync as
|
|
20939
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync5, writeFileSync } from "fs";
|
|
20451
20940
|
import { join as join18, resolve as resolve33 } from "path";
|
|
20452
20941
|
import { Command as Command26, Option as Option25 } from "clipanion";
|
|
20453
20942
|
var PluginsCreateCommand = class extends SmCommand {
|
|
@@ -20476,7 +20965,7 @@ var PluginsCreateCommand = class extends SmCommand {
|
|
|
20476
20965
|
const ctx = defaultRuntimeContext();
|
|
20477
20966
|
const baseDir = defaultProjectPluginsDir(ctx);
|
|
20478
20967
|
const targetDir = this.at ? resolve33(this.at) : join18(baseDir, this.pluginId);
|
|
20479
|
-
if (
|
|
20968
|
+
if (existsSync24(targetDir) && !this.force) {
|
|
20480
20969
|
this.printer.error(
|
|
20481
20970
|
tx(PLUGINS_TEXTS.createRefuseOverwrite, {
|
|
20482
20971
|
glyph: errGlyph,
|
|
@@ -21077,6 +21566,29 @@ function stripFrontmatterFence(text) {
|
|
|
21077
21566
|
}
|
|
21078
21567
|
var REFRESH_COMMANDS = [RefreshCommand];
|
|
21079
21568
|
|
|
21569
|
+
// cli/i18n/intentional-fail.texts.ts
|
|
21570
|
+
var INTENTIONAL_FAIL_TEXTS = {
|
|
21571
|
+
triggering: "Triggering an intentional uncaught error to exercise Sentry error reporting...",
|
|
21572
|
+
errorMessage: "skill-map intentional failure (Sentry self-test)"
|
|
21573
|
+
};
|
|
21574
|
+
|
|
21575
|
+
// cli/commands/intentional-fail.ts
|
|
21576
|
+
var IntentionalFailCommand = class extends SmCommand {
|
|
21577
|
+
static paths = [["intentional-fail"]];
|
|
21578
|
+
// No `static usage` on purpose: that is what keeps the verb out of every
|
|
21579
|
+
// help / reference surface Clipanion drives from command definitions.
|
|
21580
|
+
// A self-test crash has no meaningful "done in <...>" line.
|
|
21581
|
+
emitElapsed = false;
|
|
21582
|
+
async run() {
|
|
21583
|
+
this.printer.warn(INTENTIONAL_FAIL_TEXTS.triggering);
|
|
21584
|
+
setTimeout(() => {
|
|
21585
|
+
throw new Error(INTENTIONAL_FAIL_TEXTS.errorMessage);
|
|
21586
|
+
}, 0);
|
|
21587
|
+
await new Promise((resolve40) => setTimeout(resolve40, 5e3));
|
|
21588
|
+
return 1;
|
|
21589
|
+
}
|
|
21590
|
+
};
|
|
21591
|
+
|
|
21080
21592
|
// cli/commands/scan.ts
|
|
21081
21593
|
import { Command as Command31, Option as Option29 } from "clipanion";
|
|
21082
21594
|
|
|
@@ -21202,6 +21714,12 @@ var RUNTIME_TEXTS = {
|
|
|
21202
21714
|
|
|
21203
21715
|
// core/watcher/runtime.ts
|
|
21204
21716
|
var DEFAULT_RUN_INITIAL_BATCH = true;
|
|
21717
|
+
async function rebuildWatcherDbOnDrift(dbPath, events) {
|
|
21718
|
+
const drift = await maybeResetOnDrift(dbPath, { currentVersion: VERSION, assumeYes: true });
|
|
21719
|
+
if (drift.kind === "reset") {
|
|
21720
|
+
events.onDriftReset?.({ dbVersion: drift.dbVersion, currentVersion: drift.currentVersion });
|
|
21721
|
+
}
|
|
21722
|
+
}
|
|
21205
21723
|
function createWatcherRuntime(opts) {
|
|
21206
21724
|
const events = opts.events ?? {};
|
|
21207
21725
|
const cwd = opts.runtimeContext.cwd;
|
|
@@ -21245,6 +21763,7 @@ function createWatcherRuntime(opts) {
|
|
|
21245
21763
|
};
|
|
21246
21764
|
let handleBatch = null;
|
|
21247
21765
|
const start = async () => {
|
|
21766
|
+
await rebuildWatcherDbOnDrift(opts.dbPath, events);
|
|
21248
21767
|
cfg = loadEffectiveConfig();
|
|
21249
21768
|
ignoreFilter = composeIgnoreFilter(cfg, readIgnoreFileText(cwd));
|
|
21250
21769
|
applyConfigDerivedState(cfg);
|
|
@@ -21301,9 +21820,9 @@ function createWatcherRuntime(opts) {
|
|
|
21301
21820
|
overrideMaxNodes: opts.maxNodesOverride ?? null
|
|
21302
21821
|
};
|
|
21303
21822
|
if (cfg.scan.referencePaths.length > 0) {
|
|
21304
|
-
const
|
|
21305
|
-
if (
|
|
21306
|
-
runOptions.referenceablePaths =
|
|
21823
|
+
const walk3 = walkReferencePaths(cfg.scan.referencePaths, cwd);
|
|
21824
|
+
if (walk3.paths.size > 0) {
|
|
21825
|
+
runOptions.referenceablePaths = walk3.paths;
|
|
21307
21826
|
runOptions.cwd = cwd;
|
|
21308
21827
|
}
|
|
21309
21828
|
}
|
|
@@ -21600,6 +22119,16 @@ async function runWatchLoop(opts) {
|
|
|
21600
22119
|
printer.warn(`${message}
|
|
21601
22120
|
`);
|
|
21602
22121
|
},
|
|
22122
|
+
onDriftReset: (info) => {
|
|
22123
|
+
context.stderr.write(
|
|
22124
|
+
tx(DB_DRIFT_TEXTS.driftReset, {
|
|
22125
|
+
glyph: stderrAnsi.yellow("\u26A0"),
|
|
22126
|
+
dbVersion: info.dbVersion,
|
|
22127
|
+
currentVersion: info.currentVersion,
|
|
22128
|
+
hint: stderrAnsi.dim(DB_DRIFT_TEXTS.driftResetHint)
|
|
22129
|
+
})
|
|
22130
|
+
);
|
|
22131
|
+
},
|
|
21603
22132
|
onConfigLoaded: ({ debounceMs }) => {
|
|
21604
22133
|
if (opts.json) return;
|
|
21605
22134
|
context.stderr.write(
|
|
@@ -21821,7 +22350,7 @@ var ScanCommand = class extends SmCommand {
|
|
|
21821
22350
|
description: "Long-running mode: watch the roots and trigger an incremental scan after each debounced batch of filesystem events. Alias of `sm watch`."
|
|
21822
22351
|
});
|
|
21823
22352
|
yes = Option29.Boolean("--yes", false, {
|
|
21824
|
-
description: "Non-interactive mode
|
|
22353
|
+
description: "Non-interactive mode. For ambiguous activeProvider auto-detect, multiple provider markers (.claude/, .codex/, AGENTS.md, .cursor/) under the scan tree exit non-zero instead of prompting; set the lens manually via `sm config set activeProvider <id>` and re-run. Also auto-confirms the pre-1.0 schema-drift rebuild (when the DB was written by a different skill-map major.minor it is deleted and regenerated) instead of prompting."
|
|
21825
22354
|
});
|
|
21826
22355
|
maxNodes = Option29.String("--max-nodes", {
|
|
21827
22356
|
required: false,
|
|
@@ -21871,7 +22400,11 @@ var ScanCommand = class extends SmCommand {
|
|
|
21871
22400
|
style,
|
|
21872
22401
|
...parsedMaxNodes.value !== void 0 ? { maxNodes: parsedMaxNodes.value } : {}
|
|
21873
22402
|
});
|
|
21874
|
-
|
|
22403
|
+
if (outcome.kind === "ok") {
|
|
22404
|
+
setScanExtensions(buildScanExtensionSet(outcome.executedExtensionIds));
|
|
22405
|
+
return this.renderOutcome(outcome.result, outcome.persistedTo, outcome.dbPath, outcome.strict);
|
|
22406
|
+
}
|
|
22407
|
+
return this.renderFailure(outcome);
|
|
21875
22408
|
}
|
|
21876
22409
|
/**
|
|
21877
22410
|
* Parse `--max-nodes <N>`. Returns either the integer value (or
|
|
@@ -22355,7 +22888,7 @@ function renderDeltaIssues(issues) {
|
|
|
22355
22888
|
|
|
22356
22889
|
// cli/commands/serve.ts
|
|
22357
22890
|
import { spawn as spawn2 } from "child_process";
|
|
22358
|
-
import { existsSync as
|
|
22891
|
+
import { existsSync as existsSync30 } from "fs";
|
|
22359
22892
|
import { Command as Command33, Option as Option31 } from "clipanion";
|
|
22360
22893
|
|
|
22361
22894
|
// kernel/util/dev-mode.ts
|
|
@@ -22389,7 +22922,7 @@ import { WebSocketServer } from "ws";
|
|
|
22389
22922
|
// server/app.ts
|
|
22390
22923
|
import { Hono } from "hono";
|
|
22391
22924
|
import { bodyLimit } from "hono/body-limit";
|
|
22392
|
-
import { HTTPException as
|
|
22925
|
+
import { HTTPException as HTTPException16 } from "hono/http-exception";
|
|
22393
22926
|
|
|
22394
22927
|
// core/config/service.ts
|
|
22395
22928
|
var ConfigService = class {
|
|
@@ -22510,6 +23043,11 @@ var SERVER_TEXTS = {
|
|
|
22510
23043
|
// watcher loop continues, a transient FS error must not kill the
|
|
22511
23044
|
// broadcaster.
|
|
22512
23045
|
watcherBatchFailed: "skill-map server: watcher batch failed ({{message}}).\n",
|
|
23046
|
+
// Logged once when the pre-1.0 schema-drift check rebuilt the DB on
|
|
23047
|
+
// watcher boot (the on-disk cache was written by a different
|
|
23048
|
+
// major.minor). The scan that follows repopulates it; .sm sidecars
|
|
23049
|
+
// are untouched. See spec/db-schema.md §Schema drift (pre-1.0).
|
|
23050
|
+
watcherDriftReset: "skill-map server: local cache rebuilt (was {{dbVersion}}, now on {{currentVersion}}); .sm sidecars untouched.\n",
|
|
22513
23051
|
// chokidar surfaced an error. The watcher stays open per IFsWatcher's
|
|
22514
23052
|
// contract; the BFF also broadcasts a `watcher.error` advisory so the
|
|
22515
23053
|
// SPA can surface it in the live event log.
|
|
@@ -22621,6 +23159,10 @@ var SERVER_TEXTS = {
|
|
|
22621
23159
|
preferencesBodyEmpty: "Request body must contain at least one known preference (e.g. `updateCheck.enabled`).",
|
|
22622
23160
|
preferencesUpdateCheckNotObject: '`updateCheck` must be an object (e.g. `{"updateCheck": {"enabled": false}}`).',
|
|
22623
23161
|
preferencesUpdateCheckEnabledNotBoolean: "`updateCheck.enabled` must be a boolean.",
|
|
23162
|
+
preferencesTelemetryNotObject: '`telemetry` must be an object (e.g. `{"telemetry": {"errorsEnabled": true}}`).',
|
|
23163
|
+
preferencesTelemetryErrorsEnabledNotBoolean: "`telemetry.errorsEnabled` must be a boolean.",
|
|
23164
|
+
preferencesTelemetryUsageCliEnabledNotBoolean: "`telemetry.usageCliEnabled` must be a boolean.",
|
|
23165
|
+
preferencesTelemetryUsageUiEnabledNotBoolean: "`telemetry.usageUiEnabled` must be a boolean.",
|
|
22624
23166
|
preferencesPersistFailed: "Could not persist preferences: {{message}}",
|
|
22625
23167
|
// ---- project-preferences route (routes/project-preferences.ts) ----------
|
|
22626
23168
|
//
|
|
@@ -22780,6 +23322,53 @@ function createSecurityHeaders() {
|
|
|
22780
23322
|
};
|
|
22781
23323
|
}
|
|
22782
23324
|
|
|
23325
|
+
// server/telemetry/sentry.ts
|
|
23326
|
+
import { HTTPException } from "hono/http-exception";
|
|
23327
|
+
var sdk2 = null;
|
|
23328
|
+
async function initSentryBff(version, loadSdk = () => import("@sentry/node")) {
|
|
23329
|
+
if (sdk2) return;
|
|
23330
|
+
if (!isTelemetryActive(SENTRY_DSN_NODE)) return;
|
|
23331
|
+
const Sentry = await loadSdk();
|
|
23332
|
+
Sentry.init({
|
|
23333
|
+
dsn: SENTRY_DSN_NODE,
|
|
23334
|
+
release: `@skill-map/cli@${version}`,
|
|
23335
|
+
environment: resolveTelemetryEnv(),
|
|
23336
|
+
// Shared project with the CLI; the `surface` tag separates the two.
|
|
23337
|
+
initialScope: { tags: { surface: "bff" } },
|
|
23338
|
+
// Errors only: skip the OpenTelemetry ESM loader hooks (we run no
|
|
23339
|
+
// tracing). They call the deprecated `module.register()` (a Node >= 26
|
|
23340
|
+
// DEP0205 warning) and add startup cost for nothing here.
|
|
23341
|
+
registerEsmLoaderHooks: false,
|
|
23342
|
+
defaultIntegrations: false,
|
|
23343
|
+
integrations: [],
|
|
23344
|
+
tracesSampleRate: 0,
|
|
23345
|
+
sendDefaultPii: false,
|
|
23346
|
+
beforeSend: (event) => scrubEvent(event)
|
|
23347
|
+
});
|
|
23348
|
+
sdk2 = Sentry;
|
|
23349
|
+
}
|
|
23350
|
+
function shouldCaptureError(err) {
|
|
23351
|
+
if (err instanceof HTTPException) return err.status >= 500;
|
|
23352
|
+
return true;
|
|
23353
|
+
}
|
|
23354
|
+
function createSentryRequestCapture() {
|
|
23355
|
+
return async function sentryRequestCapture(c, next) {
|
|
23356
|
+
try {
|
|
23357
|
+
await next();
|
|
23358
|
+
} catch (err) {
|
|
23359
|
+
const client2 = sdk2;
|
|
23360
|
+
if (client2 && shouldCaptureError(err)) {
|
|
23361
|
+
client2.withScope((scope) => {
|
|
23362
|
+
scope.setTag("route", c.req.routePath ?? c.req.path);
|
|
23363
|
+
scope.setTag("method", c.req.method);
|
|
23364
|
+
client2.captureException(err);
|
|
23365
|
+
});
|
|
23366
|
+
}
|
|
23367
|
+
throw err;
|
|
23368
|
+
}
|
|
23369
|
+
};
|
|
23370
|
+
}
|
|
23371
|
+
|
|
22783
23372
|
// server/routes/annotations.ts
|
|
22784
23373
|
var ENVELOPE_KIND = "annotations.registered";
|
|
22785
23374
|
function registerAnnotationsRoute(app, deps) {
|
|
@@ -22796,10 +23385,10 @@ function registerAnnotationsRoute(app, deps) {
|
|
|
22796
23385
|
}
|
|
22797
23386
|
|
|
22798
23387
|
// server/routes/contributions.ts
|
|
22799
|
-
import { HTTPException as
|
|
23388
|
+
import { HTTPException as HTTPException3 } from "hono/http-exception";
|
|
22800
23389
|
|
|
22801
23390
|
// server/util/parse-query.ts
|
|
22802
|
-
import { HTTPException } from "hono/http-exception";
|
|
23391
|
+
import { HTTPException as HTTPException2 } from "hono/http-exception";
|
|
22803
23392
|
function parseCsv(value) {
|
|
22804
23393
|
if (value === void 0) return [];
|
|
22805
23394
|
return value.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
@@ -22808,7 +23397,7 @@ function parsePagination(query, defaults) {
|
|
|
22808
23397
|
const offset = parseNonNegativeInt(query.offset, "offset", 0);
|
|
22809
23398
|
const limit = parseNonNegativeInt(query.limit, "limit", defaults.limit);
|
|
22810
23399
|
if (limit > defaults.max) {
|
|
22811
|
-
throw new
|
|
23400
|
+
throw new HTTPException2(400, {
|
|
22812
23401
|
message: tx(SERVER_TEXTS.paginationLimitTooLarge, {
|
|
22813
23402
|
value: limit,
|
|
22814
23403
|
max: defaults.max
|
|
@@ -22822,7 +23411,7 @@ function parseBooleanFlag(value) {
|
|
|
22822
23411
|
}
|
|
22823
23412
|
function parseRequiredString(value, name) {
|
|
22824
23413
|
if (typeof value !== "string" || value.length === 0) {
|
|
22825
|
-
throw new
|
|
23414
|
+
throw new HTTPException2(400, {
|
|
22826
23415
|
message: tx(SERVER_TEXTS.queryRequiredString, { name })
|
|
22827
23416
|
});
|
|
22828
23417
|
}
|
|
@@ -22833,7 +23422,7 @@ function parseNonNegativeInt(raw, name, fallback) {
|
|
|
22833
23422
|
const trimmed = raw.trim();
|
|
22834
23423
|
const parsed = Number.parseInt(trimmed, 10);
|
|
22835
23424
|
if (!Number.isInteger(parsed) || parsed < 0 || String(parsed) !== trimmed) {
|
|
22836
|
-
throw new
|
|
23425
|
+
throw new HTTPException2(400, {
|
|
22837
23426
|
message: tx(SERVER_TEXTS.paginationInvalidInteger, { name, value: raw })
|
|
22838
23427
|
});
|
|
22839
23428
|
}
|
|
@@ -22844,7 +23433,7 @@ function parseNonNegativeInt(raw, name, fallback) {
|
|
|
22844
23433
|
var QUALIFIED_ID_SEGMENT = /^[A-Za-z0-9._-]+$/;
|
|
22845
23434
|
function parseQualifiedIdSegment(value, name) {
|
|
22846
23435
|
if (!QUALIFIED_ID_SEGMENT.test(value)) {
|
|
22847
|
-
throw new
|
|
23436
|
+
throw new HTTPException3(400, {
|
|
22848
23437
|
message: tx(SERVER_TEXTS.qualifiedIdMalformed, {
|
|
22849
23438
|
name,
|
|
22850
23439
|
value: sanitizeForTerminal(value)
|
|
@@ -22874,7 +23463,7 @@ function registerContributionsRoutes(app, deps) {
|
|
|
22874
23463
|
(e) => e.pluginId === pluginId && e.extensionId === extensionId && e.contributionId === contributionId
|
|
22875
23464
|
);
|
|
22876
23465
|
if (!catalogEntry) {
|
|
22877
|
-
throw new
|
|
23466
|
+
throw new HTTPException3(404, {
|
|
22878
23467
|
message: tx(SERVER_TEXTS.contributionUnknown, {
|
|
22879
23468
|
pluginId: sanitizeForTerminal(pluginId),
|
|
22880
23469
|
extensionId: sanitizeForTerminal(extensionId),
|
|
@@ -22905,7 +23494,7 @@ function registerContributionsRoutes(app, deps) {
|
|
|
22905
23494
|
}
|
|
22906
23495
|
|
|
22907
23496
|
// server/routes/config.ts
|
|
22908
|
-
import { HTTPException as
|
|
23497
|
+
import { HTTPException as HTTPException4 } from "hono/http-exception";
|
|
22909
23498
|
|
|
22910
23499
|
// server/envelope.ts
|
|
22911
23500
|
var REST_ENVELOPE_SCHEMA_VERSION = "1";
|
|
@@ -22944,7 +23533,7 @@ function registerConfigRoute(app, deps) {
|
|
|
22944
23533
|
try {
|
|
22945
23534
|
loaded = deps.configService.get();
|
|
22946
23535
|
} catch (err) {
|
|
22947
|
-
throw new
|
|
23536
|
+
throw new HTTPException4(500, { message: formatErrorMessage(err) });
|
|
22948
23537
|
}
|
|
22949
23538
|
for (const warn of loaded.warnings) {
|
|
22950
23539
|
log.warn(sanitizeForTerminal(warn));
|
|
@@ -22962,7 +23551,7 @@ function registerConfigRoute(app, deps) {
|
|
|
22962
23551
|
}
|
|
22963
23552
|
|
|
22964
23553
|
// server/routes/favorites.ts
|
|
22965
|
-
import { HTTPException as
|
|
23554
|
+
import { HTTPException as HTTPException5 } from "hono/http-exception";
|
|
22966
23555
|
|
|
22967
23556
|
// server/path-codec.ts
|
|
22968
23557
|
var PathCodecError = class extends Error {
|
|
@@ -23002,7 +23591,7 @@ function registerFavoritesRoutes(app, deps) {
|
|
|
23002
23591
|
}
|
|
23003
23592
|
);
|
|
23004
23593
|
if (!result || !result.found) {
|
|
23005
|
-
throw new
|
|
23594
|
+
throw new HTTPException5(404, {
|
|
23006
23595
|
message: tx(SERVER_TEXTS.nodeNotFound, { path: sanitizeForTerminal(nodePath) })
|
|
23007
23596
|
});
|
|
23008
23597
|
}
|
|
@@ -23022,14 +23611,14 @@ function decodePath(pathB64) {
|
|
|
23022
23611
|
return decodeNodePath(pathB64);
|
|
23023
23612
|
} catch (err) {
|
|
23024
23613
|
if (err instanceof PathCodecError) {
|
|
23025
|
-
throw new
|
|
23614
|
+
throw new HTTPException5(404, { message: SERVER_TEXTS.pathB64Malformed });
|
|
23026
23615
|
}
|
|
23027
23616
|
throw err;
|
|
23028
23617
|
}
|
|
23029
23618
|
}
|
|
23030
23619
|
|
|
23031
23620
|
// server/routes/graph.ts
|
|
23032
|
-
import { HTTPException as
|
|
23621
|
+
import { HTTPException as HTTPException6 } from "hono/http-exception";
|
|
23033
23622
|
var DEFAULT_FORMAT2 = "ascii";
|
|
23034
23623
|
var FORMAT_ID_PATTERN = /^[a-z0-9-]+$/;
|
|
23035
23624
|
var FORMAT_ID_MAX = 32;
|
|
@@ -23037,7 +23626,7 @@ function registerGraphRoute(app, deps) {
|
|
|
23037
23626
|
app.get("/api/graph", async (c) => {
|
|
23038
23627
|
const format = c.req.query("format") ?? DEFAULT_FORMAT2;
|
|
23039
23628
|
if (format.length > FORMAT_ID_MAX || !FORMAT_ID_PATTERN.test(format)) {
|
|
23040
|
-
throw new
|
|
23629
|
+
throw new HTTPException6(400, {
|
|
23041
23630
|
// Sanitize defensively, the regex above already rejects ANSI
|
|
23042
23631
|
// and control bytes, but the message interpolates user input
|
|
23043
23632
|
// and the BFF mirrors error envelopes into the server log.
|
|
@@ -23053,7 +23642,7 @@ function registerGraphRoute(app, deps) {
|
|
|
23053
23642
|
const formatter = formatters.find((f) => f.formatId === format);
|
|
23054
23643
|
if (!formatter) {
|
|
23055
23644
|
const available = formatters.map((f) => f.formatId).sort().join(", ");
|
|
23056
|
-
throw new
|
|
23645
|
+
throw new HTTPException6(400, {
|
|
23057
23646
|
message: tx(SERVER_TEXTS.graphUnknownFormat, {
|
|
23058
23647
|
format,
|
|
23059
23648
|
available: available || "(none)"
|
|
@@ -23090,7 +23679,7 @@ function contentTypeFor(format) {
|
|
|
23090
23679
|
}
|
|
23091
23680
|
|
|
23092
23681
|
// server/health.ts
|
|
23093
|
-
import { existsSync as
|
|
23682
|
+
import { existsSync as existsSync25 } from "fs";
|
|
23094
23683
|
var FALLBACK_SCHEMA_VERSION = "1";
|
|
23095
23684
|
function buildHealth(deps) {
|
|
23096
23685
|
const dev = isDevBuild();
|
|
@@ -23099,7 +23688,7 @@ function buildHealth(deps) {
|
|
|
23099
23688
|
schemaVersion: FALLBACK_SCHEMA_VERSION,
|
|
23100
23689
|
specVersion: deps.specVersion,
|
|
23101
23690
|
implVersion: VERSION,
|
|
23102
|
-
db:
|
|
23691
|
+
db: existsSync25(deps.dbPath) ? "present" : "missing",
|
|
23103
23692
|
cwd: deps.cwd,
|
|
23104
23693
|
dbPath: deps.dbPath,
|
|
23105
23694
|
// Only emit when truthy so a published install keeps the wire
|
|
@@ -23226,7 +23815,7 @@ function registerLinksRoute(app, deps) {
|
|
|
23226
23815
|
}
|
|
23227
23816
|
|
|
23228
23817
|
// server/routes/nodes.ts
|
|
23229
|
-
import { HTTPException as
|
|
23818
|
+
import { HTTPException as HTTPException7 } from "hono/http-exception";
|
|
23230
23819
|
|
|
23231
23820
|
// server/node-body.ts
|
|
23232
23821
|
import { constants as fsConstants2 } from "fs";
|
|
@@ -23337,7 +23926,7 @@ function registerNodesRoutes(app, deps) {
|
|
|
23337
23926
|
nodePath = decodeNodePath(pathB64);
|
|
23338
23927
|
} catch (err) {
|
|
23339
23928
|
if (err instanceof PathCodecError) {
|
|
23340
|
-
throw new
|
|
23929
|
+
throw new HTTPException7(404, { message: SERVER_TEXTS.pathB64Malformed });
|
|
23341
23930
|
}
|
|
23342
23931
|
throw err;
|
|
23343
23932
|
}
|
|
@@ -23350,7 +23939,7 @@ function registerNodesRoutes(app, deps) {
|
|
|
23350
23939
|
bundle: null,
|
|
23351
23940
|
isFavorite: false,
|
|
23352
23941
|
contributions: [],
|
|
23353
|
-
tags:
|
|
23942
|
+
tags: []
|
|
23354
23943
|
};
|
|
23355
23944
|
}
|
|
23356
23945
|
const favSet = await adapter.favorites.listPaths();
|
|
@@ -23360,16 +23949,16 @@ function registerNodesRoutes(app, deps) {
|
|
|
23360
23949
|
bundle: b,
|
|
23361
23950
|
isFavorite: favSet.has(b.node.path),
|
|
23362
23951
|
contributions: contributions2,
|
|
23363
|
-
tags:
|
|
23952
|
+
tags: tagRows.map((r) => r.tag)
|
|
23364
23953
|
};
|
|
23365
23954
|
}
|
|
23366
23955
|
);
|
|
23367
23956
|
const bundle = result?.bundle ?? null;
|
|
23368
23957
|
const isFavorite = result?.isFavorite ?? false;
|
|
23369
23958
|
const contributions = result?.contributions ?? [];
|
|
23370
|
-
const tags = result?.tags ??
|
|
23959
|
+
const tags = result?.tags ?? [];
|
|
23371
23960
|
if (!bundle) {
|
|
23372
|
-
throw new
|
|
23961
|
+
throw new HTTPException7(404, {
|
|
23373
23962
|
message: tx(SERVER_TEXTS.nodeNotFound, { path: nodePath })
|
|
23374
23963
|
});
|
|
23375
23964
|
}
|
|
@@ -23421,7 +24010,7 @@ function registerNodesRoutes(app, deps) {
|
|
|
23421
24010
|
const contribByPath = contributionsOmitted ? /* @__PURE__ */ new Map() : await groupContributionsByPath(
|
|
23422
24011
|
await adapter.contributions.listForPaths(pagePaths)
|
|
23423
24012
|
);
|
|
23424
|
-
const tagByPath =
|
|
24013
|
+
const tagByPath = groupTagsByPath(
|
|
23425
24014
|
await adapter.tags.listForPaths(pagePaths)
|
|
23426
24015
|
);
|
|
23427
24016
|
return { contributionsByPath: contribByPath, tagsByPath: tagByPath };
|
|
@@ -23434,7 +24023,7 @@ function registerNodesRoutes(app, deps) {
|
|
|
23434
24023
|
...n,
|
|
23435
24024
|
isFavorite: favSet.has(n.path),
|
|
23436
24025
|
contributions: contributionsByPath.get(n.path) ?? [],
|
|
23437
|
-
tags: tagsByPath.get(n.path) ??
|
|
24026
|
+
tags: tagsByPath.get(n.path) ?? []
|
|
23438
24027
|
}));
|
|
23439
24028
|
return c.json(
|
|
23440
24029
|
buildListEnvelope({
|
|
@@ -23457,24 +24046,15 @@ function registerNodesRoutes(app, deps) {
|
|
|
23457
24046
|
function parseIncludes(raw) {
|
|
23458
24047
|
return new Set(parseCsv(raw));
|
|
23459
24048
|
}
|
|
23460
|
-
function
|
|
23461
|
-
const byAuthor = /* @__PURE__ */ new Set();
|
|
23462
|
-
const byUser = /* @__PURE__ */ new Set();
|
|
23463
|
-
for (const r of rows) (r.source === "author" ? byAuthor : byUser).add(r.tag);
|
|
23464
|
-
return {
|
|
23465
|
-
byAuthor: [...byAuthor].sort(),
|
|
23466
|
-
byUser: [...byUser].sort()
|
|
23467
|
-
};
|
|
23468
|
-
}
|
|
23469
|
-
async function groupTagsByPath(rows) {
|
|
24049
|
+
function groupTagsByPath(rows) {
|
|
23470
24050
|
const buckets = /* @__PURE__ */ new Map();
|
|
23471
24051
|
for (const r of rows) {
|
|
23472
|
-
const
|
|
23473
|
-
if (
|
|
23474
|
-
else buckets.set(r.nodePath,
|
|
24052
|
+
const set = buckets.get(r.nodePath);
|
|
24053
|
+
if (set) set.add(r.tag);
|
|
24054
|
+
else buckets.set(r.nodePath, /* @__PURE__ */ new Set([r.tag]));
|
|
23475
24055
|
}
|
|
23476
24056
|
const out = /* @__PURE__ */ new Map();
|
|
23477
|
-
for (const [path,
|
|
24057
|
+
for (const [path, set] of buckets) out.set(path, [...set].sort());
|
|
23478
24058
|
return out;
|
|
23479
24059
|
}
|
|
23480
24060
|
async function groupContributionsByPath(rows) {
|
|
@@ -23488,11 +24068,11 @@ async function groupContributionsByPath(rows) {
|
|
|
23488
24068
|
}
|
|
23489
24069
|
|
|
23490
24070
|
// server/routes/plugins.ts
|
|
23491
|
-
import { HTTPException as
|
|
24071
|
+
import { HTTPException as HTTPException9 } from "hono/http-exception";
|
|
23492
24072
|
|
|
23493
24073
|
// server/util/parse-body.ts
|
|
23494
24074
|
import { Ajv2020 as Ajv20207 } from "ajv/dist/2020.js";
|
|
23495
|
-
import { HTTPException as
|
|
24075
|
+
import { HTTPException as HTTPException8 } from "hono/http-exception";
|
|
23496
24076
|
function makeBodyValidator(schema, messages) {
|
|
23497
24077
|
const ajv = new Ajv20207({ strict: false, allErrors: false });
|
|
23498
24078
|
const validate = ajv.compile(schema);
|
|
@@ -23501,16 +24081,16 @@ function makeBodyValidator(schema, messages) {
|
|
|
23501
24081
|
try {
|
|
23502
24082
|
raw = await req.json();
|
|
23503
24083
|
} catch {
|
|
23504
|
-
throw new
|
|
24084
|
+
throw new HTTPException8(400, { message: messages.notJson });
|
|
23505
24085
|
}
|
|
23506
24086
|
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
|
|
23507
|
-
throw new
|
|
24087
|
+
throw new HTTPException8(400, { message: messages.notObject });
|
|
23508
24088
|
}
|
|
23509
24089
|
if (validate(raw)) {
|
|
23510
24090
|
return raw;
|
|
23511
24091
|
}
|
|
23512
24092
|
const message = resolveErrorMessage(validate.errors, messages);
|
|
23513
|
-
throw new
|
|
24093
|
+
throw new HTTPException8(400, { message });
|
|
23514
24094
|
};
|
|
23515
24095
|
}
|
|
23516
24096
|
function resolveErrorMessage(errors, messages) {
|
|
@@ -23611,18 +24191,18 @@ function registerPluginsRoute(app, deps) {
|
|
|
23611
24191
|
app.patch("/api/plugins/:id", async (c) => {
|
|
23612
24192
|
const id = c.req.param("id");
|
|
23613
24193
|
if (id.includes("/")) {
|
|
23614
|
-
throw new
|
|
24194
|
+
throw new HTTPException9(400, {
|
|
23615
24195
|
message: tx(SERVER_TEXTS.pluginsCascadeRouteQualifiedRejected, { id })
|
|
23616
24196
|
});
|
|
23617
24197
|
}
|
|
23618
24198
|
const handle = findHandle(id, deps);
|
|
23619
24199
|
if (!handle) {
|
|
23620
|
-
throw new
|
|
24200
|
+
throw new HTTPException9(404, {
|
|
23621
24201
|
message: tx(SERVER_TEXTS.pluginsUnknown, { id })
|
|
23622
24202
|
});
|
|
23623
24203
|
}
|
|
23624
24204
|
if (isPluginLocked(id)) {
|
|
23625
|
-
throw new
|
|
24205
|
+
throw new HTTPException9(403, {
|
|
23626
24206
|
message: tx(SERVER_TEXTS.pluginsLocked, { id })
|
|
23627
24207
|
});
|
|
23628
24208
|
}
|
|
@@ -23636,18 +24216,18 @@ function registerPluginsRoute(app, deps) {
|
|
|
23636
24216
|
const extensionId = c.req.param("extensionId");
|
|
23637
24217
|
const handle = findHandle(bundleId, deps);
|
|
23638
24218
|
if (!handle) {
|
|
23639
|
-
throw new
|
|
24219
|
+
throw new HTTPException9(404, {
|
|
23640
24220
|
message: tx(SERVER_TEXTS.pluginsUnknown, { id: bundleId })
|
|
23641
24221
|
});
|
|
23642
24222
|
}
|
|
23643
24223
|
if (!hasExtension(handle, extensionId)) {
|
|
23644
|
-
throw new
|
|
24224
|
+
throw new HTTPException9(404, {
|
|
23645
24225
|
message: tx(SERVER_TEXTS.pluginsExtensionUnknown, { bundleId, extensionId })
|
|
23646
24226
|
});
|
|
23647
24227
|
}
|
|
23648
24228
|
const qualified = qualifiedExtensionId(bundleId, extensionId);
|
|
23649
24229
|
if (isPluginLocked(qualified) || isPluginLocked(bundleId)) {
|
|
23650
|
-
throw new
|
|
24230
|
+
throw new HTTPException9(403, {
|
|
23651
24231
|
message: tx(SERVER_TEXTS.pluginsExtensionLocked, { bundleId, extensionId })
|
|
23652
24232
|
});
|
|
23653
24233
|
}
|
|
@@ -23923,7 +24503,7 @@ function hasExtension(handle, extensionId) {
|
|
|
23923
24503
|
}
|
|
23924
24504
|
|
|
23925
24505
|
// server/routes/preferences.ts
|
|
23926
|
-
import { HTTPException as
|
|
24506
|
+
import { HTTPException as HTTPException10 } from "hono/http-exception";
|
|
23927
24507
|
function registerPreferencesRoute(app, _deps) {
|
|
23928
24508
|
app.get("/api/preferences", (c) => {
|
|
23929
24509
|
return c.json(buildEnvelope());
|
|
@@ -23936,20 +24516,43 @@ function registerPreferencesRoute(app, _deps) {
|
|
|
23936
24516
|
}
|
|
23937
24517
|
function buildEnvelope() {
|
|
23938
24518
|
return {
|
|
23939
|
-
updateCheck: { enabled: isUpdateCheckEnabled() }
|
|
24519
|
+
updateCheck: { enabled: isUpdateCheckEnabled() },
|
|
24520
|
+
telemetry: {
|
|
24521
|
+
errorsEnabled: isErrorTelemetryEnabled(),
|
|
24522
|
+
usageCliEnabled: isUsageCliTelemetryEnabled(),
|
|
24523
|
+
usageUiEnabled: isUsageUiTelemetryEnabled(),
|
|
24524
|
+
anonymousId: readAnonymousId(),
|
|
24525
|
+
environment: resolveTelemetryEnv()
|
|
24526
|
+
}
|
|
23940
24527
|
};
|
|
23941
24528
|
}
|
|
23942
24529
|
function applyPatch(body) {
|
|
23943
|
-
|
|
23944
|
-
|
|
24530
|
+
try {
|
|
24531
|
+
if (body.updateCheck && typeof body.updateCheck.enabled === "boolean") {
|
|
23945
24532
|
writeUserSettings({ updateCheck: { enabled: body.updateCheck.enabled } });
|
|
23946
|
-
} catch (err) {
|
|
23947
|
-
throw new HTTPException9(400, {
|
|
23948
|
-
message: tx(SERVER_TEXTS.preferencesPersistFailed, {
|
|
23949
|
-
message: formatErrorMessage(err)
|
|
23950
|
-
})
|
|
23951
|
-
});
|
|
23952
24533
|
}
|
|
24534
|
+
if (body.telemetry) {
|
|
24535
|
+
applyTelemetryPatch(body.telemetry);
|
|
24536
|
+
}
|
|
24537
|
+
} catch (err) {
|
|
24538
|
+
throw new HTTPException10(400, {
|
|
24539
|
+
message: tx(SERVER_TEXTS.preferencesPersistFailed, {
|
|
24540
|
+
message: formatErrorMessage(err)
|
|
24541
|
+
})
|
|
24542
|
+
});
|
|
24543
|
+
}
|
|
24544
|
+
}
|
|
24545
|
+
function applyTelemetryPatch(t) {
|
|
24546
|
+
if (typeof t.errorsEnabled === "boolean") {
|
|
24547
|
+
writeUserSettings({ telemetry: { errorsEnabled: t.errorsEnabled } });
|
|
24548
|
+
}
|
|
24549
|
+
if (typeof t.usageCliEnabled === "boolean") {
|
|
24550
|
+
writeUserSettings({ telemetry: { usageCliEnabled: t.usageCliEnabled } });
|
|
24551
|
+
if (t.usageCliEnabled) ensureAnonymousId();
|
|
24552
|
+
}
|
|
24553
|
+
if (typeof t.usageUiEnabled === "boolean") {
|
|
24554
|
+
writeUserSettings({ telemetry: { usageUiEnabled: t.usageUiEnabled } });
|
|
24555
|
+
if (t.usageUiEnabled) ensureAnonymousId();
|
|
23953
24556
|
}
|
|
23954
24557
|
}
|
|
23955
24558
|
var PATCH_BODY_SCHEMA = {
|
|
@@ -23963,6 +24566,15 @@ var PATCH_BODY_SCHEMA = {
|
|
|
23963
24566
|
properties: {
|
|
23964
24567
|
enabled: { type: "boolean" }
|
|
23965
24568
|
}
|
|
24569
|
+
},
|
|
24570
|
+
telemetry: {
|
|
24571
|
+
type: "object",
|
|
24572
|
+
additionalProperties: false,
|
|
24573
|
+
properties: {
|
|
24574
|
+
errorsEnabled: { type: "boolean" },
|
|
24575
|
+
usageCliEnabled: { type: "boolean" },
|
|
24576
|
+
usageUiEnabled: { type: "boolean" }
|
|
24577
|
+
}
|
|
23966
24578
|
}
|
|
23967
24579
|
}
|
|
23968
24580
|
};
|
|
@@ -23973,20 +24585,24 @@ var parsePatchBody2 = makeBodyValidator(PATCH_BODY_SCHEMA, {
|
|
|
23973
24585
|
mapping: {
|
|
23974
24586
|
":minProperties": SERVER_TEXTS.preferencesBodyEmpty,
|
|
23975
24587
|
"/updateCheck:type:object": SERVER_TEXTS.preferencesUpdateCheckNotObject,
|
|
23976
|
-
"/updateCheck/enabled:type:boolean": SERVER_TEXTS.preferencesUpdateCheckEnabledNotBoolean
|
|
24588
|
+
"/updateCheck/enabled:type:boolean": SERVER_TEXTS.preferencesUpdateCheckEnabledNotBoolean,
|
|
24589
|
+
"/telemetry:type:object": SERVER_TEXTS.preferencesTelemetryNotObject,
|
|
24590
|
+
"/telemetry/errorsEnabled:type:boolean": SERVER_TEXTS.preferencesTelemetryErrorsEnabledNotBoolean,
|
|
24591
|
+
"/telemetry/usageCliEnabled:type:boolean": SERVER_TEXTS.preferencesTelemetryUsageCliEnabledNotBoolean,
|
|
24592
|
+
"/telemetry/usageUiEnabled:type:boolean": SERVER_TEXTS.preferencesTelemetryUsageUiEnabledNotBoolean
|
|
23977
24593
|
}
|
|
23978
24594
|
});
|
|
23979
24595
|
|
|
23980
24596
|
// server/routes/project-ignore.ts
|
|
23981
|
-
import { HTTPException as
|
|
24597
|
+
import { HTTPException as HTTPException11 } from "hono/http-exception";
|
|
23982
24598
|
|
|
23983
24599
|
// server/util/skillmapignore-io.ts
|
|
23984
|
-
import { existsSync as
|
|
24600
|
+
import { existsSync as existsSync26, readFileSync as readFileSync17, writeFileSync as writeFileSync2 } from "fs";
|
|
23985
24601
|
import { resolve as resolve35 } from "path";
|
|
23986
24602
|
var IGNORE_FILENAME2 = ".skillmapignore";
|
|
23987
24603
|
function readPatterns(cwd) {
|
|
23988
24604
|
const path = resolve35(cwd, IGNORE_FILENAME2);
|
|
23989
|
-
if (!
|
|
24605
|
+
if (!existsSync26(path)) return [];
|
|
23990
24606
|
let raw;
|
|
23991
24607
|
try {
|
|
23992
24608
|
raw = readFileSync17(path, "utf8");
|
|
@@ -23997,7 +24613,7 @@ function readPatterns(cwd) {
|
|
|
23997
24613
|
}
|
|
23998
24614
|
function writePatterns(cwd, nextPatterns) {
|
|
23999
24615
|
const path = resolve35(cwd, IGNORE_FILENAME2);
|
|
24000
|
-
const prior =
|
|
24616
|
+
const prior = existsSync26(path) ? safeRead(path) : "";
|
|
24001
24617
|
const content = buildContent(prior, nextPatterns);
|
|
24002
24618
|
writeFileSync2(path, content, "utf8");
|
|
24003
24619
|
}
|
|
@@ -24065,12 +24681,12 @@ async function applyPatch2(deps, body) {
|
|
|
24065
24681
|
for (const raw of body.patterns) {
|
|
24066
24682
|
const t = raw.trim();
|
|
24067
24683
|
if (t.length === 0) {
|
|
24068
|
-
throw new
|
|
24684
|
+
throw new HTTPException11(400, {
|
|
24069
24685
|
message: SERVER_TEXTS.projectIgnorePatternEmpty
|
|
24070
24686
|
});
|
|
24071
24687
|
}
|
|
24072
24688
|
if (seen.has(t)) {
|
|
24073
|
-
throw new
|
|
24689
|
+
throw new HTTPException11(400, {
|
|
24074
24690
|
message: tx(SERVER_TEXTS.projectIgnorePatternDuplicate, { pattern: t })
|
|
24075
24691
|
});
|
|
24076
24692
|
}
|
|
@@ -24081,7 +24697,7 @@ async function applyPatch2(deps, body) {
|
|
|
24081
24697
|
try {
|
|
24082
24698
|
writePatterns(cwd, trimmed);
|
|
24083
24699
|
} catch (err) {
|
|
24084
|
-
throw new
|
|
24700
|
+
throw new HTTPException11(400, {
|
|
24085
24701
|
message: tx(SERVER_TEXTS.projectIgnorePersistFailed, {
|
|
24086
24702
|
message: formatErrorMessage(err)
|
|
24087
24703
|
})
|
|
@@ -24159,7 +24775,7 @@ var parsePatchBody3 = makeBodyValidator(PATCH_BODY_SCHEMA2, {
|
|
|
24159
24775
|
|
|
24160
24776
|
// server/routes/project-preferences.ts
|
|
24161
24777
|
import { statSync as statSync9 } from "fs";
|
|
24162
|
-
import { HTTPException as
|
|
24778
|
+
import { HTTPException as HTTPException12 } from "hono/http-exception";
|
|
24163
24779
|
function registerProjectPreferencesRoute(app, deps) {
|
|
24164
24780
|
app.get("/api/project-preferences", (c) => {
|
|
24165
24781
|
return c.json(buildEnvelope3(deps));
|
|
@@ -24187,7 +24803,7 @@ async function applyPatch3(deps, body) {
|
|
|
24187
24803
|
const cwd = deps.runtimeContext.cwd;
|
|
24188
24804
|
const missingPaths = collectMissingPaths(writes, cwd);
|
|
24189
24805
|
if (missingPaths.length > 0) {
|
|
24190
|
-
throw new
|
|
24806
|
+
throw new HTTPException12(400, {
|
|
24191
24807
|
message: tx(SERVER_TEXTS.projectPrefsPathNotFound, {
|
|
24192
24808
|
paths: missingPaths.join(", ")
|
|
24193
24809
|
})
|
|
@@ -24196,7 +24812,7 @@ async function applyPatch3(deps, body) {
|
|
|
24196
24812
|
const exposures = writes.map((w) => projectPathExposure({ key: w.key, value: w.value, cwd })).filter((e) => e.expandsSurface);
|
|
24197
24813
|
if (exposures.length > 0 && body.confirm !== true) {
|
|
24198
24814
|
const exposed = exposures.flatMap((e) => e.exposedPaths);
|
|
24199
|
-
throw new
|
|
24815
|
+
throw new HTTPException12(412, {
|
|
24200
24816
|
message: tx(SERVER_TEXTS.projectPrefsConfirmRequired, {
|
|
24201
24817
|
paths: exposed.join(", ")
|
|
24202
24818
|
})
|
|
@@ -24248,7 +24864,7 @@ function runWrite(w, cwd) {
|
|
|
24248
24864
|
try {
|
|
24249
24865
|
writeConfigValue(w.key, w.value, { target: "project-local", cwd });
|
|
24250
24866
|
} catch (err) {
|
|
24251
|
-
throw new
|
|
24867
|
+
throw new HTTPException12(400, {
|
|
24252
24868
|
message: tx(SERVER_TEXTS.projectPrefsPersistFailed, {
|
|
24253
24869
|
key: w.key,
|
|
24254
24870
|
message: formatErrorMessage(err)
|
|
@@ -24347,8 +24963,8 @@ var parsePatchBody4 = makeBodyValidator(PATCH_BODY_SCHEMA3, {
|
|
|
24347
24963
|
});
|
|
24348
24964
|
|
|
24349
24965
|
// server/routes/active-provider.ts
|
|
24350
|
-
import { existsSync as
|
|
24351
|
-
import { HTTPException as
|
|
24966
|
+
import { existsSync as existsSync27 } from "fs";
|
|
24967
|
+
import { HTTPException as HTTPException13 } from "hono/http-exception";
|
|
24352
24968
|
function registerActiveProviderRoute(app, deps) {
|
|
24353
24969
|
app.get("/api/active-provider", (c) => {
|
|
24354
24970
|
return c.json(buildEnvelope4(deps));
|
|
@@ -24373,14 +24989,14 @@ function applyLensSwitch(deps, newValue) {
|
|
|
24373
24989
|
try {
|
|
24374
24990
|
writeConfigValue("activeProvider", newValue, { target: "project", cwd });
|
|
24375
24991
|
} catch (err) {
|
|
24376
|
-
throw new
|
|
24992
|
+
throw new HTTPException13(400, {
|
|
24377
24993
|
message: tx(SERVER_TEXTS.activeProviderPersistFailed, {
|
|
24378
24994
|
message: formatErrorMessage(err)
|
|
24379
24995
|
})
|
|
24380
24996
|
});
|
|
24381
24997
|
}
|
|
24382
24998
|
const dbPath = resolveDbPath({ db: void 0, cwd });
|
|
24383
|
-
if (!
|
|
24999
|
+
if (!existsSync27(dbPath)) return { dropped: null };
|
|
24384
25000
|
const dropResult = dropScanZone(dbPath);
|
|
24385
25001
|
return {
|
|
24386
25002
|
dropped: {
|
|
@@ -24409,7 +25025,7 @@ var parsePatchBody5 = makeBodyValidator(PATCH_BODY_SCHEMA4, {
|
|
|
24409
25025
|
});
|
|
24410
25026
|
|
|
24411
25027
|
// server/routes/scan.ts
|
|
24412
|
-
import { HTTPException as
|
|
25028
|
+
import { HTTPException as HTTPException14 } from "hono/http-exception";
|
|
24413
25029
|
|
|
24414
25030
|
// server/scan-mutex.ts
|
|
24415
25031
|
var inFlight = null;
|
|
@@ -24501,6 +25117,14 @@ function createWatcherService(opts) {
|
|
|
24501
25117
|
onPluginWarning: (message) => {
|
|
24502
25118
|
log.warn(sanitizeForTerminal(message));
|
|
24503
25119
|
},
|
|
25120
|
+
onDriftReset: (info) => {
|
|
25121
|
+
log.warn(
|
|
25122
|
+
tx(SERVER_TEXTS.watcherDriftReset, {
|
|
25123
|
+
dbVersion: info.dbVersion,
|
|
25124
|
+
currentVersion: info.currentVersion
|
|
25125
|
+
})
|
|
25126
|
+
);
|
|
25127
|
+
},
|
|
24504
25128
|
onReady: (info) => {
|
|
24505
25129
|
opts.broadcaster.broadcast(
|
|
24506
25130
|
buildWatcherStartedEvent({ roots: info.roots, debounceMs: info.debounceMs })
|
|
@@ -24567,7 +25191,7 @@ function registerScanRoute(app, deps) {
|
|
|
24567
25191
|
}
|
|
24568
25192
|
async function runPersistedScan(c, deps) {
|
|
24569
25193
|
if (deps.options.noBuiltIns || deps.options.noPlugins) {
|
|
24570
|
-
throw new
|
|
25194
|
+
throw new HTTPException14(400, { message: SERVER_TEXTS.scanPostRequiresFullPipeline });
|
|
24571
25195
|
}
|
|
24572
25196
|
const dbExists = await tryWithSqlite(
|
|
24573
25197
|
{ databasePath: deps.options.dbPath, autoBackup: false },
|
|
@@ -24604,7 +25228,7 @@ async function runPersistedScan(c, deps) {
|
|
|
24604
25228
|
...deps.options.maxNodes !== void 0 ? { maxNodes: deps.options.maxNodes } : {}
|
|
24605
25229
|
});
|
|
24606
25230
|
if (outcome.kind !== "ok") {
|
|
24607
|
-
throw new
|
|
25231
|
+
throw new HTTPException14(500, {
|
|
24608
25232
|
message: outcome.kind === "guard-trip" ? tx(SERVER_TEXTS.scanGuardTrip, { existing: outcome.existing }) : outcome.message
|
|
24609
25233
|
});
|
|
24610
25234
|
}
|
|
@@ -24612,7 +25236,7 @@ async function runPersistedScan(c, deps) {
|
|
|
24612
25236
|
});
|
|
24613
25237
|
} catch (err) {
|
|
24614
25238
|
if (err instanceof ScanBusyError) {
|
|
24615
|
-
throw new
|
|
25239
|
+
throw new HTTPException14(409, { message: SERVER_TEXTS.scanPostBusy });
|
|
24616
25240
|
}
|
|
24617
25241
|
throw err;
|
|
24618
25242
|
}
|
|
@@ -24643,13 +25267,8 @@ async function loadPersistedScan(deps) {
|
|
|
24643
25267
|
if (list) list.push(r);
|
|
24644
25268
|
else byPath3.set(r.nodePath, [r]);
|
|
24645
25269
|
}
|
|
24646
|
-
const
|
|
24647
|
-
|
|
24648
|
-
const list = tagBuckets.get(r.nodePath);
|
|
24649
|
-
if (list) list.push({ tag: r.tag, source: r.source });
|
|
24650
|
-
else tagBuckets.set(r.nodePath, [{ tag: r.tag, source: r.source }]);
|
|
24651
|
-
}
|
|
24652
|
-
return { loaded, favSet, contribByPath: byPath3, tagBuckets };
|
|
25270
|
+
const tagsByPath = groupTagsByPath2(tagRows);
|
|
25271
|
+
return { loaded, favSet, contribByPath: byPath3, tagsByPath };
|
|
24653
25272
|
}
|
|
24654
25273
|
);
|
|
24655
25274
|
if (opened === null) {
|
|
@@ -24661,22 +25280,24 @@ async function loadPersistedScan(deps) {
|
|
|
24661
25280
|
...n,
|
|
24662
25281
|
isFavorite: opened.favSet.has(n.path),
|
|
24663
25282
|
contributions: opened.contribByPath.get(n.path) ?? [],
|
|
24664
|
-
tags:
|
|
25283
|
+
tags: opened.tagsByPath.get(n.path) ?? []
|
|
24665
25284
|
}))
|
|
24666
25285
|
};
|
|
24667
25286
|
}
|
|
24668
|
-
function
|
|
24669
|
-
const
|
|
24670
|
-
const
|
|
24671
|
-
|
|
24672
|
-
|
|
24673
|
-
|
|
24674
|
-
|
|
24675
|
-
|
|
25287
|
+
function groupTagsByPath2(rows) {
|
|
25288
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
25289
|
+
for (const r of rows) {
|
|
25290
|
+
const set = buckets.get(r.nodePath);
|
|
25291
|
+
if (set) set.add(r.tag);
|
|
25292
|
+
else buckets.set(r.nodePath, /* @__PURE__ */ new Set([r.tag]));
|
|
25293
|
+
}
|
|
25294
|
+
const out = /* @__PURE__ */ new Map();
|
|
25295
|
+
for (const [path, set] of buckets) out.set(path, [...set].sort());
|
|
25296
|
+
return out;
|
|
24676
25297
|
}
|
|
24677
25298
|
async function runFreshScan(deps) {
|
|
24678
25299
|
if (deps.options.noBuiltIns || deps.options.noPlugins) {
|
|
24679
|
-
throw new
|
|
25300
|
+
throw new HTTPException14(400, { message: SERVER_TEXTS.freshScanRequiresPipeline });
|
|
24680
25301
|
}
|
|
24681
25302
|
const resolveEnabledOverride = await buildBffResolverOverride(deps);
|
|
24682
25303
|
const outcome = await runScanForCommand({
|
|
@@ -24711,7 +25332,7 @@ async function runFreshScan(deps) {
|
|
|
24711
25332
|
...deps.options.maxNodes !== void 0 ? { maxNodes: deps.options.maxNodes } : {}
|
|
24712
25333
|
});
|
|
24713
25334
|
if (outcome.kind !== "ok") {
|
|
24714
|
-
throw new
|
|
25335
|
+
throw new HTTPException14(500, {
|
|
24715
25336
|
message: outcome.kind === "guard-trip" ? tx(SERVER_TEXTS.freshScanGuardTrip, { existing: outcome.existing }) : outcome.message
|
|
24716
25337
|
});
|
|
24717
25338
|
}
|
|
@@ -24751,7 +25372,7 @@ function emptyScanResult() {
|
|
|
24751
25372
|
}
|
|
24752
25373
|
|
|
24753
25374
|
// server/routes/sidecar.ts
|
|
24754
|
-
import { HTTPException as
|
|
25375
|
+
import { HTTPException as HTTPException15 } from "hono/http-exception";
|
|
24755
25376
|
import { resolve as resolve36 } from "path";
|
|
24756
25377
|
var STATUS_FRESH = "fresh";
|
|
24757
25378
|
var ENVELOPE_KIND2 = "sidecar.bumped";
|
|
@@ -24788,11 +25409,11 @@ function registerSidecarRoutes(app, deps) {
|
|
|
24788
25409
|
assertContained(deps.runtimeContext.cwd, node.path);
|
|
24789
25410
|
absPath = resolve36(deps.runtimeContext.cwd, node.path);
|
|
24790
25411
|
} catch (err) {
|
|
24791
|
-
throw new
|
|
25412
|
+
throw new HTTPException15(400, { message: formatErrorMessage(err) });
|
|
24792
25413
|
}
|
|
24793
25414
|
const result = invokeBump2(node, absPath, body);
|
|
24794
25415
|
if (result.report.ok === false && result.report.reason === "fresh") {
|
|
24795
|
-
throw new
|
|
25416
|
+
throw new HTTPException15(409, { message: SERVER_TEXTS.sidecarFreshRefusal });
|
|
24796
25417
|
}
|
|
24797
25418
|
if (result.report.ok === true && result.report.noop === true) {
|
|
24798
25419
|
const envelope2 = {
|
|
@@ -24819,7 +25440,7 @@ function registerSidecarRoutes(app, deps) {
|
|
|
24819
25440
|
}
|
|
24820
25441
|
} catch (err) {
|
|
24821
25442
|
if (err instanceof EConsentRequiredError) throw err;
|
|
24822
|
-
throw new
|
|
25443
|
+
throw new HTTPException15(500, { message: formatErrorMessage(err) });
|
|
24823
25444
|
}
|
|
24824
25445
|
if (body.confirm === true) {
|
|
24825
25446
|
deps.configService.reload();
|
|
@@ -24856,7 +25477,7 @@ async function loadNode(deps, nodePath) {
|
|
|
24856
25477
|
);
|
|
24857
25478
|
const node = persisted?.nodes.find((n) => n.path === nodePath);
|
|
24858
25479
|
if (!node) {
|
|
24859
|
-
throw new
|
|
25480
|
+
throw new HTTPException15(404, {
|
|
24860
25481
|
message: tx(SERVER_TEXTS.nodeNotFound, { path: sanitizeForTerminal(nodePath) })
|
|
24861
25482
|
});
|
|
24862
25483
|
}
|
|
@@ -24864,7 +25485,7 @@ async function loadNode(deps, nodePath) {
|
|
|
24864
25485
|
}
|
|
24865
25486
|
function invokeBump2(node, absPath, body) {
|
|
24866
25487
|
if (!nodeBumpAction.invoke) {
|
|
24867
|
-
throw new
|
|
25488
|
+
throw new HTTPException15(500, { message: SERVER_TEXTS.sidecarBumpInvokeMissing });
|
|
24868
25489
|
}
|
|
24869
25490
|
const input = {};
|
|
24870
25491
|
if (body.force === true) input.force = true;
|
|
@@ -24910,7 +25531,7 @@ function registerUpdateStatusRoute(app, deps) {
|
|
|
24910
25531
|
}
|
|
24911
25532
|
|
|
24912
25533
|
// server/static.ts
|
|
24913
|
-
import { existsSync as
|
|
25534
|
+
import { existsSync as existsSync28 } from "fs";
|
|
24914
25535
|
import { readFile as readFile6 } from "fs/promises";
|
|
24915
25536
|
import { extname, join as join19 } from "path";
|
|
24916
25537
|
import { serveStatic } from "@hono/node-server/serve-static";
|
|
@@ -24965,7 +25586,7 @@ function createSpaFallback(opts) {
|
|
|
24965
25586
|
if (c.req.method !== "GET" && c.req.method !== "HEAD") return c.notFound();
|
|
24966
25587
|
if (opts.uiDist === null) return htmlResponse(c, placeholder);
|
|
24967
25588
|
const indexPath = join19(opts.uiDist, INDEX_HTML);
|
|
24968
|
-
if (!
|
|
25589
|
+
if (!existsSync28(indexPath)) return htmlResponse(c, placeholder);
|
|
24969
25590
|
return fileResponse(c, indexPath);
|
|
24970
25591
|
};
|
|
24971
25592
|
}
|
|
@@ -25051,13 +25672,13 @@ function attachBroadcasterRoute(app, broadcaster) {
|
|
|
25051
25672
|
|
|
25052
25673
|
// server/app.ts
|
|
25053
25674
|
var BODY_LIMIT_BYTES = 1024 * 1024;
|
|
25054
|
-
var DbMissingError = class extends
|
|
25675
|
+
var DbMissingError = class extends HTTPException16 {
|
|
25055
25676
|
constructor(message) {
|
|
25056
25677
|
super(500, { message });
|
|
25057
25678
|
this.name = "DbMissingError";
|
|
25058
25679
|
}
|
|
25059
25680
|
};
|
|
25060
|
-
var BulkValidationError = class extends
|
|
25681
|
+
var BulkValidationError = class extends HTTPException16 {
|
|
25061
25682
|
id;
|
|
25062
25683
|
code;
|
|
25063
25684
|
constructor(init) {
|
|
@@ -25067,7 +25688,7 @@ var BulkValidationError = class extends HTTPException15 {
|
|
|
25067
25688
|
this.code = init.code;
|
|
25068
25689
|
}
|
|
25069
25690
|
};
|
|
25070
|
-
var LoopbackGateError = class extends
|
|
25691
|
+
var LoopbackGateError = class extends HTTPException16 {
|
|
25071
25692
|
code;
|
|
25072
25693
|
constructor(init) {
|
|
25073
25694
|
super(403, { message: init.message });
|
|
@@ -25080,6 +25701,7 @@ function createApp(deps) {
|
|
|
25080
25701
|
const configService = new ConfigService({
|
|
25081
25702
|
cwd: deps.runtimeContext.cwd
|
|
25082
25703
|
});
|
|
25704
|
+
app.use("*", createSentryRequestCapture());
|
|
25083
25705
|
app.use("*", createLoopbackGate({ port: deps.options.port }));
|
|
25084
25706
|
app.use("*", createSecurityHeaders());
|
|
25085
25707
|
app.use(
|
|
@@ -25087,7 +25709,7 @@ function createApp(deps) {
|
|
|
25087
25709
|
bodyLimit({
|
|
25088
25710
|
maxSize: BODY_LIMIT_BYTES,
|
|
25089
25711
|
onError: () => {
|
|
25090
|
-
throw new
|
|
25712
|
+
throw new HTTPException16(413, { message: tx(SERVER_TEXTS.bodyTooLarge, { maxBytes: String(BODY_LIMIT_BYTES) }) });
|
|
25091
25713
|
}
|
|
25092
25714
|
})
|
|
25093
25715
|
);
|
|
@@ -25133,7 +25755,7 @@ function createApp(deps) {
|
|
|
25133
25755
|
registerActiveProviderRoute(app, routeDeps);
|
|
25134
25756
|
registerProjectIgnoreRoute(app, routeDeps);
|
|
25135
25757
|
app.all("/api/*", (c) => {
|
|
25136
|
-
throw new
|
|
25758
|
+
throw new HTTPException16(404, {
|
|
25137
25759
|
message: tx(SERVER_TEXTS.unknownApiEndpoint, { path: sanitizeForTerminal(c.req.path) })
|
|
25138
25760
|
});
|
|
25139
25761
|
});
|
|
@@ -25141,7 +25763,7 @@ function createApp(deps) {
|
|
|
25141
25763
|
app.use("*", createStaticHandler({ uiDist: deps.options.uiDist, noUi: deps.options.noUi }));
|
|
25142
25764
|
app.get("*", createSpaFallback({ uiDist: deps.options.uiDist, noUi: deps.options.noUi }));
|
|
25143
25765
|
app.notFound((c) => {
|
|
25144
|
-
throw new
|
|
25766
|
+
throw new HTTPException16(404, {
|
|
25145
25767
|
message: tx(SERVER_TEXTS.unknownPath, { path: sanitizeForTerminal(c.req.path) })
|
|
25146
25768
|
});
|
|
25147
25769
|
});
|
|
@@ -25196,7 +25818,7 @@ function formatError2(err, c) {
|
|
|
25196
25818
|
};
|
|
25197
25819
|
return c.json(envelope, 403);
|
|
25198
25820
|
}
|
|
25199
|
-
if (err instanceof
|
|
25821
|
+
if (err instanceof HTTPException16) {
|
|
25200
25822
|
const status = err.status;
|
|
25201
25823
|
const envelope = {
|
|
25202
25824
|
ok: false,
|
|
@@ -25295,7 +25917,7 @@ var WsBroadcaster = class {
|
|
|
25295
25917
|
* stop the rest from receiving the event. A failing socket is closed
|
|
25296
25918
|
* + unregistered so the next broadcast doesn't waste cycles on it.
|
|
25297
25919
|
*
|
|
25298
|
-
* Backpressure check (per
|
|
25920
|
+
* Backpressure check (per context/kernel.md §Kernel boundaries): if a
|
|
25299
25921
|
* client's `bufferedAmount` exceeds `MAX_BUFFERED_BYTES`, it's evicted
|
|
25300
25922
|
* with close code 1009. The check runs BEFORE `send` so the threshold
|
|
25301
25923
|
* acts as an admission gate, not a post-mortem.
|
|
@@ -25315,8 +25937,8 @@ var WsBroadcaster = class {
|
|
|
25315
25937
|
return;
|
|
25316
25938
|
}
|
|
25317
25939
|
const snapshot = Array.from(this.#clients);
|
|
25318
|
-
for (const
|
|
25319
|
-
this.#deliver(
|
|
25940
|
+
for (const client2 of snapshot) {
|
|
25941
|
+
this.#deliver(client2, payload);
|
|
25320
25942
|
}
|
|
25321
25943
|
}
|
|
25322
25944
|
/**
|
|
@@ -25330,9 +25952,9 @@ var WsBroadcaster = class {
|
|
|
25330
25952
|
this.#shutDown = true;
|
|
25331
25953
|
const snapshot = Array.from(this.#clients);
|
|
25332
25954
|
this.#clients.clear();
|
|
25333
|
-
for (const
|
|
25955
|
+
for (const client2 of snapshot) {
|
|
25334
25956
|
try {
|
|
25335
|
-
|
|
25957
|
+
client2.close(CLOSE_CODE_GOING_AWAY, "server shutdown");
|
|
25336
25958
|
} catch {
|
|
25337
25959
|
}
|
|
25338
25960
|
}
|
|
@@ -25341,31 +25963,31 @@ var WsBroadcaster = class {
|
|
|
25341
25963
|
* Per-client delivery: backpressure check, then `send()`. Eviction +
|
|
25342
25964
|
* unregistration on either failure mode.
|
|
25343
25965
|
*/
|
|
25344
|
-
#deliver(
|
|
25345
|
-
if (
|
|
25346
|
-
this.#clients.delete(
|
|
25966
|
+
#deliver(client2, payload) {
|
|
25967
|
+
if (client2.readyState !== READY_STATE_OPEN) {
|
|
25968
|
+
this.#clients.delete(client2);
|
|
25347
25969
|
return;
|
|
25348
25970
|
}
|
|
25349
|
-
if (
|
|
25350
|
-
this.#clients.delete(
|
|
25971
|
+
if (client2.bufferedAmount > MAX_BUFFERED_BYTES) {
|
|
25972
|
+
this.#clients.delete(client2);
|
|
25351
25973
|
try {
|
|
25352
|
-
|
|
25974
|
+
client2.close(CLOSE_CODE_MESSAGE_TOO_BIG, "backpressure exceeded");
|
|
25353
25975
|
} catch {
|
|
25354
25976
|
}
|
|
25355
25977
|
log.warn(
|
|
25356
25978
|
tx(SERVER_TEXTS.wsBackpressureEvicted, {
|
|
25357
|
-
buffered: String(
|
|
25979
|
+
buffered: String(client2.bufferedAmount),
|
|
25358
25980
|
threshold: String(MAX_BUFFERED_BYTES)
|
|
25359
25981
|
})
|
|
25360
25982
|
);
|
|
25361
25983
|
return;
|
|
25362
25984
|
}
|
|
25363
25985
|
try {
|
|
25364
|
-
|
|
25986
|
+
client2.send(payload);
|
|
25365
25987
|
} catch (err) {
|
|
25366
|
-
this.#clients.delete(
|
|
25988
|
+
this.#clients.delete(client2);
|
|
25367
25989
|
try {
|
|
25368
|
-
|
|
25990
|
+
client2.close();
|
|
25369
25991
|
} catch {
|
|
25370
25992
|
}
|
|
25371
25993
|
const message = formatErrorMessage(err);
|
|
@@ -25574,7 +26196,7 @@ function validateNoUi(noUi, uiDist) {
|
|
|
25574
26196
|
}
|
|
25575
26197
|
|
|
25576
26198
|
// server/paths.ts
|
|
25577
|
-
import { existsSync as
|
|
26199
|
+
import { existsSync as existsSync29, statSync as statSync10 } from "fs";
|
|
25578
26200
|
import { dirname as dirname18, isAbsolute as isAbsolute11, join as join20, resolve as resolve37 } from "path";
|
|
25579
26201
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
25580
26202
|
var DEFAULT_UI_REL = join20("ui", "dist", "ui", "browser");
|
|
@@ -25589,10 +26211,10 @@ function resolveExplicitUiDist(ctx, raw) {
|
|
|
25589
26211
|
return isAbsolute11(raw) ? raw : resolve37(ctx.cwd, raw);
|
|
25590
26212
|
}
|
|
25591
26213
|
function isUiBundleDir(path) {
|
|
25592
|
-
if (!
|
|
26214
|
+
if (!existsSync29(path)) return false;
|
|
25593
26215
|
try {
|
|
25594
26216
|
if (!statSync10(path).isDirectory()) return false;
|
|
25595
|
-
return
|
|
26217
|
+
return existsSync29(join20(path, INDEX_HTML2));
|
|
25596
26218
|
} catch {
|
|
25597
26219
|
return false;
|
|
25598
26220
|
}
|
|
@@ -26126,7 +26748,7 @@ var ServeCommand = class extends SmCommand {
|
|
|
26126
26748
|
emitElapsed = false;
|
|
26127
26749
|
// CLI orchestrator with multi-flag handling, each `if (this.flag)`
|
|
26128
26750
|
// branch is one cyclomatic point. Splitting per branch scatters the
|
|
26129
|
-
// validation away from the flag it gates. Per
|
|
26751
|
+
// validation away from the flag it gates. Per context/lint.md
|
|
26130
26752
|
// category 1 ("CLI orchestrators with multi-flag handling").
|
|
26131
26753
|
// eslint-disable-next-line complexity
|
|
26132
26754
|
async run() {
|
|
@@ -26147,7 +26769,7 @@ var ServeCommand = class extends SmCommand {
|
|
|
26147
26769
|
return ExitCode.Error;
|
|
26148
26770
|
}
|
|
26149
26771
|
const dbPath = resolveDbPath({ db: this.db, ...runtimeCtx });
|
|
26150
|
-
if (this.db !== void 0 && !
|
|
26772
|
+
if (this.db !== void 0 && !existsSync30(dbPath)) {
|
|
26151
26773
|
this.printer.info(
|
|
26152
26774
|
tx(SERVE_TEXTS.dbNotFound, {
|
|
26153
26775
|
glyph: errGlyph,
|
|
@@ -26229,6 +26851,7 @@ var ServeCommand = class extends SmCommand {
|
|
|
26229
26851
|
this.printer.info(formatValidationError(validation.error, stderrAnsi));
|
|
26230
26852
|
return ExitCode.Error;
|
|
26231
26853
|
}
|
|
26854
|
+
await initSentryBff(VERSION);
|
|
26232
26855
|
let handle;
|
|
26233
26856
|
try {
|
|
26234
26857
|
handle = await createServer(validation.options);
|
|
@@ -27412,7 +28035,7 @@ var STUB_COMMANDS = [
|
|
|
27412
28035
|
];
|
|
27413
28036
|
|
|
27414
28037
|
// cli/commands/tutorial.ts
|
|
27415
|
-
import { cpSync as cpSync2, existsSync as
|
|
28038
|
+
import { cpSync as cpSync2, existsSync as existsSync31, mkdirSync as mkdirSync6, rmSync as rmSync2, statSync as statSync11 } from "fs";
|
|
27416
28039
|
import { dirname as dirname19, join as join21, resolve as resolve39 } from "path";
|
|
27417
28040
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
27418
28041
|
import { Command as Command37, Option as Option35 } from "clipanion";
|
|
@@ -27511,7 +28134,7 @@ var TutorialCommand = class extends SmCommand {
|
|
|
27511
28134
|
const spec = VARIANT_SPECS[variant];
|
|
27512
28135
|
const targetDir = join21(ctx.cwd, ".claude", "skills", spec.slug);
|
|
27513
28136
|
const targetDisplay = `.claude/skills/${spec.slug}/`;
|
|
27514
|
-
if (
|
|
28137
|
+
if (existsSync31(targetDir) && !this.force) {
|
|
27515
28138
|
this.printer.error(
|
|
27516
28139
|
tx(TUTORIAL_TEXTS.alreadyExists, {
|
|
27517
28140
|
glyph: errGlyph,
|
|
@@ -27594,7 +28217,7 @@ function resolveSkillSourceDir(variant) {
|
|
|
27594
28217
|
resolve39(here, "../cli/tutorial", spec.slug)
|
|
27595
28218
|
];
|
|
27596
28219
|
for (const candidate of candidates) {
|
|
27597
|
-
if (
|
|
28220
|
+
if (existsSync31(candidate) && statSync11(candidate).isDirectory()) {
|
|
27598
28221
|
cachedSourceDirs.set(variant, candidate);
|
|
27599
28222
|
return candidate;
|
|
27600
28223
|
}
|
|
@@ -27698,6 +28321,7 @@ cli.register(RootHelpCommand);
|
|
|
27698
28321
|
cli.register(HelpCommand);
|
|
27699
28322
|
cli.register(InitCommand);
|
|
27700
28323
|
cli.register(TutorialCommand);
|
|
28324
|
+
cli.register(IntentionalFailCommand);
|
|
27701
28325
|
cli.register(ScanCommand);
|
|
27702
28326
|
cli.register(ScanCompareCommand);
|
|
27703
28327
|
cli.register(ServeCommand);
|
|
@@ -27731,6 +28355,13 @@ var logLevel = resolveLogLevel({
|
|
|
27731
28355
|
configureLogger(new Logger({ level: logLevel, stream: process.stderr }));
|
|
27732
28356
|
var bareArgs = resolveBareInvocation(args);
|
|
27733
28357
|
var routedArgs = routeHelpArgs(bareArgs ?? args, cli);
|
|
28358
|
+
var telemetryVerb = routedArgs[0];
|
|
28359
|
+
await maybeRunFirstRunPrompt();
|
|
28360
|
+
if (telemetryVerb !== "serve") {
|
|
28361
|
+
await initSentryCli(VERSION);
|
|
28362
|
+
setTelemetryVerbTag(telemetryVerb);
|
|
28363
|
+
await initUsageCli();
|
|
28364
|
+
}
|
|
27734
28365
|
var lifecycleDispatcher = makeHookDispatcher(
|
|
27735
28366
|
builtIns().hooks ?? [],
|
|
27736
28367
|
new InMemoryProgressEmitter()
|
|
@@ -27767,10 +28398,18 @@ var exitCode = await cli.run(routedArgs, {
|
|
|
27767
28398
|
stdout: process.stdout,
|
|
27768
28399
|
stderr: process.stderr
|
|
27769
28400
|
});
|
|
28401
|
+
if (telemetryVerb !== void 0 && telemetryVerb !== "") {
|
|
28402
|
+
const knownVerbs = new Set(
|
|
28403
|
+
registeredVerbPaths(cli).map((path) => path[0]).filter((token) => token !== void 0)
|
|
28404
|
+
);
|
|
28405
|
+
captureCliInvocation(telemetryVerb, extractFlagNames(routedArgs.slice(1)), knownVerbs);
|
|
28406
|
+
}
|
|
27770
28407
|
await lifecycleDispatcher.dispatch(
|
|
27771
28408
|
"shutdown",
|
|
27772
28409
|
makeEvent("shutdown", { exitCode })
|
|
27773
28410
|
);
|
|
28411
|
+
await closeSentryCli();
|
|
28412
|
+
await flushUsageCli();
|
|
27774
28413
|
process.exit(exitCode);
|
|
27775
28414
|
function resolveBareInvocation(rawArgs) {
|
|
27776
28415
|
if (rawArgs.length === 0) return resolveBareDefault();
|
|
@@ -27779,7 +28418,7 @@ function resolveBareInvocation(rawArgs) {
|
|
|
27779
28418
|
if (first !== void 0 && first.startsWith("-") && !passthrough.has(first)) {
|
|
27780
28419
|
const isSingleDashLong = !first.startsWith("--") && first.length > 2;
|
|
27781
28420
|
if (isSingleDashLong) return null;
|
|
27782
|
-
if (
|
|
28421
|
+
if (existsSync32(defaultProjectDbPath(defaultRuntimeContext()))) {
|
|
27783
28422
|
return ["serve", ...rawArgs];
|
|
27784
28423
|
}
|
|
27785
28424
|
return resolveBareDefault();
|
|
@@ -27788,7 +28427,7 @@ function resolveBareInvocation(rawArgs) {
|
|
|
27788
28427
|
}
|
|
27789
28428
|
function resolveBareDefault() {
|
|
27790
28429
|
const ctx = defaultRuntimeContext();
|
|
27791
|
-
if (
|
|
28430
|
+
if (existsSync32(defaultProjectDbPath(ctx))) {
|
|
27792
28431
|
return ["serve"];
|
|
27793
28432
|
}
|
|
27794
28433
|
const stderr = process.stderr;
|