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.
- package/README.md +311 -139
- package/dist/auditHandler-VWCXSDVO.js +24 -0
- package/dist/autopilotInit-Q62MX5M6.js +178 -0
- package/dist/bin/lurq.js +3255 -23319
- package/dist/browserAuth-J7APSD4P.js +96 -0
- package/dist/cache-AEAKCV4I.js +12 -0
- package/dist/check-2JTQDQA6.js +2613 -0
- package/dist/check-PQSQ2MMA.js +192 -0
- package/dist/checkUpgrade-5YW77OBL.js +24 -0
- package/dist/chunk-2GAYNIHG.js +40 -0
- package/dist/chunk-2O6Z37IT.js +423 -0
- package/dist/chunk-2WD4QAH4.js +116 -0
- package/dist/chunk-3676XOBU.js +2235 -0
- package/dist/chunk-3D2MPGOQ.js +181 -0
- package/dist/chunk-3GHJR7XR.js +2049 -0
- package/dist/chunk-4ESO33J6.js +34 -0
- package/dist/chunk-4JHDSVIN.js +1229 -0
- package/dist/chunk-4KCPOMG6.js +68 -0
- package/dist/chunk-53SG6XWA.js +141 -0
- package/dist/chunk-5ABNDB3I.js +37 -0
- package/dist/chunk-5ALIJFDA.js +114 -0
- package/dist/chunk-63MUJNZL.js +318 -0
- package/dist/chunk-64ZF7KO4.js +1571 -0
- package/dist/chunk-6DM6UQFD.js +318 -0
- package/dist/chunk-6EQ3ARXA.js +304 -0
- package/dist/chunk-6L67QMCP.js +92 -0
- package/dist/chunk-72Y2FDVS.js +172 -0
- package/dist/chunk-A3TYI2TF.js +288 -0
- package/dist/chunk-B2UUYRBX.js +536 -0
- package/dist/chunk-BJT2LHAR.js +320 -0
- package/dist/chunk-BM3LF5T6.js +132 -0
- package/dist/chunk-BN3NM47Q.js +214 -0
- package/dist/chunk-BUALU27S.js +123 -0
- package/dist/chunk-C6GLE626.js +160 -0
- package/dist/chunk-D3K6IZVR.js +12845 -0
- package/dist/chunk-D3VTZS7V.js +222 -0
- package/dist/chunk-DM37MTOO.js +636 -0
- package/dist/chunk-DONTW3NE.js +171 -0
- package/dist/chunk-DSROTZ3Z.js +38 -0
- package/dist/chunk-ELGCAM7P.js +184 -0
- package/dist/chunk-EQCXENKZ.js +130 -0
- package/dist/chunk-EYAKM6YN.js +96 -0
- package/dist/chunk-FDFPMBCA.js +112 -0
- package/dist/chunk-FVYFV5HA.js +154 -0
- package/dist/chunk-GE37NVLN.js +15 -0
- package/dist/chunk-GM5Y555O.js +143 -0
- package/dist/chunk-GRJXQBQZ.js +793 -0
- package/dist/chunk-HWFX4BC4.js +177 -0
- package/dist/chunk-I3M2KJXB.js +56 -0
- package/dist/chunk-IFNONGRQ.js +603 -0
- package/dist/chunk-J3U2CPSK.js +136 -0
- package/dist/chunk-J6XQ7OPZ.js +74 -0
- package/dist/chunk-KMV4Y6R3.js +127 -0
- package/dist/chunk-KP5ZDDPE.js +340 -0
- package/dist/chunk-KYKSZ3XZ.js +202 -0
- package/dist/chunk-LEGPB7DO.js +81 -0
- package/dist/chunk-LUAKDHLZ.js +40 -0
- package/dist/chunk-MHUEVBVO.js +35 -0
- package/dist/chunk-MYGSLK44.js +37 -0
- package/dist/chunk-NEZNAPPN.js +41 -0
- package/dist/chunk-NPVN2THM.js +197 -0
- package/dist/chunk-NYZU5NCW.js +23 -0
- package/dist/chunk-O4KHCV6I.js +320 -0
- package/dist/chunk-ONDVTXUO.js +240 -0
- package/dist/chunk-OTJFHESY.js +176 -0
- package/dist/chunk-P6MIKSPY.js +243 -0
- package/dist/chunk-Q5VPF4WI.js +26 -0
- package/dist/chunk-QCRBX3LV.js +165 -0
- package/dist/chunk-QKGYDMWB.js +7409 -0
- package/dist/chunk-QSL6QN5X.js +115 -0
- package/dist/chunk-QUIMQ7YL.js +73 -0
- package/dist/chunk-RG3YLU6E.js +49 -0
- package/dist/chunk-SHCEBP3J.js +107 -0
- package/dist/chunk-SHZFOSXC.js +462 -0
- package/dist/chunk-SOXFMMXC.js +151 -0
- package/dist/chunk-TDTYV6QI.js +119 -0
- package/dist/chunk-TNTXWSYA.js +19 -0
- package/dist/chunk-TUXQ32WE.js +2251 -0
- package/dist/chunk-VGCDRK3S.js +37 -0
- package/dist/chunk-VLRHFMGJ.js +58 -0
- package/dist/chunk-WHX2ZFCG.js +4048 -0
- package/dist/chunk-X4W64MHU.js +2005 -0
- package/dist/chunk-XBQA4I36.js +92 -0
- package/dist/chunk-XGOV6KV4.js +413 -0
- package/dist/chunk-XIFQIFSZ.js +940 -0
- package/dist/chunk-XLXPVN3Y.js +15393 -0
- package/dist/chunk-ZJMLQ37G.js +90 -0
- package/dist/client-MU4EUDZV.js +15 -0
- package/dist/commands-UOOTMBKA.js +1023 -0
- package/dist/compat-Q5DVPITN.js +201 -0
- package/dist/constants-DJFOFZ44.js +24 -0
- package/dist/dashboardIssue-KBKLQMGC.js +274 -0
- package/dist/diff-I4SNUHU3.js +13 -0
- package/dist/dist-4XXIXHHZ.js +12945 -0
- package/dist/domains-VRT4BRVZ.js +14 -0
- package/dist/e2b-FVPHDULZ.js +172 -0
- package/dist/env-UN6KGZ7S.js +15 -0
- package/dist/envCheck-KUPTORFQ.js +71 -0
- package/dist/extract-6WG265BL.js +455 -0
- package/dist/fetch-PJ3SC32M.js +22 -0
- package/dist/firstTouch-COJCMOB3.js +55 -0
- package/dist/fix-3EDWNKW3.js +220 -0
- package/dist/handlers-4Q37WIFT.js +55 -0
- package/dist/hook-NA66XJLC.js +412 -0
- package/dist/http-25GJ6SKK.js +6928 -0
- package/dist/ingestQueue-FSVJC3AA.js +37 -0
- package/dist/install-G2HHXYJ2.js +20 -0
- package/dist/installSkill-5TIMYIJZ.js +70 -0
- package/dist/inventory-5AWSLJYU.js +31 -0
- package/dist/local-XPZMRH5R.js +13 -0
- package/dist/manifest-ENMIC54B.js +13 -0
- package/dist/mcp-PENLL2PK.js +500 -0
- package/dist/mcpConfig-3LYPRY5Q.js +80 -0
- package/dist/mcpHandlers-PO7GENA7.js +22 -0
- package/dist/mcpScan-ULP626TQ.js +1735 -0
- package/dist/mcpScanWorkflow-27O44FOE.js +98 -0
- package/dist/mcpStack-ZNYIFXZ5.js +20 -0
- package/dist/model-ASMZWLM7.js +214 -0
- package/dist/packages-RWF2LXST.js +40 -0
- package/dist/plan-SDILIDZT.js +465 -0
- package/dist/planView-ZAKRTGVS.js +82 -0
- package/dist/policy-JAGNMQJI.js +115 -0
- package/dist/probe-3IK646WN.js +610 -0
- package/dist/publicUpgrades-WTBGLDIK.js +42 -0
- package/dist/references-I6Y4BSRR.js +16 -0
- package/dist/release-7IU2R4YG.js +128 -0
- package/dist/remote-FN6225AH.js +42 -0
- package/dist/rename-OS6JYBZQ.js +204 -0
- package/dist/reportRuns-O4X7ZFTX.js +77 -0
- package/dist/safeFetch-COERXGFN.js +19 -0
- package/dist/sandbox-SAPSRKEI.js +39 -0
- package/dist/sarif-WLVJFJ7U.js +125 -0
- package/dist/server-OLTY3NYT.js +59 -0
- package/dist/surface-7MHE3RL7.js +143 -0
- package/dist/surface-JS2XU3U3.js +38 -0
- package/dist/surfaceHandlers-4B6TMZ34.js +20 -0
- package/dist/typecheck-NGOKNAJY.js +282 -0
- package/dist/types-M5JP2W3D.js +10 -0
- package/dist/uninstall-I2IJP5JZ.js +200 -0
- package/dist/upgrade-FS26DJGG.js +650 -0
- package/dist/upgradePlan-ZS5IOVMZ.js +181 -0
- package/dist/upkeepHandler-VFGYXJKB.js +13 -0
- package/dist/userConfig-ICASHI47.js +20 -0
- package/dist/watch-OCNEUXG6.js +204 -0
- package/package.json +26 -20
- package/templates/agent-rules.md +4 -4
- package/templates/skill-instructions.md +35 -17
- 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,220 @@
|
|
|
1
|
+
import { createRequire as __lurqCreateRequire } from 'node:module'; const require = __lurqCreateRequire(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
SCAN_LIMIT,
|
|
4
|
+
fixableTargets,
|
|
5
|
+
parseUpgradeSpec,
|
|
6
|
+
planUpgrades
|
|
7
|
+
} from "./chunk-QCRBX3LV.js";
|
|
8
|
+
import {
|
|
9
|
+
manifestFindings
|
|
10
|
+
} from "./chunk-5ALIJFDA.js";
|
|
11
|
+
import {
|
|
12
|
+
applyEdits,
|
|
13
|
+
deterministic,
|
|
14
|
+
editsByFile,
|
|
15
|
+
unifiedDiff
|
|
16
|
+
} from "./chunk-BM3LF5T6.js";
|
|
17
|
+
import "./chunk-3GHJR7XR.js";
|
|
18
|
+
import {
|
|
19
|
+
init_esm_shims
|
|
20
|
+
} from "./chunk-I3M2KJXB.js";
|
|
21
|
+
|
|
22
|
+
// src/cli/fix.ts
|
|
23
|
+
init_esm_shims();
|
|
24
|
+
import { readFileSync, writeFileSync } from "fs";
|
|
25
|
+
import { join, relative } from "path";
|
|
26
|
+
async function derivePlanTargets(dir, opts) {
|
|
27
|
+
const { buildUpgradePlan } = await import("./upgradePlan-ZS5IOVMZ.js");
|
|
28
|
+
try {
|
|
29
|
+
const plan = await buildUpgradePlan(dir, {
|
|
30
|
+
url: opts.url,
|
|
31
|
+
apiKey: opts.apiKey,
|
|
32
|
+
repo: opts.repo
|
|
33
|
+
});
|
|
34
|
+
return fixableTargets(plan.upgrades, opts.max);
|
|
35
|
+
} catch (err) {
|
|
36
|
+
if (err instanceof Error && err.name === "MissingKeyError") {
|
|
37
|
+
throw new Error(
|
|
38
|
+
`${err.message}
|
|
39
|
+
|
|
40
|
+
Or skip the lookup: \`lurq fix --upgrade pkg@from..to\` reads both versions straight from npm and needs no key.`
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
throw err;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function stage(root, findings) {
|
|
47
|
+
const writes = /* @__PURE__ */ new Map();
|
|
48
|
+
let diff = "";
|
|
49
|
+
for (const [file, edits] of editsByFile(findings.filter(deterministic))) {
|
|
50
|
+
const before = readFileSync(join(root, file), "utf8");
|
|
51
|
+
diff += unifiedDiff(file, before, edits);
|
|
52
|
+
writes.set(file, applyEdits(before, edits));
|
|
53
|
+
}
|
|
54
|
+
return { writes, diff };
|
|
55
|
+
}
|
|
56
|
+
async function runFix(dir, opts) {
|
|
57
|
+
const { scanReferences } = await import("./references-I6Y4BSRR.js");
|
|
58
|
+
const { checkUpgrade } = await import("./upgrade-FS26DJGG.js");
|
|
59
|
+
const { renamePlan } = await import("./rename-OS6JYBZQ.js");
|
|
60
|
+
const asked = Boolean(opts.plan) || (opts.upgrade?.length ?? 0) > 0;
|
|
61
|
+
const split = asked ? {
|
|
62
|
+
...fixableTargets(opts.plan ? planUpgrades(opts.plan) : [], opts.max),
|
|
63
|
+
named: (opts.upgrade ?? []).map(parseUpgradeSpec)
|
|
64
|
+
} : { ...await derivePlanTargets(dir, opts), named: [] };
|
|
65
|
+
const targets = [...split.targets, ...split.named];
|
|
66
|
+
const skipped = split.skipped;
|
|
67
|
+
const { runDomains, SOURCE_DOMAINS } = await import("./domains-VRT4BRVZ.js");
|
|
68
|
+
const source = await runDomains({ dir, domains: SOURCE_DOMAINS });
|
|
69
|
+
const unchecked = source.skipped.map((d) => ({ domain: d.domain, reason: d.reason }));
|
|
70
|
+
if (targets.length === 0 && source.findings.length === 0) {
|
|
71
|
+
const empty = {
|
|
72
|
+
root: dir,
|
|
73
|
+
files: [],
|
|
74
|
+
applied: [],
|
|
75
|
+
remaining: [],
|
|
76
|
+
refused: [],
|
|
77
|
+
skipped,
|
|
78
|
+
unchecked
|
|
79
|
+
};
|
|
80
|
+
const lines = skipped.length ? [
|
|
81
|
+
`Nothing attempted in ${dir}. ${skipped.length} upgrade(s) deliberately not tried:`,
|
|
82
|
+
...skippedLines(skipped)
|
|
83
|
+
] : [
|
|
84
|
+
`Nothing to fix in ${dir}: ${asked ? "no upgrade named here needs a change" : "every dependency is current or held by the repo policy"}, ${// Never "the source is clean" when no detector survived to look.
|
|
85
|
+
// `ran` is empty only when every source domain threw, and that is
|
|
86
|
+
// the one case where the reassuring sentence would be false.
|
|
87
|
+
source.ran.length ? "and nothing in the source needs changing" : "and the source could not be scanned"}.`
|
|
88
|
+
];
|
|
89
|
+
lines.push(...uncheckedLines(unchecked));
|
|
90
|
+
console.log(opts.json ? JSON.stringify(empty, null, 2) : lines.join("\n"));
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
let findings = [];
|
|
94
|
+
const refused = [];
|
|
95
|
+
if (targets.length > 0) {
|
|
96
|
+
const refs = scanReferences(dir, { limit: SCAN_LIMIT });
|
|
97
|
+
const report = await checkUpgrade(targets, refs, { rootDir: dir });
|
|
98
|
+
const renames = renamePlan(report);
|
|
99
|
+
const { findManifests } = await import("./upgradePlan-ZS5IOVMZ.js");
|
|
100
|
+
const manifests = manifestFindings(dir, targets, {
|
|
101
|
+
files: findManifests(dir).map((abs) => relative(dir, abs) || "package.json")
|
|
102
|
+
});
|
|
103
|
+
findings = [...renames.findings, ...manifests.findings];
|
|
104
|
+
refused.push(
|
|
105
|
+
...renames.refused,
|
|
106
|
+
...manifests.refused.map((r) => ({ symbol: r.package, file: r.file, reason: r.reason }))
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
findings = [...findings, ...source.findings];
|
|
110
|
+
const result = {
|
|
111
|
+
root: dir,
|
|
112
|
+
files: [],
|
|
113
|
+
applied: [],
|
|
114
|
+
remaining: [],
|
|
115
|
+
refused: refused.map((r) => ({ symbol: r.symbol, file: r.file, reason: r.reason })),
|
|
116
|
+
skipped,
|
|
117
|
+
unchecked
|
|
118
|
+
};
|
|
119
|
+
const { writes, diff } = stage(dir, findings);
|
|
120
|
+
result.files = [...writes.keys()];
|
|
121
|
+
for (const f of findings) {
|
|
122
|
+
if (deterministic(f)) {
|
|
123
|
+
result.applied.push({
|
|
124
|
+
code: f.code,
|
|
125
|
+
summary: f.fix.summary,
|
|
126
|
+
files: [...new Set(f.fix.edits.map((e) => e.file))]
|
|
127
|
+
});
|
|
128
|
+
} else {
|
|
129
|
+
result.remaining.push({
|
|
130
|
+
code: f.code,
|
|
131
|
+
detail: f.detail,
|
|
132
|
+
instruction: f.fix?.task?.instruction,
|
|
133
|
+
files: f.fix?.task?.files,
|
|
134
|
+
evidence: f.fix?.task?.evidence
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (opts.apply && writes.size > 0) {
|
|
139
|
+
const { writeFileAtomic } = await import("./installSkill-5TIMYIJZ.js");
|
|
140
|
+
for (const [file, contents] of writes) writeFileAtomic(join(dir, file), contents);
|
|
141
|
+
}
|
|
142
|
+
if (opts.sarif) {
|
|
143
|
+
const { toSarif } = await import("./sarif-WLVJFJ7U.js");
|
|
144
|
+
const { VERSION } = await import("./constants-DJFOFZ44.js");
|
|
145
|
+
const sarif = toSarif(findings, {
|
|
146
|
+
version: VERSION,
|
|
147
|
+
// Read from disk rather than reusing the staged text: regions must
|
|
148
|
+
// describe the file as it is, not as it would be after the fix.
|
|
149
|
+
read: (file) => {
|
|
150
|
+
try {
|
|
151
|
+
return readFileSync(join(dir, file), "utf8");
|
|
152
|
+
} catch {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
writeFileSync(opts.sarif, `${JSON.stringify(sarif, null, 2)}
|
|
158
|
+
`, "utf8");
|
|
159
|
+
console.error(`wrote ${opts.sarif}`);
|
|
160
|
+
}
|
|
161
|
+
if (opts.json) {
|
|
162
|
+
console.log(JSON.stringify({ ...result, applied_to_disk: Boolean(opts.apply) }, null, 2));
|
|
163
|
+
} else {
|
|
164
|
+
console.log(formatFix(result, diff, Boolean(opts.apply)));
|
|
165
|
+
}
|
|
166
|
+
if (opts.exitCode && result.remaining.length > 0) process.exitCode = 1;
|
|
167
|
+
}
|
|
168
|
+
function uncheckedLines(unchecked) {
|
|
169
|
+
if (unchecked.length === 0) return [];
|
|
170
|
+
return [
|
|
171
|
+
"",
|
|
172
|
+
`${unchecked.length} domain(s) could not be checked:`,
|
|
173
|
+
...unchecked.map((u) => ` ${u.domain}: ${u.reason}`)
|
|
174
|
+
];
|
|
175
|
+
}
|
|
176
|
+
function skippedLines(skipped) {
|
|
177
|
+
return skipped.map((s) => ` ${s.package}: ${s.reason}`);
|
|
178
|
+
}
|
|
179
|
+
function formatFix(result, diff, applied) {
|
|
180
|
+
const out = [];
|
|
181
|
+
if (result.applied.length === 0 && result.remaining.length === 0) {
|
|
182
|
+
return [
|
|
183
|
+
`Nothing to fix in ${result.root}: no upgrade in this plan renames a symbol this code references,`,
|
|
184
|
+
"every declared range already admits its target, and nothing in the source names a retired model",
|
|
185
|
+
"or reads an undeclared variable."
|
|
186
|
+
].join(" ");
|
|
187
|
+
}
|
|
188
|
+
if (result.applied.length > 0) {
|
|
189
|
+
out.push(
|
|
190
|
+
applied ? `Wrote ${result.files.length} file(s) in ${result.root}:` : `Would write ${result.files.length} file(s) in ${result.root} (pass --apply):`
|
|
191
|
+
);
|
|
192
|
+
for (const a of result.applied) out.push(` ${a.summary} \u2014 ${a.files.join(", ")}`);
|
|
193
|
+
if (!applied && diff) out.push("", diff.trimEnd());
|
|
194
|
+
}
|
|
195
|
+
if (result.remaining.length > 0) {
|
|
196
|
+
out.push("", `${result.remaining.length} change(s) lurq will not make for you:`);
|
|
197
|
+
for (const r of result.remaining) {
|
|
198
|
+
out.push(` ${r.detail}`);
|
|
199
|
+
if (r.instruction) out.push(` do: ${r.instruction}`);
|
|
200
|
+
if (r.files?.length) out.push(` in: ${r.files.join(", ")}`);
|
|
201
|
+
for (const e of r.evidence ?? []) out.push(` use: ${e}`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (result.skipped.length > 0) {
|
|
205
|
+
out.push("", `${result.skipped.length} upgrade(s) deliberately not attempted:`);
|
|
206
|
+
out.push(...skippedLines(result.skipped));
|
|
207
|
+
}
|
|
208
|
+
out.push(...uncheckedLines(result.unchecked));
|
|
209
|
+
if (result.refused.length > 0) {
|
|
210
|
+
out.push("", "Proven renames left alone:");
|
|
211
|
+
for (const r of result.refused) out.push(` ${r.file ?? r.symbol}: ${r.reason}`);
|
|
212
|
+
}
|
|
213
|
+
return out.join("\n");
|
|
214
|
+
}
|
|
215
|
+
export {
|
|
216
|
+
formatFix,
|
|
217
|
+
runFix,
|
|
218
|
+
stage,
|
|
219
|
+
uncheckedLines
|
|
220
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { createRequire as __lurqCreateRequire } from 'node:module'; const require = __lurqCreateRequire(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
USAGE_PAGE_SIZE,
|
|
4
|
+
handleCompare,
|
|
5
|
+
handleCompat,
|
|
6
|
+
handleEvaluate,
|
|
7
|
+
handlePolicy,
|
|
8
|
+
handleRecommend,
|
|
9
|
+
handleReportOutcome,
|
|
10
|
+
handleUsage,
|
|
11
|
+
handleVerify,
|
|
12
|
+
latestDataAsOf,
|
|
13
|
+
rowToEvaluate
|
|
14
|
+
} from "./chunk-64ZF7KO4.js";
|
|
15
|
+
import "./chunk-QSL6QN5X.js";
|
|
16
|
+
import "./chunk-NYZU5NCW.js";
|
|
17
|
+
import "./chunk-2WD4QAH4.js";
|
|
18
|
+
import "./chunk-DSROTZ3Z.js";
|
|
19
|
+
import "./chunk-TUXQ32WE.js";
|
|
20
|
+
import "./chunk-BUALU27S.js";
|
|
21
|
+
import "./chunk-QUIMQ7YL.js";
|
|
22
|
+
import "./chunk-6DM6UQFD.js";
|
|
23
|
+
import "./chunk-EYAKM6YN.js";
|
|
24
|
+
import "./chunk-D3VTZS7V.js";
|
|
25
|
+
import "./chunk-4ESO33J6.js";
|
|
26
|
+
import "./chunk-NPVN2THM.js";
|
|
27
|
+
import "./chunk-VLRHFMGJ.js";
|
|
28
|
+
import "./chunk-VGCDRK3S.js";
|
|
29
|
+
import "./chunk-4JHDSVIN.js";
|
|
30
|
+
import "./chunk-GM5Y555O.js";
|
|
31
|
+
import "./chunk-2GAYNIHG.js";
|
|
32
|
+
import "./chunk-KYKSZ3XZ.js";
|
|
33
|
+
import "./chunk-A3TYI2TF.js";
|
|
34
|
+
import "./chunk-HWFX4BC4.js";
|
|
35
|
+
import "./chunk-6L67QMCP.js";
|
|
36
|
+
import "./chunk-3GHJR7XR.js";
|
|
37
|
+
import "./chunk-GRJXQBQZ.js";
|
|
38
|
+
import "./chunk-WHX2ZFCG.js";
|
|
39
|
+
import "./chunk-LUAKDHLZ.js";
|
|
40
|
+
import "./chunk-MYGSLK44.js";
|
|
41
|
+
import "./chunk-72Y2FDVS.js";
|
|
42
|
+
import "./chunk-I3M2KJXB.js";
|
|
43
|
+
export {
|
|
44
|
+
USAGE_PAGE_SIZE,
|
|
45
|
+
handleCompare,
|
|
46
|
+
handleCompat,
|
|
47
|
+
handleEvaluate,
|
|
48
|
+
handlePolicy,
|
|
49
|
+
handleRecommend,
|
|
50
|
+
handleReportOutcome,
|
|
51
|
+
handleUsage,
|
|
52
|
+
handleVerify,
|
|
53
|
+
latestDataAsOf,
|
|
54
|
+
rowToEvaluate
|
|
55
|
+
};
|
|
@@ -0,0 +1,412 @@
|
|
|
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/cli/hook.ts
|
|
7
|
+
init_esm_shims();
|
|
8
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
9
|
+
import { tmpdir } from "os";
|
|
10
|
+
import { basename, dirname, isAbsolute, join } from "path";
|
|
11
|
+
|
|
12
|
+
// src/benchmark/normalize.ts
|
|
13
|
+
init_esm_shims();
|
|
14
|
+
var NPM_NAME_RE = /^(@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/;
|
|
15
|
+
function isValidNpmName(name) {
|
|
16
|
+
if (!name || name.length > 214) return false;
|
|
17
|
+
return NPM_NAME_RE.test(name);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// src/cli/hook.ts
|
|
21
|
+
var MANAGERS = /* @__PURE__ */ new Map([
|
|
22
|
+
["npm", /* @__PURE__ */ new Set(["install", "i", "in", "add"])],
|
|
23
|
+
["pnpm", /* @__PURE__ */ new Set(["add", "install", "i"])],
|
|
24
|
+
["yarn", /* @__PURE__ */ new Set(["add"])],
|
|
25
|
+
["bun", /* @__PURE__ */ new Set(["add", "install", "i"])]
|
|
26
|
+
]);
|
|
27
|
+
var VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
28
|
+
"--prefix",
|
|
29
|
+
"--registry",
|
|
30
|
+
"--filter",
|
|
31
|
+
"-F",
|
|
32
|
+
"-C",
|
|
33
|
+
"--dir",
|
|
34
|
+
"--cwd",
|
|
35
|
+
"--workspace",
|
|
36
|
+
"-w",
|
|
37
|
+
"--tag",
|
|
38
|
+
"--cache",
|
|
39
|
+
"--userconfig"
|
|
40
|
+
]);
|
|
41
|
+
var MAX_NAMES = 10;
|
|
42
|
+
var VERIFY_TIMEOUT_MS = 8e3;
|
|
43
|
+
function specName(spec) {
|
|
44
|
+
if (/[:/\\]/.test(spec.replace(/^@[^/]+\//, "")) || /\.(tgz|tar|tar\.gz)$/.test(spec))
|
|
45
|
+
return null;
|
|
46
|
+
const at = spec.indexOf("@", 1);
|
|
47
|
+
const name = at === -1 ? spec : spec.slice(0, at);
|
|
48
|
+
return isValidNpmName(name) ? name : null;
|
|
49
|
+
}
|
|
50
|
+
var ALLOW_ENV = "LURQ_ALLOW=1";
|
|
51
|
+
function commandStart(tokens) {
|
|
52
|
+
let i = 0;
|
|
53
|
+
while (i < tokens.length && (tokens[i] === "sudo" || /^[A-Za-z_]\w*=/.test(tokens[i]))) {
|
|
54
|
+
if (tokens[i] === ALLOW_ENV) return -1;
|
|
55
|
+
i++;
|
|
56
|
+
}
|
|
57
|
+
return i;
|
|
58
|
+
}
|
|
59
|
+
function installTargets(command) {
|
|
60
|
+
const names = /* @__PURE__ */ new Set();
|
|
61
|
+
for (const segment of command.split(/&&|\|\||[;|\n]/)) {
|
|
62
|
+
const tokens = segment.trim().split(/\s+/);
|
|
63
|
+
const i = commandStart(tokens);
|
|
64
|
+
if (i === -1) continue;
|
|
65
|
+
if (!MANAGERS.get(tokens[i] ?? "")?.has(tokens[i + 1] ?? "")) continue;
|
|
66
|
+
for (let j = i + 2; j < tokens.length; j++) {
|
|
67
|
+
const t = tokens[j];
|
|
68
|
+
if (VALUE_FLAGS.has(t)) j++;
|
|
69
|
+
else if (!t.startsWith("-")) {
|
|
70
|
+
const name = specName(t);
|
|
71
|
+
if (name) names.add(name);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return [...names];
|
|
76
|
+
}
|
|
77
|
+
var RUNNERS = [
|
|
78
|
+
["npx", null],
|
|
79
|
+
["bunx", null],
|
|
80
|
+
["pnpm", "dlx"],
|
|
81
|
+
["yarn", "dlx"]
|
|
82
|
+
];
|
|
83
|
+
function runTargets(command, hasLocalBin = () => false) {
|
|
84
|
+
const names = /* @__PURE__ */ new Set();
|
|
85
|
+
for (const segment of command.split(/&&|\|\||[;|\n]/)) {
|
|
86
|
+
const tokens = segment.trim().split(/\s+/);
|
|
87
|
+
const i = commandStart(tokens);
|
|
88
|
+
if (i === -1) continue;
|
|
89
|
+
const runner = RUNNERS.find(
|
|
90
|
+
([bin, verb]) => tokens[i] === bin && (verb === null || tokens[i + 1] === verb)
|
|
91
|
+
);
|
|
92
|
+
if (!runner) continue;
|
|
93
|
+
for (let j = i + (runner[1] ? 2 : 1); j < tokens.length; j++) {
|
|
94
|
+
const t = tokens[j];
|
|
95
|
+
if (t === "-p" || t === "--package") {
|
|
96
|
+
const name = specName(tokens[++j] ?? "");
|
|
97
|
+
if (name) names.add(name);
|
|
98
|
+
} else if (t.startsWith("--package=")) {
|
|
99
|
+
const name = specName(t.slice("--package=".length));
|
|
100
|
+
if (name) names.add(name);
|
|
101
|
+
} else if (!t.startsWith("-")) {
|
|
102
|
+
const name = specName(t);
|
|
103
|
+
if (name && !hasLocalBin(name)) names.add(name);
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return [...names];
|
|
109
|
+
}
|
|
110
|
+
var DEP_FIELDS = [
|
|
111
|
+
"dependencies",
|
|
112
|
+
"devDependencies",
|
|
113
|
+
"peerDependencies",
|
|
114
|
+
"optionalDependencies"
|
|
115
|
+
];
|
|
116
|
+
function addedDependencies(before, after) {
|
|
117
|
+
const read = (text) => {
|
|
118
|
+
try {
|
|
119
|
+
const v = JSON.parse(text);
|
|
120
|
+
return v && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
121
|
+
} catch {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
const [was, now] = [read(before || "{}"), read(after)];
|
|
126
|
+
if (!was || !now) return null;
|
|
127
|
+
const had = new Set(DEP_FIELDS.flatMap((f) => Object.keys(was[f] ?? {})));
|
|
128
|
+
const added = /* @__PURE__ */ new Set();
|
|
129
|
+
for (const field of DEP_FIELDS) {
|
|
130
|
+
for (const [name, range] of Object.entries(now[field] ?? {})) {
|
|
131
|
+
if (had.has(name) || typeof range !== "string" || /[:/]/.test(range)) continue;
|
|
132
|
+
if (specName(name) === name) added.add(name);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return [...added];
|
|
136
|
+
}
|
|
137
|
+
var clip = (s, n = 240) => {
|
|
138
|
+
const line = s.replace(/\s+/g, " ").trim();
|
|
139
|
+
return line.length > n ? `${line.slice(0, n - 1)}\u2026` : line;
|
|
140
|
+
};
|
|
141
|
+
function decide(checked, opts = {}) {
|
|
142
|
+
const list = (xs) => xs.map((x) => x.name).join(", ");
|
|
143
|
+
const why = (xs) => xs.map((x) => `${x.name}: ${clip(x.verdict.reasons[0] ?? x.verdict.scope)}`).join("; ");
|
|
144
|
+
const invalid = opts.deny === false ? [] : checked.filter((c) => c.verdict.level === "invalid");
|
|
145
|
+
if (invalid.length) {
|
|
146
|
+
return {
|
|
147
|
+
permissionDecision: "deny",
|
|
148
|
+
permissionDecisionReason: `lurq: ${list(invalid)} ${invalid.length === 1 ? "is not a real npm package" : "are not real npm packages"}, so this was stopped before it ran. Check the name for a typo or a package that never existed, and tell the user what lurq found. (${why(invalid)})`
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
const high = checked.filter((c) => c.verdict.level === "high");
|
|
152
|
+
if (high.length) {
|
|
153
|
+
return {
|
|
154
|
+
permissionDecision: "ask",
|
|
155
|
+
permissionDecisionReason: `lurq flags ${list(high)} as high risk: ${why(high)}. Run \`lurq verify <package>\` for the evidence.`
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
function unseen(sessionId, keys, dir = join(tmpdir(), "lurq-hooks")) {
|
|
161
|
+
if (typeof sessionId !== "string" || !sessionId || keys.length === 0) return [];
|
|
162
|
+
const path = join(dir, `${sessionId.replace(/[^\w-]/g, "").slice(0, 80)}.json`);
|
|
163
|
+
let seen = [];
|
|
164
|
+
try {
|
|
165
|
+
seen = JSON.parse(readFileSync(path, "utf8")).seen ?? [];
|
|
166
|
+
} catch {
|
|
167
|
+
}
|
|
168
|
+
const fresh = [...new Set(keys)].filter((k) => !seen.includes(k));
|
|
169
|
+
if (fresh.length) {
|
|
170
|
+
mkdirSync(dir, { recursive: true });
|
|
171
|
+
writeFileSync(path, JSON.stringify({ seen: [...seen, ...fresh] }));
|
|
172
|
+
}
|
|
173
|
+
return fresh;
|
|
174
|
+
}
|
|
175
|
+
function isJsProject(cwd) {
|
|
176
|
+
if (typeof cwd !== "string" || !cwd) return false;
|
|
177
|
+
for (let dir = cwd; ; dir = dirname(dir)) {
|
|
178
|
+
if (existsSync(join(dir, "package.json"))) return true;
|
|
179
|
+
if (dirname(dir) === dir) return false;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
var PROMPT_TIPS = [
|
|
183
|
+
{
|
|
184
|
+
kind: "choose",
|
|
185
|
+
test: /\b(which|best|recommend\w*|alternatives?|librar(y|ies)|sdks?|frameworks?)\b/i,
|
|
186
|
+
tip: "lurq is connected. If this means choosing a library, name the candidates you know and check them with lurq compare (evaluate for one), then verify the pick: your sense of package health is from training, lurq is current."
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
kind: "upgrade",
|
|
190
|
+
test: /\b(upgrad\w*|bump\w*|migrat\w*|deprecat\w*|breaking|major version|latest version)\b/i,
|
|
191
|
+
tip: "lurq is connected. For an upgrade, call lurq diff_surface with the package and both versions to see what was removed or changed, and compat for the resulting set, before editing code."
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
kind: "install",
|
|
195
|
+
test: /\b(install\w*|dependenc(y|ies)|add (a |an |the )?(package|library|dep))\b/i,
|
|
196
|
+
tip: "lurq is connected. Call lurq verify before adding a package and usage before writing code against one. Installs in this session are also checked automatically."
|
|
197
|
+
}
|
|
198
|
+
];
|
|
199
|
+
function promptTips(prompt2) {
|
|
200
|
+
return PROMPT_TIPS.filter((t) => t.test.test(prompt2)).map(({ kind, tip }) => ({ kind, tip }));
|
|
201
|
+
}
|
|
202
|
+
function applyEdits(text, edits) {
|
|
203
|
+
let out = text;
|
|
204
|
+
for (const e of edits) {
|
|
205
|
+
if (typeof e.old_string !== "string" || typeof e.new_string !== "string" || !out.includes(e.old_string))
|
|
206
|
+
return null;
|
|
207
|
+
out = e.replace_all ? out.split(e.old_string).join(e.new_string) : out.replace(e.old_string, () => e.new_string);
|
|
208
|
+
}
|
|
209
|
+
return out;
|
|
210
|
+
}
|
|
211
|
+
var readIfExists = (path) => existsSync(path) ? readFileSync(path, "utf8") : "";
|
|
212
|
+
function patchedPackageJsons(patch, cwd, read = readIfExists) {
|
|
213
|
+
const files = [];
|
|
214
|
+
for (const section of patch.split(/^\*\*\* (?=(?:Add|Update|Delete) File: )/m).slice(1)) {
|
|
215
|
+
const [header = "", ...rest] = section.split("\n");
|
|
216
|
+
const m = /^(Add|Update) File: (.+)$/.exec(header.trim());
|
|
217
|
+
if (!m || basename(m[2]) !== "package.json") continue;
|
|
218
|
+
const body = rest.filter((l) => !l.startsWith("*** "));
|
|
219
|
+
if (m[1] === "Add") {
|
|
220
|
+
files.push({
|
|
221
|
+
before: "",
|
|
222
|
+
after: body.filter((l) => l.startsWith("+")).map((l) => l.slice(1)).join("\n")
|
|
223
|
+
});
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
const before = read(isAbsolute(m[2]) ? m[2] : join(cwd, m[2]));
|
|
227
|
+
const edits = body.join("\n").split(/^@@.*$/m).map((h) => h.split("\n").filter((l) => /^[ +-]/.test(l))).filter((h) => h.length > 0).map((h) => ({
|
|
228
|
+
old_string: h.filter((l) => !l.startsWith("+")).map((l) => l.slice(1)).join("\n"),
|
|
229
|
+
new_string: h.filter((l) => !l.startsWith("-")).map((l) => l.slice(1)).join("\n")
|
|
230
|
+
}));
|
|
231
|
+
const after = applyEdits(before, edits);
|
|
232
|
+
if (after !== null) files.push({ before, after });
|
|
233
|
+
}
|
|
234
|
+
return files;
|
|
235
|
+
}
|
|
236
|
+
function targets(tool, input, cwd) {
|
|
237
|
+
const none = { installs: [], runs: [] };
|
|
238
|
+
if (typeof input.command === "string" && tool === "Bash") {
|
|
239
|
+
return {
|
|
240
|
+
installs: installTargets(input.command),
|
|
241
|
+
runs: runTargets(input.command, (n) => existsSync(join(cwd, "node_modules", ".bin", n)))
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
if (typeof input.command === "string" && tool === "apply_patch") {
|
|
245
|
+
const installs = patchedPackageJsons(input.command, cwd).flatMap(
|
|
246
|
+
(f) => addedDependencies(f.before, f.after) ?? []
|
|
247
|
+
);
|
|
248
|
+
return { installs: [...new Set(installs)], runs: [] };
|
|
249
|
+
}
|
|
250
|
+
if (typeof input.file_path !== "string" || basename(input.file_path) !== "package.json")
|
|
251
|
+
return none;
|
|
252
|
+
const before = readIfExists(input.file_path);
|
|
253
|
+
const after = tool === "Write" ? typeof input.content === "string" ? input.content : null : tool === "Edit" ? applyEdits(before, [input]) : tool === "MultiEdit" && Array.isArray(input.edits) ? applyEdits(before, input.edits) : null;
|
|
254
|
+
return { installs: after === null ? [] : addedDependencies(before, after) ?? [], runs: [] };
|
|
255
|
+
}
|
|
256
|
+
var usageNudge = (names) => `Before writing code against ${names.join(", ")}, call lurq usage with ${names.length === 1 ? "it" : "each"} (with knownVersion if you remember one): APIs move, and what you remember is from training.`;
|
|
257
|
+
function freshUsage(sessionId, names) {
|
|
258
|
+
return unseen(
|
|
259
|
+
sessionId,
|
|
260
|
+
names.map((n) => `usage:${n}`)
|
|
261
|
+
).map((k) => k.slice("usage:".length));
|
|
262
|
+
}
|
|
263
|
+
async function preToolUse(input, nudge) {
|
|
264
|
+
const cwd = typeof input.cwd === "string" ? input.cwd : process.cwd();
|
|
265
|
+
const { installs, runs } = targets(input.tool_name, input.tool_input ?? {}, cwd);
|
|
266
|
+
const names = [.../* @__PURE__ */ new Set([...installs, ...runs])].slice(0, MAX_NAMES);
|
|
267
|
+
if (names.length === 0) return null;
|
|
268
|
+
const { callTool } = await import("./remote-FN6225AH.js");
|
|
269
|
+
const settled = await Promise.allSettled(
|
|
270
|
+
names.map(
|
|
271
|
+
(name) => callTool(
|
|
272
|
+
"verify",
|
|
273
|
+
{ package: name },
|
|
274
|
+
{ timeoutMs: VERIFY_TIMEOUT_MS }
|
|
275
|
+
).then((r) => ({ name, verdict: r.verdict }))
|
|
276
|
+
)
|
|
277
|
+
);
|
|
278
|
+
const checked = settled.flatMap(
|
|
279
|
+
(s) => s.status === "fulfilled" && s.value.verdict ? [{ name: s.value.name, verdict: s.value.verdict }] : []
|
|
280
|
+
);
|
|
281
|
+
const installed = checked.filter((c) => installs.includes(c.name));
|
|
282
|
+
const decision = decide(installed) ?? decide(
|
|
283
|
+
checked.filter((c) => !installs.includes(c.name)),
|
|
284
|
+
{ deny: false }
|
|
285
|
+
);
|
|
286
|
+
if (decision) return { decision };
|
|
287
|
+
if (!nudge) return null;
|
|
288
|
+
const clean = installed.filter((c) => c.verdict.level !== "invalid" && c.verdict.level !== "high").map((c) => c.name);
|
|
289
|
+
const fresh = freshUsage(input.session_id, clean);
|
|
290
|
+
return fresh.length ? { context: `lurq verified ${fresh.join(", ")} before this ran. ${usageNudge(fresh)}` } : null;
|
|
291
|
+
}
|
|
292
|
+
function postToolUse(input) {
|
|
293
|
+
const command = input.tool_input?.command;
|
|
294
|
+
if (typeof command !== "string") return null;
|
|
295
|
+
const fresh = freshUsage(input.session_id, installTargets(command));
|
|
296
|
+
return fresh.length ? { context: `lurq checked ${fresh.join(", ")} before install. ${usageNudge(fresh)}` } : null;
|
|
297
|
+
}
|
|
298
|
+
var brief = (agent) => `lurq is connected to this session: package installs${agent === "cursor" ? "" : " and package.json dependency edits"} are verified automatically. Call lurq usage before writing code against a package API you know from training, and compare or evaluate when choosing a package.`;
|
|
299
|
+
var SCAN_TIMEOUT_MS = 5e3;
|
|
300
|
+
async function sessionDrift(input) {
|
|
301
|
+
const cwd = input.cwd;
|
|
302
|
+
if (typeof cwd !== "string" || !isJsProject(cwd)) return null;
|
|
303
|
+
if (unseen(input.session_id, [`scan:${cwd}`]).length === 0) return null;
|
|
304
|
+
try {
|
|
305
|
+
const { buildUpgradePlan, planHeadline, scanSourceDrift } = await import("./upgradePlan-ZS5IOVMZ.js");
|
|
306
|
+
const [plan, source] = await Promise.all([
|
|
307
|
+
buildUpgradePlan(cwd, { timeoutMs: SCAN_TIMEOUT_MS }),
|
|
308
|
+
scanSourceDrift(cwd)
|
|
309
|
+
]);
|
|
310
|
+
const headline = planHeadline(plan, source);
|
|
311
|
+
return headline && `lurq scanned this project: ${headline}`;
|
|
312
|
+
} catch {
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
async function sessionStart(input, agent) {
|
|
317
|
+
const { apiKey, getAlerts } = await import("./remote-FN6225AH.js");
|
|
318
|
+
apiKey();
|
|
319
|
+
const [alerts, drift] = await Promise.all([
|
|
320
|
+
getAlerts({ timeoutMs: 3e3, agent: agent === "claude" ? "claude-code" : agent }).catch(
|
|
321
|
+
() => null
|
|
322
|
+
),
|
|
323
|
+
sessionDrift(input)
|
|
324
|
+
]);
|
|
325
|
+
const text = [isJsProject(input.cwd) ? brief(agent) : null, drift, alerts].filter(Boolean).join("\n\n");
|
|
326
|
+
return text ? { context: text } : null;
|
|
327
|
+
}
|
|
328
|
+
function prompt(input) {
|
|
329
|
+
if (typeof input.prompt !== "string" || !isJsProject(input.cwd)) return null;
|
|
330
|
+
const tips = promptTips(input.prompt);
|
|
331
|
+
const fresh = new Set(
|
|
332
|
+
unseen(
|
|
333
|
+
input.session_id,
|
|
334
|
+
tips.map((t) => t.kind)
|
|
335
|
+
)
|
|
336
|
+
);
|
|
337
|
+
const text = tips.filter((t) => fresh.has(t.kind)).map((t) => t.tip).join("\n");
|
|
338
|
+
return text ? { context: text } : null;
|
|
339
|
+
}
|
|
340
|
+
var HOOK_AGENTS = ["claude", "codex", "cursor"];
|
|
341
|
+
function normalize(agent, input) {
|
|
342
|
+
if (agent !== "cursor") return input;
|
|
343
|
+
const command = typeof input.command === "string" ? input.command : input.tool_input?.command;
|
|
344
|
+
return {
|
|
345
|
+
session_id: input.conversation_id ?? input.session_id,
|
|
346
|
+
cwd: input.cwd ?? input.workspace_roots?.[0],
|
|
347
|
+
...typeof command === "string" ? { tool_name: "Bash", tool_input: { command } } : {}
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
var CLAUDE_EVENT = {
|
|
351
|
+
"session-start": "SessionStart",
|
|
352
|
+
prompt: "UserPromptSubmit",
|
|
353
|
+
"pre-tool-use": "PreToolUse"
|
|
354
|
+
};
|
|
355
|
+
function render(agent, event, outcome) {
|
|
356
|
+
if (!outcome) return null;
|
|
357
|
+
const { decision, context } = outcome;
|
|
358
|
+
if (agent === "cursor") {
|
|
359
|
+
if (decision) {
|
|
360
|
+
const message = decision.permissionDecisionReason;
|
|
361
|
+
return {
|
|
362
|
+
permission: decision.permissionDecision,
|
|
363
|
+
user_message: message,
|
|
364
|
+
agent_message: message
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
return context ? { additional_context: context } : null;
|
|
368
|
+
}
|
|
369
|
+
const hookEventName = CLAUDE_EVENT[event];
|
|
370
|
+
if (!hookEventName) return null;
|
|
371
|
+
if (decision) {
|
|
372
|
+
const asCodex = agent === "codex" && decision.permissionDecision === "ask" ? {
|
|
373
|
+
permissionDecision: "deny",
|
|
374
|
+
permissionDecisionReason: `${decision.permissionDecisionReason} Tell the user what lurq found. Only if they approve, run the install again prefixed with ${ALLOW_ENV}.`
|
|
375
|
+
} : decision;
|
|
376
|
+
return { hookSpecificOutput: { hookEventName, ...asCodex } };
|
|
377
|
+
}
|
|
378
|
+
return context ? { hookSpecificOutput: { hookEventName, additionalContext: context } } : null;
|
|
379
|
+
}
|
|
380
|
+
async function readStdin() {
|
|
381
|
+
const chunks = [];
|
|
382
|
+
for await (const chunk of process.stdin) chunks.push(chunk);
|
|
383
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
384
|
+
}
|
|
385
|
+
async function runHook(event, agentName = "claude") {
|
|
386
|
+
try {
|
|
387
|
+
const agent = HOOK_AGENTS.find((a) => a === agentName);
|
|
388
|
+
if (!agent) return;
|
|
389
|
+
const input = normalize(agent, JSON.parse(await readStdin()));
|
|
390
|
+
const outcome = event === "pre-tool-use" ? await preToolUse(input, agent !== "cursor") : event === "post-tool-use" ? postToolUse(input) : event === "session-start" ? await sessionStart(input, agent) : event === "prompt" ? prompt(input) : null;
|
|
391
|
+
const output = render(agent, event, outcome);
|
|
392
|
+
if (output) process.stdout.write(JSON.stringify(output));
|
|
393
|
+
} catch {
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
export {
|
|
397
|
+
ALLOW_ENV,
|
|
398
|
+
HOOK_AGENTS,
|
|
399
|
+
addedDependencies,
|
|
400
|
+
applyEdits,
|
|
401
|
+
decide,
|
|
402
|
+
installTargets,
|
|
403
|
+
isJsProject,
|
|
404
|
+
normalize,
|
|
405
|
+
patchedPackageJsons,
|
|
406
|
+
promptTips,
|
|
407
|
+
render,
|
|
408
|
+
runHook,
|
|
409
|
+
runTargets,
|
|
410
|
+
specName,
|
|
411
|
+
unseen
|
|
412
|
+
};
|