@pretense/cli 0.6.8 → 0.6.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +31 -2
  2. package/dist/index.js +316 -27
  3. package/package.json +3 -3
package/README.md CHANGED
@@ -22,6 +22,36 @@ Both install the `pretense` command — the package name is scoped, the binary i
22
22
 
23
23
  Requires Node.js 20 or newer.
24
24
 
25
+ ## Verifying a release
26
+
27
+ The released binaries are signed with Sigstore cosign (keyless — no key to
28
+ fetch or leak). Each release ships a `SHA256SUMS` file plus a
29
+ `SHA256SUMS.cosign.bundle` signature covering it. To verify a download:
30
+
31
+ ```bash
32
+ VERSION=0.6.8
33
+ TARGET=pretense-darwin-arm64
34
+ BASE="https://www.pretense.ai/releases/${VERSION}"
35
+
36
+ curl -fsSLO "${BASE}/SHA256SUMS"
37
+ curl -fsSLO "${BASE}/SHA256SUMS.cosign.bundle"
38
+ curl -fsSLO "${BASE}/${TARGET}"
39
+
40
+ cosign verify-blob \
41
+ --bundle SHA256SUMS.cosign.bundle \
42
+ --certificate-identity "https://github.com/pretense-a1/pretense/.github/workflows/release.yml@refs/tags/v${VERSION}" \
43
+ --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
44
+ SHA256SUMS
45
+
46
+ grep " ${TARGET}$" SHA256SUMS | sha256sum -c - # shasum -a 256 -c - on macOS
47
+ ```
48
+
49
+ Verify the signature over `SHA256SUMS` *before* trusting any hash inside it —
50
+ checking a binary against an unverified checksum file proves nothing.
51
+
52
+ Full details, including the regexp form for automation and why npm provenance
53
+ is not used, are in [docs/verifying-releases.md](../docs/verifying-releases.md).
54
+
25
55
  ## First 5 minutes
26
56
 
27
57
  1. Initialize Pretense in your repo.
@@ -106,5 +136,4 @@ MIT. See [LICENSE](./LICENSE).
106
136
 
107
137
  - Homepage: https://pretense.ai
108
138
  - Docs: https://pretense.ai/docs
109
- - Source: https://github.com/jimmymalhan/pretense
110
- - Issues: https://github.com/jimmymalhan/pretense/issues
139
+ - Support: https://pretense.ai/contact
package/dist/index.js CHANGED
@@ -10383,6 +10383,97 @@ function listPresetIds() {
10383
10383
  return Object.keys(COMPLIANCE_PRESETS);
10384
10384
  }
10385
10385
 
10386
+ // ../packages/compliance-engine/dist/detector-frameworks.js
10387
+ init_esm_shims();
10388
+ var COMPLIANCE_FRAMEWORKS = ["SOC2", "ISO_27001", "ISO_27701", "NIST_800_53", "NIST_800_171", "FedRAMP", "FISMA", "NIS2", "NYDFS_500", "DORA", "APRA_CPS234", "HIPAA", "HITECH", "HITRUST", "GDPR", "UK_GDPR", "CCPA_CPRA", "LGPD", "PIPEDA", "POPIA", "PIPL", "PDPA_SG", "COPPA", "FERPA", "DPDP", "APPI", "PIPA_KR", "AU_PRIVACY", "FADP", "PDPA_TH", "CJIS", "IRS_1075", "SOX", "GLBA", "CMMC_L2", "PCI_DSS"];
10389
+ var DETECTOR_FRAMEWORKS = {
10390
+ "aba-routing": ["APRA_CPS234", "DORA", "GLBA", "NYDFS_500", "SOX"],
10391
+ "advertising-id": ["APPI", "AU_PRIVACY", "CCPA_CPRA", "COPPA", "DPDP", "FADP", "FERPA", "GDPR", "ISO_27701", "LGPD", "PDPA_SG", "PDPA_TH", "PIPA_KR", "PIPEDA", "PIPL", "POPIA", "UK_GDPR"],
10392
+ "anthropic-api-key": ["APRA_CPS234", "DORA", "FISMA", "FedRAMP", "HITRUST", "ISO_27001", "ISO_27701", "NIS2", "NIST_800_171", "NIST_800_53", "NYDFS_500", "SOC2", "SOX"],
10393
+ "anthropic-api-key-short": ["APRA_CPS234", "DORA", "FISMA", "FedRAMP", "HITRUST", "ISO_27001", "ISO_27701", "NIS2", "NIST_800_171", "NIST_800_53", "NYDFS_500", "SOC2", "SOX"],
10394
+ "aws-access-key": ["APRA_CPS234", "DORA", "FISMA", "FedRAMP", "HITRUST", "ISO_27001", "ISO_27701", "NIS2", "NIST_800_171", "NIST_800_53", "NYDFS_500", "SOC2", "SOX"],
10395
+ "aws-secret-key": ["APRA_CPS234", "DORA", "FISMA", "FedRAMP", "HITRUST", "ISO_27001", "ISO_27701", "NIS2", "NIST_800_171", "NIST_800_53", "NYDFS_500", "SOC2", "SOX"],
10396
+ "aws-temp-access-key": ["APRA_CPS234", "DORA", "FISMA", "FedRAMP", "HITRUST", "ISO_27001", "ISO_27701", "NIS2", "NIST_800_171", "NIST_800_53", "NYDFS_500", "SOC2", "SOX"],
10397
+ "azure-connection-string": ["APRA_CPS234", "DORA", "FISMA", "FedRAMP", "HITRUST", "ISO_27001", "ISO_27701", "NIS2", "NIST_800_171", "NIST_800_53", "NYDFS_500", "SOC2", "SOX"],
10398
+ "azure-key": ["APRA_CPS234", "DORA", "FISMA", "FedRAMP", "HITRUST", "ISO_27001", "ISO_27701", "NIS2", "NIST_800_171", "NIST_800_53", "NYDFS_500", "SOC2", "SOX"],
10399
+ "bank-account-labeled": ["APRA_CPS234", "DORA", "GLBA", "IRS_1075", "NYDFS_500", "SOX"],
10400
+ "bitcoin-address": ["DORA", "NYDFS_500"],
10401
+ "card-cvv": ["CCPA_CPRA", "GLBA", "PCI_DSS"],
10402
+ "clinical-diagnosis": ["HIPAA", "HITECH", "HITRUST"],
10403
+ "credit-card": ["APRA_CPS234", "CCPA_CPRA", "DORA", "GLBA", "PCI_DSS"],
10404
+ "cui-contract-number": ["CMMC_L2", "NIST_800_171"],
10405
+ "cui-part-number": ["CMMC_L2", "NIST_800_171"],
10406
+ "cui-program-code": ["CMMC_L2", "NIST_800_171"],
10407
+ "database-url": ["APRA_CPS234", "DORA", "FISMA", "FedRAMP", "HITRUST", "ISO_27001", "ISO_27701", "NIS2", "NIST_800_171", "NIST_800_53", "NYDFS_500", "SOC2", "SOX"],
10408
+ "dob": ["APPI", "AU_PRIVACY", "CCPA_CPRA", "COPPA", "DPDP", "FADP", "FERPA", "GDPR", "HIPAA", "HITECH", "HITRUST", "ISO_27701", "LGPD", "PDPA_SG", "PDPA_TH", "PIPA_KR", "PIPEDA", "PIPL", "POPIA", "UK_GDPR"],
10409
+ "drivers-license": ["APPI", "AU_PRIVACY", "CCPA_CPRA", "CJIS", "DPDP", "FADP", "FISMA", "GDPR", "IRS_1075", "ISO_27001", "ISO_27701", "LGPD", "PDPA_SG", "PDPA_TH", "PIPA_KR", "PIPEDA", "PIPL", "POPIA", "UK_GDPR"],
10410
+ "email": ["APPI", "AU_PRIVACY", "CCPA_CPRA", "CJIS", "COPPA", "DPDP", "FADP", "FERPA", "GDPR", "GLBA", "HIPAA", "HITECH", "ISO_27001", "ISO_27701", "LGPD", "NIST_800_53", "PDPA_SG", "PDPA_TH", "PIPA_KR", "PIPEDA", "PIPL", "POPIA", "UK_GDPR"],
10411
+ "email-labeled": ["APPI", "AU_PRIVACY", "CCPA_CPRA", "CJIS", "COPPA", "DPDP", "FADP", "FERPA", "GDPR", "GLBA", "HIPAA", "HITECH", "ISO_27001", "ISO_27701", "LGPD", "NIST_800_53", "PDPA_SG", "PDPA_TH", "PIPA_KR", "PIPEDA", "PIPL", "POPIA", "UK_GDPR"],
10412
+ "env-secret": ["APRA_CPS234", "DORA", "FISMA", "FedRAMP", "HITRUST", "ISO_27001", "ISO_27701", "NIS2", "NIST_800_171", "NIST_800_53", "NYDFS_500", "SOC2", "SOX"],
10413
+ "env-secret-unquoted": ["APRA_CPS234", "DORA", "FISMA", "FedRAMP", "HITRUST", "ISO_27001", "ISO_27701", "NIS2", "NIST_800_171", "NIST_800_53", "NYDFS_500", "SOC2", "SOX"],
10414
+ "eth-wallet": ["DORA", "NYDFS_500"],
10415
+ "gcp-service-account": ["APRA_CPS234", "DORA", "FISMA", "FedRAMP", "HITRUST", "ISO_27001", "ISO_27701", "NIS2", "NIST_800_171", "NIST_800_53", "NYDFS_500", "SOC2", "SOX"],
10416
+ "generic-secret": ["APRA_CPS234", "DORA", "FISMA", "FedRAMP", "HITRUST", "ISO_27001", "ISO_27701", "NIS2", "NIST_800_171", "NIST_800_53", "NYDFS_500", "SOC2", "SOX"],
10417
+ "generic-secret-assignment": ["APRA_CPS234", "DORA", "FISMA", "FedRAMP", "HITRUST", "ISO_27001", "ISO_27701", "NIS2", "NIST_800_171", "NIST_800_53", "NYDFS_500", "SOC2", "SOX"],
10418
+ "github-fine-grained": ["APRA_CPS234", "DORA", "FISMA", "FedRAMP", "HITRUST", "ISO_27001", "ISO_27701", "NIS2", "NIST_800_171", "NIST_800_53", "NYDFS_500", "SOC2", "SOX"],
10419
+ "github-token": ["APRA_CPS234", "DORA", "FISMA", "FedRAMP", "HITRUST", "ISO_27001", "ISO_27701", "NIS2", "NIST_800_171", "NIST_800_53", "NYDFS_500", "SOC2", "SOX"],
10420
+ "github-token-split": ["APRA_CPS234", "DORA", "FISMA", "FedRAMP", "HITRUST", "ISO_27001", "ISO_27701", "NIS2", "NIST_800_171", "NIST_800_53", "NYDFS_500", "SOC2", "SOX"],
10421
+ "google-api-key": ["APRA_CPS234", "DORA", "FISMA", "FedRAMP", "HITRUST", "ISO_27001", "ISO_27701", "NIS2", "NIST_800_171", "NIST_800_53", "NYDFS_500", "SOC2", "SOX"],
10422
+ "google-oauth-refresh": ["APRA_CPS234", "DORA", "FISMA", "FedRAMP", "HITRUST", "ISO_27001", "ISO_27701", "NIS2", "NIST_800_171", "NIST_800_53", "NYDFS_500", "SOC2", "SOX"],
10423
+ "iban": ["DORA", "FADP", "GDPR", "GLBA", "UK_GDPR"],
10424
+ "iban-grouped": ["DORA", "FADP", "GDPR", "GLBA", "UK_GDPR"],
10425
+ "iban-spaced": ["DORA", "FADP", "GDPR", "GLBA", "UK_GDPR"],
10426
+ "icd10-bracketed": ["HIPAA", "HITECH", "HITRUST"],
10427
+ "icd10-code": ["HIPAA", "HITECH", "HITRUST"],
10428
+ "imei": ["APPI", "AU_PRIVACY", "CCPA_CPRA", "COPPA", "DPDP", "FADP", "FERPA", "GDPR", "ISO_27701", "LGPD", "PDPA_SG", "PDPA_TH", "PIPA_KR", "PIPEDA", "PIPL", "POPIA", "UK_GDPR"],
10429
+ "imsi": ["APPI", "AU_PRIVACY", "CCPA_CPRA", "COPPA", "DPDP", "FADP", "FERPA", "GDPR", "ISO_27701", "LGPD", "PDPA_SG", "PDPA_TH", "PIPA_KR", "PIPEDA", "PIPL", "POPIA", "UK_GDPR"],
10430
+ "insurance-member-id": ["HIPAA", "HITECH", "HITRUST"],
10431
+ "ip-address": ["AU_PRIVACY", "CCPA_CPRA", "CJIS", "FISMA", "FedRAMP", "GDPR", "ISO_27001", "ISO_27701", "NIS2", "NIST_800_53", "SOC2", "UK_GDPR"],
10432
+ "ipv6-address": ["AU_PRIVACY", "CCPA_CPRA", "CJIS", "FISMA", "FedRAMP", "GDPR", "ISO_27001", "ISO_27701", "NIS2", "NIST_800_53", "SOC2", "UK_GDPR"],
10433
+ "jwt-token": ["APRA_CPS234", "DORA", "FISMA", "FedRAMP", "HITRUST", "ISO_27001", "ISO_27701", "NIS2", "NIST_800_171", "NIST_800_53", "NYDFS_500", "SOC2", "SOX"],
10434
+ "mac-address": ["AU_PRIVACY", "CCPA_CPRA", "CJIS", "FISMA", "FedRAMP", "GDPR", "ISO_27001", "ISO_27701", "NIS2", "NIST_800_53", "SOC2", "UK_GDPR"],
10435
+ "medical-record-number": ["HIPAA", "HITECH", "HITRUST"],
10436
+ "medicare-mbi-labeled": ["HIPAA", "HITECH", "HITRUST"],
10437
+ "national-id": ["APPI", "AU_PRIVACY", "CCPA_CPRA", "CJIS", "COPPA", "DPDP", "FADP", "FISMA", "GDPR", "IRS_1075", "ISO_27001", "ISO_27701", "LGPD", "PDPA_SG", "PDPA_TH", "PIPA_KR", "PIPEDA", "PIPL", "POPIA", "UK_GDPR"],
10438
+ "national-id-grouped": ["APPI", "AU_PRIVACY", "CCPA_CPRA", "CJIS", "COPPA", "DPDP", "FADP", "FISMA", "GDPR", "IRS_1075", "ISO_27001", "ISO_27701", "LGPD", "PDPA_SG", "PDPA_TH", "PIPA_KR", "PIPEDA", "PIPL", "POPIA", "UK_GDPR"],
10439
+ "npi-labeled": ["HIPAA", "HITECH", "HITRUST"],
10440
+ "openai-api-key": ["APRA_CPS234", "DORA", "FISMA", "FedRAMP", "HITRUST", "ISO_27001", "ISO_27701", "NIS2", "NIST_800_171", "NIST_800_53", "NYDFS_500", "SOC2", "SOX"],
10441
+ "passport": ["APPI", "AU_PRIVACY", "CCPA_CPRA", "CJIS", "DPDP", "FADP", "FISMA", "GDPR", "IRS_1075", "ISO_27001", "ISO_27701", "LGPD", "PDPA_SG", "PDPA_TH", "PIPA_KR", "PIPEDA", "PIPL", "POPIA", "UK_GDPR"],
10442
+ "phone-intl": ["APPI", "AU_PRIVACY", "CCPA_CPRA", "CJIS", "COPPA", "DPDP", "FADP", "FERPA", "GDPR", "GLBA", "HIPAA", "HITECH", "ISO_27001", "ISO_27701", "LGPD", "NIST_800_53", "PDPA_SG", "PDPA_TH", "PIPA_KR", "PIPEDA", "PIPL", "POPIA", "UK_GDPR"],
10443
+ "phone-intl-labeled": ["APPI", "AU_PRIVACY", "CCPA_CPRA", "CJIS", "COPPA", "DPDP", "FADP", "FERPA", "GDPR", "GLBA", "HIPAA", "HITECH", "ISO_27001", "ISO_27701", "LGPD", "NIST_800_53", "PDPA_SG", "PDPA_TH", "PIPA_KR", "PIPEDA", "PIPL", "POPIA", "UK_GDPR"],
10444
+ "phone-us": ["APPI", "AU_PRIVACY", "CCPA_CPRA", "CJIS", "COPPA", "DPDP", "FADP", "FERPA", "GDPR", "GLBA", "HIPAA", "HITECH", "ISO_27001", "ISO_27701", "LGPD", "NIST_800_53", "PDPA_SG", "PDPA_TH", "PIPA_KR", "PIPEDA", "PIPL", "POPIA", "UK_GDPR"],
10445
+ "private-key": ["APRA_CPS234", "DORA", "FISMA", "FedRAMP", "HITRUST", "ISO_27001", "ISO_27701", "NIS2", "NIST_800_171", "NIST_800_53", "NYDFS_500", "SOC2", "SOX"],
10446
+ "private-key-block": ["APRA_CPS234", "DORA", "FISMA", "FedRAMP", "HITRUST", "ISO_27001", "ISO_27701", "NIS2", "NIST_800_171", "NIST_800_53", "NYDFS_500", "SOC2", "SOX"],
10447
+ "private-key-body": ["APRA_CPS234", "DORA", "FISMA", "FedRAMP", "HITRUST", "ISO_27001", "ISO_27701", "NIS2", "NIST_800_171", "NIST_800_53", "NYDFS_500", "SOC2", "SOX"],
10448
+ "sendgrid-key": ["APRA_CPS234", "DORA", "FISMA", "FedRAMP", "HITRUST", "ISO_27001", "ISO_27701", "NIS2", "NIST_800_171", "NIST_800_53", "NYDFS_500", "SOC2", "SOX"],
10449
+ "slack-token": ["APRA_CPS234", "DORA", "FISMA", "FedRAMP", "HITRUST", "ISO_27001", "ISO_27701", "NIS2", "NIST_800_171", "NIST_800_53", "NYDFS_500", "SOC2", "SOX"],
10450
+ "slack-webhook-url": ["APRA_CPS234", "DORA", "FISMA", "FedRAMP", "HITRUST", "ISO_27001", "ISO_27701", "NIS2", "NIST_800_171", "NIST_800_53", "NYDFS_500", "SOC2", "SOX"],
10451
+ "ssn": ["APPI", "AU_PRIVACY", "CCPA_CPRA", "CJIS", "DPDP", "FADP", "FERPA", "FISMA", "GDPR", "GLBA", "HIPAA", "HITECH", "IRS_1075", "ISO_27001", "ISO_27701", "LGPD", "NIST_800_53", "PDPA_SG", "PDPA_TH", "PIPA_KR", "PIPEDA", "PIPL", "POPIA", "UK_GDPR"],
10452
+ "stripe-publishable-key": ["APRA_CPS234", "DORA", "FISMA", "FedRAMP", "HITRUST", "ISO_27001", "ISO_27701", "NIS2", "NIST_800_171", "NIST_800_53", "NYDFS_500", "SOC2", "SOX"],
10453
+ "stripe-restricted-key": ["APRA_CPS234", "DORA", "FISMA", "FedRAMP", "HITRUST", "ISO_27001", "ISO_27701", "NIS2", "NIST_800_171", "NIST_800_53", "NYDFS_500", "SOC2", "SOX"],
10454
+ "stripe-secret-key": ["APRA_CPS234", "DORA", "FISMA", "FedRAMP", "HITRUST", "ISO_27001", "ISO_27701", "NIS2", "NIST_800_171", "NIST_800_53", "NYDFS_500", "SOC2", "SOX"],
10455
+ "swift-bic": ["FADP", "GDPR", "UK_GDPR"],
10456
+ "twilio-account-sid": ["APRA_CPS234", "DORA", "FISMA", "FedRAMP", "HITRUST", "ISO_27001", "ISO_27701", "NIS2", "NIST_800_171", "NIST_800_53", "NYDFS_500", "SOC2", "SOX"],
10457
+ "twilio-auth-token": ["APRA_CPS234", "DORA", "FISMA", "FedRAMP", "HITRUST", "ISO_27001", "ISO_27701", "NIS2", "NIST_800_171", "NIST_800_53", "NYDFS_500", "SOC2", "SOX"],
10458
+ "uk-nhs-number-labeled": ["GDPR", "UK_GDPR"],
10459
+ "uk-nino-labeled": ["APPI", "AU_PRIVACY", "CCPA_CPRA", "CJIS", "DPDP", "FADP", "FISMA", "GDPR", "IRS_1075", "ISO_27001", "ISO_27701", "LGPD", "PDPA_SG", "PDPA_TH", "PIPA_KR", "PIPEDA", "PIPL", "POPIA", "UK_GDPR"],
10460
+ "us-ein": ["APRA_CPS234", "DORA", "GLBA", "IRS_1075", "NYDFS_500", "SOX"],
10461
+ "vat-number": ["FADP", "GDPR", "UK_GDPR"],
10462
+ "vault-token": ["APRA_CPS234", "DORA", "FISMA", "FedRAMP", "HITRUST", "ISO_27001", "ISO_27701", "NIS2", "NIST_800_171", "NIST_800_53", "NYDFS_500", "SOC2", "SOX"],
10463
+ "vehicle-vin": ["APPI", "AU_PRIVACY", "CCPA_CPRA", "COPPA", "DPDP", "FADP", "FERPA", "GDPR", "HIPAA", "HITECH", "HITRUST", "ISO_27701", "LGPD", "PDPA_SG", "PDPA_TH", "PIPA_KR", "PIPEDA", "PIPL", "POPIA", "UK_GDPR"]
10464
+ };
10465
+ function frameworksForDetector(detector) {
10466
+ return Object.prototype.hasOwnProperty.call(DETECTOR_FRAMEWORKS, detector) ? DETECTOR_FRAMEWORKS[detector] : [];
10467
+ }
10468
+ function detectorsMatchFramework(detectors, framework) {
10469
+ const want = framework.toUpperCase();
10470
+ return detectors.some((d) => frameworksForDetector(d).some((f) => f.toUpperCase() === want));
10471
+ }
10472
+ function resolveFrameworkName(input) {
10473
+ const want = input.toUpperCase();
10474
+ return COMPLIANCE_FRAMEWORKS.find((f) => f.toUpperCase() === want) ?? null;
10475
+ }
10476
+
10386
10477
  // ../packages/scanner/dist/index.js
10387
10478
  import { gunzipSync } from "zlib";
10388
10479
  var SECRET_LABEL_TAILS = [
@@ -11282,7 +11373,125 @@ var EXTENDED_PATTERNS = [
11282
11373
  pattern: /\b(?:patient|pt|resident|inpatient|outpatient)\b[\s\S]{0,25}?\bdiagnos(?:ed|is)\b(?:\s+(?:with|of))?\s+([A-Za-z][A-Za-z0-9()\- ]{2,45}?)(?=\s*(?:\[|[.;,]|\btoday\b|\bon file\b|$))/gid,
11283
11374
  valueGroup: 1
11284
11375
  },
11285
- // ── CMMC L2 CUI (controlled unclassified info: part / program / contract) ────
11376
+ // ── CMMC L2 / CUI REAL marking conventions ────────────────────────────────
11377
+ //
11378
+ // Read this before quoting any CMMC_L2 number.
11379
+ //
11380
+ // The three detectors AFTER this block (`cui-part-number`, `cui-program-code`,
11381
+ // `cui-contract-number`) are fitted to our own corpus generator. Their comment
11382
+ // says so outright: it names the fixtures `PN-9FRUDT`, `PRG-1B3ZEK`,
11383
+ // `CTR-2025-270565`. `PN-`, `PRG-` and `CTR-` are prefixes the generator
11384
+ // invents; they are not a convention any defence contractor uses. Those three
11385
+ // are the ONLY kinds mapped to CMMC_L2 (`_CUI` in compliance.py), so the
11386
+ // framework's score has been the corpus measuring itself. Excluding them,
11387
+ // CMMC_L2 had n = 0.
11388
+ //
11389
+ // The detectors in THIS block are different in kind: every one matches a
11390
+ // marking published in a public standard, so a hit means the same thing in a
11391
+ // real contractor's repository as it does in our corpus.
11392
+ //
11393
+ // - CUI banner / portion markings ....... 32 CFR Part 2002, DoD CUI Registry
11394
+ // - Distribution statements A–F ......... DoDI 5230.24
11395
+ // - ECCN classifications ................ EAR, 15 CFR Part 774 (CCL)
11396
+ // - ITAR citations ...................... 22 CFR Parts 120–130
11397
+ // - National Stock Number ............... NATO/DoD 13-digit NSN
11398
+ // - CAGE code ........................... NATO Commercial And Government Entity
11399
+ //
11400
+ // Bare `CUI` is deliberately NOT matched: three letters that occur in ordinary
11401
+ // prose and identifiers would flood the egress path with false positives. Only
11402
+ // the structured forms — `CUI//CATEGORY` and the `(CUI)` portion mark — are
11403
+ // distinctive enough to be safe.
11404
+ {
11405
+ name: "cui-banner-marking",
11406
+ category: "proprietary",
11407
+ severity: "high",
11408
+ defaultAction: "redact",
11409
+ // `CUI//SP-CTI`, `CUI//SP-PROPIN`, `CUI//SP-EXPT`, `CUI//NOFORN`,
11410
+ // `CONTROLLED//SP-CTI`, and the multi-category `CUI//SP-CTI/SP-EXPT`.
11411
+ pattern: /\b(?:CUI|CONTROLLED)\/\/(?:[A-Z][A-Z0-9-]{1,15})(?:\/[A-Z][A-Z0-9-]{1,15})*\b/g
11412
+ },
11413
+ {
11414
+ name: "cui-portion-marking",
11415
+ category: "proprietary",
11416
+ severity: "medium",
11417
+ defaultAction: "redact",
11418
+ // Portion marks that precede a paragraph: `(CUI)`, `(CUI//SP-CTI)`.
11419
+ pattern: /\((?:CUI|CUI\/\/[A-Z][A-Z0-9-]{1,15})\)/g
11420
+ },
11421
+ {
11422
+ name: "cui-distribution-statement",
11423
+ category: "proprietary",
11424
+ severity: "high",
11425
+ defaultAction: "redact",
11426
+ // DoDI 5230.24. Statement A is public-release, but its PRESENCE still marks
11427
+ // the document as controlled-technical-data governed, so all of A–F match.
11428
+ pattern: /\bDISTRIBUTION\s+STATEMENT\s+[A-F]\b/gi
11429
+ },
11430
+ {
11431
+ name: "export-control-eccn",
11432
+ category: "proprietary",
11433
+ severity: "high",
11434
+ defaultAction: "redact",
11435
+ // Commerce Control List entries: one digit, a letter A–E, three digits
11436
+ // (`5A002`, `3A001`, `9D515`). That shape is NOT distinctive on its own — it
11437
+ // collides with version strings and identifiers, which this detector proved
11438
+ // when the label was optional:
11439
+ //
11440
+ // "release 5A002 build" → matched ← false positive
11441
+ // "v1.2.3 and 9D515" → matched ← false positive
11442
+ //
11443
+ // So the ECCN label is REQUIRED, matching the label-anchoring rule the rest
11444
+ // of this file follows for non-self-shaped kinds. Real documents carry it;
11445
+ // ECCNs are quoted as "ECCN 5A002", not bare.
11446
+ //
11447
+ // EAR99 is a separate detector below rather than an alternative here: with a
11448
+ // `valueGroup`, an alternative that does not participate in the capture
11449
+ // yields no value and the match is dropped — which is exactly what happened
11450
+ // when the two were combined (EAR99 silently stopped matching).
11451
+ pattern: /\b(?:ECCN|Export\s+Control\s+Classification\s+Number)\s*[:#=]?\s*(\d[A-E]\d{3}(?:\.[a-z](?:\.\d+)?)?)\b/gid,
11452
+ valueGroup: 1
11453
+ },
11454
+ {
11455
+ name: "export-control-ear99",
11456
+ category: "proprietary",
11457
+ severity: "medium",
11458
+ defaultAction: "redact",
11459
+ // A fixed literal, not a shape, so it needs no label anchor and cannot
11460
+ // collide with version strings the way the numeric ECCN form does.
11461
+ pattern: /\bEAR99\b/g
11462
+ },
11463
+ {
11464
+ name: "itar-citation",
11465
+ category: "proprietary",
11466
+ severity: "high",
11467
+ defaultAction: "redact",
11468
+ // ITAR lives at 22 CFR 120–130; USML categories are Roman numerals I–XXI.
11469
+ pattern: /\b(?:ITAR\b|22\s*C\.?F\.?R\.?\s*(?:Part\s*)?1[2-3]\d\b|USML\s+Category\s+(?:X{0,2}(?:IX|IV|V?I{0,3}))\b)/gi
11470
+ },
11471
+ {
11472
+ name: "national-stock-number",
11473
+ category: "proprietary",
11474
+ severity: "medium",
11475
+ defaultAction: "redact",
11476
+ // NSN: 4-digit FSC, 2-digit NCB, 7-digit item number, conventionally written
11477
+ // NNNN-NN-NNN-NNNN. Self-shaped and distinctive enough to need no label.
11478
+ pattern: /\b\d{4}-\d{2}-\d{3}-\d{4}\b/g
11479
+ },
11480
+ {
11481
+ name: "cage-code",
11482
+ category: "proprietary",
11483
+ severity: "medium",
11484
+ defaultAction: "redact",
11485
+ // A CAGE code is 5 alphanumerics — far too generic to match unanchored, so
11486
+ // this requires the label. Same reasoning as the other label-gated kinds.
11487
+ pattern: /\bCAGE(?:\s*(?:code|CD))?\s*[:#=]?\s*([0-9A-Z]{5})\b/gid,
11488
+ valueGroup: 1
11489
+ },
11490
+ // ── LEGACY: corpus-fitted CUI detectors ─────────────────────────────────────
11491
+ // These match prefixes our own generator invents. Kept so the historical
11492
+ // baseline stays comparable, but a CMMC_L2 figure resting on them is not a
11493
+ // measurement — see the block above. Prefer the standards-based detectors.
11494
+ //
11286
11495
  // K2 precision: raised the shape floors so short ticket/Jira-style refs don't
11287
11496
  // match. part/program require 5+ chars AND at least one letter after the prefix
11288
11497
  // (excludes `PN-1234`); contract requires the full `CTR-<4-digit-year>-<4+>`
@@ -14833,11 +15042,13 @@ var AuditStore = class {
14833
15042
  INSERT INTO audit_log
14834
15043
  (timestamp, request_id, provider, model, direction,
14835
15044
  original_size, processed_size, secrets_blocked, pii_redacted,
14836
- mutations_applied, scan_duration_ms, mutation_duration_ms, action, details)
15045
+ mutations_applied, scan_duration_ms, mutation_duration_ms, action, details,
15046
+ detector_kinds)
14837
15047
  VALUES
14838
15048
  (@timestamp, @requestId, @provider, @model, @direction,
14839
15049
  @originalSize, @processedSize, @secretsBlocked, @piiRedacted,
14840
- @mutationsApplied, @scanDurationMs, @mutationDurationMs, @action, @details)
15050
+ @mutationsApplied, @scanDurationMs, @mutationDurationMs, @action, @details,
15051
+ @detectorKinds)
14841
15052
  `);
14842
15053
  this.pruneByAgeStmt = this.db.prepare(
14843
15054
  `DELETE FROM audit_log WHERE id IN (
@@ -14868,7 +15079,8 @@ var AuditStore = class {
14868
15079
  scan_duration_ms REAL NOT NULL DEFAULT 0,
14869
15080
  mutation_duration_ms REAL NOT NULL DEFAULT 0,
14870
15081
  action TEXT NOT NULL DEFAULT 'forwarded',
14871
- details TEXT
15082
+ details TEXT,
15083
+ detector_kinds TEXT
14872
15084
  );
14873
15085
 
14874
15086
  CREATE INDEX IF NOT EXISTS idx_timestamp ON audit_log(timestamp);
@@ -14898,7 +15110,11 @@ var AuditStore = class {
14898
15110
  scanDurationMs: entry.scanDurationMs,
14899
15111
  mutationDurationMs: entry.mutationDurationMs,
14900
15112
  action: entry.action,
14901
- details: entry.details ? JSON.stringify(entry.details) : null
15113
+ details: entry.details ? JSON.stringify(entry.details) : null,
15114
+ // Deduped + sorted so the column is stable for a given set of findings:
15115
+ // two requests that tripped the same detectors serialise identically,
15116
+ // which keeps grouping and diffing meaningful.
15117
+ detectorKinds: entry.detectorKinds && entry.detectorKinds.length > 0 ? JSON.stringify([...new Set(entry.detectorKinds)].sort()) : null
14902
15118
  });
14903
15119
  if (this.pruneEveryInserts > 0 && ++this.insertsSincePrune >= this.pruneEveryInserts) {
14904
15120
  this.insertsSincePrune = 0;
@@ -15030,7 +15246,20 @@ var AuditStore = class {
15030
15246
  scanDurationMs: row["scan_duration_ms"],
15031
15247
  mutationDurationMs: row["mutation_duration_ms"],
15032
15248
  action: row["action"],
15033
- details: row["details"] ? JSON.parse(row["details"]) : void 0
15249
+ details: row["details"] ? JSON.parse(row["details"]) : void 0,
15250
+ // Tolerate a malformed/legacy value rather than throwing: a corrupt column
15251
+ // in ONE row must not make the whole audit log unreadable, which is the
15252
+ // opposite of what an audit trail is for.
15253
+ detectorKinds: (() => {
15254
+ const raw2 = row["detector_kinds"];
15255
+ if (typeof raw2 !== "string" || raw2.length === 0) return void 0;
15256
+ try {
15257
+ const parsed = JSON.parse(raw2);
15258
+ return Array.isArray(parsed) ? parsed : void 0;
15259
+ } catch {
15260
+ return void 0;
15261
+ }
15262
+ })()
15034
15263
  };
15035
15264
  }
15036
15265
  };
@@ -16238,6 +16467,11 @@ Upgrade to ${_upgradeTier === "enterprise" ? "Enterprise" : "Pro"} for ${_upgrad
16238
16467
  if (deep.scanIncomplete) {
16239
16468
  const reasons = [...deep.incompleteReasons];
16240
16469
  safeLog({
16470
+ // The detector names that fired. The proxy already collected these in
16471
+ // `foundTypes` for telemetry; the audit row discarded them, which is why
16472
+ // an audit trail could not be filtered by compliance framework — frameworks
16473
+ // are defined over KINDS, and counts alone cannot say which kind was seen.
16474
+ detectorKinds: [...foundTypes],
16241
16475
  timestamp: Date.now(),
16242
16476
  requestId,
16243
16477
  provider,
@@ -16278,6 +16512,11 @@ Upgrade to ${_upgradeTier === "enterprise" ? "Enterprise" : "Pro"} for ${_upgrad
16278
16512
  }
16279
16513
  if (config.blockMode === "reject" && sensitiveFound > 0) {
16280
16514
  safeLog({
16515
+ // The detector names that fired. The proxy already collected these in
16516
+ // `foundTypes` for telemetry; the audit row discarded them, which is why
16517
+ // an audit trail could not be filtered by compliance framework — frameworks
16518
+ // are defined over KINDS, and counts alone cannot say which kind was seen.
16519
+ detectorKinds: [...foundTypes],
16281
16520
  timestamp: Date.now(),
16282
16521
  requestId,
16283
16522
  provider,
@@ -16350,6 +16589,11 @@ Upgrade to ${_upgradeTier === "enterprise" ? "Enterprise" : "Pro"} for ${_upgrad
16350
16589
  const riskResult = riskScorer.score(riskCtx);
16351
16590
  if (riskResult.recommendation === "block") {
16352
16591
  safeLog({
16592
+ // The detector names that fired. The proxy already collected these in
16593
+ // `foundTypes` for telemetry; the audit row discarded them, which is why
16594
+ // an audit trail could not be filtered by compliance framework — frameworks
16595
+ // are defined over KINDS, and counts alone cannot say which kind was seen.
16596
+ detectorKinds: [...foundTypes],
16353
16597
  timestamp: Date.now(),
16354
16598
  requestId,
16355
16599
  provider,
@@ -16413,6 +16657,11 @@ Upgrade to ${_upgradeTier === "enterprise" ? "Enterprise" : "Pro"} for ${_upgrad
16413
16657
  });
16414
16658
  } catch {
16415
16659
  safeLog({
16660
+ // The detector names that fired. The proxy already collected these in
16661
+ // `foundTypes` for telemetry; the audit row discarded them, which is why
16662
+ // an audit trail could not be filtered by compliance framework — frameworks
16663
+ // are defined over KINDS, and counts alone cannot say which kind was seen.
16664
+ detectorKinds: [...foundTypes],
16416
16665
  timestamp: Date.now(),
16417
16666
  requestId,
16418
16667
  provider,
@@ -16445,6 +16694,11 @@ Upgrade to ${_upgradeTier === "enterprise" ? "Enterprise" : "Pro"} for ${_upgrad
16445
16694
  const anyTokenized = tokensMutated > 0 || sensitiveFound > 0;
16446
16695
  const action = anyTokenized ? "mutated" : "forwarded";
16447
16696
  safeLog({
16697
+ // The detector names that fired. The proxy already collected these in
16698
+ // `foundTypes` for telemetry; the audit row discarded them, which is why
16699
+ // an audit trail could not be filtered by compliance framework — frameworks
16700
+ // are defined over KINDS, and counts alone cannot say which kind was seen.
16701
+ detectorKinds: [...foundTypes],
16448
16702
  timestamp: Date.now(),
16449
16703
  requestId,
16450
16704
  provider,
@@ -17155,15 +17409,17 @@ function getNestedValue(obj, key) {
17155
17409
  }
17156
17410
  function setNestedValue(obj, key, value) {
17157
17411
  const parts = key.split(".");
17412
+ const leaf = parts.pop();
17413
+ if (leaf === void 0) return;
17158
17414
  let cur = obj;
17159
- for (let i = 0; i < parts.length - 1; i++) {
17160
- const p = parts[i];
17161
- if (cur[p] == null || typeof cur[p] !== "object" || Array.isArray(cur[p])) {
17415
+ for (const p of parts) {
17416
+ const child = cur[p];
17417
+ if (child == null || typeof child !== "object" || Array.isArray(child)) {
17162
17418
  cur[p] = {};
17163
17419
  }
17164
17420
  cur = cur[p];
17165
17421
  }
17166
- cur[parts[parts.length - 1]] = value;
17422
+ cur[leaf] = value;
17167
17423
  }
17168
17424
  function parseValue(raw2) {
17169
17425
  if (raw2.startsWith("[") && raw2.endsWith("]")) {
@@ -17391,10 +17647,12 @@ async function resolvePlanTier(opts = {}) {
17391
17647
  }
17392
17648
  return tierFromBackend(apiKey);
17393
17649
  }
17650
+ var QUOTA_PERIOD_DAYS = 7;
17394
17651
  function collectUsage(dbPath) {
17395
17652
  const now = /* @__PURE__ */ new Date();
17396
- const monthStartTs = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1, 0, 0, 0, 0);
17397
- const sinceMs = Date.now() - monthStartTs;
17653
+ const periodMs = QUOTA_PERIOD_DAYS * 24 * 60 * 60 * 1e3;
17654
+ const monthStartTs = Date.now() - periodMs;
17655
+ const sinceMs = periodMs;
17398
17656
  let store = null;
17399
17657
  try {
17400
17658
  store = new AuditStore(dbPath);
@@ -17403,8 +17661,10 @@ function collectUsage(dbPath) {
17403
17661
  monthStartTs,
17404
17662
  metrics: {
17405
17663
  tenantId: "local",
17664
+ // The trailing window these numbers actually cover — previously the
17665
+ // calendar month, which did not match the aggregation OR the quota.
17406
17666
  periodStart: new Date(monthStartTs).toISOString().split("T")[0],
17407
- periodEnd: new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth() + 1, 0)).toISOString().split("T")[0],
17667
+ periodEnd: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
17408
17668
  mutationsApplied: stats.totalMutationsApplied,
17409
17669
  secretsBlocked: stats.totalSecretsBlocked,
17410
17670
  piiRedacted: stats.totalPiiRedacted,
@@ -17420,9 +17680,8 @@ function collectUsage(dbPath) {
17420
17680
  store?.close();
17421
17681
  }
17422
17682
  }
17423
- function nextMonthReset() {
17424
- const now = /* @__PURE__ */ new Date();
17425
- return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth() + 1, 1)).toISOString().split("T")[0];
17683
+ function resetCadenceLabel() {
17684
+ return `every ${QUOTA_PERIOD_DAYS} days`;
17426
17685
  }
17427
17686
  function priceLabel(tier) {
17428
17687
  const cents = PLAN_CONFIGS[tier].pricePerSeatCents;
@@ -17471,7 +17730,8 @@ function emptyUsagePayload(resolution) {
17471
17730
  ...resolution.detail ? { tierDetail: resolution.detail } : {},
17472
17731
  periodStart: null,
17473
17732
  periodEnd: null,
17474
- resetDate: nextMonthReset(),
17733
+ resetDate: null,
17734
+ resetCadenceDays: QUOTA_PERIOD_DAYS,
17475
17735
  usage: {
17476
17736
  mutations: { current: 0, limit: planConfig ? planConfig.mutationsPerMonth : null, percent: 0, allowed: true },
17477
17737
  scans: { current: 0, limit: planConfig ? planConfig.scansPerMonth : null, percent: 0, allowed: true }
@@ -17508,7 +17768,8 @@ function usageCommand() {
17508
17768
  ...resolution.detail ? { tierDetail: resolution.detail } : {},
17509
17769
  periodStart: usageMetrics.periodStart,
17510
17770
  periodEnd: usageMetrics.periodEnd,
17511
- resetDate: nextMonthReset(),
17771
+ resetDate: null,
17772
+ resetCadenceDays: QUOTA_PERIOD_DAYS,
17512
17773
  usage: {
17513
17774
  mutations: {
17514
17775
  current: usageMetrics.mutationsApplied,
@@ -17532,7 +17793,7 @@ function usageCommand() {
17532
17793
  );
17533
17794
  return;
17534
17795
  }
17535
- const resetDate = nextMonthReset();
17796
+ const resetDate = resetCadenceLabel();
17536
17797
  console.log(
17537
17798
  chalk7.cyan(
17538
17799
  "\n Pretense Usage \u2014 " + (/* @__PURE__ */ new Date()).toLocaleDateString("en-US", { month: "long", year: "numeric" })
@@ -18905,11 +19166,34 @@ function toSafeAuditRow(e) {
18905
19166
  mutationsApplied: e.mutationsApplied,
18906
19167
  secretsBlocked: e.secretsBlocked,
18907
19168
  piiRedacted: e.piiRedacted,
18908
- scanDurationMs: e.scanDurationMs
19169
+ scanDurationMs: e.scanDurationMs,
19170
+ // Detector NAMES only. This stays leak-proof: "ssn" records that a social
19171
+ // security number was seen, never the number. Carrying it is what lets the
19172
+ // row be attributed to a compliance framework at all.
19173
+ detectorKinds: e.detectorKinds ?? []
18909
19174
  };
18910
19175
  }
18911
19176
  function auditCommand() {
18912
- return new Command17("audit").description("Show recent mutation/tokenization audit entries (leak-proof; JSONL with --json)").option("-l, --limit <n>", "Number of entries to show", "20").option("--json", "Output as JSONL (one JSON object per line, jq-pipeable)", false).option("--db <path>", "Path to the audit database (default: ~/.pretense/audit.db)").action((opts) => {
19177
+ return new Command17("audit").description("Show recent mutation/tokenization audit entries (leak-proof; JSONL with --json)").option("-l, --limit <n>", "Number of entries to show", "20").option("--json", "Output as JSONL (one JSON object per line, jq-pipeable)", false).option("--db <path>", "Path to the audit database (default: ~/.pretense/audit.db)").option(
19178
+ "--framework <name>",
19179
+ "Only entries whose detectors map to this compliance framework (e.g. HIPAA, SOC2, PCI_DSS). Use --list-frameworks to see all 36."
19180
+ ).option("--list-frameworks", "List the compliance frameworks that can be filtered on", false).action((opts) => {
19181
+ if (opts.listFrameworks) {
19182
+ for (const f of COMPLIANCE_FRAMEWORKS) console.log(f);
19183
+ return;
19184
+ }
19185
+ let framework = null;
19186
+ if (opts.framework) {
19187
+ framework = resolveFrameworkName(opts.framework);
19188
+ if (!framework) {
19189
+ console.error(
19190
+ chalk16.red(`
19191
+ x Unknown framework: "${opts.framework}"
19192
+ `) + chalk16.dim(" Run ") + chalk16.cyan("pretense audit --list-frameworks") + chalk16.dim(" to see all 36.\n")
19193
+ );
19194
+ process.exit(1);
19195
+ }
19196
+ }
18913
19197
  const limit = parseInt(opts.limit ?? "20", 10);
18914
19198
  if (!Number.isFinite(limit) || limit <= 0) {
18915
19199
  console.error(chalk16.red(`
@@ -18921,7 +19205,11 @@ function auditCommand() {
18921
19205
  let rows;
18922
19206
  try {
18923
19207
  const store = new AuditStore(dbPath);
18924
- rows = store.getRecentEntries(limit).map(toSafeAuditRow);
19208
+ const readLimit = framework ? Math.max(limit * 20, 500) : limit;
19209
+ rows = store.getRecentEntries(readLimit).map(toSafeAuditRow);
19210
+ if (framework) {
19211
+ rows = rows.filter((r) => detectorsMatchFramework(r.detectorKinds ?? [], framework)).slice(0, limit);
19212
+ }
18925
19213
  store.close();
18926
19214
  } catch {
18927
19215
  if (opts.json) {
@@ -19009,7 +19297,8 @@ function creditsCommand() {
19009
19297
  tier,
19010
19298
  tierSource: resolution.source,
19011
19299
  ...resolution.detail ? { tierDetail: resolution.detail } : {},
19012
- resetDate: nextMonthReset(),
19300
+ resetDate: null,
19301
+ resetCadenceDays: QUOTA_PERIOD_DAYS,
19013
19302
  mutations: {
19014
19303
  used: m.mutationsApplied,
19015
19304
  limit: tier ? mutationLimit : null,
@@ -19028,7 +19317,7 @@ function creditsCommand() {
19028
19317
  return;
19029
19318
  }
19030
19319
  console.log(chalk17.cyan("\n Pretense Credits"));
19031
- console.log(chalk17.dim(` Plan: ${tierLabel(tier)} | Resets: ${nextMonthReset()}
19320
+ console.log(chalk17.dim(` Plan: ${tierLabel(tier)} | Resets: ${resetCadenceLabel()}
19032
19321
  `));
19033
19322
  if (!tier) {
19034
19323
  for (const line of unverifiedNote(resolution.detail)) {
@@ -19110,8 +19399,8 @@ function upgradeCommand() {
19110
19399
  console.log(" " + chalk18.dim("\u2500".repeat(18 + col * TIERS.length)));
19111
19400
  const rows = [
19112
19401
  ["Price", (t) => priceLabel(t)],
19113
- ["Mutations/mo", (t) => quota(PLAN_CONFIGS[t].mutationsPerMonth)],
19114
- ["Requests/mo", (t) => quota(PLAN_CONFIGS[t].scansPerMonth)],
19402
+ ["Mutations/7d", (t) => quota(PLAN_CONFIGS[t].mutationsPerMonth)],
19403
+ ["Requests/7d", (t) => quota(PLAN_CONFIGS[t].scansPerMonth)],
19115
19404
  ["Seats", (t) => seats(PLAN_CONFIGS[t].maxSeats)],
19116
19405
  ["SSO", (t) => yesNo(PLAN_CONFIGS[t].ssoEnabled)],
19117
19406
  ["On-prem", (t) => yesNo(PLAN_CONFIGS[t].onPremEnabled)]
@@ -19170,7 +19459,7 @@ function findClosestCommand(input, knownCommands) {
19170
19459
  return bestDist <= 3 ? best : null;
19171
19460
  }
19172
19461
  var program = new Command20();
19173
- program.name("pretense").description(chalk19.bold("AI firewall") + " \u2014 mutates proprietary code before LLM API calls").version("0.6.8", "-v, --version").addHelpText("after", `
19462
+ program.name("pretense").description(chalk19.bold("AI firewall") + " \u2014 mutates proprietary code before LLM API calls").version("0.6.9", "-v, --version").addHelpText("after", `
19174
19463
  ${chalk19.bold("Quick start:")}
19175
19464
  ${chalk19.cyan("pretense scan")} ${chalk19.dim("<file|dir>")} Scan for secrets and PII
19176
19465
  ${chalk19.cyan("pretense mutate")} ${chalk19.dim("<file>")} Preview code mutations
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pretense/cli",
3
- "version": "0.6.8",
3
+ "version": "0.6.9",
4
4
  "description": "Local-first AI firewall that mutates proprietary code before sending to LLM APIs",
5
5
  "type": "module",
6
6
  "bin": {
@@ -23,9 +23,9 @@
23
23
  "tsx": "^4.0.0",
24
24
  "typescript": "^5.4.0",
25
25
  "vitest": "^1.0.0",
26
+ "@pretense/billing": "0.1.0",
26
27
  "@pretense/compliance-engine": "0.1.0",
27
28
  "@pretense/protocol": "0.1.0",
28
- "@pretense/billing": "0.1.0",
29
29
  "@pretense/learner": "0.2.0",
30
30
  "@pretense/mutator": "0.2.0",
31
31
  "@pretense/proxy": "0.1.0",
@@ -76,6 +76,6 @@
76
76
  "dev": "tsx src/index.ts",
77
77
  "postinstall": "node -e \"fs.existsSync('dist/postinstall.js') && import('./dist/postinstall.js')\" || true",
78
78
  "test": "vitest run",
79
- "lint": "tsc --noEmit"
79
+ "lint": "tsc --noEmit -p tsconfig.lint.json"
80
80
  }
81
81
  }