lurqrun 0.1.6 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (239) hide show
  1. package/README.md +311 -139
  2. package/dist/auditHandler-VWCXSDVO.js +24 -0
  3. package/dist/autopilotInit-Q62MX5M6.js +178 -0
  4. package/dist/bin/lurq.js +3255 -23319
  5. package/dist/browserAuth-J7APSD4P.js +96 -0
  6. package/dist/cache-AEAKCV4I.js +12 -0
  7. package/dist/check-2JTQDQA6.js +2613 -0
  8. package/dist/check-PQSQ2MMA.js +192 -0
  9. package/dist/checkUpgrade-5YW77OBL.js +24 -0
  10. package/dist/chunk-2GAYNIHG.js +40 -0
  11. package/dist/chunk-2O6Z37IT.js +423 -0
  12. package/dist/chunk-2WD4QAH4.js +116 -0
  13. package/dist/chunk-3676XOBU.js +2235 -0
  14. package/dist/chunk-3D2MPGOQ.js +181 -0
  15. package/dist/chunk-3GHJR7XR.js +2049 -0
  16. package/dist/chunk-4ESO33J6.js +34 -0
  17. package/dist/chunk-4JHDSVIN.js +1229 -0
  18. package/dist/chunk-4KCPOMG6.js +68 -0
  19. package/dist/chunk-53SG6XWA.js +141 -0
  20. package/dist/chunk-5ABNDB3I.js +37 -0
  21. package/dist/chunk-5ALIJFDA.js +114 -0
  22. package/dist/chunk-63MUJNZL.js +318 -0
  23. package/dist/chunk-64ZF7KO4.js +1571 -0
  24. package/dist/chunk-6DM6UQFD.js +318 -0
  25. package/dist/chunk-6EQ3ARXA.js +304 -0
  26. package/dist/chunk-6L67QMCP.js +92 -0
  27. package/dist/chunk-72Y2FDVS.js +172 -0
  28. package/dist/chunk-A3TYI2TF.js +288 -0
  29. package/dist/chunk-B2UUYRBX.js +536 -0
  30. package/dist/chunk-BJT2LHAR.js +320 -0
  31. package/dist/chunk-BM3LF5T6.js +132 -0
  32. package/dist/chunk-BN3NM47Q.js +214 -0
  33. package/dist/chunk-BUALU27S.js +123 -0
  34. package/dist/chunk-C6GLE626.js +160 -0
  35. package/dist/chunk-D3K6IZVR.js +12845 -0
  36. package/dist/chunk-D3VTZS7V.js +222 -0
  37. package/dist/chunk-DM37MTOO.js +636 -0
  38. package/dist/chunk-DONTW3NE.js +171 -0
  39. package/dist/chunk-DSROTZ3Z.js +38 -0
  40. package/dist/chunk-ELGCAM7P.js +184 -0
  41. package/dist/chunk-EQCXENKZ.js +130 -0
  42. package/dist/chunk-EYAKM6YN.js +96 -0
  43. package/dist/chunk-FDFPMBCA.js +112 -0
  44. package/dist/chunk-FVYFV5HA.js +154 -0
  45. package/dist/chunk-GE37NVLN.js +15 -0
  46. package/dist/chunk-GM5Y555O.js +143 -0
  47. package/dist/chunk-GRJXQBQZ.js +793 -0
  48. package/dist/chunk-HWFX4BC4.js +177 -0
  49. package/dist/chunk-I3M2KJXB.js +56 -0
  50. package/dist/chunk-IFNONGRQ.js +603 -0
  51. package/dist/chunk-J3U2CPSK.js +136 -0
  52. package/dist/chunk-J6XQ7OPZ.js +74 -0
  53. package/dist/chunk-KMV4Y6R3.js +127 -0
  54. package/dist/chunk-KP5ZDDPE.js +340 -0
  55. package/dist/chunk-KYKSZ3XZ.js +202 -0
  56. package/dist/chunk-LEGPB7DO.js +81 -0
  57. package/dist/chunk-LUAKDHLZ.js +40 -0
  58. package/dist/chunk-MHUEVBVO.js +35 -0
  59. package/dist/chunk-MYGSLK44.js +37 -0
  60. package/dist/chunk-NEZNAPPN.js +41 -0
  61. package/dist/chunk-NPVN2THM.js +197 -0
  62. package/dist/chunk-NYZU5NCW.js +23 -0
  63. package/dist/chunk-O4KHCV6I.js +320 -0
  64. package/dist/chunk-ONDVTXUO.js +240 -0
  65. package/dist/chunk-OTJFHESY.js +176 -0
  66. package/dist/chunk-P6MIKSPY.js +243 -0
  67. package/dist/chunk-Q5VPF4WI.js +26 -0
  68. package/dist/chunk-QCRBX3LV.js +165 -0
  69. package/dist/chunk-QKGYDMWB.js +7409 -0
  70. package/dist/chunk-QSL6QN5X.js +115 -0
  71. package/dist/chunk-QUIMQ7YL.js +73 -0
  72. package/dist/chunk-RG3YLU6E.js +49 -0
  73. package/dist/chunk-SHCEBP3J.js +107 -0
  74. package/dist/chunk-SHZFOSXC.js +462 -0
  75. package/dist/chunk-SOXFMMXC.js +151 -0
  76. package/dist/chunk-TDTYV6QI.js +119 -0
  77. package/dist/chunk-TNTXWSYA.js +19 -0
  78. package/dist/chunk-TUXQ32WE.js +2251 -0
  79. package/dist/chunk-VGCDRK3S.js +37 -0
  80. package/dist/chunk-VLRHFMGJ.js +58 -0
  81. package/dist/chunk-WHX2ZFCG.js +4048 -0
  82. package/dist/chunk-X4W64MHU.js +2005 -0
  83. package/dist/chunk-XBQA4I36.js +92 -0
  84. package/dist/chunk-XGOV6KV4.js +413 -0
  85. package/dist/chunk-XIFQIFSZ.js +940 -0
  86. package/dist/chunk-XLXPVN3Y.js +15393 -0
  87. package/dist/chunk-ZJMLQ37G.js +90 -0
  88. package/dist/client-MU4EUDZV.js +15 -0
  89. package/dist/commands-UOOTMBKA.js +1023 -0
  90. package/dist/compat-Q5DVPITN.js +201 -0
  91. package/dist/constants-DJFOFZ44.js +24 -0
  92. package/dist/dashboardIssue-KBKLQMGC.js +274 -0
  93. package/dist/diff-I4SNUHU3.js +13 -0
  94. package/dist/dist-4XXIXHHZ.js +12945 -0
  95. package/dist/domains-VRT4BRVZ.js +14 -0
  96. package/dist/e2b-FVPHDULZ.js +172 -0
  97. package/dist/env-UN6KGZ7S.js +15 -0
  98. package/dist/envCheck-KUPTORFQ.js +71 -0
  99. package/dist/extract-6WG265BL.js +455 -0
  100. package/dist/fetch-PJ3SC32M.js +22 -0
  101. package/dist/firstTouch-COJCMOB3.js +55 -0
  102. package/dist/fix-3EDWNKW3.js +220 -0
  103. package/dist/handlers-4Q37WIFT.js +55 -0
  104. package/dist/hook-NA66XJLC.js +412 -0
  105. package/dist/http-25GJ6SKK.js +6928 -0
  106. package/dist/ingestQueue-FSVJC3AA.js +37 -0
  107. package/dist/install-G2HHXYJ2.js +20 -0
  108. package/dist/installSkill-5TIMYIJZ.js +70 -0
  109. package/dist/inventory-5AWSLJYU.js +31 -0
  110. package/dist/local-XPZMRH5R.js +13 -0
  111. package/dist/manifest-ENMIC54B.js +13 -0
  112. package/dist/mcp-PENLL2PK.js +500 -0
  113. package/dist/mcpConfig-3LYPRY5Q.js +80 -0
  114. package/dist/mcpHandlers-PO7GENA7.js +22 -0
  115. package/dist/mcpScan-ULP626TQ.js +1735 -0
  116. package/dist/mcpScanWorkflow-27O44FOE.js +98 -0
  117. package/dist/mcpStack-ZNYIFXZ5.js +20 -0
  118. package/dist/model-ASMZWLM7.js +214 -0
  119. package/dist/packages-RWF2LXST.js +40 -0
  120. package/dist/plan-SDILIDZT.js +465 -0
  121. package/dist/planView-ZAKRTGVS.js +82 -0
  122. package/dist/policy-JAGNMQJI.js +115 -0
  123. package/dist/probe-3IK646WN.js +610 -0
  124. package/dist/publicUpgrades-WTBGLDIK.js +42 -0
  125. package/dist/references-I6Y4BSRR.js +16 -0
  126. package/dist/release-7IU2R4YG.js +128 -0
  127. package/dist/remote-FN6225AH.js +42 -0
  128. package/dist/rename-OS6JYBZQ.js +204 -0
  129. package/dist/reportRuns-O4X7ZFTX.js +77 -0
  130. package/dist/safeFetch-COERXGFN.js +19 -0
  131. package/dist/sandbox-SAPSRKEI.js +39 -0
  132. package/dist/sarif-WLVJFJ7U.js +125 -0
  133. package/dist/server-OLTY3NYT.js +59 -0
  134. package/dist/surface-7MHE3RL7.js +143 -0
  135. package/dist/surface-JS2XU3U3.js +38 -0
  136. package/dist/surfaceHandlers-4B6TMZ34.js +20 -0
  137. package/dist/typecheck-NGOKNAJY.js +282 -0
  138. package/dist/types-M5JP2W3D.js +10 -0
  139. package/dist/uninstall-I2IJP5JZ.js +200 -0
  140. package/dist/upgrade-FS26DJGG.js +650 -0
  141. package/dist/upgradePlan-ZS5IOVMZ.js +181 -0
  142. package/dist/upkeepHandler-VFGYXJKB.js +13 -0
  143. package/dist/userConfig-ICASHI47.js +20 -0
  144. package/dist/watch-OCNEUXG6.js +204 -0
  145. package/package.json +26 -20
  146. package/templates/agent-rules.md +4 -4
  147. package/templates/skill-instructions.md +35 -17
  148. package/dist/bin/lurq.js.map +0 -1
  149. package/dist/index.d.ts +0 -805
  150. package/dist/index.js +0 -23570
  151. package/dist/index.js.map +0 -1
  152. package/drizzle/0000_confused_fabian_cortez.sql +0 -54
  153. package/drizzle/0001_aspiring_timeslip.sql +0 -15
  154. package/drizzle/0002_lyrical_lila_cheney.sql +0 -14
  155. package/drizzle/0003_violet_giant_girl.sql +0 -14
  156. package/drizzle/0004_slim_valeria_richards.sql +0 -15
  157. package/drizzle/0005_petite_luke_cage.sql +0 -12
  158. package/drizzle/0006_calm_nighthawk.sql +0 -3
  159. package/drizzle/0007_slimy_naoko.sql +0 -1
  160. package/drizzle/0008_peaceful_tigra.sql +0 -1
  161. package/drizzle/0009_smooth_harrier.sql +0 -10
  162. package/drizzle/0010_curved_amphibian.sql +0 -2
  163. package/drizzle/0011_compat_provenance_closures.sql +0 -11
  164. package/drizzle/0012_api_surfaces.sql +0 -9
  165. package/drizzle/0013_nice_thanos.sql +0 -1
  166. package/drizzle/0014_luxuriant_malcolm_colcord.sql +0 -10
  167. package/drizzle/0015_past_fat_cobra.sql +0 -11
  168. package/drizzle/0016_futuristic_junta.sql +0 -50
  169. package/drizzle/0017_absent_grandmaster.sql +0 -20
  170. package/drizzle/0018_rainy_proemial_gods.sql +0 -1
  171. package/drizzle/0019_dashing_korvac.sql +0 -11
  172. package/drizzle/0020_normal_vulture.sql +0 -3
  173. package/drizzle/0021_dazzling_blue_shield.sql +0 -17
  174. package/drizzle/0022_massive_ghost_rider.sql +0 -24
  175. package/drizzle/0023_next_shatterstar.sql +0 -1
  176. package/drizzle/0024_dusty_maelstrom.sql +0 -15
  177. package/drizzle/0025_gifted_harpoon.sql +0 -5
  178. package/drizzle/0026_awesome_mordo.sql +0 -1
  179. package/drizzle/0027_charming_ulik.sql +0 -2
  180. package/drizzle/0028_romantic_loners.sql +0 -2
  181. package/drizzle/0029_busy_korvac.sql +0 -16
  182. package/drizzle/0030_steady_mathemanic.sql +0 -6
  183. package/drizzle/0031_redundant_maginty.sql +0 -2
  184. package/drizzle/0032_stack_resolutions.sql +0 -14
  185. package/drizzle/0033_graceful_tyger_tiger.sql +0 -3
  186. package/drizzle/0034_uneven_thunderbird.sql +0 -1
  187. package/drizzle/0035_api_key_scopes.sql +0 -1
  188. package/drizzle/0036_policy_audit.sql +0 -21
  189. package/drizzle/0037_subscription_seats.sql +0 -1
  190. package/drizzle/0038_symbol_offsets.sql +0 -2
  191. package/drizzle/0039_mcp_runtime_scans.sql +0 -80
  192. package/drizzle/0040_subscription_overage_and_interval.sql +0 -4
  193. package/drizzle/0041_account_email.sql +0 -38
  194. package/drizzle/0042_alert_channels.sql +0 -21
  195. package/drizzle/meta/0000_snapshot.json +0 -370
  196. package/drizzle/meta/0001_snapshot.json +0 -482
  197. package/drizzle/meta/0002_snapshot.json +0 -575
  198. package/drizzle/meta/0003_snapshot.json +0 -667
  199. package/drizzle/meta/0004_snapshot.json +0 -769
  200. package/drizzle/meta/0005_snapshot.json +0 -864
  201. package/drizzle/meta/0006_snapshot.json +0 -882
  202. package/drizzle/meta/0007_snapshot.json +0 -876
  203. package/drizzle/meta/0008_snapshot.json +0 -882
  204. package/drizzle/meta/0009_snapshot.json +0 -948
  205. package/drizzle/meta/0010_snapshot.json +0 -969
  206. package/drizzle/meta/0011_snapshot.json +0 -1049
  207. package/drizzle/meta/0012_snapshot.json +0 -1115
  208. package/drizzle/meta/0013_snapshot.json +0 -1121
  209. package/drizzle/meta/0014_snapshot.json +0 -1190
  210. package/drizzle/meta/0015_snapshot.json +0 -1280
  211. package/drizzle/meta/0016_snapshot.json +0 -1683
  212. package/drizzle/meta/0017_snapshot.json +0 -1834
  213. package/drizzle/meta/0018_snapshot.json +0 -1834
  214. package/drizzle/meta/0019_snapshot.json +0 -1909
  215. package/drizzle/meta/0020_snapshot.json +0 -1921
  216. package/drizzle/meta/0021_snapshot.json +0 -2045
  217. package/drizzle/meta/0022_snapshot.json +0 -2246
  218. package/drizzle/meta/0023_snapshot.json +0 -2252
  219. package/drizzle/meta/0024_snapshot.json +0 -2375
  220. package/drizzle/meta/0025_snapshot.json +0 -2407
  221. package/drizzle/meta/0026_snapshot.json +0 -2413
  222. package/drizzle/meta/0027_snapshot.json +0 -2425
  223. package/drizzle/meta/0028_snapshot.json +0 -2438
  224. package/drizzle/meta/0029_snapshot.json +0 -2546
  225. package/drizzle/meta/0030_snapshot.json +0 -2586
  226. package/drizzle/meta/0031_snapshot.json +0 -2598
  227. package/drizzle/meta/0032_snapshot.json +0 -2699
  228. package/drizzle/meta/0033_snapshot.json +0 -2712
  229. package/drizzle/meta/0034_snapshot.json +0 -2719
  230. package/drizzle/meta/0035_snapshot.json +0 -2726
  231. package/drizzle/meta/0036_snapshot.json +0 -2876
  232. package/drizzle/meta/0037_snapshot.json +0 -2883
  233. package/drizzle/meta/0038_snapshot.json +0 -2895
  234. package/drizzle/meta/0039_snapshot.json +0 -3501
  235. package/drizzle/meta/0040_snapshot.json +0 -3527
  236. package/drizzle/meta/0041_snapshot.json +0 -3824
  237. package/drizzle/meta/0042_snapshot.json +0 -3960
  238. package/drizzle/meta/_journal.json +0 -307
  239. package/src/data/seed.json +0 -253
@@ -0,0 +1,636 @@
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
+ readUserConfig,
11
+ resolveApiKey,
12
+ writeUserConfig
13
+ } from "./chunk-J6XQ7OPZ.js";
14
+ import {
15
+ DEFAULT_ENDPOINT,
16
+ PACKAGE_NAME
17
+ } from "./chunk-72Y2FDVS.js";
18
+ import {
19
+ init_esm_shims
20
+ } from "./chunk-I3M2KJXB.js";
21
+
22
+ // src/cli/installSkill.ts
23
+ init_esm_shims();
24
+ import {
25
+ accessSync,
26
+ chmodSync,
27
+ constants,
28
+ existsSync,
29
+ mkdirSync,
30
+ readFileSync,
31
+ realpathSync,
32
+ renameSync,
33
+ rmSync,
34
+ statSync,
35
+ writeFileSync
36
+ } from "fs";
37
+ import { copyFileSync } from "fs";
38
+ import { homedir } from "os";
39
+ import { delimiter, dirname, join } from "path";
40
+ var ENV_KEYS = [
41
+ "DATABASE_URL",
42
+ "GITHUB_TOKEN",
43
+ "EMBEDDING_PROVIDER",
44
+ "EMBEDDING_API_KEY",
45
+ "EMBEDDING_MODEL",
46
+ "SUMMARY_PROVIDER",
47
+ "SUMMARY_API_KEY",
48
+ "SUMMARY_MODEL"
49
+ ];
50
+ function home(...p) {
51
+ return join(homedir(), ...p);
52
+ }
53
+ function vscodeUserDir() {
54
+ if (process.platform === "darwin") return home("Library", "Application Support", "Code", "User");
55
+ if (process.platform === "win32") {
56
+ return join(process.env.APPDATA ?? home("AppData", "Roaming"), "Code", "User");
57
+ }
58
+ return join(process.env.XDG_CONFIG_HOME || home(".config"), "Code", "User");
59
+ }
60
+ function agentSpecs() {
61
+ return [
62
+ {
63
+ id: "claude-code",
64
+ label: "Claude Code",
65
+ format: "mcpServers",
66
+ path: home(".claude.json"),
67
+ detected: existsSync(home(".claude.json")) || existsSync(home(".claude")),
68
+ // Personal skills are read from ~/.claude/skills/<name>/SKILL.md and apply
69
+ // across every project, which is exactly the scope a global install wants.
70
+ instructions: { kind: "skill", path: home(".claude", "skills", "lurq", "SKILL.md") }
71
+ },
72
+ {
73
+ // No instructions target: Cursor's user-level rules live in its settings
74
+ // UI, not a file on disk, and its `.cursor/rules/*.mdc` files are
75
+ // per-project, so there is nothing a machine-wide install can write once.
76
+ // The MCP tool descriptions carry the guidance here.
77
+ id: "cursor",
78
+ label: "Cursor",
79
+ format: "mcpServers",
80
+ path: home(".cursor", "mcp.json"),
81
+ detected: existsSync(home(".cursor"))
82
+ },
83
+ {
84
+ id: "windsurf",
85
+ label: "Windsurf",
86
+ format: "mcpServers",
87
+ path: home(".codeium", "windsurf", "mcp_config.json"),
88
+ detected: existsSync(home(".codeium")),
89
+ instructions: {
90
+ kind: "shared",
91
+ path: home(".codeium", "windsurf", "memories", "global_rules.md")
92
+ }
93
+ },
94
+ {
95
+ // No instructions target, for two reasons that compound. A
96
+ // `.instructions.md` file is only applied automatically when its
97
+ // frontmatter carries an `applyTo` glob; without one, VS Code requires the
98
+ // user to attach it to a request by hand. And the user-profile directory
99
+ // that gets scanned is not a documented default at all: the workspace
100
+ // default is `.github/instructions`, and profile-level folders come from
101
+ // the `chat.instructionsFilesLocations` setting. Guessing the path would
102
+ // write a file nobody reads while reporting success, which is the same
103
+ // mistake the hardcoded macOS config path made above.
104
+ id: "copilot",
105
+ label: "VS Code / GitHub Copilot",
106
+ format: "servers",
107
+ path: join(vscodeUserDir(), "mcp.json"),
108
+ detected: existsSync(vscodeUserDir())
109
+ },
110
+ {
111
+ id: "codex",
112
+ label: "OpenAI Codex CLI",
113
+ format: "toml",
114
+ path: home(".codex", "config.toml"),
115
+ detected: existsSync(home(".codex")),
116
+ instructions: { kind: "shared", path: home(".codex", "AGENTS.md") }
117
+ },
118
+ {
119
+ id: "gemini-cli",
120
+ label: "Gemini CLI",
121
+ format: "mcpServers",
122
+ path: home(".gemini", "settings.json"),
123
+ detected: existsSync(home(".gemini", "settings.json")),
124
+ // ~/.gemini/GEMINI.md is the global tier of the context hierarchy, loaded
125
+ // ahead of any workspace file.
126
+ instructions: { kind: "shared", path: home(".gemini", "GEMINI.md") }
127
+ },
128
+ {
129
+ // Shares the ~/.gemini root with the Gemini CLI but keeps its own file, so
130
+ // detection has to look at the file rather than at the directory. It reads
131
+ // the same global GEMINI.md; upserting the block twice is a no-op, so
132
+ // having both selected is harmless.
133
+ id: "antigravity",
134
+ label: "Google Antigravity",
135
+ format: "mcpServers",
136
+ path: home(".gemini", "config", "mcp_config.json"),
137
+ detected: existsSync(home(".gemini", "config")),
138
+ instructions: { kind: "shared", path: home(".gemini", "GEMINI.md") }
139
+ },
140
+ {
141
+ id: "kiro",
142
+ label: "Kiro",
143
+ format: "mcpServers",
144
+ path: home(".kiro", "settings", "mcp.json"),
145
+ detected: existsSync(home(".kiro")),
146
+ // Steering files in ~/.kiro/steering apply to every workspace and are
147
+ // always included.
148
+ instructions: { kind: "file", path: home(".kiro", "steering", "lurq.md") }
149
+ }
150
+ ];
151
+ }
152
+ var SUPPORTED_AGENTS = [
153
+ "claude-code",
154
+ "cursor",
155
+ "windsurf",
156
+ "copilot",
157
+ "codex",
158
+ "gemini-cli",
159
+ "antigravity",
160
+ "kiro"
161
+ ];
162
+ function collectEnv() {
163
+ const env = {};
164
+ for (const key of ENV_KEYS) {
165
+ const value = process.env[key];
166
+ if (value) env[key] = value;
167
+ }
168
+ return env;
169
+ }
170
+ function readJsonObject(path) {
171
+ if (!existsSync(path)) return {};
172
+ const text = readFileSync(path, "utf8").trim();
173
+ if (!text) return {};
174
+ try {
175
+ return JSON.parse(text);
176
+ } catch (err) {
177
+ throw new Error(
178
+ `${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.`
179
+ );
180
+ }
181
+ }
182
+ var KEY_FILE_MODE = 384;
183
+ function writeFileAtomic(path, contents, newFileMode = KEY_FILE_MODE) {
184
+ let target = path;
185
+ let mode = newFileMode;
186
+ try {
187
+ target = realpathSync(path);
188
+ mode = statSync(target).mode & 511;
189
+ } catch {
190
+ }
191
+ mkdirSync(dirname(target), { recursive: true });
192
+ const tmp = `${target}.lurq-${process.pid}.tmp`;
193
+ try {
194
+ writeFileSync(tmp, contents, { encoding: "utf8", mode });
195
+ chmodSync(tmp, mode);
196
+ renameSync(tmp, target);
197
+ } catch (err) {
198
+ try {
199
+ rmSync(tmp, { force: true });
200
+ } catch {
201
+ }
202
+ throw err;
203
+ }
204
+ }
205
+ function writeJson(path, obj) {
206
+ writeFileAtomic(path, JSON.stringify(obj, null, 2) + "\n");
207
+ }
208
+ function buildServerEntry(env, withType) {
209
+ const entry = { command: "npx", args: ["-y", PACKAGE_NAME, "serve"] };
210
+ if (withType) entry.type = "stdio";
211
+ if (Object.keys(env).length) entry.env = env;
212
+ return entry;
213
+ }
214
+ function buildRemoteServerEntry(agentId, opts) {
215
+ const headers = { Authorization: `Bearer ${opts.apiKey}`, "X-Lurq-Client": agentId };
216
+ switch (agentId) {
217
+ case "cursor":
218
+ case "kiro":
219
+ return { url: opts.url, headers };
220
+ case "windsurf":
221
+ case "antigravity":
222
+ return { serverUrl: opts.url, headers };
223
+ case "gemini-cli":
224
+ return { httpUrl: opts.url, headers };
225
+ case "claude-code":
226
+ case "copilot":
227
+ default:
228
+ return { type: "http", url: opts.url, headers };
229
+ }
230
+ }
231
+ function buildTomlBlock(env) {
232
+ const lines = [
233
+ "[mcp_servers.lurq]",
234
+ 'command = "npx"',
235
+ `args = ["-y", "${PACKAGE_NAME}", "serve"]`
236
+ ];
237
+ if (Object.keys(env).length) {
238
+ lines.push("", "[mcp_servers.lurq.env]");
239
+ for (const [k, v] of Object.entries(env)) lines.push(`${k} = ${JSON.stringify(v)}`);
240
+ }
241
+ return lines.join("\n") + "\n";
242
+ }
243
+ function buildRemoteTomlBlock(opts) {
244
+ return [
245
+ "[mcp_servers.lurq]",
246
+ `url = ${JSON.stringify(opts.url)}`,
247
+ `http_headers = { Authorization = ${JSON.stringify(`Bearer ${opts.apiKey}`)}, X-Lurq-Client = "codex" }`
248
+ ].join("\n") + "\n";
249
+ }
250
+ function installJsonEntry(spec, entry) {
251
+ const key = spec.format === "servers" ? "servers" : "mcpServers";
252
+ const config = readJsonObject(spec.path);
253
+ if (typeof config[key] !== "object" || config[key] === null) config[key] = {};
254
+ config[key].lurq = entry;
255
+ writeJson(spec.path, config);
256
+ return { agent: spec.id, path: spec.path, status: "installed" };
257
+ }
258
+ function stripTomlBlock(text, header = "[mcp_servers.lurq]") {
259
+ const lines = text.split("\n");
260
+ const start = lines.findIndex((l) => l.trim() === header);
261
+ if (start === -1) return text;
262
+ let end = start + 1;
263
+ while (end < lines.length) {
264
+ const t = lines[end].trim();
265
+ if (t.startsWith("[") && !t.startsWith(`${header.slice(0, -1)}.`)) break;
266
+ end++;
267
+ }
268
+ return [...lines.slice(0, start), ...lines.slice(end)].join("\n").replace(/\n{3,}/g, "\n\n");
269
+ }
270
+ function installTomlBlock(spec, block) {
271
+ const current = existsSync(spec.path) ? readFileSync(spec.path, "utf8") : "";
272
+ const existing = stripTomlBlock(current).replace(/\s+$/, "");
273
+ const sep = existing ? "\n\n" : "";
274
+ writeFileAtomic(spec.path, existing + sep + block);
275
+ return {
276
+ agent: spec.id,
277
+ path: spec.path,
278
+ status: "installed",
279
+ message: current.includes("[mcp_servers.lurq]") ? "replaced the existing lurq entry" : void 0
280
+ };
281
+ }
282
+ var BLOCK_START = "<!-- lurq:start -->";
283
+ var BLOCK_END = "<!-- lurq:end -->";
284
+ 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.";
285
+ function template(name) {
286
+ const src = join(packageRoot(), "templates", name);
287
+ return existsSync(src) ? readFileSync(src, "utf8") : null;
288
+ }
289
+ function upsertMarkedBlock(text, block) {
290
+ const wrapped = `${BLOCK_START}
291
+ ${block.trim()}
292
+ ${BLOCK_END}`;
293
+ const start = text.indexOf(BLOCK_START);
294
+ if (start === -1) {
295
+ const existing = text.replace(/\s+$/, "");
296
+ return existing ? `${existing}
297
+
298
+ ${wrapped}
299
+ ` : `${wrapped}
300
+ `;
301
+ }
302
+ const endAt = text.indexOf(BLOCK_END, start);
303
+ const after = endAt === -1 ? "" : text.slice(endAt + BLOCK_END.length);
304
+ return `${text.slice(0, start)}${wrapped}${after.replace(/^\n*/, "\n")}`;
305
+ }
306
+ function removeMarkedBlock(text) {
307
+ const start = text.indexOf(BLOCK_START);
308
+ if (start === -1) return text;
309
+ const endAt = text.indexOf(BLOCK_END, start);
310
+ const before = text.slice(0, start).replace(/\s+$/, "");
311
+ const after = endAt === -1 ? "" : text.slice(endAt + BLOCK_END.length).replace(/^\s+/, "");
312
+ const joined = [before, after].filter(Boolean).join("\n\n");
313
+ return joined ? joined.replace(/\s*$/, "\n") : "";
314
+ }
315
+ function buildSkillFile(guide) {
316
+ return [
317
+ "---",
318
+ "name: lurq",
319
+ `description: ${SKILL_DESCRIPTION}`,
320
+ "---",
321
+ "",
322
+ guide.trim(),
323
+ ""
324
+ ].join("\n");
325
+ }
326
+ function installInstructions(spec) {
327
+ const target = spec.instructions;
328
+ if (!target) return null;
329
+ if (target.kind === "shared") {
330
+ const brief = template("agent-rules.md");
331
+ if (!brief) return null;
332
+ const current = existsSync(target.path) ? readFileSync(target.path, "utf8") : "";
333
+ writeFileAtomic(target.path, upsertMarkedBlock(current, brief), 420);
334
+ return target.path;
335
+ }
336
+ const guide = template("skill-instructions.md");
337
+ if (!guide) return null;
338
+ writeFileAtomic(target.path, target.kind === "skill" ? buildSkillFile(guide) : guide, 420);
339
+ return target.path;
340
+ }
341
+ var HOOK_TARGETS = {
342
+ claude: {
343
+ label: "Claude Code hooks",
344
+ // Hooks live in settings.json; Claude Code's MCP servers are in ~/.claude.json.
345
+ path: () => home(".claude", "settings.json"),
346
+ flat: false,
347
+ hooks: [
348
+ {
349
+ event: "SessionStart",
350
+ matcher: "startup|clear|compact",
351
+ arg: "session-start",
352
+ timeout: 10
353
+ },
354
+ { event: "UserPromptSubmit", arg: "prompt", timeout: 5 },
355
+ {
356
+ event: "PreToolUse",
357
+ matcher: "Bash|Edit|Write|MultiEdit",
358
+ arg: "pre-tool-use",
359
+ timeout: 30
360
+ }
361
+ ]
362
+ },
363
+ codex: {
364
+ label: "Codex hooks",
365
+ path: () => home(".codex", "hooks.json"),
366
+ flat: false,
367
+ hooks: [
368
+ {
369
+ event: "SessionStart",
370
+ matcher: "startup|clear|compact",
371
+ arg: "session-start",
372
+ timeout: 10
373
+ },
374
+ { event: "UserPromptSubmit", arg: "prompt", timeout: 5 },
375
+ { event: "PreToolUse", matcher: "Bash|apply_patch", arg: "pre-tool-use", timeout: 30 }
376
+ ]
377
+ },
378
+ cursor: {
379
+ label: "Cursor hooks",
380
+ path: () => home(".cursor", "hooks.json"),
381
+ flat: true,
382
+ hooks: [
383
+ { event: "sessionStart", arg: "session-start", timeout: 10 },
384
+ { event: "beforeShellExecution", arg: "pre-tool-use", timeout: 30 },
385
+ { event: "postToolUse", matcher: "Shell", arg: "post-tool-use", timeout: 5 }
386
+ ]
387
+ }
388
+ };
389
+ var AGENT_HOOKS = {
390
+ "claude-code": "claude",
391
+ codex: "codex",
392
+ cursor: "cursor"
393
+ };
394
+ var hookAgentFor = (specId) => AGENT_HOOKS[specId] ?? null;
395
+ var hooksPath = (agent) => HOOK_TARGETS[agent].path();
396
+ var hooksLabel = (agent) => HOOK_TARGETS[agent].label;
397
+ var isLurqCommand = (h) => typeof h?.command === "string" && / hook (--agent [a-z]+ )?(session-start|prompt|pre-tool-use|post-tool-use)$/.test(h.command);
398
+ var holdsLurq = (g) => isLurqCommand(g) || Array.isArray(g?.hooks) && g.hooks.some(isLurqCommand);
399
+ var hasLurqHooks = (config) => Object.values(config.hooks ?? {}).some(
400
+ (groups) => Array.isArray(groups) && groups.some(holdsLurq)
401
+ );
402
+ function withoutLurqHooks(config) {
403
+ if (!hasLurqHooks(config)) return config;
404
+ const hooks = {};
405
+ for (const [event, groups] of Object.entries(config.hooks)) {
406
+ if (!Array.isArray(groups)) {
407
+ hooks[event] = groups;
408
+ continue;
409
+ }
410
+ const kept = groups.flatMap((g) => {
411
+ if (isLurqCommand(g)) return [];
412
+ if (!holdsLurq(g)) return [g];
413
+ const rest = g.hooks.filter((h) => !isLurqCommand(h));
414
+ return rest.length ? [{ ...g, hooks: rest }] : [];
415
+ });
416
+ if (kept.length || groups.length === 0) hooks[event] = kept;
417
+ }
418
+ const next = { ...config, hooks };
419
+ if (Object.keys(hooks).length === 0) delete next.hooks;
420
+ return next;
421
+ }
422
+ function withLurqHooks(config, agent, lurq) {
423
+ const target = HOOK_TARGETS[agent];
424
+ const base = withoutLurqHooks(config);
425
+ const hooks = { ...base.hooks };
426
+ const flag = agent === "claude" ? "" : ` --agent ${agent}`;
427
+ for (const h of target.hooks) {
428
+ const command = `${lurq} hook${flag} ${h.arg}`;
429
+ const matcher = h.matcher ? { matcher: h.matcher } : {};
430
+ const entry = target.flat ? { command, ...matcher, timeout: h.timeout } : { ...matcher, hooks: [{ type: "command", command, timeout: h.timeout }] };
431
+ hooks[h.event] = [...Array.isArray(hooks[h.event]) ? hooks[h.event] : [], entry];
432
+ }
433
+ return target.flat ? { version: 1, ...base, hooks } : { ...base, hooks };
434
+ }
435
+ function lurqCommandFor(agent, invocation) {
436
+ if (agent !== "cursor" || !invocation.path) return invocation.command;
437
+ return /\s/.test(invocation.path) ? JSON.stringify(invocation.path) : invocation.path;
438
+ }
439
+ function hasLurqEntry(spec) {
440
+ try {
441
+ if (!existsSync(spec.path)) return false;
442
+ if (spec.format === "toml")
443
+ return readFileSync(spec.path, "utf8").includes("[mcp_servers.lurq]");
444
+ const servers = readJsonObject(spec.path)[spec.format === "servers" ? "servers" : "mcpServers"];
445
+ return !!servers && typeof servers === "object" && Object.hasOwn(servers, "lurq");
446
+ } catch {
447
+ return false;
448
+ }
449
+ }
450
+ function installHooks(agent, path = hooksPath(agent), invocation = lurqInvocation()) {
451
+ if (!invocation.onPath) return null;
452
+ const codexToml = home(".codex", "config.toml");
453
+ if (agent === "codex" && existsSync(codexToml) && /^\s*\[\[?hooks[.\]]/m.test(readFileSync(codexToml, "utf8")))
454
+ return null;
455
+ const lurq = lurqCommandFor(agent, invocation);
456
+ writeJson(path, withLurqHooks(readJsonObject(path), agent, lurq));
457
+ writeUserConfig({ hooks: { ...readUserConfig().hooks, [agent]: { command: lurq } } });
458
+ return path;
459
+ }
460
+ function installAgent(spec, mode) {
461
+ try {
462
+ const result = spec.format === "toml" ? installTomlBlock(
463
+ spec,
464
+ mode.kind === "remote" ? buildRemoteTomlBlock(mode) : buildTomlBlock(mode.env)
465
+ ) : installJsonEntry(
466
+ spec,
467
+ mode.kind === "remote" ? buildRemoteServerEntry(spec.id, mode) : buildServerEntry(mode.env, spec.format === "servers")
468
+ );
469
+ try {
470
+ result.instructionsPath = installInstructions(spec) ?? void 0;
471
+ } catch (err) {
472
+ result.message = `MCP entry written; instructions file failed: ${err instanceof Error ? err.message : String(err)}`;
473
+ }
474
+ const hookAgent = hookAgentFor(spec.id);
475
+ if (hookAgent) {
476
+ try {
477
+ result.hookPath = installHooks(hookAgent) ?? void 0;
478
+ } catch (err) {
479
+ result.message = `MCP entry written; hooks failed: ${err instanceof Error ? err.message : String(err)}`;
480
+ }
481
+ }
482
+ return result;
483
+ } catch (err) {
484
+ return {
485
+ agent: spec.id,
486
+ path: spec.path,
487
+ status: "error",
488
+ message: err instanceof Error ? err.message : String(err)
489
+ };
490
+ }
491
+ }
492
+ function installInstructionsFile() {
493
+ const src = join(packageRoot(), "templates", "skill-instructions.md");
494
+ if (!existsSync(src)) return null;
495
+ const destDir = lurqHome();
496
+ const dest = join(destDir, "skill-instructions.md");
497
+ mkdirSync(destDir, { recursive: true });
498
+ copyFileSync(src, dest);
499
+ return dest;
500
+ }
501
+ function resolveAgents(target) {
502
+ const specs = agentSpecs();
503
+ if (target === "all") return specs.filter((s) => s.detected);
504
+ const spec = specs.find((s) => s.id === target);
505
+ if (!spec) {
506
+ throw new Error(`Unknown agent "${target}". Supported: ${SUPPORTED_AGENTS.join(", ")}, all.`);
507
+ }
508
+ return [spec];
509
+ }
510
+ function lurqInvocation() {
511
+ const candidates = process.platform === "win32" ? [".cmd", ".exe", ""] : [""];
512
+ for (const dir of (process.env.PATH ?? "").split(delimiter)) {
513
+ if (!dir) continue;
514
+ for (const ext of candidates) {
515
+ try {
516
+ const path = join(dir, `lurq${ext}`);
517
+ accessSync(path, constants.X_OK);
518
+ return { command: "lurq", onPath: true, path };
519
+ } catch {
520
+ }
521
+ }
522
+ }
523
+ return { command: `npx ${PACKAGE_NAME}`, onPath: false };
524
+ }
525
+ function printInstallReport(results, instructionsPath, mode) {
526
+ const specs = agentSpecs();
527
+ const short = (p) => p.startsWith(homedir()) ? `~${p.slice(homedir().length)}` : p;
528
+ console.log("lurq MCP server registration:");
529
+ for (const r of results) {
530
+ const spec = specs.find((s) => s.id === r.agent);
531
+ const mark = r.status === "installed" ? "\u2713" : r.status === "skipped" ? "\u2022" : "\u2717";
532
+ console.log(
533
+ ` ${mark} ${spec.label.padEnd(26)} ${short(r.path)}${r.message ? ` (${r.message})` : ""}`
534
+ );
535
+ }
536
+ const withSkills = results.filter((r) => r.instructionsPath);
537
+ if (withSkills.length) {
538
+ console.log("\nAgent instructions (so it reaches for lurq on its own):");
539
+ for (const r of withSkills) {
540
+ const spec = specs.find((s) => s.id === r.agent);
541
+ console.log(` \u2713 ${spec.label.padEnd(26)} ${short(r.instructionsPath)}`);
542
+ }
543
+ }
544
+ const hooked = results.filter((r) => r.hookPath);
545
+ if (hooked.length) {
546
+ console.log("\nHooks (installs verified first, lurq suggested where it helps):");
547
+ for (const r of hooked)
548
+ console.log(
549
+ ` \u2713 ${specs.find((s) => s.id === r.agent).label.padEnd(26)} ${short(r.hookPath)}`
550
+ );
551
+ if (hooked.some((r) => r.agent === "codex"))
552
+ console.log(" \u2022 Codex skips new hooks until you trust them: run /hooks in Codex once.");
553
+ }
554
+ if (instructionsPath) console.log(`
555
+ Full guide: ${short(instructionsPath)}`);
556
+ console.log("\nNext steps:");
557
+ if (mode.kind === "local") {
558
+ console.log(" 1. Ensure DATABASE_URL (and any API keys) are set in the config env above.");
559
+ console.log(" 2. Restart the agent so it picks up the new MCP server.");
560
+ } else {
561
+ console.log(" 1. Restart the agent so it picks up the new MCP server.");
562
+ }
563
+ console.log(" \u2022 Ask it to add a package. It should check the package with lurq first.");
564
+ const { command, onPath } = lurqInvocation();
565
+ console.log(` \u2022 Or use the terminal directly: \`${command} verify zod\`.`);
566
+ if (!onPath) {
567
+ console.log(`
568
+ There is no \`lurq\` command on this machine yet. Everything above`);
569
+ console.log(" worked and your agents are wired up; only the terminal shortcut is");
570
+ console.log(` missing, so use \`npx ${PACKAGE_NAME} <command>\`. To get \`lurq\` itself:`);
571
+ console.log(` npm install -g ${PACKAGE_NAME}`);
572
+ }
573
+ }
574
+ async function runInstallSkill(opts) {
575
+ const selected = resolveAgents(opts.agent ?? "claude-code");
576
+ if (selected.length === 0) {
577
+ console.log("No supported agents detected on this machine.");
578
+ return;
579
+ }
580
+ const remote = !opts.local;
581
+ let mode;
582
+ if (remote) {
583
+ const apiKey = resolveApiKey(opts.apiKey);
584
+ if (!apiKey) {
585
+ throw new Error(
586
+ "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."
587
+ );
588
+ }
589
+ const url = opts.url ?? process.env.LURQ_ENDPOINT ?? DEFAULT_ENDPOINT;
590
+ mode = { kind: "remote", url, apiKey };
591
+ } else {
592
+ const env = collectEnv();
593
+ if (!env.DATABASE_URL) {
594
+ logger.warn(
595
+ "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."
596
+ );
597
+ }
598
+ mode = { kind: "local", env };
599
+ }
600
+ const results = selected.map((spec) => installAgent(spec, mode));
601
+ const instructionsPath = installInstructionsFile();
602
+ printInstallReport(results, instructionsPath, mode);
603
+ }
604
+
605
+ export {
606
+ agentSpecs,
607
+ SUPPORTED_AGENTS,
608
+ readJsonObject,
609
+ writeFileAtomic,
610
+ writeJson,
611
+ buildServerEntry,
612
+ buildRemoteServerEntry,
613
+ buildTomlBlock,
614
+ buildRemoteTomlBlock,
615
+ stripTomlBlock,
616
+ BLOCK_START,
617
+ upsertMarkedBlock,
618
+ removeMarkedBlock,
619
+ buildSkillFile,
620
+ installInstructions,
621
+ hookAgentFor,
622
+ hooksPath,
623
+ hooksLabel,
624
+ hasLurqHooks,
625
+ withoutLurqHooks,
626
+ withLurqHooks,
627
+ lurqCommandFor,
628
+ hasLurqEntry,
629
+ installHooks,
630
+ installAgent,
631
+ installInstructionsFile,
632
+ resolveAgents,
633
+ lurqInvocation,
634
+ printInstallReport,
635
+ runInstallSkill
636
+ };