lurqrun 0.1.6 → 0.1.7
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/README.md +18 -10
- package/dist/auditHandler-JCOJSUYW.js +23 -0
- package/dist/bin/lurq.js +3090 -23306
- package/dist/browserAuth-B6G27USY.js +95 -0
- package/dist/cache-DKI34SVS.js +12 -0
- package/dist/check-2FDGUOV7.js +192 -0
- package/dist/checkUpgrade-F2WESBPM.js +103 -0
- package/dist/chunk-2VMOWSRY.js +300 -0
- package/dist/chunk-3GHJR7XR.js +2049 -0
- package/dist/chunk-3N3Q3DSW.js +214 -0
- package/dist/chunk-4ESO33J6.js +34 -0
- package/dist/chunk-4F7ZESPO.js +132 -0
- package/dist/chunk-4KCPOMG6.js +68 -0
- package/dist/chunk-4ZYCIMY4.js +155 -0
- package/dist/chunk-63MUJNZL.js +318 -0
- package/dist/chunk-6AAEVQ7I.js +6336 -0
- package/dist/chunk-6EQ3ARXA.js +304 -0
- package/dist/chunk-6L3U7DZ5.js +23 -0
- package/dist/chunk-AHBP533R.js +220 -0
- package/dist/chunk-B43LCXBM.js +223 -0
- package/dist/chunk-BCANRN5B.js +33 -0
- package/dist/chunk-BUALU27S.js +123 -0
- package/dist/chunk-BULZAX2U.js +1554 -0
- package/dist/chunk-BV32UNLL.js +154 -0
- package/dist/chunk-C6GLE626.js +160 -0
- package/dist/chunk-CIDWNUAP.js +145 -0
- package/dist/chunk-CUP36ND3.js +2126 -0
- package/dist/chunk-CW5HJ3LX.js +96 -0
- package/dist/chunk-DB2VF5UD.js +315 -0
- package/dist/chunk-DWAGCNEL.js +210 -0
- package/dist/chunk-EA3DP5HG.js +293 -0
- package/dist/chunk-ELGCAM7P.js +184 -0
- package/dist/chunk-FW6BD2NO.js +1915 -0
- package/dist/chunk-GE37NVLN.js +15 -0
- package/dist/chunk-GPTKSNR6.js +171 -0
- package/dist/chunk-GRJXQBQZ.js +793 -0
- package/dist/chunk-HWBBVLOT.js +92 -0
- package/dist/chunk-I2JUKN6Z.js +24 -0
- package/dist/chunk-I3M2KJXB.js +56 -0
- package/dist/chunk-IQ4SDKKY.js +733 -0
- package/dist/chunk-J5FZYN4A.js +116 -0
- package/dist/chunk-L6N4JFW2.js +263 -0
- package/dist/chunk-LBFGSSF2.js +126 -0
- package/dist/chunk-LEGPB7DO.js +81 -0
- package/dist/chunk-LO35HLQZ.js +219 -0
- package/dist/chunk-LRD4GBYE.js +73 -0
- package/dist/chunk-LUAKDHLZ.js +40 -0
- package/dist/chunk-LYMPYETA.js +965 -0
- package/dist/chunk-M6NYEXCW.js +38 -0
- package/dist/chunk-MHUEVBVO.js +35 -0
- package/dist/chunk-MYGSLK44.js +37 -0
- package/dist/chunk-NH5I4KHH.js +201 -0
- package/dist/chunk-OQS4GUSI.js +1391 -0
- package/dist/chunk-PSX3GIAN.js +176 -0
- package/dist/chunk-Q5VPF4WI.js +26 -0
- package/dist/chunk-QKGYDMWB.js +7409 -0
- package/dist/chunk-QSL6QN5X.js +115 -0
- package/dist/chunk-SHCEBP3J.js +107 -0
- package/dist/chunk-SJOVFF7B.js +201 -0
- package/dist/chunk-ST2X2GYN.js +373 -0
- package/dist/chunk-TNTXWSYA.js +19 -0
- package/dist/chunk-U5AT3TD4.js +2128 -0
- package/dist/chunk-UDVNA7EA.js +483 -0
- package/dist/chunk-VL2E5T2A.js +109 -0
- package/dist/chunk-VLRHFMGJ.js +58 -0
- package/dist/chunk-WHX2ZFCG.js +4048 -0
- package/dist/chunk-WQ23REYT.js +37 -0
- package/dist/chunk-XBIDMHSK.js +15425 -0
- package/dist/chunk-Y5GI7RL3.js +66 -0
- package/dist/client-IRKAMDYZ.js +15 -0
- package/dist/commands-FIL5ET6P.js +911 -0
- package/dist/compat-R3UTFQ5Q.js +201 -0
- package/dist/dashboardIssue-WYYMBFIB.js +192 -0
- package/dist/diff-GZA2774U.js +13 -0
- package/dist/dist-4XXIXHHZ.js +12945 -0
- package/dist/e2b-FVPHDULZ.js +172 -0
- package/dist/extract-NKGKLIVT.js +444 -0
- package/dist/fetch-QYTUJVG2.js +22 -0
- package/dist/firstTouch-QQTJH4QX.js +55 -0
- package/dist/handlers-GEJAVZDK.js +53 -0
- package/dist/http-ETPYMBGD.js +5360 -0
- package/dist/ingestQueue-VKN4KBIF.js +35 -0
- package/dist/install-EK3W2COX.js +20 -0
- package/dist/installSkill-VQJWJVCL.js +52 -0
- package/dist/inventory-5AWSLJYU.js +31 -0
- package/dist/local-XPZMRH5R.js +13 -0
- package/dist/mcp-3JHD5SUZ.js +472 -0
- package/dist/mcpHandlers-QFN4MCFL.js +22 -0
- package/dist/mcpScan-QZHWENIP.js +1871 -0
- package/dist/mcpScanWorkflow-JPYCBGC4.js +81 -0
- package/dist/mcpStack-BYJCZNIV.js +20 -0
- package/dist/packages-THVFMWFP.js +42 -0
- package/dist/plan-V4OEI3J4.js +463 -0
- package/dist/planView-JRXGCG2N.js +79 -0
- package/dist/policy-3P6BGRDN.js +115 -0
- package/dist/references-6N7VEPHT.js +14 -0
- package/dist/release-4U4ABNSJ.js +128 -0
- package/dist/remote-YGCXYI4V.js +32 -0
- package/dist/reportRuns-CKQQYLY4.js +64 -0
- package/dist/sandbox-MEZLAODF.js +39 -0
- package/dist/server-KFUD5X57.js +55 -0
- package/dist/surface-V62GE554.js +141 -0
- package/dist/surface-YL6O67W3.js +36 -0
- package/dist/surfaceHandlers-X2IZKA3E.js +20 -0
- package/dist/typecheck-X7U5ELMR.js +268 -0
- package/dist/types-M5JP2W3D.js +10 -0
- package/dist/uninstall-P7RGBX6G.js +174 -0
- package/dist/upgrade-NQ5IWFZB.js +596 -0
- package/dist/upgradePlan-A4LRKLEG.js +146 -0
- package/dist/userConfig-Z3GQE776.js +20 -0
- package/dist/watch-DGFMWUDL.js +214 -0
- package/package.json +26 -21
- package/templates/agent-rules.md +4 -4
- package/templates/skill-instructions.md +13 -14
- package/dist/bin/lurq.js.map +0 -1
- package/dist/index.d.ts +0 -805
- package/dist/index.js +0 -23570
- package/dist/index.js.map +0 -1
- package/drizzle/0000_confused_fabian_cortez.sql +0 -54
- package/drizzle/0001_aspiring_timeslip.sql +0 -15
- package/drizzle/0002_lyrical_lila_cheney.sql +0 -14
- package/drizzle/0003_violet_giant_girl.sql +0 -14
- package/drizzle/0004_slim_valeria_richards.sql +0 -15
- package/drizzle/0005_petite_luke_cage.sql +0 -12
- package/drizzle/0006_calm_nighthawk.sql +0 -3
- package/drizzle/0007_slimy_naoko.sql +0 -1
- package/drizzle/0008_peaceful_tigra.sql +0 -1
- package/drizzle/0009_smooth_harrier.sql +0 -10
- package/drizzle/0010_curved_amphibian.sql +0 -2
- package/drizzle/0011_compat_provenance_closures.sql +0 -11
- package/drizzle/0012_api_surfaces.sql +0 -9
- package/drizzle/0013_nice_thanos.sql +0 -1
- package/drizzle/0014_luxuriant_malcolm_colcord.sql +0 -10
- package/drizzle/0015_past_fat_cobra.sql +0 -11
- package/drizzle/0016_futuristic_junta.sql +0 -50
- package/drizzle/0017_absent_grandmaster.sql +0 -20
- package/drizzle/0018_rainy_proemial_gods.sql +0 -1
- package/drizzle/0019_dashing_korvac.sql +0 -11
- package/drizzle/0020_normal_vulture.sql +0 -3
- package/drizzle/0021_dazzling_blue_shield.sql +0 -17
- package/drizzle/0022_massive_ghost_rider.sql +0 -24
- package/drizzle/0023_next_shatterstar.sql +0 -1
- package/drizzle/0024_dusty_maelstrom.sql +0 -15
- package/drizzle/0025_gifted_harpoon.sql +0 -5
- package/drizzle/0026_awesome_mordo.sql +0 -1
- package/drizzle/0027_charming_ulik.sql +0 -2
- package/drizzle/0028_romantic_loners.sql +0 -2
- package/drizzle/0029_busy_korvac.sql +0 -16
- package/drizzle/0030_steady_mathemanic.sql +0 -6
- package/drizzle/0031_redundant_maginty.sql +0 -2
- package/drizzle/0032_stack_resolutions.sql +0 -14
- package/drizzle/0033_graceful_tyger_tiger.sql +0 -3
- package/drizzle/0034_uneven_thunderbird.sql +0 -1
- package/drizzle/0035_api_key_scopes.sql +0 -1
- package/drizzle/0036_policy_audit.sql +0 -21
- package/drizzle/0037_subscription_seats.sql +0 -1
- package/drizzle/0038_symbol_offsets.sql +0 -2
- package/drizzle/0039_mcp_runtime_scans.sql +0 -80
- package/drizzle/0040_subscription_overage_and_interval.sql +0 -4
- package/drizzle/0041_account_email.sql +0 -38
- package/drizzle/0042_alert_channels.sql +0 -21
- package/drizzle/meta/0000_snapshot.json +0 -370
- package/drizzle/meta/0001_snapshot.json +0 -482
- package/drizzle/meta/0002_snapshot.json +0 -575
- package/drizzle/meta/0003_snapshot.json +0 -667
- package/drizzle/meta/0004_snapshot.json +0 -769
- package/drizzle/meta/0005_snapshot.json +0 -864
- package/drizzle/meta/0006_snapshot.json +0 -882
- package/drizzle/meta/0007_snapshot.json +0 -876
- package/drizzle/meta/0008_snapshot.json +0 -882
- package/drizzle/meta/0009_snapshot.json +0 -948
- package/drizzle/meta/0010_snapshot.json +0 -969
- package/drizzle/meta/0011_snapshot.json +0 -1049
- package/drizzle/meta/0012_snapshot.json +0 -1115
- package/drizzle/meta/0013_snapshot.json +0 -1121
- package/drizzle/meta/0014_snapshot.json +0 -1190
- package/drizzle/meta/0015_snapshot.json +0 -1280
- package/drizzle/meta/0016_snapshot.json +0 -1683
- package/drizzle/meta/0017_snapshot.json +0 -1834
- package/drizzle/meta/0018_snapshot.json +0 -1834
- package/drizzle/meta/0019_snapshot.json +0 -1909
- package/drizzle/meta/0020_snapshot.json +0 -1921
- package/drizzle/meta/0021_snapshot.json +0 -2045
- package/drizzle/meta/0022_snapshot.json +0 -2246
- package/drizzle/meta/0023_snapshot.json +0 -2252
- package/drizzle/meta/0024_snapshot.json +0 -2375
- package/drizzle/meta/0025_snapshot.json +0 -2407
- package/drizzle/meta/0026_snapshot.json +0 -2413
- package/drizzle/meta/0027_snapshot.json +0 -2425
- package/drizzle/meta/0028_snapshot.json +0 -2438
- package/drizzle/meta/0029_snapshot.json +0 -2546
- package/drizzle/meta/0030_snapshot.json +0 -2586
- package/drizzle/meta/0031_snapshot.json +0 -2598
- package/drizzle/meta/0032_snapshot.json +0 -2699
- package/drizzle/meta/0033_snapshot.json +0 -2712
- package/drizzle/meta/0034_snapshot.json +0 -2719
- package/drizzle/meta/0035_snapshot.json +0 -2726
- package/drizzle/meta/0036_snapshot.json +0 -2876
- package/drizzle/meta/0037_snapshot.json +0 -2883
- package/drizzle/meta/0038_snapshot.json +0 -2895
- package/drizzle/meta/0039_snapshot.json +0 -3501
- package/drizzle/meta/0040_snapshot.json +0 -3527
- package/drizzle/meta/0041_snapshot.json +0 -3824
- package/drizzle/meta/0042_snapshot.json +0 -3960
- package/drizzle/meta/_journal.json +0 -307
- package/src/data/seed.json +0 -253
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
import { createRequire as __lurqCreateRequire } from 'node:module'; const require = __lurqCreateRequire(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
packageRoot
|
|
4
|
+
} from "./chunk-LUAKDHLZ.js";
|
|
5
|
+
import {
|
|
6
|
+
logger
|
|
7
|
+
} from "./chunk-MYGSLK44.js";
|
|
8
|
+
import {
|
|
9
|
+
lurqHome,
|
|
10
|
+
resolveApiKey
|
|
11
|
+
} from "./chunk-Y5GI7RL3.js";
|
|
12
|
+
import {
|
|
13
|
+
DEFAULT_ENDPOINT,
|
|
14
|
+
PACKAGE_NAME
|
|
15
|
+
} from "./chunk-GPTKSNR6.js";
|
|
16
|
+
import {
|
|
17
|
+
init_esm_shims
|
|
18
|
+
} from "./chunk-I3M2KJXB.js";
|
|
19
|
+
|
|
20
|
+
// src/cli/installSkill.ts
|
|
21
|
+
init_esm_shims();
|
|
22
|
+
import {
|
|
23
|
+
accessSync,
|
|
24
|
+
chmodSync,
|
|
25
|
+
constants,
|
|
26
|
+
existsSync,
|
|
27
|
+
mkdirSync,
|
|
28
|
+
readFileSync,
|
|
29
|
+
realpathSync,
|
|
30
|
+
renameSync,
|
|
31
|
+
rmSync,
|
|
32
|
+
statSync,
|
|
33
|
+
writeFileSync
|
|
34
|
+
} from "fs";
|
|
35
|
+
import { copyFileSync } from "fs";
|
|
36
|
+
import { homedir } from "os";
|
|
37
|
+
import { delimiter, dirname, join } from "path";
|
|
38
|
+
var ENV_KEYS = [
|
|
39
|
+
"DATABASE_URL",
|
|
40
|
+
"GITHUB_TOKEN",
|
|
41
|
+
"EMBEDDING_PROVIDER",
|
|
42
|
+
"EMBEDDING_API_KEY",
|
|
43
|
+
"EMBEDDING_MODEL",
|
|
44
|
+
"SUMMARY_PROVIDER",
|
|
45
|
+
"SUMMARY_API_KEY",
|
|
46
|
+
"SUMMARY_MODEL"
|
|
47
|
+
];
|
|
48
|
+
function home(...p) {
|
|
49
|
+
return join(homedir(), ...p);
|
|
50
|
+
}
|
|
51
|
+
function vscodeUserDir() {
|
|
52
|
+
if (process.platform === "darwin") return home("Library", "Application Support", "Code", "User");
|
|
53
|
+
if (process.platform === "win32") {
|
|
54
|
+
return join(process.env.APPDATA ?? home("AppData", "Roaming"), "Code", "User");
|
|
55
|
+
}
|
|
56
|
+
return join(process.env.XDG_CONFIG_HOME || home(".config"), "Code", "User");
|
|
57
|
+
}
|
|
58
|
+
function agentSpecs() {
|
|
59
|
+
return [
|
|
60
|
+
{
|
|
61
|
+
id: "claude-code",
|
|
62
|
+
label: "Claude Code",
|
|
63
|
+
format: "mcpServers",
|
|
64
|
+
path: home(".claude.json"),
|
|
65
|
+
detected: existsSync(home(".claude.json")) || existsSync(home(".claude")),
|
|
66
|
+
// Personal skills are read from ~/.claude/skills/<name>/SKILL.md and apply
|
|
67
|
+
// across every project, which is exactly the scope a global install wants.
|
|
68
|
+
instructions: { kind: "skill", path: home(".claude", "skills", "lurq", "SKILL.md") }
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
// No instructions target: Cursor's user-level rules live in its settings
|
|
72
|
+
// UI, not a file on disk, and its `.cursor/rules/*.mdc` files are
|
|
73
|
+
// per-project, so there is nothing a machine-wide install can write once.
|
|
74
|
+
// The MCP tool descriptions carry the guidance here.
|
|
75
|
+
id: "cursor",
|
|
76
|
+
label: "Cursor",
|
|
77
|
+
format: "mcpServers",
|
|
78
|
+
path: home(".cursor", "mcp.json"),
|
|
79
|
+
detected: existsSync(home(".cursor"))
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
id: "windsurf",
|
|
83
|
+
label: "Windsurf",
|
|
84
|
+
format: "mcpServers",
|
|
85
|
+
path: home(".codeium", "windsurf", "mcp_config.json"),
|
|
86
|
+
detected: existsSync(home(".codeium")),
|
|
87
|
+
instructions: {
|
|
88
|
+
kind: "shared",
|
|
89
|
+
path: home(".codeium", "windsurf", "memories", "global_rules.md")
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
// No instructions target, for two reasons that compound. A
|
|
94
|
+
// `.instructions.md` file is only applied automatically when its
|
|
95
|
+
// frontmatter carries an `applyTo` glob; without one, VS Code requires the
|
|
96
|
+
// user to attach it to a request by hand. And the user-profile directory
|
|
97
|
+
// that gets scanned is not a documented default at all: the workspace
|
|
98
|
+
// default is `.github/instructions`, and profile-level folders come from
|
|
99
|
+
// the `chat.instructionsFilesLocations` setting. Guessing the path would
|
|
100
|
+
// write a file nobody reads while reporting success, which is the same
|
|
101
|
+
// mistake the hardcoded macOS config path made above.
|
|
102
|
+
id: "copilot",
|
|
103
|
+
label: "VS Code / GitHub Copilot",
|
|
104
|
+
format: "servers",
|
|
105
|
+
path: join(vscodeUserDir(), "mcp.json"),
|
|
106
|
+
detected: existsSync(vscodeUserDir())
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
id: "codex",
|
|
110
|
+
label: "OpenAI Codex CLI",
|
|
111
|
+
format: "toml",
|
|
112
|
+
path: home(".codex", "config.toml"),
|
|
113
|
+
detected: existsSync(home(".codex")),
|
|
114
|
+
instructions: { kind: "shared", path: home(".codex", "AGENTS.md") }
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
id: "gemini-cli",
|
|
118
|
+
label: "Gemini CLI",
|
|
119
|
+
format: "mcpServers",
|
|
120
|
+
path: home(".gemini", "settings.json"),
|
|
121
|
+
detected: existsSync(home(".gemini", "settings.json")),
|
|
122
|
+
// ~/.gemini/GEMINI.md is the global tier of the context hierarchy, loaded
|
|
123
|
+
// ahead of any workspace file.
|
|
124
|
+
instructions: { kind: "shared", path: home(".gemini", "GEMINI.md") }
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
// Shares the ~/.gemini root with the Gemini CLI but keeps its own file, so
|
|
128
|
+
// detection has to look at the file rather than at the directory. It reads
|
|
129
|
+
// the same global GEMINI.md; upserting the block twice is a no-op, so
|
|
130
|
+
// having both selected is harmless.
|
|
131
|
+
id: "antigravity",
|
|
132
|
+
label: "Google Antigravity",
|
|
133
|
+
format: "mcpServers",
|
|
134
|
+
path: home(".gemini", "config", "mcp_config.json"),
|
|
135
|
+
detected: existsSync(home(".gemini", "config")),
|
|
136
|
+
instructions: { kind: "shared", path: home(".gemini", "GEMINI.md") }
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
id: "kiro",
|
|
140
|
+
label: "Kiro",
|
|
141
|
+
format: "mcpServers",
|
|
142
|
+
path: home(".kiro", "settings", "mcp.json"),
|
|
143
|
+
detected: existsSync(home(".kiro")),
|
|
144
|
+
// Steering files in ~/.kiro/steering apply to every workspace and are
|
|
145
|
+
// always included.
|
|
146
|
+
instructions: { kind: "file", path: home(".kiro", "steering", "lurq.md") }
|
|
147
|
+
}
|
|
148
|
+
];
|
|
149
|
+
}
|
|
150
|
+
var SUPPORTED_AGENTS = [
|
|
151
|
+
"claude-code",
|
|
152
|
+
"cursor",
|
|
153
|
+
"windsurf",
|
|
154
|
+
"copilot",
|
|
155
|
+
"codex",
|
|
156
|
+
"gemini-cli",
|
|
157
|
+
"antigravity",
|
|
158
|
+
"kiro"
|
|
159
|
+
];
|
|
160
|
+
function collectEnv() {
|
|
161
|
+
const env = {};
|
|
162
|
+
for (const key of ENV_KEYS) {
|
|
163
|
+
const value = process.env[key];
|
|
164
|
+
if (value) env[key] = value;
|
|
165
|
+
}
|
|
166
|
+
return env;
|
|
167
|
+
}
|
|
168
|
+
function readJsonObject(path) {
|
|
169
|
+
if (!existsSync(path)) return {};
|
|
170
|
+
const text = readFileSync(path, "utf8").trim();
|
|
171
|
+
if (!text) return {};
|
|
172
|
+
try {
|
|
173
|
+
return JSON.parse(text);
|
|
174
|
+
} catch (err) {
|
|
175
|
+
throw new Error(
|
|
176
|
+
`${path} is not plain JSON (${err instanceof Error ? err.message : String(err)}). If it has comments or trailing commas, lurq will not rewrite it and lose them: remove them and re-run, or edit the "lurq" server entry by hand.`
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
var KEY_FILE_MODE = 384;
|
|
181
|
+
function writeFileAtomic(path, contents, newFileMode = KEY_FILE_MODE) {
|
|
182
|
+
let target = path;
|
|
183
|
+
let mode = newFileMode;
|
|
184
|
+
try {
|
|
185
|
+
target = realpathSync(path);
|
|
186
|
+
mode = statSync(target).mode & 511;
|
|
187
|
+
} catch {
|
|
188
|
+
}
|
|
189
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
190
|
+
const tmp = `${target}.lurq-${process.pid}.tmp`;
|
|
191
|
+
try {
|
|
192
|
+
writeFileSync(tmp, contents, { encoding: "utf8", mode });
|
|
193
|
+
chmodSync(tmp, mode);
|
|
194
|
+
renameSync(tmp, target);
|
|
195
|
+
} catch (err) {
|
|
196
|
+
try {
|
|
197
|
+
rmSync(tmp, { force: true });
|
|
198
|
+
} catch {
|
|
199
|
+
}
|
|
200
|
+
throw err;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
function writeJson(path, obj) {
|
|
204
|
+
writeFileAtomic(path, JSON.stringify(obj, null, 2) + "\n");
|
|
205
|
+
}
|
|
206
|
+
function buildServerEntry(env, withType) {
|
|
207
|
+
const entry = { command: "npx", args: ["-y", PACKAGE_NAME, "serve"] };
|
|
208
|
+
if (withType) entry.type = "stdio";
|
|
209
|
+
if (Object.keys(env).length) entry.env = env;
|
|
210
|
+
return entry;
|
|
211
|
+
}
|
|
212
|
+
function buildRemoteServerEntry(agentId, opts) {
|
|
213
|
+
const headers = { Authorization: `Bearer ${opts.apiKey}` };
|
|
214
|
+
switch (agentId) {
|
|
215
|
+
case "cursor":
|
|
216
|
+
case "kiro":
|
|
217
|
+
return { url: opts.url, headers };
|
|
218
|
+
case "windsurf":
|
|
219
|
+
case "antigravity":
|
|
220
|
+
return { serverUrl: opts.url, headers };
|
|
221
|
+
case "gemini-cli":
|
|
222
|
+
return { httpUrl: opts.url, headers };
|
|
223
|
+
case "claude-code":
|
|
224
|
+
case "copilot":
|
|
225
|
+
default:
|
|
226
|
+
return { type: "http", url: opts.url, headers };
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
function buildTomlBlock(env) {
|
|
230
|
+
const lines = ["[mcp_servers.lurq]", 'command = "npx"', `args = ["-y", "${PACKAGE_NAME}", "serve"]`];
|
|
231
|
+
if (Object.keys(env).length) {
|
|
232
|
+
lines.push("", "[mcp_servers.lurq.env]");
|
|
233
|
+
for (const [k, v] of Object.entries(env)) lines.push(`${k} = ${JSON.stringify(v)}`);
|
|
234
|
+
}
|
|
235
|
+
return lines.join("\n") + "\n";
|
|
236
|
+
}
|
|
237
|
+
function buildRemoteTomlBlock(opts) {
|
|
238
|
+
return [
|
|
239
|
+
"[mcp_servers.lurq]",
|
|
240
|
+
`url = ${JSON.stringify(opts.url)}`,
|
|
241
|
+
`http_headers = { Authorization = ${JSON.stringify(`Bearer ${opts.apiKey}`)} }`
|
|
242
|
+
].join("\n") + "\n";
|
|
243
|
+
}
|
|
244
|
+
function installJsonEntry(spec, entry) {
|
|
245
|
+
const key = spec.format === "servers" ? "servers" : "mcpServers";
|
|
246
|
+
const config = readJsonObject(spec.path);
|
|
247
|
+
if (typeof config[key] !== "object" || config[key] === null) config[key] = {};
|
|
248
|
+
config[key].lurq = entry;
|
|
249
|
+
writeJson(spec.path, config);
|
|
250
|
+
return { agent: spec.id, path: spec.path, status: "installed" };
|
|
251
|
+
}
|
|
252
|
+
function stripTomlBlock(text, header = "[mcp_servers.lurq]") {
|
|
253
|
+
const lines = text.split("\n");
|
|
254
|
+
const start = lines.findIndex((l) => l.trim() === header);
|
|
255
|
+
if (start === -1) return text;
|
|
256
|
+
let end = start + 1;
|
|
257
|
+
while (end < lines.length) {
|
|
258
|
+
const t = lines[end].trim();
|
|
259
|
+
if (t.startsWith("[") && !t.startsWith(`${header.slice(0, -1)}.`)) break;
|
|
260
|
+
end++;
|
|
261
|
+
}
|
|
262
|
+
return [...lines.slice(0, start), ...lines.slice(end)].join("\n").replace(/\n{3,}/g, "\n\n");
|
|
263
|
+
}
|
|
264
|
+
function installTomlBlock(spec, block) {
|
|
265
|
+
const current = existsSync(spec.path) ? readFileSync(spec.path, "utf8") : "";
|
|
266
|
+
const existing = stripTomlBlock(current).replace(/\s+$/, "");
|
|
267
|
+
const sep = existing ? "\n\n" : "";
|
|
268
|
+
writeFileAtomic(spec.path, existing + sep + block);
|
|
269
|
+
return {
|
|
270
|
+
agent: spec.id,
|
|
271
|
+
path: spec.path,
|
|
272
|
+
status: "installed",
|
|
273
|
+
message: current.includes("[mcp_servers.lurq]") ? "replaced the existing lurq entry" : void 0
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
var BLOCK_START = "<!-- lurq:start -->";
|
|
277
|
+
var BLOCK_END = "<!-- lurq:end -->";
|
|
278
|
+
var SKILL_DESCRIPTION = "Get current, evidence-scored facts about npm packages instead of recalling them. Use before adding, choosing, comparing, or upgrading any JS/TS dependency, before hand-rolling something a package may already do, and before writing code against a package API that may have moved since training. Covers hallucinated and typosquatted package names, advisories, version-exact export surfaces, and stack compatibility.";
|
|
279
|
+
function template(name) {
|
|
280
|
+
const src = join(packageRoot(), "templates", name);
|
|
281
|
+
return existsSync(src) ? readFileSync(src, "utf8") : null;
|
|
282
|
+
}
|
|
283
|
+
function upsertMarkedBlock(text, block) {
|
|
284
|
+
const wrapped = `${BLOCK_START}
|
|
285
|
+
${block.trim()}
|
|
286
|
+
${BLOCK_END}`;
|
|
287
|
+
const start = text.indexOf(BLOCK_START);
|
|
288
|
+
if (start === -1) {
|
|
289
|
+
const existing = text.replace(/\s+$/, "");
|
|
290
|
+
return existing ? `${existing}
|
|
291
|
+
|
|
292
|
+
${wrapped}
|
|
293
|
+
` : `${wrapped}
|
|
294
|
+
`;
|
|
295
|
+
}
|
|
296
|
+
const endAt = text.indexOf(BLOCK_END, start);
|
|
297
|
+
const after = endAt === -1 ? "" : text.slice(endAt + BLOCK_END.length);
|
|
298
|
+
return `${text.slice(0, start)}${wrapped}${after.replace(/^\n*/, "\n")}`;
|
|
299
|
+
}
|
|
300
|
+
function removeMarkedBlock(text) {
|
|
301
|
+
const start = text.indexOf(BLOCK_START);
|
|
302
|
+
if (start === -1) return text;
|
|
303
|
+
const endAt = text.indexOf(BLOCK_END, start);
|
|
304
|
+
const before = text.slice(0, start).replace(/\s+$/, "");
|
|
305
|
+
const after = endAt === -1 ? "" : text.slice(endAt + BLOCK_END.length).replace(/^\s+/, "");
|
|
306
|
+
const joined = [before, after].filter(Boolean).join("\n\n");
|
|
307
|
+
return joined ? joined.replace(/\s*$/, "\n") : "";
|
|
308
|
+
}
|
|
309
|
+
function buildSkillFile(guide) {
|
|
310
|
+
return [
|
|
311
|
+
"---",
|
|
312
|
+
"name: lurq",
|
|
313
|
+
`description: ${SKILL_DESCRIPTION}`,
|
|
314
|
+
"---",
|
|
315
|
+
"",
|
|
316
|
+
guide.trim(),
|
|
317
|
+
""
|
|
318
|
+
].join("\n");
|
|
319
|
+
}
|
|
320
|
+
function installInstructions(spec) {
|
|
321
|
+
const target = spec.instructions;
|
|
322
|
+
if (!target) return null;
|
|
323
|
+
if (target.kind === "shared") {
|
|
324
|
+
const brief = template("agent-rules.md");
|
|
325
|
+
if (!brief) return null;
|
|
326
|
+
const current = existsSync(target.path) ? readFileSync(target.path, "utf8") : "";
|
|
327
|
+
writeFileAtomic(target.path, upsertMarkedBlock(current, brief), 420);
|
|
328
|
+
return target.path;
|
|
329
|
+
}
|
|
330
|
+
const guide = template("skill-instructions.md");
|
|
331
|
+
if (!guide) return null;
|
|
332
|
+
writeFileAtomic(target.path, target.kind === "skill" ? buildSkillFile(guide) : guide, 420);
|
|
333
|
+
return target.path;
|
|
334
|
+
}
|
|
335
|
+
function installAgent(spec, mode) {
|
|
336
|
+
try {
|
|
337
|
+
const result = spec.format === "toml" ? installTomlBlock(
|
|
338
|
+
spec,
|
|
339
|
+
mode.kind === "remote" ? buildRemoteTomlBlock(mode) : buildTomlBlock(mode.env)
|
|
340
|
+
) : installJsonEntry(
|
|
341
|
+
spec,
|
|
342
|
+
mode.kind === "remote" ? buildRemoteServerEntry(spec.id, mode) : buildServerEntry(mode.env, spec.format === "servers")
|
|
343
|
+
);
|
|
344
|
+
try {
|
|
345
|
+
result.instructionsPath = installInstructions(spec) ?? void 0;
|
|
346
|
+
} catch (err) {
|
|
347
|
+
result.message = `MCP entry written; instructions file failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
348
|
+
}
|
|
349
|
+
return result;
|
|
350
|
+
} catch (err) {
|
|
351
|
+
return {
|
|
352
|
+
agent: spec.id,
|
|
353
|
+
path: spec.path,
|
|
354
|
+
status: "error",
|
|
355
|
+
message: err instanceof Error ? err.message : String(err)
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
function installInstructionsFile() {
|
|
360
|
+
const src = join(packageRoot(), "templates", "skill-instructions.md");
|
|
361
|
+
if (!existsSync(src)) return null;
|
|
362
|
+
const destDir = lurqHome();
|
|
363
|
+
const dest = join(destDir, "skill-instructions.md");
|
|
364
|
+
mkdirSync(destDir, { recursive: true });
|
|
365
|
+
copyFileSync(src, dest);
|
|
366
|
+
return dest;
|
|
367
|
+
}
|
|
368
|
+
function resolveAgents(target) {
|
|
369
|
+
const specs = agentSpecs();
|
|
370
|
+
if (target === "all") return specs.filter((s) => s.detected);
|
|
371
|
+
const spec = specs.find((s) => s.id === target);
|
|
372
|
+
if (!spec) {
|
|
373
|
+
throw new Error(`Unknown agent "${target}". Supported: ${SUPPORTED_AGENTS.join(", ")}, all.`);
|
|
374
|
+
}
|
|
375
|
+
return [spec];
|
|
376
|
+
}
|
|
377
|
+
function lurqInvocation() {
|
|
378
|
+
const candidates = process.platform === "win32" ? [".cmd", ".exe", ""] : [""];
|
|
379
|
+
for (const dir of (process.env.PATH ?? "").split(delimiter)) {
|
|
380
|
+
if (!dir) continue;
|
|
381
|
+
for (const ext of candidates) {
|
|
382
|
+
try {
|
|
383
|
+
accessSync(join(dir, `lurq${ext}`), constants.X_OK);
|
|
384
|
+
return { command: "lurq", onPath: true };
|
|
385
|
+
} catch {
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
return { command: `npx ${PACKAGE_NAME}`, onPath: false };
|
|
390
|
+
}
|
|
391
|
+
function printInstallReport(results, instructionsPath, mode) {
|
|
392
|
+
const specs = agentSpecs();
|
|
393
|
+
const short = (p) => p.startsWith(homedir()) ? `~${p.slice(homedir().length)}` : p;
|
|
394
|
+
console.log("lurq MCP server registration:");
|
|
395
|
+
for (const r of results) {
|
|
396
|
+
const spec = specs.find((s) => s.id === r.agent);
|
|
397
|
+
const mark = r.status === "installed" ? "\u2713" : r.status === "skipped" ? "\u2022" : "\u2717";
|
|
398
|
+
console.log(
|
|
399
|
+
` ${mark} ${spec.label.padEnd(26)} ${short(r.path)}${r.message ? ` (${r.message})` : ""}`
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
const withSkills = results.filter((r) => r.instructionsPath);
|
|
403
|
+
if (withSkills.length) {
|
|
404
|
+
console.log("\nAgent instructions (so it reaches for lurq on its own):");
|
|
405
|
+
for (const r of withSkills) {
|
|
406
|
+
const spec = specs.find((s) => s.id === r.agent);
|
|
407
|
+
console.log(` \u2713 ${spec.label.padEnd(26)} ${short(r.instructionsPath)}`);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
if (instructionsPath) console.log(`
|
|
411
|
+
Full guide: ${short(instructionsPath)}`);
|
|
412
|
+
console.log("\nNext steps:");
|
|
413
|
+
if (mode.kind === "local") {
|
|
414
|
+
console.log(" 1. Ensure DATABASE_URL (and any API keys) are set in the config env above.");
|
|
415
|
+
console.log(" 2. Restart the agent so it picks up the new MCP server.");
|
|
416
|
+
} else {
|
|
417
|
+
console.log(" 1. Restart the agent so it picks up the new MCP server.");
|
|
418
|
+
}
|
|
419
|
+
console.log(" \u2022 Ask it to add a package. It should check the package with lurq first.");
|
|
420
|
+
const { command, onPath } = lurqInvocation();
|
|
421
|
+
console.log(` \u2022 Or use the terminal directly: \`${command} verify zod\`.`);
|
|
422
|
+
if (!onPath) {
|
|
423
|
+
console.log(`
|
|
424
|
+
There is no \`lurq\` command on this machine yet. Everything above`);
|
|
425
|
+
console.log(" worked and your agents are wired up; only the terminal shortcut is");
|
|
426
|
+
console.log(` missing, so use \`npx ${PACKAGE_NAME} <command>\`. To get \`lurq\` itself:`);
|
|
427
|
+
console.log(` npm install -g ${PACKAGE_NAME}`);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
async function runInstallSkill(opts) {
|
|
431
|
+
const selected = resolveAgents(opts.agent ?? "claude-code");
|
|
432
|
+
if (selected.length === 0) {
|
|
433
|
+
console.log("No supported agents detected on this machine.");
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
const remote = !opts.local;
|
|
437
|
+
let mode;
|
|
438
|
+
if (remote) {
|
|
439
|
+
const apiKey = resolveApiKey(opts.apiKey);
|
|
440
|
+
if (!apiKey) {
|
|
441
|
+
throw new Error(
|
|
442
|
+
"An API key is required for a hosted install. Pass --api-key <key> (or set LURQ_API_KEY), or run `lurq setup` for the guided setup, or --local to self-host."
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
const url = opts.url ?? process.env.LURQ_ENDPOINT ?? DEFAULT_ENDPOINT;
|
|
446
|
+
mode = { kind: "remote", url, apiKey };
|
|
447
|
+
} else {
|
|
448
|
+
const env = collectEnv();
|
|
449
|
+
if (!env.DATABASE_URL) {
|
|
450
|
+
logger.warn(
|
|
451
|
+
"DATABASE_URL is not set, the local server entry will have no DATABASE_URL. Set it (in .env) and re-run, or edit the config."
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
mode = { kind: "local", env };
|
|
455
|
+
}
|
|
456
|
+
const results = selected.map((spec) => installAgent(spec, mode));
|
|
457
|
+
const instructionsPath = installInstructionsFile();
|
|
458
|
+
printInstallReport(results, instructionsPath, mode);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
export {
|
|
462
|
+
agentSpecs,
|
|
463
|
+
SUPPORTED_AGENTS,
|
|
464
|
+
readJsonObject,
|
|
465
|
+
writeFileAtomic,
|
|
466
|
+
writeJson,
|
|
467
|
+
buildServerEntry,
|
|
468
|
+
buildRemoteServerEntry,
|
|
469
|
+
buildTomlBlock,
|
|
470
|
+
buildRemoteTomlBlock,
|
|
471
|
+
stripTomlBlock,
|
|
472
|
+
BLOCK_START,
|
|
473
|
+
upsertMarkedBlock,
|
|
474
|
+
removeMarkedBlock,
|
|
475
|
+
buildSkillFile,
|
|
476
|
+
installInstructions,
|
|
477
|
+
installAgent,
|
|
478
|
+
installInstructionsFile,
|
|
479
|
+
resolveAgents,
|
|
480
|
+
lurqInvocation,
|
|
481
|
+
printInstallReport,
|
|
482
|
+
runInstallSkill
|
|
483
|
+
};
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { createRequire as __lurqCreateRequire } from 'node:module'; const require = __lurqCreateRequire(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
MCP_TIER,
|
|
4
|
+
contractOf,
|
|
5
|
+
resolveAnnotations
|
|
6
|
+
} from "./chunk-6EQ3ARXA.js";
|
|
7
|
+
import {
|
|
8
|
+
init_esm_shims
|
|
9
|
+
} from "./chunk-I3M2KJXB.js";
|
|
10
|
+
|
|
11
|
+
// src/compat/mcpStack.ts
|
|
12
|
+
init_esm_shims();
|
|
13
|
+
var TOKENS_PER_TOOL = 250;
|
|
14
|
+
var CROWDED_TOOL_COUNT = 60;
|
|
15
|
+
var NPM_NAME = /^(?:@[a-z0-9-][a-z0-9-._]*\/)?[a-z0-9-][a-z0-9-._]*$/;
|
|
16
|
+
async function loadStackMembers(db, servers, tenantId = 0) {
|
|
17
|
+
const out = [];
|
|
18
|
+
for (const s of servers) {
|
|
19
|
+
if (s.tools) {
|
|
20
|
+
out.push({
|
|
21
|
+
server: s.server,
|
|
22
|
+
version: s.version,
|
|
23
|
+
tools: s.tools.map((t) => ({ name: t.name, annotations: resolveAnnotations(t.annotations) }))
|
|
24
|
+
});
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
const [{ enqueueSurface }, { loadStored, rowsToSurface }] = await Promise.all([
|
|
28
|
+
import("./surface-YL6O67W3.js"),
|
|
29
|
+
import("./surfaceHandlers-X2IZKA3E.js")
|
|
30
|
+
]);
|
|
31
|
+
const stored = await loadStored(db, s.server, s.version, tenantId, "mcp_server");
|
|
32
|
+
if (!stored || stored.rows.length === 0 || stored.verdict === "verified_false") {
|
|
33
|
+
if (stored?.verdict !== "verified_false" && NPM_NAME.test(s.server)) {
|
|
34
|
+
await enqueueSurface(db, s.server, s.version, "mcp_server").catch(() => {
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
out.push({ server: s.server, version: s.version, tools: null });
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const surface = rowsToSurface(s.server, s.version, stored.rows, MCP_TIER);
|
|
41
|
+
out.push({
|
|
42
|
+
server: s.server,
|
|
43
|
+
version: s.version,
|
|
44
|
+
// A symbol written by an older extractor has no contract; the spec
|
|
45
|
+
// defaults (writes, destroys) are the honest reading of "we don't know".
|
|
46
|
+
tools: surface.symbols.map((sym) => ({
|
|
47
|
+
name: sym.path,
|
|
48
|
+
annotations: contractOf(sym)?.annotations ?? resolveAnnotations(null)
|
|
49
|
+
}))
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
return out;
|
|
53
|
+
}
|
|
54
|
+
function analyzeStack(inputs) {
|
|
55
|
+
const members = [];
|
|
56
|
+
const unread = [];
|
|
57
|
+
const byTool = /* @__PURE__ */ new Map();
|
|
58
|
+
for (const m of inputs) {
|
|
59
|
+
if (!m.tools) {
|
|
60
|
+
unread.push(m.server);
|
|
61
|
+
members.push({ server: m.server, version: m.version, tools: null, writes: 0, destroys: 0 });
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
let writes = 0;
|
|
65
|
+
let destroys = 0;
|
|
66
|
+
for (const t of m.tools) {
|
|
67
|
+
const canWrite = !t.annotations.readOnlyHint;
|
|
68
|
+
if (canWrite) writes++;
|
|
69
|
+
if (t.annotations.destructiveHint) destroys++;
|
|
70
|
+
const seen = byTool.get(t.name);
|
|
71
|
+
if (seen) {
|
|
72
|
+
if (!seen.servers.includes(m.server)) seen.servers.push(m.server);
|
|
73
|
+
seen.writes ||= canWrite;
|
|
74
|
+
} else {
|
|
75
|
+
byTool.set(t.name, { servers: [m.server], writes: canWrite });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
members.push({ server: m.server, version: m.version, tools: m.tools.length, writes, destroys });
|
|
79
|
+
}
|
|
80
|
+
const collisions = [...byTool.entries()].filter(([, v]) => v.servers.length > 1).map(([tool, v]) => ({ tool, servers: v.servers, writes: v.writes })).sort((a, b) => a.tool < b.tool ? -1 : 1);
|
|
81
|
+
const readMembers = members.filter((m) => m.tools !== null);
|
|
82
|
+
const totalTools = unread.length ? null : readMembers.reduce((n, m) => n + (m.tools ?? 0), 0);
|
|
83
|
+
const overall = collisions.length > 0 ? "conflict" : unread.length > 0 ? "unknown" : "compatible";
|
|
84
|
+
const note = collisions.length > 0 ? `${collisions.length} tool name(s) are exposed by more than one server; the agent has no way to say which one it means` : unread.length > 0 ? `${unread.length} server(s) have not been probed, so the namespace was only partly assembled \u2014 this is NOT a clean bill` : `${totalTools} tool(s) across ${readMembers.length} server(s), no name collisions`;
|
|
85
|
+
return {
|
|
86
|
+
overall,
|
|
87
|
+
members,
|
|
88
|
+
collisions,
|
|
89
|
+
totalTools,
|
|
90
|
+
estimatedContextTokens: totalTools === null ? null : totalTools * TOKENS_PER_TOOL,
|
|
91
|
+
unread,
|
|
92
|
+
note
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
async function checkMcpStack(db, servers, tenantId = 0) {
|
|
96
|
+
return analyzeStack(await loadStackMembers(db, servers, tenantId));
|
|
97
|
+
}
|
|
98
|
+
function isCrowded(report) {
|
|
99
|
+
return (report.totalTools ?? 0) >= CROWDED_TOOL_COUNT;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export {
|
|
103
|
+
TOKENS_PER_TOOL,
|
|
104
|
+
CROWDED_TOOL_COUNT,
|
|
105
|
+
loadStackMembers,
|
|
106
|
+
analyzeStack,
|
|
107
|
+
checkMcpStack,
|
|
108
|
+
isCrowded
|
|
109
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { createRequire as __lurqCreateRequire } from 'node:module'; const require = __lurqCreateRequire(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
init_esm_shims
|
|
4
|
+
} from "./chunk-I3M2KJXB.js";
|
|
5
|
+
|
|
6
|
+
// src/core/types.ts
|
|
7
|
+
init_esm_shims();
|
|
8
|
+
var DEFAULT_ECOSYSTEM = "npm";
|
|
9
|
+
var CATEGORIES = [
|
|
10
|
+
"framework",
|
|
11
|
+
"meta-framework",
|
|
12
|
+
"state-management",
|
|
13
|
+
"routing",
|
|
14
|
+
"orm",
|
|
15
|
+
"database-client",
|
|
16
|
+
"ui-component-library",
|
|
17
|
+
"styling",
|
|
18
|
+
"forms",
|
|
19
|
+
"validation",
|
|
20
|
+
"data-fetching",
|
|
21
|
+
"http-client",
|
|
22
|
+
"auth",
|
|
23
|
+
"testing",
|
|
24
|
+
"build-tool",
|
|
25
|
+
"bundler",
|
|
26
|
+
"linting",
|
|
27
|
+
"date-time",
|
|
28
|
+
"animation",
|
|
29
|
+
"charts",
|
|
30
|
+
"i18n",
|
|
31
|
+
"utility",
|
|
32
|
+
"other"
|
|
33
|
+
];
|
|
34
|
+
function isCategory(value) {
|
|
35
|
+
return CATEGORIES.includes(value);
|
|
36
|
+
}
|
|
37
|
+
var FRONTEND_CATEGORIES = [
|
|
38
|
+
"ui-component-library",
|
|
39
|
+
"styling",
|
|
40
|
+
"state-management",
|
|
41
|
+
"forms",
|
|
42
|
+
"validation",
|
|
43
|
+
"data-fetching",
|
|
44
|
+
"charts",
|
|
45
|
+
"animation",
|
|
46
|
+
"date-time",
|
|
47
|
+
"utility"
|
|
48
|
+
];
|
|
49
|
+
function isFrontendCategory(category) {
|
|
50
|
+
return category != null && FRONTEND_CATEGORIES.includes(category);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export {
|
|
54
|
+
DEFAULT_ECOSYSTEM,
|
|
55
|
+
CATEGORIES,
|
|
56
|
+
isCategory,
|
|
57
|
+
isFrontendCategory
|
|
58
|
+
};
|