@cmnwlth/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,2195 @@
1
+ // src/schema.ts
2
+ import { z } from "zod";
3
+ var NOTE_KINDS = ["memory", "decision", "work-state", "person"];
4
+ var KIND_DIR = {
5
+ memory: "memory",
6
+ decision: "decisions",
7
+ "work-state": "work-state",
8
+ person: "people"
9
+ };
10
+ var IsoDate = z.preprocess(
11
+ (v) => v instanceof Date ? v.toISOString().slice(0, 10) : v,
12
+ z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "expected a YYYY-MM-DD date")
13
+ );
14
+ var SafeId = z.string().min(1).refine((s) => !s.includes("/") && !s.includes("\\") && s !== "." && s !== "..", {
15
+ message: "id must be a single path segment (no '/', '\\\\', or '..')"
16
+ });
17
+ var baseShape = {
18
+ id: SafeId,
19
+ title: z.string().min(1),
20
+ tags: z.array(z.string()).default([]),
21
+ created: IsoDate,
22
+ updated: IsoDate.optional(),
23
+ author: z.string().optional(),
24
+ /**
25
+ * Originating project the note was captured from — a stable repo identity (git `origin`
26
+ * slug, else the repo-root basename). Lets a shared brain group/filter notes by project
27
+ * (ADR-0015). Optional: pre-existing and non-project notes are "unattributed".
28
+ */
29
+ source: z.string().optional(),
30
+ /** Wikilink targets (`[[id]]` or bare id) to related notes. */
31
+ relates: z.array(z.string()).default([])
32
+ };
33
+ var MemoryFrontmatter = z.object({
34
+ ...baseShape,
35
+ kind: z.literal("memory"),
36
+ status: z.enum(["active", "superseded", "stale"]).default("active"),
37
+ /** Last time this fact was checked against reality (Kage-style verification). */
38
+ verified: IsoDate.optional(),
39
+ sources: z.array(z.string()).default([]),
40
+ superseded_by: z.string().nullable().optional()
41
+ }).passthrough();
42
+ var DecisionFrontmatter = z.object({
43
+ ...baseShape,
44
+ kind: z.literal("decision"),
45
+ status: z.enum(["proposed", "accepted", "superseded"]).default("proposed"),
46
+ supersedes: z.array(z.string()).default([]),
47
+ superseded_by: z.string().nullable().optional(),
48
+ deciders: z.array(z.string()).default([])
49
+ }).passthrough();
50
+ var WorkStateFrontmatter = z.object({
51
+ ...baseShape,
52
+ kind: z.literal("work-state"),
53
+ owner: z.string().optional(),
54
+ status: z.enum(["planned", "in-progress", "blocked", "done"]).default("planned")
55
+ }).passthrough();
56
+ var PersonFrontmatter = z.object({
57
+ ...baseShape,
58
+ kind: z.literal("person"),
59
+ name: z.string().min(1),
60
+ org: z.string().optional(),
61
+ role: z.string().optional()
62
+ }).passthrough();
63
+ var Frontmatter = z.discriminatedUnion("kind", [
64
+ MemoryFrontmatter,
65
+ DecisionFrontmatter,
66
+ WorkStateFrontmatter,
67
+ PersonFrontmatter
68
+ ]);
69
+ var SCHEMA_VERSION = 1;
70
+
71
+ // src/config.ts
72
+ import { promises as fs } from "fs";
73
+ import path from "path";
74
+ function scanOptions(config) {
75
+ return { detectEntropy: config.secretScan.entropy, allowlist: config.secretScan.allowlist };
76
+ }
77
+ var FEATURE_FLAGS = [
78
+ {
79
+ name: "autoAdr",
80
+ description: "Auto-create ADR/decision notes in the brain when a decision is captured",
81
+ default: false
82
+ },
83
+ {
84
+ name: "autoPromote",
85
+ description: "Auto-promote captured notes straight into canon instead of holding them in the review queue. Curation gating (dedup/validation) still applies; only the manual review step is skipped (ADR-0014). Set false to require manual /commonwealth:promote.",
86
+ default: true
87
+ }
88
+ ];
89
+ function defaultFeatures() {
90
+ const features = {};
91
+ for (const flag of FEATURE_FLAGS) {
92
+ features[flag.name] = flag.default;
93
+ }
94
+ return features;
95
+ }
96
+ function defaultBrainConfig(name) {
97
+ return {
98
+ name,
99
+ schemaVersion: SCHEMA_VERSION,
100
+ remotes: [],
101
+ curation: {},
102
+ features: defaultFeatures(),
103
+ secretScan: { entropy: false, allowlist: [] }
104
+ };
105
+ }
106
+ var schemaSkewWarned = /* @__PURE__ */ new Set();
107
+ function warnSchemaSkewOnce(brainDir, found) {
108
+ const key = path.resolve(brainDir);
109
+ if (schemaSkewWarned.has(key)) return;
110
+ schemaSkewWarned.add(key);
111
+ console.error(
112
+ `[commonwealth] brain at ${key} is schema v${found} but this build understands v${SCHEMA_VERSION}; some fields may not be read correctly \u2014 upgrade Commonwealth.`
113
+ );
114
+ }
115
+ var CONFIG_REL = path.join(".commonwealth", "config.json");
116
+ function brainConfigPath(brainDir) {
117
+ return path.join(brainDir, CONFIG_REL);
118
+ }
119
+ async function loadBrainConfig(brainDir) {
120
+ const defaults = defaultBrainConfig(path.basename(path.resolve(brainDir)));
121
+ let raw;
122
+ try {
123
+ raw = await fs.readFile(brainConfigPath(brainDir), "utf8");
124
+ } catch {
125
+ return defaults;
126
+ }
127
+ let parsed;
128
+ try {
129
+ parsed = JSON.parse(raw);
130
+ } catch {
131
+ return defaults;
132
+ }
133
+ const obj = typeof parsed === "object" && parsed !== null ? parsed : {};
134
+ if (typeof obj.schemaVersion === "number" && obj.schemaVersion > SCHEMA_VERSION) {
135
+ warnSchemaSkewOnce(brainDir, obj.schemaVersion);
136
+ }
137
+ return {
138
+ name: typeof obj.name === "string" ? obj.name : defaults.name,
139
+ schemaVersion: typeof obj.schemaVersion === "number" ? obj.schemaVersion : defaults.schemaVersion,
140
+ remotes: Array.isArray(obj.remotes) ? obj.remotes : defaults.remotes,
141
+ curation: typeof obj.curation === "object" && obj.curation !== null ? obj.curation : defaults.curation,
142
+ // Defaults first so missing flags are filled; file values win, unknown keys preserved.
143
+ features: {
144
+ ...defaults.features,
145
+ ...typeof obj.features === "object" && obj.features !== null ? obj.features : {}
146
+ },
147
+ secretScan: normalizeSecretScan(obj.secretScan, defaults.secretScan)
148
+ };
149
+ }
150
+ function normalizeSecretScan(raw, fallback) {
151
+ if (typeof raw !== "object" || raw === null) return fallback;
152
+ const obj = raw;
153
+ return {
154
+ entropy: typeof obj.entropy === "boolean" ? obj.entropy : fallback.entropy,
155
+ allowlist: Array.isArray(obj.allowlist) && obj.allowlist.every((v) => typeof v === "string") ? obj.allowlist : fallback.allowlist
156
+ };
157
+ }
158
+ async function saveBrainConfig(brainDir, config) {
159
+ const file = brainConfigPath(brainDir);
160
+ await fs.mkdir(path.dirname(file), { recursive: true });
161
+ const tmp = `${file}.${process.pid}.tmp`;
162
+ await fs.writeFile(tmp, `${JSON.stringify(config, null, 2)}
163
+ `, "utf8");
164
+ await fs.rename(tmp, file);
165
+ }
166
+ async function isFeatureEnabled(brainDir, feature) {
167
+ const config = await loadBrainConfig(brainDir);
168
+ return Boolean(config.features[feature]);
169
+ }
170
+ async function setFeature(brainDir, feature, on) {
171
+ const config = await loadBrainConfig(brainDir);
172
+ config.features[feature] = on;
173
+ await saveBrainConfig(brainDir, config);
174
+ }
175
+
176
+ // src/ids.ts
177
+ import GithubSlugger from "github-slugger";
178
+ import { customAlphabet } from "nanoid";
179
+ var nano = customAlphabet("0123456789abcdefghijklmnopqrstuvwxyz", 4);
180
+ function shortId() {
181
+ return nano();
182
+ }
183
+ function slugify(title) {
184
+ const slugger = new GithubSlugger();
185
+ const slug = slugger.slug(title).slice(0, 60);
186
+ return slug.replace(/-+$/, "");
187
+ }
188
+ function makeNoteId(title, created, suffix = shortId()) {
189
+ return `${created}-${slugify(title)}-${suffix}`;
190
+ }
191
+ function sourceSegment(source) {
192
+ if (typeof source !== "string") return "";
193
+ const seg = source.trim().replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^[-.]+|[-.]+$/g, "").slice(0, 80);
194
+ return seg;
195
+ }
196
+ function pathForNote(kind, id, source) {
197
+ const seg = sourceSegment(source);
198
+ return seg ? `${seg}/${KIND_DIR[kind]}/${id}.md` : `${KIND_DIR[kind]}/${id}.md`;
199
+ }
200
+ function today(date = /* @__PURE__ */ new Date()) {
201
+ return date.toISOString().slice(0, 10);
202
+ }
203
+
204
+ // src/notes.ts
205
+ import { promises as fs2 } from "fs";
206
+ import path2 from "path";
207
+ import matter from "gray-matter";
208
+ var KEY_ORDER = [
209
+ "id",
210
+ "kind",
211
+ "title",
212
+ "name",
213
+ "org",
214
+ "role",
215
+ "owner",
216
+ "status",
217
+ "tags",
218
+ "created",
219
+ "updated",
220
+ "author",
221
+ "source",
222
+ "verified",
223
+ "deciders",
224
+ "supersedes",
225
+ "superseded_by",
226
+ "sources",
227
+ "relates"
228
+ ];
229
+ function orderFrontmatter(fm) {
230
+ const out = {};
231
+ for (const key of KEY_ORDER) {
232
+ if (key in fm && fm[key] !== void 0) out[key] = fm[key];
233
+ }
234
+ for (const key of Object.keys(fm).sort()) {
235
+ if (!(key in out) && fm[key] !== void 0) out[key] = fm[key];
236
+ }
237
+ return out;
238
+ }
239
+ function parseNote(raw, notePath) {
240
+ const parsed = matter(raw);
241
+ const frontmatter = Frontmatter.parse(parsed.data);
242
+ return { frontmatter, body: parsed.content.trim(), path: notePath };
243
+ }
244
+ function resolveWithinBrain(brainDir, relPath) {
245
+ const base = path2.resolve(brainDir);
246
+ const abs = path2.resolve(base, relPath);
247
+ if (abs !== base && !abs.startsWith(base + path2.sep)) {
248
+ throw new Error(`Path escapes the brain directory: ${relPath}`);
249
+ }
250
+ return abs;
251
+ }
252
+ function serializeNote(note) {
253
+ const ordered = orderFrontmatter(note.frontmatter);
254
+ const body = note.body.trim();
255
+ return matter.stringify(body ? `${body}
256
+ ` : "", ordered);
257
+ }
258
+ async function writeNote(brainDir, input) {
259
+ const created = input.created ?? today();
260
+ const id = makeNoteId(input.title, created);
261
+ const relPath = pathForNote(input.kind, id, input.source);
262
+ const absPath = resolveWithinBrain(brainDir, relPath);
263
+ const raw = {
264
+ ...input.fields ?? {},
265
+ id,
266
+ kind: input.kind,
267
+ title: input.title,
268
+ tags: input.tags ?? [],
269
+ created,
270
+ ...input.author ? { author: input.author } : {},
271
+ ...input.source ? { source: input.source } : {}
272
+ };
273
+ const frontmatter = Frontmatter.parse(raw);
274
+ const note = { frontmatter, body: input.body.trim(), path: relPath };
275
+ const content = serializeNote(note);
276
+ await fs2.mkdir(path2.dirname(absPath), { recursive: true });
277
+ const tmp = `${absPath}.${shortId()}.tmp`;
278
+ await fs2.writeFile(tmp, content, "utf8");
279
+ try {
280
+ await fs2.link(tmp, absPath);
281
+ } catch (err) {
282
+ await fs2.rm(tmp, { force: true });
283
+ if (err.code === "EEXIST") {
284
+ throw new Error(`Refusing to overwrite an existing note at ${relPath} (id collision)`);
285
+ }
286
+ throw err;
287
+ }
288
+ await fs2.rm(tmp, { force: true });
289
+ return note;
290
+ }
291
+ async function overwriteNote(brainDir, note) {
292
+ const absPath = resolveWithinBrain(brainDir, note.path);
293
+ const content = serializeNote(note);
294
+ await fs2.mkdir(path2.dirname(absPath), { recursive: true });
295
+ const tmp = `${absPath}.${shortId()}.tmp`;
296
+ await fs2.writeFile(tmp, content, "utf8");
297
+ await fs2.rename(tmp, absPath);
298
+ }
299
+ async function supersedeNote(brainDir, relPath, survivorId) {
300
+ const note = await readNote(brainDir, relPath);
301
+ const fm = note.frontmatter;
302
+ if (fm.kind !== "memory" && fm.kind !== "decision") return note;
303
+ if (fm.status === "superseded" && fm.superseded_by === survivorId) return note;
304
+ const updated = {
305
+ ...note,
306
+ frontmatter: { ...fm, status: "superseded", superseded_by: survivorId }
307
+ };
308
+ await overwriteNote(brainDir, updated);
309
+ return updated;
310
+ }
311
+ async function readNote(brainDir, relPath) {
312
+ const raw = await fs2.readFile(resolveWithinBrain(brainDir, relPath), "utf8");
313
+ return parseNote(raw, relPath);
314
+ }
315
+ var KIND_FOLDERS = new Set(Object.values(KIND_DIR));
316
+ var NON_NOTE_DIRS = /* @__PURE__ */ new Set([".git", ".commonwealth", "index", "staging", "node_modules"]);
317
+ async function listNotes(brainDir, kind) {
318
+ const found = [];
319
+ async function walk(absDir) {
320
+ let entries;
321
+ try {
322
+ entries = await fs2.readdir(absDir, { withFileTypes: true });
323
+ } catch {
324
+ return;
325
+ }
326
+ for (const entry of entries) {
327
+ if (entry.isDirectory()) {
328
+ if (NON_NOTE_DIRS.has(entry.name)) continue;
329
+ await walk(path2.join(absDir, entry.name));
330
+ } else if (entry.isFile() && entry.name.endsWith(".md") && entry.name !== "INDEX.md" && KIND_FOLDERS.has(path2.basename(absDir))) {
331
+ found.push(path2.relative(brainDir, path2.join(absDir, entry.name)));
332
+ }
333
+ }
334
+ }
335
+ await walk(brainDir);
336
+ const notes = [];
337
+ for (const rel of found.sort()) {
338
+ let note;
339
+ try {
340
+ note = await readNote(brainDir, rel);
341
+ } catch (err) {
342
+ console.error(
343
+ `[commonwealth] skipping unreadable note ${rel}: ${err instanceof Error ? err.message : err}`
344
+ );
345
+ continue;
346
+ }
347
+ if (!kind || note.frontmatter.kind === kind) notes.push(note);
348
+ }
349
+ return notes;
350
+ }
351
+
352
+ // src/scaffold.ts
353
+ import { execFile } from "child_process";
354
+ import { existsSync, promises as fs3 } from "fs";
355
+ import path3 from "path";
356
+ import { promisify } from "util";
357
+ var pexec = promisify(execFile);
358
+ var KIND_DIRS = Object.values(KIND_DIR);
359
+ var KIND_INDEX_TITLE = {
360
+ memory: "Memory",
361
+ decisions: "Decisions",
362
+ "work-state": "Work-state",
363
+ people: "People"
364
+ };
365
+ var IGNORED_ENTRIES = /* @__PURE__ */ new Set([".git", ".gitkeep"]);
366
+ var BRAIN_ENTRIES = /* @__PURE__ */ new Set([
367
+ ".commonwealth",
368
+ ".gitattributes",
369
+ ".gitignore",
370
+ "COMMONWEALTH.md",
371
+ "index",
372
+ ...KIND_DIRS
373
+ ]);
374
+ var GITATTRIBUTES = ["COMMONWEALTH.md merge=union", "**/INDEX.md merge=union", ""].join("\n");
375
+ var GITIGNORE = ["index/", "staging/", "*.db", "*.db-shm", "*.db-wal", ".DS_Store", ""].join(
376
+ "\n"
377
+ );
378
+ async function initGitRepo(dir) {
379
+ if (existsSync(path3.join(dir, ".git"))) return;
380
+ try {
381
+ await pexec("git", ["init", "-q", "-b", "main", dir]);
382
+ await pexec("git", ["add", "-A"], { cwd: dir });
383
+ let identity = [];
384
+ try {
385
+ const email = (await pexec("git", ["config", "user.email"], { cwd: dir })).stdout.trim();
386
+ if (email.length === 0) throw new Error("no identity");
387
+ } catch {
388
+ identity = ["-c", "user.name=Commonwealth", "-c", "user.email=commonwealth@localhost"];
389
+ }
390
+ await pexec(
391
+ "git",
392
+ [...identity, "commit", "-q", "-m", "Initialize Commonwealth brain scaffold"],
393
+ {
394
+ cwd: dir
395
+ }
396
+ );
397
+ } catch {
398
+ }
399
+ }
400
+ async function isSafeToInit(dir) {
401
+ if (existsSync(path3.join(dir, ".commonwealth", "schema-version"))) return true;
402
+ let entries;
403
+ try {
404
+ entries = await fs3.readdir(dir);
405
+ } catch {
406
+ return true;
407
+ }
408
+ for (const entry of entries) {
409
+ if (IGNORED_ENTRIES.has(entry)) continue;
410
+ if (!BRAIN_ENTRIES.has(entry)) return false;
411
+ }
412
+ return true;
413
+ }
414
+ async function writeFile(file, contents) {
415
+ await fs3.mkdir(path3.dirname(file), { recursive: true });
416
+ await fs3.writeFile(file, contents, "utf8");
417
+ }
418
+ async function writeFileIfAbsent(file, contents) {
419
+ await fs3.mkdir(path3.dirname(file), { recursive: true });
420
+ try {
421
+ await fs3.writeFile(file, contents, { encoding: "utf8", flag: "wx" });
422
+ } catch (err) {
423
+ if (err.code === "EEXIST") return;
424
+ throw err;
425
+ }
426
+ }
427
+ async function initBrain(dir, opts = {}) {
428
+ if (!opts.force && !await isSafeToInit(dir)) {
429
+ throw new Error(
430
+ `Refusing to initialize a Commonwealth brain in a non-empty directory: ${dir}. Pass { force: true } to proceed.`
431
+ );
432
+ }
433
+ const name = opts.name ?? path3.basename(path3.resolve(dir));
434
+ await fs3.mkdir(dir, { recursive: true });
435
+ for (const kindDir of KIND_DIRS) {
436
+ const abs = path3.join(dir, kindDir);
437
+ await fs3.mkdir(abs, { recursive: true });
438
+ await writeFile(path3.join(abs, ".gitkeep"), "");
439
+ const title = KIND_INDEX_TITLE[kindDir] ?? kindDir;
440
+ await writeFile(path3.join(abs, "INDEX.md"), `# ${title} index
441
+
442
+ _generated_
443
+ `);
444
+ }
445
+ await writeFileIfAbsent(path3.join(dir, ".commonwealth", "schema-version"), `${SCHEMA_VERSION}
446
+ `);
447
+ const config = defaultBrainConfig(name);
448
+ await writeFileIfAbsent(
449
+ path3.join(dir, ".commonwealth", "config.json"),
450
+ `${JSON.stringify(config, null, 2)}
451
+ `
452
+ );
453
+ await writeFileIfAbsent(path3.join(dir, ".gitattributes"), GITATTRIBUTES);
454
+ await writeFileIfAbsent(path3.join(dir, ".gitignore"), GITIGNORE);
455
+ const commonwealth = [
456
+ `# ${name} \u2014 Commonwealth brain`,
457
+ "",
458
+ "_This file is generated. Do not edit by hand \u2014 it is regenerated from the note set._",
459
+ "",
460
+ "Run the Commonwealth index to populate the router with active work-state and recent decisions.",
461
+ ""
462
+ ].join("\n");
463
+ await writeFile(path3.join(dir, "COMMONWEALTH.md"), commonwealth);
464
+ await initGitRepo(dir);
465
+ }
466
+ var BRAIN_KIND_DIRS = KIND_DIRS;
467
+
468
+ // src/secrets-gitleaks.generated.ts
469
+ var GITLEAKS_PATTERNS = [
470
+ {
471
+ kind: "gitleaks:1password-secret-key",
472
+ re: new RegExp(
473
+ "\\bA3-[A-Z0-9]{6}-(?:(?:[A-Z0-9]{11})|(?:[A-Z0-9]{6}-[A-Z0-9]{5}))-[A-Z0-9]{5}-[A-Z0-9]{5}-[A-Z0-9]{5}\\b",
474
+ "g"
475
+ )
476
+ },
477
+ {
478
+ kind: "gitleaks:1password-service-account-token",
479
+ re: new RegExp("ops_eyJ[a-zA-Z0-9+/]{250,}={0,3}", "g")
480
+ },
481
+ {
482
+ kind: "gitleaks:adafruit-api-key",
483
+ re: new RegExp(
484
+ `[\\w.-]{0,50}?(?:adafruit)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9_-]{32})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
485
+ "gi"
486
+ )
487
+ },
488
+ {
489
+ kind: "gitleaks:adobe-client-id",
490
+ re: new RegExp(
491
+ `[\\w.-]{0,50}?(?:adobe)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-f0-9]{32})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
492
+ "gi"
493
+ )
494
+ },
495
+ {
496
+ kind: "gitleaks:age-secret-key",
497
+ re: new RegExp("AGE-SECRET-KEY-1[QPZRY9X8GF2TVDW0S3JN54KHCE6MUA7L]{58}", "g")
498
+ },
499
+ {
500
+ kind: "gitleaks:airtable-api-key",
501
+ re: new RegExp(
502
+ `[\\w.-]{0,50}?(?:airtable)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9]{17})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
503
+ "gi"
504
+ )
505
+ },
506
+ {
507
+ kind: "gitleaks:airtable-personnal-access-token",
508
+ re: new RegExp("\\b(pat[A-Za-z0-9]{14}\\.[a-f0-9]{64})\\b", "g")
509
+ },
510
+ {
511
+ kind: "gitleaks:algolia-api-key",
512
+ re: new RegExp(
513
+ `[\\w.-]{0,50}?(?:algolia)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9]{32})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
514
+ "gi"
515
+ )
516
+ },
517
+ {
518
+ kind: "gitleaks:alibaba-secret-key",
519
+ re: new RegExp(
520
+ `[\\w.-]{0,50}?(?:alibaba)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9]{30})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
521
+ "gi"
522
+ )
523
+ },
524
+ {
525
+ kind: "gitleaks:anthropic-admin-api-key",
526
+ re: new RegExp(`\\b(sk-ant-admin01-[a-zA-Z0-9_\\-]{93}AA)(?:[\\x60'"\\s;]|\\\\[nr]|$)`, "g")
527
+ },
528
+ {
529
+ kind: "gitleaks:anthropic-api-key",
530
+ re: new RegExp(`\\b(sk-ant-api03-[a-zA-Z0-9_\\-]{93}AA)(?:[\\x60'"\\s;]|\\\\[nr]|$)`, "g")
531
+ },
532
+ { kind: "gitleaks:artifactory-api-key", re: new RegExp("\\bAKCp[A-Za-z0-9]{69}\\b", "g") },
533
+ {
534
+ kind: "gitleaks:artifactory-reference-token",
535
+ re: new RegExp("\\bcmVmd[A-Za-z0-9]{59}\\b", "g")
536
+ },
537
+ {
538
+ kind: "gitleaks:asana-client-id",
539
+ re: new RegExp(
540
+ `[\\w.-]{0,50}?(?:asana)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([0-9]{16})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
541
+ "gi"
542
+ )
543
+ },
544
+ {
545
+ kind: "gitleaks:asana-client-secret",
546
+ re: new RegExp(
547
+ `[\\w.-]{0,50}?(?:asana)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9]{32})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
548
+ "gi"
549
+ )
550
+ },
551
+ {
552
+ kind: "gitleaks:aws-access-token",
553
+ re: new RegExp("\\b((?:A3T[A-Z0-9]|AKIA|ASIA|ABIA|ACCA)[A-Z2-7]{16})\\b", "g")
554
+ },
555
+ {
556
+ kind: "gitleaks:aws-amazon-bedrock-api-key-long-lived",
557
+ re: new RegExp(`\\b(ABSK[A-Za-z0-9+/]{109,269}={0,2})(?:[\\x60'"\\s;]|\\\\[nr]|$)`, "g")
558
+ },
559
+ {
560
+ kind: "gitleaks:aws-amazon-bedrock-api-key-short-lived",
561
+ re: new RegExp("bedrock-api-key-YmVkcm9jay5hbWF6b25hd3MuY29t", "g")
562
+ },
563
+ {
564
+ kind: "gitleaks:azure-ad-client-secret",
565
+ re: new RegExp(
566
+ `(?:^|[\\\\'"\\x60\\s>=:(,)])([a-zA-Z0-9_~.]{3}\\dQ~[a-zA-Z0-9_~.-]{31,34})(?:$|[\\\\'"\\x60\\s<),])`,
567
+ "g"
568
+ )
569
+ },
570
+ {
571
+ kind: "gitleaks:beamer-api-token",
572
+ re: new RegExp(
573
+ `[\\w.-]{0,50}?(?:beamer)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}(b_[a-z0-9=_\\-]{44})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
574
+ "gi"
575
+ )
576
+ },
577
+ {
578
+ kind: "gitleaks:bitbucket-client-id",
579
+ re: new RegExp(
580
+ `[\\w.-]{0,50}?(?:bitbucket)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9]{32})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
581
+ "gi"
582
+ )
583
+ },
584
+ {
585
+ kind: "gitleaks:bitbucket-client-secret",
586
+ re: new RegExp(
587
+ `[\\w.-]{0,50}?(?:bitbucket)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9=_\\-]{64})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
588
+ "gi"
589
+ )
590
+ },
591
+ {
592
+ kind: "gitleaks:bittrex-access-key",
593
+ re: new RegExp(
594
+ `[\\w.-]{0,50}?(?:bittrex)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9]{32})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
595
+ "gi"
596
+ )
597
+ },
598
+ {
599
+ kind: "gitleaks:bittrex-secret-key",
600
+ re: new RegExp(
601
+ `[\\w.-]{0,50}?(?:bittrex)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9]{32})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
602
+ "gi"
603
+ )
604
+ },
605
+ {
606
+ kind: "gitleaks:clickhouse-cloud-api-secret-key",
607
+ re: new RegExp("\\b(4b1d[A-Za-z0-9]{38})\\b", "g")
608
+ },
609
+ { kind: "gitleaks:clojars-api-token", re: new RegExp("CLOJARS_[a-z0-9]{60}", "gi") },
610
+ {
611
+ kind: "gitleaks:cloudflare-api-key",
612
+ re: new RegExp(
613
+ `[\\w.-]{0,50}?(?:cloudflare)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9_-]{40})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
614
+ "gi"
615
+ )
616
+ },
617
+ {
618
+ kind: "gitleaks:cloudflare-global-api-key",
619
+ re: new RegExp(
620
+ `[\\w.-]{0,50}?(?:cloudflare)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-f0-9]{37})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
621
+ "gi"
622
+ )
623
+ },
624
+ {
625
+ kind: "gitleaks:cloudflare-origin-ca-key",
626
+ re: new RegExp(`\\b(v1\\.0-[a-f0-9]{24}-[a-f0-9]{146})(?:[\\x60'"\\s;]|\\\\[nr]|$)`, "g")
627
+ },
628
+ {
629
+ kind: "gitleaks:codecov-access-token",
630
+ re: new RegExp(
631
+ `[\\w.-]{0,50}?(?:codecov)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9]{32})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
632
+ "gi"
633
+ )
634
+ },
635
+ {
636
+ kind: "gitleaks:coinbase-access-token",
637
+ re: new RegExp(
638
+ `[\\w.-]{0,50}?(?:coinbase)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9_-]{64})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
639
+ "gi"
640
+ )
641
+ },
642
+ {
643
+ kind: "gitleaks:confluent-access-token",
644
+ re: new RegExp(
645
+ `[\\w.-]{0,50}?(?:confluent)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9]{16})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
646
+ "gi"
647
+ )
648
+ },
649
+ {
650
+ kind: "gitleaks:confluent-secret-key",
651
+ re: new RegExp(
652
+ `[\\w.-]{0,50}?(?:confluent)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9]{64})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
653
+ "gi"
654
+ )
655
+ },
656
+ {
657
+ kind: "gitleaks:contentful-delivery-api-token",
658
+ re: new RegExp(
659
+ `[\\w.-]{0,50}?(?:contentful)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9=_\\-]{43})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
660
+ "gi"
661
+ )
662
+ },
663
+ {
664
+ kind: "gitleaks:curl-auth-user",
665
+ re: new RegExp(
666
+ `\\bcurl\\b(?:.*|.*(?:[\\r\\n]{1,2}.*){1,5})[ \\t\\n\\r](?:-u|--user)(?:=|[ \\t]{0,5})("(:[^"]{3,}|[^:"]{3,}:|[^:"]{3,}:[^"]{3,})"|'([^:']{3,}:[^']{3,})'|((?:"[^"]{3,}"|'[^']{3,}'|[\\w$@.-]+):(?:"[^"]{3,}"|'[^']{3,}'|[\\w\${}@.-]+)))(?:\\s|\\z)`,
667
+ "g"
668
+ )
669
+ },
670
+ {
671
+ kind: "gitleaks:databricks-api-token",
672
+ re: new RegExp(`\\b(dapi[a-f0-9]{32}(?:-\\d)?)(?:[\\x60'"\\s;]|\\\\[nr]|$)`, "g")
673
+ },
674
+ {
675
+ kind: "gitleaks:datadog-access-token",
676
+ re: new RegExp(
677
+ `[\\w.-]{0,50}?(?:datadog)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9]{40})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
678
+ "gi"
679
+ )
680
+ },
681
+ {
682
+ kind: "gitleaks:defined-networking-api-token",
683
+ re: new RegExp(
684
+ `[\\w.-]{0,50}?(?:dnkey)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}(dnkey-[a-z0-9=_\\-]{26}-[a-z0-9=_\\-]{52})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
685
+ "gi"
686
+ )
687
+ },
688
+ {
689
+ kind: "gitleaks:digitalocean-access-token",
690
+ re: new RegExp(`\\b(doo_v1_[a-f0-9]{64})(?:[\\x60'"\\s;]|\\\\[nr]|$)`, "g")
691
+ },
692
+ {
693
+ kind: "gitleaks:digitalocean-pat",
694
+ re: new RegExp(`\\b(dop_v1_[a-f0-9]{64})(?:[\\x60'"\\s;]|\\\\[nr]|$)`, "g")
695
+ },
696
+ {
697
+ kind: "gitleaks:digitalocean-refresh-token",
698
+ re: new RegExp(`\\b(dor_v1_[a-f0-9]{64})(?:[\\x60'"\\s;]|\\\\[nr]|$)`, "gi")
699
+ },
700
+ {
701
+ kind: "gitleaks:discord-api-token",
702
+ re: new RegExp(
703
+ `[\\w.-]{0,50}?(?:discord)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-f0-9]{64})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
704
+ "gi"
705
+ )
706
+ },
707
+ {
708
+ kind: "gitleaks:discord-client-id",
709
+ re: new RegExp(
710
+ `[\\w.-]{0,50}?(?:discord)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([0-9]{18})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
711
+ "gi"
712
+ )
713
+ },
714
+ {
715
+ kind: "gitleaks:discord-client-secret",
716
+ re: new RegExp(
717
+ `[\\w.-]{0,50}?(?:discord)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9=_\\-]{32})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
718
+ "gi"
719
+ )
720
+ },
721
+ {
722
+ kind: "gitleaks:droneci-access-token",
723
+ re: new RegExp(
724
+ `[\\w.-]{0,50}?(?:droneci)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9]{32})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
725
+ "gi"
726
+ )
727
+ },
728
+ {
729
+ kind: "gitleaks:dropbox-api-token",
730
+ re: new RegExp(
731
+ `[\\w.-]{0,50}?(?:dropbox)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9]{15})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
732
+ "gi"
733
+ )
734
+ },
735
+ {
736
+ kind: "gitleaks:dropbox-long-lived-api-token",
737
+ re: new RegExp(
738
+ `[\\w.-]{0,50}?(?:dropbox)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9]{11}(AAAAAAAAAA)[a-z0-9\\-_=]{43})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
739
+ "gi"
740
+ )
741
+ },
742
+ {
743
+ kind: "gitleaks:dropbox-short-lived-api-token",
744
+ re: new RegExp(
745
+ `[\\w.-]{0,50}?(?:dropbox)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}(sl\\.[a-z0-9\\-=_]{135})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
746
+ "gi"
747
+ )
748
+ },
749
+ {
750
+ kind: "gitleaks:facebook-access-token",
751
+ re: new RegExp(`\\b(\\d{15,16}(\\||%)[0-9a-z\\-_]{27,40})(?:[\\x60'"\\s;]|\\\\[nr]|$)`, "gi")
752
+ },
753
+ {
754
+ kind: "gitleaks:facebook-secret",
755
+ re: new RegExp(
756
+ `[\\w.-]{0,50}?(?:facebook)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-f0-9]{32})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
757
+ "gi"
758
+ )
759
+ },
760
+ {
761
+ kind: "gitleaks:fastly-api-token",
762
+ re: new RegExp(
763
+ `[\\w.-]{0,50}?(?:fastly)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9=_\\-]{32})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
764
+ "gi"
765
+ )
766
+ },
767
+ {
768
+ kind: "gitleaks:finicity-api-token",
769
+ re: new RegExp(
770
+ `[\\w.-]{0,50}?(?:finicity)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-f0-9]{32})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
771
+ "gi"
772
+ )
773
+ },
774
+ {
775
+ kind: "gitleaks:finicity-client-secret",
776
+ re: new RegExp(
777
+ `[\\w.-]{0,50}?(?:finicity)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9]{20})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
778
+ "gi"
779
+ )
780
+ },
781
+ {
782
+ kind: "gitleaks:finnhub-access-token",
783
+ re: new RegExp(
784
+ `[\\w.-]{0,50}?(?:finnhub)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9]{20})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
785
+ "gi"
786
+ )
787
+ },
788
+ {
789
+ kind: "gitleaks:flickr-access-token",
790
+ re: new RegExp(
791
+ `[\\w.-]{0,50}?(?:flickr)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9]{32})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
792
+ "gi"
793
+ )
794
+ },
795
+ {
796
+ kind: "gitleaks:flyio-access-token",
797
+ re: new RegExp(
798
+ `\\b((?:fo1_[\\w-]{43}|fm1[ar]_[a-zA-Z0-9+\\/]{100,}={0,3}|fm2_[a-zA-Z0-9+\\/]{100,}={0,3}))(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
799
+ "g"
800
+ )
801
+ },
802
+ {
803
+ kind: "gitleaks:freemius-secret-key",
804
+ re: new RegExp(`["']secret_key["']\\s*=>\\s*["'](sk_[\\S]{29})["']`, "gi")
805
+ },
806
+ {
807
+ kind: "gitleaks:freshbooks-access-token",
808
+ re: new RegExp(
809
+ `[\\w.-]{0,50}?(?:freshbooks)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9]{64})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
810
+ "gi"
811
+ )
812
+ },
813
+ {
814
+ kind: "gitleaks:gcp-api-key",
815
+ re: new RegExp(`\\b(AIza[\\w-]{35})(?:[\\x60'"\\s;]|\\\\[nr]|$)`, "g")
816
+ },
817
+ { kind: "gitleaks:github-app-token", re: new RegExp("(?:ghu|ghs)_[0-9a-zA-Z]{36}", "g") },
818
+ { kind: "gitleaks:github-fine-grained-pat", re: new RegExp("github_pat_\\w{82}", "g") },
819
+ { kind: "gitleaks:github-oauth", re: new RegExp("gho_[0-9a-zA-Z]{36}", "g") },
820
+ { kind: "gitleaks:github-pat", re: new RegExp("ghp_[0-9a-zA-Z]{36}", "g") },
821
+ { kind: "gitleaks:github-refresh-token", re: new RegExp("ghr_[0-9a-zA-Z]{36}", "g") },
822
+ {
823
+ kind: "gitleaks:gitlab-cicd-job-token",
824
+ re: new RegExp("glcbt-[0-9a-zA-Z]{1,5}_[0-9a-zA-Z_-]{20}", "g")
825
+ },
826
+ { kind: "gitleaks:gitlab-deploy-token", re: new RegExp("gldt-[0-9a-zA-Z_\\-]{20}", "g") },
827
+ {
828
+ kind: "gitleaks:gitlab-feature-flag-client-token",
829
+ re: new RegExp("glffct-[0-9a-zA-Z_\\-]{20}", "g")
830
+ },
831
+ { kind: "gitleaks:gitlab-feed-token", re: new RegExp("glft-[0-9a-zA-Z_\\-]{20}", "g") },
832
+ { kind: "gitleaks:gitlab-incoming-mail-token", re: new RegExp("glimt-[0-9a-zA-Z_\\-]{25}", "g") },
833
+ {
834
+ kind: "gitleaks:gitlab-kubernetes-agent-token",
835
+ re: new RegExp("glagent-[0-9a-zA-Z_\\-]{50}", "g")
836
+ },
837
+ { kind: "gitleaks:gitlab-oauth-app-secret", re: new RegExp("gloas-[0-9a-zA-Z_\\-]{64}", "g") },
838
+ { kind: "gitleaks:gitlab-pat", re: new RegExp("glpat-[\\w-]{20}", "g") },
839
+ {
840
+ kind: "gitleaks:gitlab-pat-routable",
841
+ re: new RegExp("\\bglpat-[0-9a-zA-Z_-]{27,300}\\.[0-9a-z]{2}[0-9a-z]{7}\\b", "g")
842
+ },
843
+ { kind: "gitleaks:gitlab-ptt", re: new RegExp("glptt-[0-9a-f]{40}", "g") },
844
+ { kind: "gitleaks:gitlab-rrt", re: new RegExp("GR1348941[\\w-]{20}", "g") },
845
+ {
846
+ kind: "gitleaks:gitlab-runner-authentication-token",
847
+ re: new RegExp("glrt-[0-9a-zA-Z_\\-]{20}", "g")
848
+ },
849
+ {
850
+ kind: "gitleaks:gitlab-runner-authentication-token-routable",
851
+ re: new RegExp("\\bglrt-t\\d_[0-9a-zA-Z_\\-]{27,300}\\.[0-9a-z]{2}[0-9a-z]{7}\\b", "g")
852
+ },
853
+ { kind: "gitleaks:gitlab-scim-token", re: new RegExp("glsoat-[0-9a-zA-Z_\\-]{20}", "g") },
854
+ { kind: "gitleaks:gitlab-session-cookie", re: new RegExp("_gitlab_session=[0-9a-z]{32}", "g") },
855
+ {
856
+ kind: "gitleaks:gitter-access-token",
857
+ re: new RegExp(
858
+ `[\\w.-]{0,50}?(?:gitter)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9_-]{40})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
859
+ "gi"
860
+ )
861
+ },
862
+ {
863
+ kind: "gitleaks:grafana-api-key",
864
+ re: new RegExp(`\\b(eyJrIjoi[A-Za-z0-9]{70,400}={0,3})(?:[\\x60'"\\s;]|\\\\[nr]|$)`, "gi")
865
+ },
866
+ {
867
+ kind: "gitleaks:grafana-cloud-api-token",
868
+ re: new RegExp(`\\b(glc_[A-Za-z0-9+/]{32,400}={0,3})(?:[\\x60'"\\s;]|\\\\[nr]|$)`, "gi")
869
+ },
870
+ {
871
+ kind: "gitleaks:grafana-service-account-token",
872
+ re: new RegExp(`\\b(glsa_[A-Za-z0-9]{32}_[A-Fa-f0-9]{8})(?:[\\x60'"\\s;]|\\\\[nr]|$)`, "gi")
873
+ },
874
+ {
875
+ kind: "gitleaks:harness-api-key",
876
+ re: new RegExp("(?:pat|sat)\\.[a-zA-Z0-9_-]{22}\\.[a-zA-Z0-9]{24}\\.[a-zA-Z0-9]{20}", "g")
877
+ },
878
+ {
879
+ kind: "gitleaks:hashicorp-tf-password",
880
+ re: new RegExp(
881
+ `[\\w.-]{0,50}?(?:administrator_login_password|password)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}("[a-z0-9=_\\-]{8,20}")(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
882
+ "gi"
883
+ )
884
+ },
885
+ {
886
+ kind: "gitleaks:heroku-api-key",
887
+ re: new RegExp(
888
+ `[\\w.-]{0,50}?(?:heroku)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
889
+ "gi"
890
+ )
891
+ },
892
+ {
893
+ kind: "gitleaks:heroku-api-key-v2",
894
+ re: new RegExp(`\\b((HRKU-AA[0-9a-zA-Z_-]{58}))(?:[\\x60'"\\s;]|\\\\[nr]|$)`, "g")
895
+ },
896
+ {
897
+ kind: "gitleaks:hubspot-api-key",
898
+ re: new RegExp(
899
+ `[\\w.-]{0,50}?(?:hubspot)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
900
+ "gi"
901
+ )
902
+ },
903
+ {
904
+ kind: "gitleaks:infracost-api-token",
905
+ re: new RegExp(`\\b(ico-[a-zA-Z0-9]{32})(?:[\\x60'"\\s;]|\\\\[nr]|$)`, "g")
906
+ },
907
+ {
908
+ kind: "gitleaks:intercom-api-key",
909
+ re: new RegExp(
910
+ `[\\w.-]{0,50}?(?:intercom)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9=_\\-]{60})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
911
+ "gi"
912
+ )
913
+ },
914
+ {
915
+ kind: "gitleaks:jfrog-api-key",
916
+ re: new RegExp(
917
+ `[\\w.-]{0,50}?(?:jfrog|artifactory|bintray|xray)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9]{73})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
918
+ "gi"
919
+ )
920
+ },
921
+ {
922
+ kind: "gitleaks:jfrog-identity-token",
923
+ re: new RegExp(
924
+ `[\\w.-]{0,50}?(?:jfrog|artifactory|bintray|xray)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9]{64})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
925
+ "gi"
926
+ )
927
+ },
928
+ {
929
+ kind: "gitleaks:jwt",
930
+ re: new RegExp(
931
+ `\\b(ey[a-zA-Z0-9]{17,}\\.ey[a-zA-Z0-9\\/\\\\_-]{17,}\\.(?:[a-zA-Z0-9\\/\\\\_-]{10,}={0,2})?)(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
932
+ "g"
933
+ )
934
+ },
935
+ {
936
+ kind: "gitleaks:jwt-base64",
937
+ re: new RegExp(
938
+ "\\bZXlK(?:(?<alg>aGJHY2lPaU)|(?<apu>aGNIVWlPaU)|(?<apv>aGNIWWlPaU)|(?<aud>aGRXUWlPaU)|(?<b64>aU5qUWlP)|(?<crit>amNtbDBJanBi)|(?<cty>amRIa2lPaU)|(?<epk>bGNHc2lPbn)|(?<enc>bGJtTWlPaU)|(?<jku>cWEzVWlPaU)|(?<jwk>cWQyc2lPb)|(?<iss>cGMzTWlPaU)|(?<iv>cGRpSTZJ)|(?<kid>cmFXUWlP)|(?<key_ops>clpYbGZiM0J6SWpwY)|(?<kty>cmRIa2lPaUp)|(?<nonce>dWIyNWpaU0k2)|(?<p2c>d01tTWlP)|(?<p2s>d01uTWlPaU)|(?<ppt>d2NIUWlPaU)|(?<sub>emRXSWlPaU)|(?<svt>emRuUWlP)|(?<tag>MFlXY2lPaU)|(?<typ>MGVYQWlPaUp)|(?<url>MWNtd2l)|(?<use>MWMyVWlPaUp)|(?<ver>MlpYSWlPaU)|(?<version>MlpYSnphVzl1SWpv)|(?<x>NElqb2)|(?<x5c>NE5XTWlP)|(?<x5t>NE5YUWlPaU)|(?<x5ts256>NE5YUWpVekkxTmlJNkl)|(?<x5u>NE5YVWlPaU)|(?<zip>NmFYQWlPaU))[a-zA-Z0-9\\/\\\\_+\\-\\r\\n]{40,}={0,2}",
939
+ "g"
940
+ )
941
+ },
942
+ {
943
+ kind: "gitleaks:kraken-access-token",
944
+ re: new RegExp(
945
+ `[\\w.-]{0,50}?(?:kraken)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9\\/=_\\+\\-]{80,90})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
946
+ "gi"
947
+ )
948
+ },
949
+ {
950
+ kind: "gitleaks:kucoin-access-token",
951
+ re: new RegExp(
952
+ `[\\w.-]{0,50}?(?:kucoin)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-f0-9]{24})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
953
+ "gi"
954
+ )
955
+ },
956
+ {
957
+ kind: "gitleaks:kucoin-secret-key",
958
+ re: new RegExp(
959
+ `[\\w.-]{0,50}?(?:kucoin)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
960
+ "gi"
961
+ )
962
+ },
963
+ {
964
+ kind: "gitleaks:launchdarkly-access-token",
965
+ re: new RegExp(
966
+ `[\\w.-]{0,50}?(?:launchdarkly)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9=_\\-]{40})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
967
+ "gi"
968
+ )
969
+ },
970
+ {
971
+ kind: "gitleaks:linear-client-secret",
972
+ re: new RegExp(
973
+ `[\\w.-]{0,50}?(?:linear)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-f0-9]{32})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
974
+ "gi"
975
+ )
976
+ },
977
+ {
978
+ kind: "gitleaks:linkedin-client-id",
979
+ re: new RegExp(
980
+ `[\\w.-]{0,50}?(?:linked[_-]?in)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9]{14})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
981
+ "gi"
982
+ )
983
+ },
984
+ {
985
+ kind: "gitleaks:linkedin-client-secret",
986
+ re: new RegExp(
987
+ `[\\w.-]{0,50}?(?:linked[_-]?in)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9]{16})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
988
+ "gi"
989
+ )
990
+ },
991
+ {
992
+ kind: "gitleaks:lob-api-key",
993
+ re: new RegExp(
994
+ `[\\w.-]{0,50}?(?:lob)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}((live|test)_[a-f0-9]{35})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
995
+ "gi"
996
+ )
997
+ },
998
+ {
999
+ kind: "gitleaks:lob-pub-api-key",
1000
+ re: new RegExp(
1001
+ `[\\w.-]{0,50}?(?:lob)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}((test|live)_pub_[a-f0-9]{31})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1002
+ "gi"
1003
+ )
1004
+ },
1005
+ {
1006
+ kind: "gitleaks:looker-client-id",
1007
+ re: new RegExp(
1008
+ `[\\w.-]{0,50}?(?:looker)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9]{20})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1009
+ "gi"
1010
+ )
1011
+ },
1012
+ {
1013
+ kind: "gitleaks:looker-client-secret",
1014
+ re: new RegExp(
1015
+ `[\\w.-]{0,50}?(?:looker)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9]{24})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1016
+ "gi"
1017
+ )
1018
+ },
1019
+ {
1020
+ kind: "gitleaks:mailchimp-api-key",
1021
+ re: new RegExp(
1022
+ `[\\w.-]{0,50}?(?:MailchimpSDK.initialize|mailchimp)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-f0-9]{32}-us\\d\\d)(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1023
+ "gi"
1024
+ )
1025
+ },
1026
+ {
1027
+ kind: "gitleaks:mailgun-private-api-token",
1028
+ re: new RegExp(
1029
+ `[\\w.-]{0,50}?(?:mailgun)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}(key-[a-f0-9]{32})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1030
+ "gi"
1031
+ )
1032
+ },
1033
+ {
1034
+ kind: "gitleaks:mailgun-pub-key",
1035
+ re: new RegExp(
1036
+ `[\\w.-]{0,50}?(?:mailgun)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}(pubkey-[a-f0-9]{32})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1037
+ "gi"
1038
+ )
1039
+ },
1040
+ {
1041
+ kind: "gitleaks:mailgun-signing-key",
1042
+ re: new RegExp(
1043
+ `[\\w.-]{0,50}?(?:mailgun)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-h0-9]{32}-[a-h0-9]{8}-[a-h0-9]{8})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1044
+ "gi"
1045
+ )
1046
+ },
1047
+ {
1048
+ kind: "gitleaks:mapbox-api-token",
1049
+ re: new RegExp(
1050
+ `[\\w.-]{0,50}?(?:mapbox)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}(pk\\.[a-z0-9]{60}\\.[a-z0-9]{22})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1051
+ "gi"
1052
+ )
1053
+ },
1054
+ {
1055
+ kind: "gitleaks:mattermost-access-token",
1056
+ re: new RegExp(
1057
+ `[\\w.-]{0,50}?(?:mattermost)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9]{26})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1058
+ "gi"
1059
+ )
1060
+ },
1061
+ {
1062
+ kind: "gitleaks:maxmind-license-key",
1063
+ re: new RegExp(`\\b([A-Za-z0-9]{6}_[A-Za-z0-9]{29}_mmk)(?:[\\x60'"\\s;]|\\\\[nr]|$)`, "g")
1064
+ },
1065
+ {
1066
+ kind: "gitleaks:messagebird-api-token",
1067
+ re: new RegExp(
1068
+ `[\\w.-]{0,50}?(?:message[_-]?bird)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9]{25})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1069
+ "gi"
1070
+ )
1071
+ },
1072
+ {
1073
+ kind: "gitleaks:messagebird-client-id",
1074
+ re: new RegExp(
1075
+ `[\\w.-]{0,50}?(?:message[_-]?bird)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1076
+ "gi"
1077
+ )
1078
+ },
1079
+ {
1080
+ kind: "gitleaks:microsoft-teams-webhook",
1081
+ re: new RegExp(
1082
+ "https://[a-z0-9]+\\.webhook\\.office\\.com/webhookb2/[a-z0-9]{8}-([a-z0-9]{4}-){3}[a-z0-9]{12}@[a-z0-9]{8}-([a-z0-9]{4}-){3}[a-z0-9]{12}/IncomingWebhook/[a-z0-9]{32}/[a-z0-9]{8}-([a-z0-9]{4}-){3}[a-z0-9]{12}",
1083
+ "g"
1084
+ )
1085
+ },
1086
+ {
1087
+ kind: "gitleaks:netlify-access-token",
1088
+ re: new RegExp(
1089
+ `[\\w.-]{0,50}?(?:netlify)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9=_\\-]{40,46})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1090
+ "gi"
1091
+ )
1092
+ },
1093
+ {
1094
+ kind: "gitleaks:new-relic-browser-api-token",
1095
+ re: new RegExp(
1096
+ `[\\w.-]{0,50}?(?:new-relic|newrelic|new_relic)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}(NRJS-[a-f0-9]{19})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1097
+ "gi"
1098
+ )
1099
+ },
1100
+ {
1101
+ kind: "gitleaks:new-relic-insert-key",
1102
+ re: new RegExp(
1103
+ `[\\w.-]{0,50}?(?:new-relic|newrelic|new_relic)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}(NRII-[a-z0-9-]{32})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1104
+ "gi"
1105
+ )
1106
+ },
1107
+ {
1108
+ kind: "gitleaks:new-relic-user-api-id",
1109
+ re: new RegExp(
1110
+ `[\\w.-]{0,50}?(?:new-relic|newrelic|new_relic)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9]{64})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1111
+ "gi"
1112
+ )
1113
+ },
1114
+ {
1115
+ kind: "gitleaks:new-relic-user-api-key",
1116
+ re: new RegExp(
1117
+ `[\\w.-]{0,50}?(?:new-relic|newrelic|new_relic)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}(NRAK-[a-z0-9]{27})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1118
+ "gi"
1119
+ )
1120
+ },
1121
+ {
1122
+ kind: "gitleaks:notion-api-token",
1123
+ re: new RegExp(
1124
+ `\\b(ntn_[0-9]{11}[A-Za-z0-9]{32}[A-Za-z0-9]{3})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1125
+ "g"
1126
+ )
1127
+ },
1128
+ {
1129
+ kind: "gitleaks:npm-access-token",
1130
+ re: new RegExp(`\\b(npm_[a-z0-9]{36})(?:[\\x60'"\\s;]|\\\\[nr]|$)`, "gi")
1131
+ },
1132
+ {
1133
+ kind: "gitleaks:nuget-config-password",
1134
+ re: new RegExp('<add key=\\"(?:(?:ClearText)?Password)\\"\\s*value=\\"(.{8,})\\"\\s*/>', "gi")
1135
+ },
1136
+ {
1137
+ kind: "gitleaks:nytimes-access-token",
1138
+ re: new RegExp(
1139
+ `[\\w.-]{0,50}?(?:nytimes|new-york-times,|newyorktimes)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9=_\\-]{32})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1140
+ "gi"
1141
+ )
1142
+ },
1143
+ {
1144
+ kind: "gitleaks:octopus-deploy-api-key",
1145
+ re: new RegExp(`\\b(API-[A-Z0-9]{26})(?:[\\x60'"\\s;]|\\\\[nr]|$)`, "g")
1146
+ },
1147
+ {
1148
+ kind: "gitleaks:openai-api-key",
1149
+ re: new RegExp(
1150
+ `\\b(sk-(?:proj|svcacct|admin)-(?:[A-Za-z0-9_-]{74}|[A-Za-z0-9_-]{58})T3BlbkFJ(?:[A-Za-z0-9_-]{74}|[A-Za-z0-9_-]{58})\\b|sk-[a-zA-Z0-9]{20}T3BlbkFJ[a-zA-Z0-9]{20})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1151
+ "g"
1152
+ )
1153
+ },
1154
+ {
1155
+ kind: "gitleaks:openshift-user-token",
1156
+ re: new RegExp("\\b(sha256~[\\w-]{43})(?:[^\\w-]|\\z)", "g")
1157
+ },
1158
+ {
1159
+ kind: "gitleaks:perplexity-api-key",
1160
+ re: new RegExp(`\\b(pplx-[a-zA-Z0-9]{48})(?:[\\x60'"\\s;]|\\\\[nr]|$|\\b)`, "g")
1161
+ },
1162
+ {
1163
+ kind: "gitleaks:plaid-api-token",
1164
+ re: new RegExp(
1165
+ `[\\w.-]{0,50}?(?:plaid)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}(access-(?:sandbox|development|production)-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1166
+ "gi"
1167
+ )
1168
+ },
1169
+ {
1170
+ kind: "gitleaks:plaid-client-id",
1171
+ re: new RegExp(
1172
+ `[\\w.-]{0,50}?(?:plaid)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9]{24})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1173
+ "gi"
1174
+ )
1175
+ },
1176
+ {
1177
+ kind: "gitleaks:plaid-secret-key",
1178
+ re: new RegExp(
1179
+ `[\\w.-]{0,50}?(?:plaid)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9]{30})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1180
+ "gi"
1181
+ )
1182
+ },
1183
+ {
1184
+ kind: "gitleaks:planetscale-oauth-token",
1185
+ re: new RegExp(`\\b(pscale_oauth_[\\w=\\.-]{32,64})(?:[\\x60'"\\s;]|\\\\[nr]|$)`, "g")
1186
+ },
1187
+ {
1188
+ kind: "gitleaks:prefect-api-token",
1189
+ re: new RegExp(`\\b(pnu_[a-zA-Z0-9]{36})(?:[\\x60'"\\s;]|\\\\[nr]|$)`, "g")
1190
+ },
1191
+ {
1192
+ kind: "gitleaks:private-key",
1193
+ re: new RegExp(
1194
+ "-----BEGIN[ A-Z0-9_-]{0,100}PRIVATE KEY(?: BLOCK)?-----[\\s\\S-]{64,}?KEY(?: BLOCK)?-----",
1195
+ "gi"
1196
+ )
1197
+ },
1198
+ {
1199
+ kind: "gitleaks:pulumi-api-token",
1200
+ re: new RegExp(`\\b(pul-[a-f0-9]{40})(?:[\\x60'"\\s;]|\\\\[nr]|$)`, "g")
1201
+ },
1202
+ {
1203
+ kind: "gitleaks:pypi-upload-token",
1204
+ re: new RegExp("pypi-AgEIcHlwaS5vcmc[\\w-]{50,1000}", "g")
1205
+ },
1206
+ {
1207
+ kind: "gitleaks:rapidapi-access-token",
1208
+ re: new RegExp(
1209
+ `[\\w.-]{0,50}?(?:rapidapi)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9_-]{50})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1210
+ "gi"
1211
+ )
1212
+ },
1213
+ {
1214
+ kind: "gitleaks:readme-api-token",
1215
+ re: new RegExp(`\\b(rdme_[a-z0-9]{70})(?:[\\x60'"\\s;]|\\\\[nr]|$)`, "g")
1216
+ },
1217
+ {
1218
+ kind: "gitleaks:rubygems-api-token",
1219
+ re: new RegExp(`\\b(rubygems_[a-f0-9]{48})(?:[\\x60'"\\s;]|\\\\[nr]|$)`, "g")
1220
+ },
1221
+ {
1222
+ kind: "gitleaks:scalingo-api-token",
1223
+ re: new RegExp(`\\b(tk-us-[\\w-]{48})(?:[\\x60'"\\s;]|\\\\[nr]|$)`, "g")
1224
+ },
1225
+ {
1226
+ kind: "gitleaks:sendbird-access-id",
1227
+ re: new RegExp(
1228
+ `[\\w.-]{0,50}?(?:sendbird)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1229
+ "gi"
1230
+ )
1231
+ },
1232
+ {
1233
+ kind: "gitleaks:sendbird-access-token",
1234
+ re: new RegExp(
1235
+ `[\\w.-]{0,50}?(?:sendbird)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-f0-9]{40})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1236
+ "gi"
1237
+ )
1238
+ },
1239
+ {
1240
+ kind: "gitleaks:sentry-access-token",
1241
+ re: new RegExp(
1242
+ `[\\w.-]{0,50}?(?:sentry)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-f0-9]{64})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1243
+ "gi"
1244
+ )
1245
+ },
1246
+ {
1247
+ kind: "gitleaks:sentry-org-token",
1248
+ re: new RegExp(
1249
+ "\\bsntrys_eyJpYXQiO[a-zA-Z0-9+/]{10,200}(?:LCJyZWdpb25fdXJs|InJlZ2lvbl91cmwi|cmVnaW9uX3VybCI6)[a-zA-Z0-9+/]{10,200}={0,2}_[a-zA-Z0-9+/]{43}(?:[^a-zA-Z0-9+/]|\\z)",
1250
+ "g"
1251
+ )
1252
+ },
1253
+ {
1254
+ kind: "gitleaks:sentry-user-token",
1255
+ re: new RegExp(`\\b(sntryu_[a-f0-9]{64})(?:[\\x60'"\\s;]|\\\\[nr]|$)`, "g")
1256
+ },
1257
+ {
1258
+ kind: "gitleaks:settlemint-application-access-token",
1259
+ re: new RegExp(`\\b(sm_aat_[a-zA-Z0-9]{16})(?:[\\x60'"\\s;]|\\\\[nr]|$)`, "g")
1260
+ },
1261
+ {
1262
+ kind: "gitleaks:settlemint-personal-access-token",
1263
+ re: new RegExp(`\\b(sm_pat_[a-zA-Z0-9]{16})(?:[\\x60'"\\s;]|\\\\[nr]|$)`, "g")
1264
+ },
1265
+ {
1266
+ kind: "gitleaks:settlemint-service-access-token",
1267
+ re: new RegExp(`\\b(sm_sat_[a-zA-Z0-9]{16})(?:[\\x60'"\\s;]|\\\\[nr]|$)`, "g")
1268
+ },
1269
+ {
1270
+ kind: "gitleaks:shippo-api-token",
1271
+ re: new RegExp(`\\b(shippo_(?:live|test)_[a-fA-F0-9]{40})(?:[\\x60'"\\s;]|\\\\[nr]|$)`, "g")
1272
+ },
1273
+ { kind: "gitleaks:shopify-access-token", re: new RegExp("shpat_[a-fA-F0-9]{32}", "g") },
1274
+ { kind: "gitleaks:shopify-custom-access-token", re: new RegExp("shpca_[a-fA-F0-9]{32}", "g") },
1275
+ {
1276
+ kind: "gitleaks:shopify-private-app-access-token",
1277
+ re: new RegExp("shppa_[a-fA-F0-9]{32}", "g")
1278
+ },
1279
+ { kind: "gitleaks:shopify-shared-secret", re: new RegExp("shpss_[a-fA-F0-9]{32}", "g") },
1280
+ {
1281
+ kind: "gitleaks:sidekiq-secret",
1282
+ re: new RegExp(
1283
+ `[\\w.-]{0,50}?(?:BUNDLE_ENTERPRISE__CONTRIBSYS__COM|BUNDLE_GEMS__CONTRIBSYS__COM)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-f0-9]{8}:[a-f0-9]{8})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1284
+ "gi"
1285
+ )
1286
+ },
1287
+ {
1288
+ kind: "gitleaks:sidekiq-sensitive-url",
1289
+ re: new RegExp(
1290
+ "\\bhttps?://([a-f0-9]{8}:[a-f0-9]{8})@(?:gems.contribsys.com|enterprise.contribsys.com)(?:[\\/|\\#|\\?|:]|$)",
1291
+ "gi"
1292
+ )
1293
+ },
1294
+ { kind: "gitleaks:slack-app-token", re: new RegExp("xapp-\\d-[A-Z0-9]+-\\d+-[a-z0-9]+", "gi") },
1295
+ {
1296
+ kind: "gitleaks:slack-bot-token",
1297
+ re: new RegExp("xoxb-[0-9]{10,13}-[0-9]{10,13}[a-zA-Z0-9-]*", "g")
1298
+ },
1299
+ {
1300
+ kind: "gitleaks:slack-config-access-token",
1301
+ re: new RegExp("xoxe.xox[bp]-\\d-[A-Z0-9]{163,166}", "gi")
1302
+ },
1303
+ { kind: "gitleaks:slack-config-refresh-token", re: new RegExp("xoxe-\\d-[A-Z0-9]{146}", "gi") },
1304
+ {
1305
+ kind: "gitleaks:slack-legacy-bot-token",
1306
+ re: new RegExp("xoxb-[0-9]{8,14}-[a-zA-Z0-9]{18,26}", "g")
1307
+ },
1308
+ {
1309
+ kind: "gitleaks:slack-legacy-token",
1310
+ re: new RegExp("xox[os]-\\d+-\\d+-\\d+-[a-fA-F\\d]+", "g")
1311
+ },
1312
+ {
1313
+ kind: "gitleaks:slack-legacy-workspace-token",
1314
+ re: new RegExp("xox[ar]-(?:\\d-)?[0-9a-zA-Z]{8,48}", "g")
1315
+ },
1316
+ {
1317
+ kind: "gitleaks:slack-user-token",
1318
+ re: new RegExp("xox[pe](?:-[0-9]{10,13}){3}-[a-zA-Z0-9-]{28,34}", "g")
1319
+ },
1320
+ {
1321
+ kind: "gitleaks:slack-webhook-url",
1322
+ re: new RegExp(
1323
+ "(?:https?://)?hooks.slack.com/(?:services|workflows|triggers)/[A-Za-z0-9+/]{43,56}",
1324
+ "g"
1325
+ )
1326
+ },
1327
+ {
1328
+ kind: "gitleaks:snyk-api-token",
1329
+ re: new RegExp(
1330
+ `[\\w.-]{0,50}?(?:snyk[_.-]?(?:(?:api|oauth)[_.-]?)?(?:key|token))(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1331
+ "gi"
1332
+ )
1333
+ },
1334
+ {
1335
+ kind: "gitleaks:sonar-api-token",
1336
+ re: new RegExp(
1337
+ `[\\w.-]{0,50}?(?:sonar[_.-]?(login|token))(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}((?:squ_|sqp_|sqa_)?[a-z0-9=_\\-]{40})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1338
+ "gi"
1339
+ )
1340
+ },
1341
+ {
1342
+ kind: "gitleaks:square-access-token",
1343
+ re: new RegExp(`\\b((?:EAAA|sq0atp-)[\\w-]{22,60})(?:[\\x60'"\\s;]|\\\\[nr]|$)`, "g")
1344
+ },
1345
+ {
1346
+ kind: "gitleaks:squarespace-access-token",
1347
+ re: new RegExp(
1348
+ `[\\w.-]{0,50}?(?:squarespace)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1349
+ "gi"
1350
+ )
1351
+ },
1352
+ {
1353
+ kind: "gitleaks:stripe-access-token",
1354
+ re: new RegExp(
1355
+ `\\b((?:sk|rk)_(?:test|live|prod)_[a-zA-Z0-9]{10,99})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1356
+ "g"
1357
+ )
1358
+ },
1359
+ {
1360
+ kind: "gitleaks:travisci-access-token",
1361
+ re: new RegExp(
1362
+ `[\\w.-]{0,50}?(?:travis)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9]{22})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1363
+ "gi"
1364
+ )
1365
+ },
1366
+ { kind: "gitleaks:twilio-api-key", re: new RegExp("SK[0-9a-fA-F]{32}", "g") },
1367
+ {
1368
+ kind: "gitleaks:twitch-api-token",
1369
+ re: new RegExp(
1370
+ `[\\w.-]{0,50}?(?:twitch)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9]{30})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1371
+ "gi"
1372
+ )
1373
+ },
1374
+ {
1375
+ kind: "gitleaks:twitter-access-secret",
1376
+ re: new RegExp(
1377
+ `[\\w.-]{0,50}?(?:twitter)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9]{45})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1378
+ "gi"
1379
+ )
1380
+ },
1381
+ {
1382
+ kind: "gitleaks:twitter-access-token",
1383
+ re: new RegExp(
1384
+ `[\\w.-]{0,50}?(?:twitter)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([0-9]{15,25}-[a-zA-Z0-9]{20,40})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1385
+ "gi"
1386
+ )
1387
+ },
1388
+ {
1389
+ kind: "gitleaks:twitter-api-key",
1390
+ re: new RegExp(
1391
+ `[\\w.-]{0,50}?(?:twitter)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9]{25})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1392
+ "gi"
1393
+ )
1394
+ },
1395
+ {
1396
+ kind: "gitleaks:twitter-api-secret",
1397
+ re: new RegExp(
1398
+ `[\\w.-]{0,50}?(?:twitter)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9]{50})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1399
+ "gi"
1400
+ )
1401
+ },
1402
+ {
1403
+ kind: "gitleaks:twitter-bearer-token",
1404
+ re: new RegExp(
1405
+ `[\\w.-]{0,50}?(?:twitter)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}(A{22}[a-zA-Z0-9%]{80,100})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1406
+ "gi"
1407
+ )
1408
+ },
1409
+ {
1410
+ kind: "gitleaks:typeform-api-token",
1411
+ re: new RegExp(
1412
+ `[\\w.-]{0,50}?(?:typeform)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}(tfp_[a-z0-9\\-_\\.=]{59})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1413
+ "gi"
1414
+ )
1415
+ },
1416
+ {
1417
+ kind: "gitleaks:vault-batch-token",
1418
+ re: new RegExp(`\\b(hvb\\.[\\w-]{138,300})(?:[\\x60'"\\s;]|\\\\[nr]|$)`, "g")
1419
+ },
1420
+ {
1421
+ kind: "gitleaks:yandex-access-token",
1422
+ re: new RegExp(
1423
+ `[\\w.-]{0,50}?(?:yandex)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}(t1\\.[A-Z0-9a-z_-]+[=]{0,2}\\.[A-Z0-9a-z_-]{86}[=]{0,2})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1424
+ "gi"
1425
+ )
1426
+ },
1427
+ {
1428
+ kind: "gitleaks:yandex-api-key",
1429
+ re: new RegExp(
1430
+ `[\\w.-]{0,50}?(?:yandex)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}(AQVN[A-Za-z0-9_\\-]{35,38})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1431
+ "gi"
1432
+ )
1433
+ },
1434
+ {
1435
+ kind: "gitleaks:yandex-aws-access-token",
1436
+ re: new RegExp(
1437
+ `[\\w.-]{0,50}?(?:yandex)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}(YC[a-zA-Z0-9_\\-]{38})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1438
+ "gi"
1439
+ )
1440
+ },
1441
+ {
1442
+ kind: "gitleaks:zendesk-secret-key",
1443
+ re: new RegExp(
1444
+ `[\\w.-]{0,50}?(?:zendesk)(?:[ \\t\\w.-]{0,20})[\\s'"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'"\\s=]{0,5}([a-z0-9]{40})(?:[\\x60'"\\s;]|\\\\[nr]|$)`,
1445
+ "gi"
1446
+ )
1447
+ }
1448
+ ];
1449
+
1450
+ // src/secrets.ts
1451
+ var SECRET_PATTERNS = [
1452
+ { kind: "aws-access-key-id", re: /AKIA[0-9A-Z]{16}/g },
1453
+ { kind: "github-token", re: /(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36,}/g },
1454
+ { kind: "github-pat", re: /github_pat_[A-Za-z0-9_]{20,}/g },
1455
+ // Anthropic base64url bodies include "_", and modern keys are sk-ant-api03-… .
1456
+ { kind: "anthropic-api-key", re: /sk-ant-[A-Za-z0-9_-]{20,}/g },
1457
+ // Modern OpenAI keys are prefixed (sk-proj-/sk-svcacct-/sk-admin-) with hyphens in the
1458
+ // body; keep the legacy pure-alnum form too. Ordered after anthropic so sk-ant- wins.
1459
+ {
1460
+ kind: "openai-api-key",
1461
+ re: /sk-(?:proj|svcacct|admin)-[A-Za-z0-9_-]{20,}|sk-[A-Za-z0-9]{20,}/g
1462
+ },
1463
+ { kind: "google-api-key", re: /AIza[0-9A-Za-z_-]{35}/g },
1464
+ { kind: "google-oauth-token", re: /ya29\.[0-9A-Za-z_-]{20,}/g },
1465
+ { kind: "slack-token", re: /xox[baprs]-[A-Za-z0-9-]{10,}/g },
1466
+ {
1467
+ kind: "slack-webhook",
1468
+ re: /https:\/\/hooks\.slack\.com\/services\/T[A-Za-z0-9_]+\/B[A-Za-z0-9_]+\/[A-Za-z0-9_]+/g
1469
+ },
1470
+ // Stripe (sk_live_/pk_live_/rk_test_…), SendGrid, npm, Twilio SID/key — distinctive
1471
+ // prefixes, low false-positive. Inspired by the SecretFinder pattern set.
1472
+ { kind: "stripe-key", re: /(?:sk|rk|pk)_(?:live|test)_[0-9A-Za-z]{16,}/g },
1473
+ { kind: "sendgrid-key", re: /SG\.[A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{16,}/g },
1474
+ { kind: "npm-token", re: /npm_[A-Za-z0-9]{36}/g },
1475
+ { kind: "twilio-account-sid", re: /AC[0-9a-fA-F]{32}/g },
1476
+ { kind: "twilio-api-key", re: /SK[0-9a-fA-F]{32}/g },
1477
+ { kind: "private-key", re: /-----BEGIN [A-Z ]*PRIVATE KEY-----/g },
1478
+ {
1479
+ // A sensitive word as a whole `_`-delimited token inside an identifier (so
1480
+ // aws_secret_access_key / OPENAI_API_KEY / DATABASE_PASSWORD match) followed by an
1481
+ // assignment of an 8+ char value. Token-bounded to avoid prose ("the password is…")
1482
+ // and look-alikes ("secretary = …", "tokenize the input").
1483
+ kind: "generic-secret-assignment",
1484
+ re: /(?<![A-Za-z0-9_])(?:[A-Za-z0-9]+_)*(?:password|passwd|secret|token|api[_-]?key|access[_-]?token|auth[_-]?token|client[_-]?secret|private[_-]?key|credentials?)(?:_[A-Za-z0-9]+)*\s*[:=]\s*["']?[^\s"']{8,}/gi
1485
+ }
1486
+ ];
1487
+ var ALL_PATTERNS = [
1488
+ ...SECRET_PATTERNS,
1489
+ ...GITLEAKS_PATTERNS
1490
+ ];
1491
+ function maskPreview(match) {
1492
+ if (match.length <= 6) return "*".repeat(match.length);
1493
+ return `${match.slice(0, 4)}...${match.slice(-2)}`;
1494
+ }
1495
+ function shannonEntropy(s) {
1496
+ if (s.length === 0) return 0;
1497
+ const counts = /* @__PURE__ */ new Map();
1498
+ for (const ch of s) counts.set(ch, (counts.get(ch) ?? 0) + 1);
1499
+ let bits = 0;
1500
+ for (const n of counts.values()) {
1501
+ const p = n / s.length;
1502
+ bits -= p * Math.log2(p);
1503
+ }
1504
+ return bits;
1505
+ }
1506
+ var ENTROPY_TOKEN_RES = [
1507
+ /[A-Za-z0-9+/_-]{20,}={0,2}/g,
1508
+ // base64 / base64url
1509
+ /\b[0-9a-fA-F]{32,}\b/g
1510
+ // long hex
1511
+ ];
1512
+ var BASE64_ENTROPY_MIN = 4.5;
1513
+ var HEX_ENTROPY_MIN = 3;
1514
+ function isHex(s) {
1515
+ return /^[0-9a-fA-F]+$/.test(s);
1516
+ }
1517
+ function findSecrets(text, opts = {}) {
1518
+ const allow = opts.allowlist && opts.allowlist.length > 0 ? new Set(opts.allowlist) : null;
1519
+ const byIndex = /* @__PURE__ */ new Map();
1520
+ for (const { kind, re } of ALL_PATTERNS) {
1521
+ re.lastIndex = 0;
1522
+ let m;
1523
+ while ((m = re.exec(text)) !== null) {
1524
+ const value = m[0];
1525
+ if (value.length === 0) {
1526
+ re.lastIndex += 1;
1527
+ continue;
1528
+ }
1529
+ if (allow?.has(value)) continue;
1530
+ if (!byIndex.has(m.index)) {
1531
+ byIndex.set(m.index, {
1532
+ kind,
1533
+ index: m.index,
1534
+ length: value.length,
1535
+ preview: maskPreview(value)
1536
+ });
1537
+ }
1538
+ }
1539
+ }
1540
+ if (opts.detectEntropy) addEntropyMatches(text, byIndex, allow);
1541
+ return [...byIndex.values()].sort((a, b) => a.index - b.index);
1542
+ }
1543
+ function addEntropyMatches(text, byIndex, allow) {
1544
+ for (const re of ENTROPY_TOKEN_RES) {
1545
+ re.lastIndex = 0;
1546
+ let m;
1547
+ while ((m = re.exec(text)) !== null) {
1548
+ const value = m[0];
1549
+ if (value.length === 0) {
1550
+ re.lastIndex += 1;
1551
+ continue;
1552
+ }
1553
+ if (byIndex.has(m.index) || allow?.has(value)) continue;
1554
+ const min = isHex(value) ? HEX_ENTROPY_MIN : BASE64_ENTROPY_MIN;
1555
+ if (shannonEntropy(value) < min) continue;
1556
+ byIndex.set(m.index, {
1557
+ kind: "high-entropy-string",
1558
+ index: m.index,
1559
+ length: value.length,
1560
+ preview: maskPreview(value)
1561
+ });
1562
+ }
1563
+ }
1564
+ }
1565
+ function hasSecrets(text, opts = {}) {
1566
+ if (opts.detectEntropy || opts.allowlist && opts.allowlist.length > 0) {
1567
+ return findSecrets(text, opts).length > 0;
1568
+ }
1569
+ for (const { re } of ALL_PATTERNS) {
1570
+ re.lastIndex = 0;
1571
+ if (re.test(text)) return true;
1572
+ }
1573
+ return false;
1574
+ }
1575
+ function redactSecrets(text, opts = {}) {
1576
+ const matches = findSecrets(text, opts);
1577
+ let out = text;
1578
+ for (let i = matches.length - 1; i >= 0; i--) {
1579
+ const { kind, index, length } = matches[i];
1580
+ out = `${out.slice(0, index)}[REDACTED:${kind}]${out.slice(index + length)}`;
1581
+ }
1582
+ return out;
1583
+ }
1584
+
1585
+ // src/index-db.ts
1586
+ import { promises as fs4 } from "fs";
1587
+ import path4 from "path";
1588
+ import Database from "better-sqlite3";
1589
+ var INDEX_DIR = "index";
1590
+ var DB_FILE = "commonwealth.db";
1591
+ function dbPath(brainDir) {
1592
+ return path4.join(brainDir, INDEX_DIR, DB_FILE);
1593
+ }
1594
+ function toRow(note) {
1595
+ return {
1596
+ id: note.frontmatter.id,
1597
+ kind: note.frontmatter.kind,
1598
+ title: note.frontmatter.title,
1599
+ tags: note.frontmatter.tags.join(" "),
1600
+ body: note.body,
1601
+ path: note.path,
1602
+ source: note.frontmatter.source ?? ""
1603
+ };
1604
+ }
1605
+ async function buildIndex(brainDir) {
1606
+ await fs4.mkdir(path4.join(brainDir, INDEX_DIR), { recursive: true });
1607
+ const notes = await listNotes(brainDir);
1608
+ const db = new Database(dbPath(brainDir));
1609
+ try {
1610
+ const rebuild = db.transaction((rows) => {
1611
+ db.exec("DROP TABLE IF EXISTS notes_fts;");
1612
+ db.exec(
1613
+ "CREATE VIRTUAL TABLE notes_fts USING fts5(id, kind, title, tags, body, path UNINDEXED, source UNINDEXED);"
1614
+ );
1615
+ const insert = db.prepare(
1616
+ "INSERT INTO notes_fts (id, kind, title, tags, body, path, source) VALUES (@id, @kind, @title, @tags, @body, @path, @source);"
1617
+ );
1618
+ for (const row of rows) insert.run(row);
1619
+ });
1620
+ rebuild(notes.map(toRow));
1621
+ return { indexed: notes.length };
1622
+ } finally {
1623
+ db.close();
1624
+ }
1625
+ }
1626
+ function hasFtsTable(db) {
1627
+ const row = db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'notes_fts'").get();
1628
+ return row !== void 0;
1629
+ }
1630
+ function toMatchQuery(query) {
1631
+ return query.split(/\s+/).map((t) => t.replace(/"/g, "")).filter((t) => t.length > 0).map((t) => `"${t}"`).join(" ");
1632
+ }
1633
+ async function search(brainDir, query, opts) {
1634
+ try {
1635
+ await fs4.access(dbPath(brainDir));
1636
+ } catch {
1637
+ await buildIndex(brainDir);
1638
+ }
1639
+ {
1640
+ const probe = new Database(dbPath(brainDir), { readonly: true });
1641
+ let healthy;
1642
+ try {
1643
+ healthy = hasFtsTable(probe);
1644
+ } finally {
1645
+ probe.close();
1646
+ }
1647
+ if (!healthy) await buildIndex(brainDir);
1648
+ }
1649
+ const match = toMatchQuery(query);
1650
+ if (match === "") return [];
1651
+ const limit = opts?.limit ?? 20;
1652
+ const db = new Database(dbPath(brainDir), { readonly: true });
1653
+ try {
1654
+ const params = [match];
1655
+ let sql = "SELECT id, kind, title, path, source, snippet(notes_fts, 4, '[', ']', '\u2026', 12) AS snippet, -bm25(notes_fts) AS score FROM notes_fts WHERE notes_fts MATCH ?";
1656
+ if (opts?.kind) {
1657
+ sql += " AND kind = ?";
1658
+ params.push(opts.kind);
1659
+ }
1660
+ if (opts?.source) {
1661
+ sql += " AND source = ?";
1662
+ params.push(opts.source);
1663
+ }
1664
+ sql += " ORDER BY bm25(notes_fts) LIMIT ?";
1665
+ params.push(limit);
1666
+ const rows = db.prepare(sql).all(...params);
1667
+ return rows.map((r) => ({
1668
+ id: r.id,
1669
+ kind: r.kind,
1670
+ title: r.title,
1671
+ path: r.path,
1672
+ ...r.source ? { source: r.source } : {},
1673
+ snippet: r.snippet,
1674
+ score: r.score
1675
+ }));
1676
+ } finally {
1677
+ db.close();
1678
+ }
1679
+ }
1680
+ function inlineText(value) {
1681
+ return value.replace(/[\r\n]+/g, " ").replace(/[[\]`]/g, (c) => `\\${c}`).replace(/\s+/g, " ").trim().slice(0, 200);
1682
+ }
1683
+ function isActiveWorkState(note) {
1684
+ return note.frontmatter.kind === "work-state" && note.frontmatter.status !== "done";
1685
+ }
1686
+ function byId(a, b) {
1687
+ return a.frontmatter.id < b.frontmatter.id ? -1 : a.frontmatter.id > b.frontmatter.id ? 1 : 0;
1688
+ }
1689
+ function byCreatedDesc(a, b) {
1690
+ if (a.frontmatter.created !== b.frontmatter.created) {
1691
+ return a.frontmatter.created < b.frontmatter.created ? 1 : -1;
1692
+ }
1693
+ return byId(a, b);
1694
+ }
1695
+ var UNATTRIBUTED = "(unattributed)";
1696
+ function sourceOf(note) {
1697
+ return note.frontmatter.source && note.frontmatter.source.length > 0 ? note.frontmatter.source : UNATTRIBUTED;
1698
+ }
1699
+ function bySourceLabel(a, b) {
1700
+ if (a === UNATTRIBUTED) return b === UNATTRIBUTED ? 0 : 1;
1701
+ if (b === UNATTRIBUTED) return -1;
1702
+ return a < b ? -1 : a > b ? 1 : 0;
1703
+ }
1704
+ function commonwealthMarkdown(notes) {
1705
+ const lines = [];
1706
+ lines.push("# Commonwealth");
1707
+ lines.push("");
1708
+ lines.push("> Generated router. Do not edit by hand \u2014 regenerated from the note set (ADR-0003).");
1709
+ lines.push("");
1710
+ const bySource = /* @__PURE__ */ new Map();
1711
+ for (const n of notes) {
1712
+ const key = sourceOf(n);
1713
+ (bySource.get(key) ?? bySource.set(key, []).get(key)).push(n);
1714
+ }
1715
+ const sources = [...bySource.keys()].sort(bySourceLabel);
1716
+ if (sources.length === 0) {
1717
+ lines.push("_No notes yet._");
1718
+ lines.push("");
1719
+ return lines.join("\n");
1720
+ }
1721
+ for (const source of sources) {
1722
+ const group = bySource.get(source);
1723
+ const active = group.filter(isActiveWorkState).sort(byId);
1724
+ const decisions = group.filter((n) => n.frontmatter.kind === "decision").sort(byCreatedDesc);
1725
+ lines.push(`## ${inlineText(source)}`);
1726
+ lines.push("");
1727
+ lines.push("**Active work-state**");
1728
+ if (active.length === 0) {
1729
+ lines.push("- _None._");
1730
+ } else {
1731
+ for (const n of active) {
1732
+ const status = n.frontmatter.kind === "work-state" ? n.frontmatter.status : "";
1733
+ lines.push(`- [${inlineText(n.frontmatter.title)}](${n.path}) \u2014 ${status}`);
1734
+ }
1735
+ }
1736
+ lines.push("");
1737
+ lines.push("**Recent decisions**");
1738
+ if (decisions.length === 0) {
1739
+ lines.push("- _None._");
1740
+ } else {
1741
+ for (const n of decisions) {
1742
+ lines.push(`- [${inlineText(n.frontmatter.title)}](${n.path}) \u2014 ${n.frontmatter.created}`);
1743
+ }
1744
+ }
1745
+ lines.push("");
1746
+ }
1747
+ return lines.join("\n");
1748
+ }
1749
+ function indexMarkdown(dirRel, notes) {
1750
+ const sorted = [...notes].sort(byId);
1751
+ const lines = [];
1752
+ lines.push(`# ${dirRel}`);
1753
+ lines.push("");
1754
+ lines.push("> Generated index. Do not edit by hand \u2014 regenerated from the note set.");
1755
+ lines.push("");
1756
+ if (sorted.length === 0) {
1757
+ lines.push("_None._");
1758
+ } else {
1759
+ for (const n of sorted) {
1760
+ lines.push(`- [${inlineText(n.frontmatter.title)}](${path4.posix.basename(n.path)})`);
1761
+ }
1762
+ }
1763
+ lines.push("");
1764
+ return lines.join("\n");
1765
+ }
1766
+ async function regenerateDerived(brainDir) {
1767
+ const notes = await listNotes(brainDir);
1768
+ await fs4.writeFile(path4.join(brainDir, "COMMONWEALTH.md"), commonwealthMarkdown(notes), "utf8");
1769
+ const byDir = /* @__PURE__ */ new Map();
1770
+ for (const n of notes) {
1771
+ const dir = path4.posix.dirname(n.path.split(path4.sep).join("/"));
1772
+ (byDir.get(dir) ?? byDir.set(dir, []).get(dir)).push(n);
1773
+ }
1774
+ for (const [dir, group] of byDir) {
1775
+ const abs = path4.join(brainDir, dir);
1776
+ await fs4.mkdir(abs, { recursive: true });
1777
+ await fs4.writeFile(path4.join(abs, "INDEX.md"), indexMarkdown(dir, group), "utf8");
1778
+ }
1779
+ }
1780
+
1781
+ // src/registry.ts
1782
+ import { promises as fs5 } from "fs";
1783
+ import os from "os";
1784
+ import path5 from "path";
1785
+ var MARKER_REL = path5.join(".commonwealth", "brain");
1786
+ var BRAIN_IDENTITY_REL = path5.join(".commonwealth", "schema-version");
1787
+ function expand(entry, base) {
1788
+ const home = os.homedir();
1789
+ if (entry === "~") return path5.resolve(home);
1790
+ if (entry.startsWith("~/")) return path5.resolve(home, entry.slice(2));
1791
+ return base ? path5.resolve(base, entry) : path5.resolve(entry);
1792
+ }
1793
+ function isUnder(child, parent) {
1794
+ if (parent === path5.sep) return true;
1795
+ return child === parent || child.startsWith(parent + path5.sep);
1796
+ }
1797
+ function* walkUp(startDir) {
1798
+ let current = path5.resolve(startDir);
1799
+ for (; ; ) {
1800
+ yield current;
1801
+ const parent = path5.dirname(current);
1802
+ if (parent === current) return;
1803
+ current = parent;
1804
+ }
1805
+ }
1806
+ async function readFileOrNull(file) {
1807
+ try {
1808
+ return await fs5.readFile(file, "utf8");
1809
+ } catch {
1810
+ return null;
1811
+ }
1812
+ }
1813
+ async function isFile(file) {
1814
+ try {
1815
+ const stat = await fs5.stat(file);
1816
+ return stat.isFile();
1817
+ } catch {
1818
+ return false;
1819
+ }
1820
+ }
1821
+ async function isDir(dir) {
1822
+ try {
1823
+ return (await fs5.stat(dir)).isDirectory();
1824
+ } catch {
1825
+ return false;
1826
+ }
1827
+ }
1828
+ function resolveRegistryPath(explicit) {
1829
+ if (explicit) return explicit;
1830
+ if (process.env.COMMONWEALTH_REGISTRY) return process.env.COMMONWEALTH_REGISTRY;
1831
+ if (process.env.COMMONWEALTH_CONFIG) {
1832
+ return path5.join(path5.dirname(process.env.COMMONWEALTH_CONFIG), "registry.json");
1833
+ }
1834
+ return path5.join(os.homedir(), ".commonwealth", "registry.json");
1835
+ }
1836
+ async function readRegistryFile(registryPath) {
1837
+ const raw = await readFileOrNull(registryPath);
1838
+ if (raw === null) return { status: "missing" };
1839
+ let parsed;
1840
+ try {
1841
+ parsed = JSON.parse(raw);
1842
+ } catch {
1843
+ return { status: "corrupt", raw };
1844
+ }
1845
+ const obj = typeof parsed === "object" && parsed !== null ? parsed : {};
1846
+ const mappings = Array.isArray(obj.mappings) ? obj.mappings : [];
1847
+ const clean = [];
1848
+ for (const m of mappings) {
1849
+ if (m && typeof m === "object" && typeof m.prefix === "string" && typeof m.brain === "string") {
1850
+ clean.push({ prefix: m.prefix, brain: m.brain });
1851
+ }
1852
+ }
1853
+ return { status: "ok", registry: { mappings: clean } };
1854
+ }
1855
+ async function loadRegistry(registryPath) {
1856
+ const load = await readRegistryFile(registryPath);
1857
+ return load.status === "ok" ? load.registry : null;
1858
+ }
1859
+ async function resolveBrainDir(startDir, opts = {}) {
1860
+ const start = path5.resolve(startDir);
1861
+ const registryPath = resolveRegistryPath(opts.registryPath);
1862
+ for (const dir of walkUp(start)) {
1863
+ const markerPath = path5.join(dir, MARKER_REL);
1864
+ const raw = await readFileOrNull(markerPath);
1865
+ if (raw !== null) {
1866
+ const target = raw.trim();
1867
+ if (target.length > 0) {
1868
+ const resolved = expand(target, dir);
1869
+ if (await isDir(resolved)) return resolved;
1870
+ }
1871
+ }
1872
+ }
1873
+ for (const dir of walkUp(start)) {
1874
+ if (await isFile(path5.join(dir, BRAIN_IDENTITY_REL))) return dir;
1875
+ }
1876
+ const registry = await loadRegistry(registryPath);
1877
+ if (registry) {
1878
+ let bestBrain = null;
1879
+ let bestLen = -1;
1880
+ for (const mapping of registry.mappings) {
1881
+ const prefix = expand(mapping.prefix);
1882
+ if (isUnder(start, prefix) && prefix.length > bestLen) {
1883
+ bestLen = prefix.length;
1884
+ bestBrain = expand(mapping.brain);
1885
+ }
1886
+ }
1887
+ if (bestBrain !== null) return bestBrain;
1888
+ }
1889
+ const env = opts.env ?? process.env.COMMONWEALTH_BRAIN_DIR;
1890
+ if (env && env.length > 0) return path5.resolve(env);
1891
+ return null;
1892
+ }
1893
+ async function setBrainMarker(projectDir, brainPath) {
1894
+ const markerPath = path5.join(projectDir, MARKER_REL);
1895
+ await fs5.mkdir(path5.dirname(markerPath), { recursive: true });
1896
+ await fs5.writeFile(markerPath, `${brainPath}
1897
+ `, "utf8");
1898
+ }
1899
+ function defaultRegistryPath() {
1900
+ return resolveRegistryPath();
1901
+ }
1902
+ function defaultBrainsDir() {
1903
+ return path5.join(path5.dirname(defaultRegistryPath()), "brains");
1904
+ }
1905
+ async function addRegistryMapping(prefix, brain, registryPath = defaultRegistryPath()) {
1906
+ const absPrefix = expand(prefix);
1907
+ const absBrain = expand(brain);
1908
+ const load = await readRegistryFile(registryPath);
1909
+ if (load.status === "corrupt") {
1910
+ const backup = `${registryPath}.corrupt-${Date.now()}`;
1911
+ await fs5.rename(registryPath, backup).catch(() => fs5.writeFile(backup, load.raw, "utf8"));
1912
+ throw new Error(
1913
+ `Refusing to overwrite a corrupt registry at ${registryPath} (backed up to ${backup}). Fix or remove it, then retry.`
1914
+ );
1915
+ }
1916
+ const registry = load.status === "ok" ? load.registry : { mappings: [] };
1917
+ const existing = registry.mappings.find((m) => expand(m.prefix) === absPrefix);
1918
+ let added = false;
1919
+ let updated = false;
1920
+ if (!existing) {
1921
+ registry.mappings.push({ prefix: absPrefix, brain: absBrain });
1922
+ added = true;
1923
+ } else if (expand(existing.brain) !== absBrain) {
1924
+ existing.prefix = absPrefix;
1925
+ existing.brain = absBrain;
1926
+ updated = true;
1927
+ } else {
1928
+ return { added, updated };
1929
+ }
1930
+ await fs5.mkdir(path5.dirname(registryPath), { recursive: true });
1931
+ const tmp = `${registryPath}.${process.pid}.tmp`;
1932
+ await fs5.writeFile(tmp, `${JSON.stringify(registry, null, 2)}
1933
+ `, "utf8");
1934
+ await fs5.rename(tmp, registryPath);
1935
+ return { added, updated };
1936
+ }
1937
+ async function linkBrain(name, brainDir, brainsDir = defaultBrainsDir()) {
1938
+ const safe = path5.basename(name);
1939
+ if (safe === "" || safe === "." || safe === "..") {
1940
+ return { path: "", linked: false, skipped: "invalid name" };
1941
+ }
1942
+ const target = path5.resolve(brainDir);
1943
+ const symlinkPath = path5.join(brainsDir, safe);
1944
+ try {
1945
+ await fs5.mkdir(brainsDir, { recursive: true });
1946
+ } catch (err) {
1947
+ const code = err.code;
1948
+ return { path: symlinkPath, linked: false, skipped: code ?? "cannot create brains dir" };
1949
+ }
1950
+ let existingStat = null;
1951
+ try {
1952
+ existingStat = await fs5.lstat(symlinkPath);
1953
+ } catch {
1954
+ existingStat = null;
1955
+ }
1956
+ if (existingStat) {
1957
+ if (existingStat.isSymbolicLink()) {
1958
+ let resolved = null;
1959
+ try {
1960
+ resolved = path5.resolve(brainsDir, await fs5.readlink(symlinkPath));
1961
+ } catch {
1962
+ resolved = null;
1963
+ }
1964
+ if (resolved === target) return { path: symlinkPath, linked: true };
1965
+ try {
1966
+ await fs5.unlink(symlinkPath);
1967
+ } catch (err) {
1968
+ const code = err.code;
1969
+ return { path: symlinkPath, linked: false, skipped: code ?? "symlink unsupported" };
1970
+ }
1971
+ } else {
1972
+ return { path: symlinkPath, linked: false, skipped: "exists (not a symlink)" };
1973
+ }
1974
+ }
1975
+ try {
1976
+ await fs5.symlink(target, symlinkPath);
1977
+ } catch (err) {
1978
+ const code = err.code;
1979
+ return { path: symlinkPath, linked: false, skipped: code ?? "symlink unsupported" };
1980
+ }
1981
+ return { path: symlinkPath, linked: true };
1982
+ }
1983
+
1984
+ // src/source.ts
1985
+ import { execFile as execFile2 } from "child_process";
1986
+ import { existsSync as existsSync2 } from "fs";
1987
+ import path6 from "path";
1988
+ import { promisify as promisify2 } from "util";
1989
+ var pexec2 = promisify2(execFile2);
1990
+ async function resolveProjectSource(cwd) {
1991
+ if (typeof cwd !== "string" || cwd.length === 0) return null;
1992
+ const start = path6.resolve(cwd);
1993
+ const root = findGitRoot(start);
1994
+ if (root) {
1995
+ const remote = await originUrl(root);
1996
+ const slug = remote ? slugFromRemote(remote) : null;
1997
+ return slug ?? path6.basename(root);
1998
+ }
1999
+ return path6.basename(start);
2000
+ }
2001
+ function findGitRoot(startDir) {
2002
+ let dir = path6.resolve(startDir);
2003
+ for (; ; ) {
2004
+ if (existsSync2(path6.join(dir, ".git"))) return dir;
2005
+ const parent = path6.dirname(dir);
2006
+ if (parent === dir) return null;
2007
+ dir = parent;
2008
+ }
2009
+ }
2010
+ async function originUrl(root) {
2011
+ try {
2012
+ const { stdout } = await pexec2("git", ["-C", root, "config", "--get", "remote.origin.url"]);
2013
+ const url = stdout.trim();
2014
+ return url.length > 0 ? url : null;
2015
+ } catch {
2016
+ return null;
2017
+ }
2018
+ }
2019
+ function slugFromRemote(remote) {
2020
+ let s = remote.trim().replace(/\.git$/i, "");
2021
+ s = s.replace(/^[a-z]+:\/\//i, "").replace(/^[^@]+@/, "");
2022
+ s = s.replace(/^[^/:]+[:/]/, "");
2023
+ const parts = s.split("/").filter((p) => p.length > 0);
2024
+ if (parts.length === 0) return null;
2025
+ return parts.slice(-2).join("/");
2026
+ }
2027
+
2028
+ // src/health.ts
2029
+ var DEFAULT_STALE_AFTER_DAYS = 90;
2030
+ function daysBetween(now, then) {
2031
+ const a = Date.parse(`${now}T00:00:00Z`);
2032
+ const b = Date.parse(`${then}T00:00:00Z`);
2033
+ if (Number.isNaN(a) || Number.isNaN(b)) return 0;
2034
+ return Math.max(0, Math.floor((a - b) / 864e5));
2035
+ }
2036
+ function isContradicted(note) {
2037
+ return note.frontmatter.tags.some((t) => t.toLowerCase() === "contradicted");
2038
+ }
2039
+ function brainHealth(notes, opts = {}) {
2040
+ const staleAfterDays = opts.staleAfterDays ?? DEFAULT_STALE_AFTER_DAYS;
2041
+ const now = opts.now ?? today();
2042
+ const referenced = /* @__PURE__ */ new Set();
2043
+ for (const n of notes) {
2044
+ const fm = n.frontmatter;
2045
+ const refs = [
2046
+ ...fm.relates,
2047
+ ...fm.kind === "memory" ? fm.sources : [],
2048
+ ...fm.kind === "decision" ? fm.supersedes : [],
2049
+ ...fm.kind === "memory" || fm.kind === "decision" ? [fm.superseded_by].filter((v) => typeof v === "string") : []
2050
+ ];
2051
+ for (const r of refs) referenced.add(r.replace(/^\[\[|\]\]$/g, ""));
2052
+ }
2053
+ const stale = [];
2054
+ const unverified = [];
2055
+ const contradicted = [];
2056
+ const orphaned = [];
2057
+ for (const n of notes) {
2058
+ const fm = n.frontmatter;
2059
+ const id = fm.id;
2060
+ if (fm.kind === "memory") {
2061
+ if (fm.status === "stale") {
2062
+ stale.push(id);
2063
+ } else if (fm.status === "active") {
2064
+ const lastTouch = fm.verified ?? fm.updated ?? fm.created;
2065
+ if (daysBetween(now, lastTouch) > staleAfterDays) stale.push(id);
2066
+ if (!fm.verified) unverified.push(id);
2067
+ }
2068
+ }
2069
+ if (isContradicted(n)) contradicted.push(id);
2070
+ if (!referenced.has(id)) orphaned.push(id);
2071
+ }
2072
+ const serious = /* @__PURE__ */ new Set([...stale, ...contradicted]);
2073
+ const soft = new Set([...unverified, ...orphaned].filter((id) => !serious.has(id)));
2074
+ const total = notes.length;
2075
+ const score = total === 0 ? 100 : Math.max(
2076
+ 0,
2077
+ Math.min(100, Math.round(100 * (1 - (serious.size + 0.5 * soft.size) / total)))
2078
+ );
2079
+ const bucket = (ids) => ({ count: ids.length, ids: ids.sort() });
2080
+ return {
2081
+ total,
2082
+ stale: bucket(stale),
2083
+ unverified: bucket(unverified),
2084
+ contradicted: bucket(contradicted),
2085
+ orphaned: bucket(orphaned),
2086
+ score
2087
+ };
2088
+ }
2089
+ async function computeBrainHealth(brainDir, opts = {}) {
2090
+ return brainHealth(await listNotes(brainDir), opts);
2091
+ }
2092
+
2093
+ // src/lock.ts
2094
+ import { promises as fs6 } from "fs";
2095
+ import path7 from "path";
2096
+ var LOCK_REL = path7.join(".commonwealth", "sync.lock");
2097
+ function lockPath(brainDir) {
2098
+ return path7.join(brainDir, LOCK_REL);
2099
+ }
2100
+ function isAlive(pid) {
2101
+ try {
2102
+ process.kill(pid, 0);
2103
+ return true;
2104
+ } catch {
2105
+ return false;
2106
+ }
2107
+ }
2108
+ async function readOwner(file) {
2109
+ try {
2110
+ const pid = Number.parseInt((await fs6.readFile(file, "utf8")).trim(), 10);
2111
+ return Number.isFinite(pid) ? pid : null;
2112
+ } catch {
2113
+ return null;
2114
+ }
2115
+ }
2116
+ async function acquireSyncLock(brainDir) {
2117
+ const file = lockPath(brainDir);
2118
+ await fs6.mkdir(path7.dirname(file), { recursive: true });
2119
+ for (let attempt = 0; attempt < 2; attempt++) {
2120
+ try {
2121
+ const fh = await fs6.open(file, "wx");
2122
+ try {
2123
+ await fh.write(`${process.pid}
2124
+ `);
2125
+ } finally {
2126
+ await fh.close();
2127
+ }
2128
+ return async () => {
2129
+ await fs6.rm(file, { force: true });
2130
+ };
2131
+ } catch (err) {
2132
+ if (err.code !== "EEXIST") throw err;
2133
+ const owner = await readOwner(file);
2134
+ if (owner !== null && isAlive(owner)) return null;
2135
+ await fs6.rm(file, { force: true });
2136
+ }
2137
+ }
2138
+ return null;
2139
+ }
2140
+ export {
2141
+ BRAIN_KIND_DIRS,
2142
+ DEFAULT_STALE_AFTER_DAYS,
2143
+ DecisionFrontmatter,
2144
+ FEATURE_FLAGS,
2145
+ Frontmatter,
2146
+ IsoDate,
2147
+ KIND_DIR,
2148
+ MemoryFrontmatter,
2149
+ NOTE_KINDS,
2150
+ PersonFrontmatter,
2151
+ SCHEMA_VERSION,
2152
+ SECRET_PATTERNS,
2153
+ WorkStateFrontmatter,
2154
+ acquireSyncLock,
2155
+ addRegistryMapping,
2156
+ brainConfigPath,
2157
+ brainHealth,
2158
+ buildIndex,
2159
+ computeBrainHealth,
2160
+ defaultBrainConfig,
2161
+ defaultBrainsDir,
2162
+ defaultRegistryPath,
2163
+ findSecrets,
2164
+ hasSecrets,
2165
+ initBrain,
2166
+ isFeatureEnabled,
2167
+ linkBrain,
2168
+ listNotes,
2169
+ loadBrainConfig,
2170
+ makeNoteId,
2171
+ overwriteNote,
2172
+ parseNote,
2173
+ pathForNote,
2174
+ readNote,
2175
+ redactSecrets,
2176
+ regenerateDerived,
2177
+ resolveBrainDir,
2178
+ resolveProjectSource,
2179
+ resolveWithinBrain,
2180
+ saveBrainConfig,
2181
+ scanOptions,
2182
+ search,
2183
+ serializeNote,
2184
+ setBrainMarker,
2185
+ setFeature,
2186
+ shannonEntropy,
2187
+ shortId,
2188
+ slugFromRemote,
2189
+ slugify,
2190
+ sourceSegment,
2191
+ supersedeNote,
2192
+ today,
2193
+ writeNote
2194
+ };
2195
+ //# sourceMappingURL=index.js.map