greprag 5.38.0 → 5.40.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/commands/discover.d.ts +1 -1
- package/dist/commands/discover.js +6 -5
- package/dist/commands/discover.js.map +1 -1
- package/dist/commands/fix.d.ts +28 -0
- package/dist/commands/fix.js +455 -37
- package/dist/commands/fix.js.map +1 -1
- package/dist/commands/init.js +18 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/mechanic.d.ts +64 -0
- package/dist/commands/mechanic.js +374 -0
- package/dist/commands/mechanic.js.map +1 -0
- package/dist/fix-trigger.d.ts +43 -0
- package/dist/fix-trigger.js +270 -0
- package/dist/fix-trigger.js.map +1 -0
- package/dist/guard.d.ts +92 -0
- package/dist/guard.js +404 -0
- package/dist/guard.js.map +1 -0
- package/dist/hook.js +117 -70
- package/dist/hook.js.map +1 -1
- package/dist/index.js +66 -46
- package/dist/index.js.map +1 -1
- package/dist/secret-scrubber.d.ts +48 -0
- package/dist/secret-scrubber.js +368 -0
- package/dist/secret-scrubber.js.map +1 -0
- package/package.json +1 -1
- package/skill/greprag/SKILL.md +6 -3
- package/skill/greprag/docs/corpus.md +15 -0
- package/skill/greprag/docs/discover.md +1 -1
- package/skill/greprag/docs/email.md +46 -0
- package/skill/greprag/docs/fix.md +86 -0
- package/skill/greprag/docs/inbox-watch.md +5 -4
- package/skill/greprag/docs/inbox.md +10 -8
- package/skill/greprag/docs/setup.md +1 -1
- package/skill/mechanic/SKILL.md +22 -2
- package/skill/templates/chip-spawn.md +30 -8
- package/skill/templates/workshop-chip.md +43 -0
- package/skill/greprag/docs/lore.md +0 -70
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// adr: adr/secret-scrubber.md
|
|
3
|
+
/** Secret scrubber — pure, FILESYSTEM-FREE secret detection.
|
|
4
|
+
*
|
|
5
|
+
* MIRROR of packages/core/src/secret-scrubber.ts (the canonical source). The
|
|
6
|
+
* CLI ships as a zero-dependency `tsc` artifact — the Stop hook cannot import
|
|
7
|
+
* @greprag/core — so the detection logic is duplicated here. KEEP IN SYNC with
|
|
8
|
+
* core; everything below the header is byte-identical, and the sync guard in
|
|
9
|
+
* tests/test-secret-scrubber.cjs fails the build on drift.
|
|
10
|
+
*
|
|
11
|
+
* Only the pure detection (SECRET_PATTERNS / scrubString / scrubObject /
|
|
12
|
+
* redactHighEntropy) is mirrored. The .env / process.env walker
|
|
13
|
+
* (`buildRedactionMap`) is client-only and stays in hook.ts, not here.
|
|
14
|
+
*
|
|
15
|
+
* Patterns adapted from gitleaks (https://github.com/gitleaks/gitleaks, MIT
|
|
16
|
+
* License) and published provider key formats. Detection is best-effort
|
|
17
|
+
* defense-in-depth — never a guarantee. Output: every redaction replaces the
|
|
18
|
+
* secret with `[REDACTED:TYPE]`. Replacement order inside scrubString:
|
|
19
|
+
* (1) caller value-map (longest-first); (2) provider patterns; (3) entropy.
|
|
20
|
+
*/
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.SECRET_PATTERNS = void 0;
|
|
23
|
+
exports.shannonEntropy = shannonEntropy;
|
|
24
|
+
exports.isHighEntropyToken = isHighEntropyToken;
|
|
25
|
+
exports.redactHighEntropy = redactHighEntropy;
|
|
26
|
+
exports.scrubString = scrubString;
|
|
27
|
+
exports.scrubObject = scrubObject;
|
|
28
|
+
/** Provider secret patterns.
|
|
29
|
+
*
|
|
30
|
+
* The FIRST 18 entries are the original hook ruleset, preserved VERBATIM and
|
|
31
|
+
* in their original order so existing behavior is byte-identical — DO NOT
|
|
32
|
+
* reorder, edit, or relabel them. Enriched families are appended after; because
|
|
33
|
+
* String.replace runs sequentially, an earlier (original) pattern always wins
|
|
34
|
+
* for inputs it already matched, so the original-18 outputs never change. */
|
|
35
|
+
exports.SECRET_PATTERNS = [
|
|
36
|
+
// ─── ORIGINAL 18 — behavior-preserving, verbatim from the hook ────────────
|
|
37
|
+
// Anthropic — sk-ant-* (must match before generic sk-*)
|
|
38
|
+
[/\bsk-ant-[A-Za-z0-9_-]{30,}/g, '[REDACTED:ANTHROPIC_KEY]'],
|
|
39
|
+
// OpenAI — sk-* (project keys are sk-proj-*, also covered)
|
|
40
|
+
[/\bsk-[A-Za-z0-9_-]{30,}/g, '[REDACTED:OPENAI_KEY]'],
|
|
41
|
+
// Stripe
|
|
42
|
+
[/\bsk_live_[A-Za-z0-9]{20,}/g, '[REDACTED:STRIPE_LIVE]'],
|
|
43
|
+
[/\bsk_test_[A-Za-z0-9]{20,}/g, '[REDACTED:STRIPE_TEST]'],
|
|
44
|
+
[/\brk_live_[A-Za-z0-9]{20,}/g, '[REDACTED:STRIPE_RESTRICTED]'],
|
|
45
|
+
[/\bwhsec_[A-Za-z0-9]{20,}/g, '[REDACTED:STRIPE_WEBHOOK]'],
|
|
46
|
+
// GitHub
|
|
47
|
+
[/\bghp_[A-Za-z0-9]{30,}/g, '[REDACTED:GITHUB_PAT]'],
|
|
48
|
+
[/\bgho_[A-Za-z0-9]{30,}/g, '[REDACTED:GITHUB_OAUTH]'],
|
|
49
|
+
[/\bghu_[A-Za-z0-9]{30,}/g, '[REDACTED:GITHUB_USER]'],
|
|
50
|
+
[/\bghs_[A-Za-z0-9]{30,}/g, '[REDACTED:GITHUB_SERVER]'],
|
|
51
|
+
[/\bghr_[A-Za-z0-9]{30,}/g, '[REDACTED:GITHUB_REFRESH]'],
|
|
52
|
+
// AWS
|
|
53
|
+
[/\bAKIA[A-Z0-9]{16}\b/g, '[REDACTED:AWS_ACCESS_KEY]'],
|
|
54
|
+
[/\bASIA[A-Z0-9]{16}\b/g, '[REDACTED:AWS_SESSION_KEY]'],
|
|
55
|
+
// Google — API keys vary 35–50 chars after AIza. No trailing \b: hyphens
|
|
56
|
+
// and dashes inside the key break the word boundary.
|
|
57
|
+
[/\bAIza[A-Za-z0-9_-]{30,50}/g, '[REDACTED:GOOGLE_API_KEY]'],
|
|
58
|
+
[/\bya29\.[A-Za-z0-9_-]{20,}/g, '[REDACTED:GOOGLE_OAUTH]'],
|
|
59
|
+
// Slack
|
|
60
|
+
[/\bxox[abprs]-[A-Za-z0-9-]{10,}/g, '[REDACTED:SLACK_TOKEN]'],
|
|
61
|
+
// JWTs (eyJ…eyJ…sig) — common for session tokens, Auth0, Firebase
|
|
62
|
+
[/\beyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g, '[REDACTED:JWT]'],
|
|
63
|
+
// Bearer tokens in Authorization headers (catches ad-hoc curl examples)
|
|
64
|
+
[/Authorization:\s*Bearer\s+[A-Za-z0-9_.-]{20,}/gi, 'Authorization: Bearer [REDACTED]'],
|
|
65
|
+
// ─── ENRICHED FAMILIES (gitleaks-derived; appended, never reordering above) ─
|
|
66
|
+
// ── Anthropic / OpenAI siblings ──
|
|
67
|
+
[/\bsk-ant-admin01-[A-Za-z0-9_-]{30,}/g, '[REDACTED:ANTHROPIC_ADMIN_KEY]'],
|
|
68
|
+
// ── GitHub fine-grained + Actions/refresh ──
|
|
69
|
+
[/\bgithub_pat_[A-Za-z0-9_]{22,}/g, '[REDACTED:GITHUB_FINE_GRAINED_PAT]'],
|
|
70
|
+
// ── GitLab ──
|
|
71
|
+
[/\bglpat-[A-Za-z0-9_-]{20,}/g, '[REDACTED:GITLAB_PAT]'],
|
|
72
|
+
[/\bgldt-[A-Za-z0-9_-]{20,}/g, '[REDACTED:GITLAB_DEPLOY_TOKEN]'],
|
|
73
|
+
[/\bglrt-[A-Za-z0-9_-]{20,}/g, '[REDACTED:GITLAB_RUNNER_TOKEN]'],
|
|
74
|
+
[/\bglptt-[A-Za-z0-9_-]{20,}/g, '[REDACTED:GITLAB_PIPELINE_TRIGGER]'],
|
|
75
|
+
[/\bgloas-[A-Za-z0-9_-]{20,}/g, '[REDACTED:GITLAB_OAUTH]'],
|
|
76
|
+
[/\bglcbt-[A-Za-z0-9_-]{20,}/g, '[REDACTED:GITLAB_CICD_JOB_TOKEN]'],
|
|
77
|
+
// ── Atlassian / Bitbucket ──
|
|
78
|
+
[/\bATATT3[A-Za-z0-9_=-]{20,}/g, '[REDACTED:ATLASSIAN_API_TOKEN]'],
|
|
79
|
+
[/\bATCTT3[A-Za-z0-9_=-]{20,}/g, '[REDACTED:ATLASSIAN_CONNECT_TOKEN]'],
|
|
80
|
+
[/\bATBB[A-Za-z0-9]{30,}/g, '[REDACTED:BITBUCKET_TOKEN]'],
|
|
81
|
+
// ── Stripe extras ──
|
|
82
|
+
[/\brk_test_[A-Za-z0-9]{20,}/g, '[REDACTED:STRIPE_RESTRICTED_TEST]'],
|
|
83
|
+
// ── Square ──
|
|
84
|
+
[/\bsq0atp-[A-Za-z0-9_-]{22}/g, '[REDACTED:SQUARE_ACCESS_TOKEN]'],
|
|
85
|
+
[/\bsq0csp-[A-Za-z0-9_-]{43}/g, '[REDACTED:SQUARE_OAUTH_SECRET]'],
|
|
86
|
+
[/\bEAAA[A-Za-z0-9_-]{60}/g, '[REDACTED:SQUARE_PRODUCTION_TOKEN]'],
|
|
87
|
+
// ── PayPal / Braintree ──
|
|
88
|
+
[/\baccess_token\$production\$[0-9a-z]{16}\$[0-9a-f]{32}/g, '[REDACTED:PAYPAL_BRAINTREE_TOKEN]'],
|
|
89
|
+
// ── Slack webhook + app/config tokens ──
|
|
90
|
+
[/https:\/\/hooks\.slack\.com\/services\/T[A-Za-z0-9_]+\/B[A-Za-z0-9_]+\/[A-Za-z0-9_]+/g, '[REDACTED:SLACK_WEBHOOK]'],
|
|
91
|
+
[/\bxapp-[0-9]-[A-Za-z0-9-]{10,}/g, '[REDACTED:SLACK_APP_TOKEN]'],
|
|
92
|
+
[/\bxoxe\.xox[bp]-[0-9]-[A-Za-z0-9-]{10,}/g, '[REDACTED:SLACK_CONFIG_TOKEN]'],
|
|
93
|
+
// ── Twilio ──
|
|
94
|
+
[/\bSK[0-9a-fA-F]{32}\b/g, '[REDACTED:TWILIO_API_KEY]'],
|
|
95
|
+
[/\bAC[0-9a-fA-F]{32}\b/g, '[REDACTED:TWILIO_ACCOUNT_SID]'],
|
|
96
|
+
// ── SendGrid / Mailgun / Mailchimp / Sendinblue ──
|
|
97
|
+
[/\bSG\.[A-Za-z0-9_-]{22}\.[A-Za-z0-9_-]{43}/g, '[REDACTED:SENDGRID_KEY]'],
|
|
98
|
+
[/\bkey-[0-9a-zA-Z]{32}\b/g, '[REDACTED:MAILGUN_KEY]'],
|
|
99
|
+
[/\b[0-9a-f]{32}-us[0-9]{1,2}\b/g, '[REDACTED:MAILCHIMP_KEY]'],
|
|
100
|
+
[/\bxkeysib-[a-f0-9]{64}-[A-Za-z0-9]{16}/g, '[REDACTED:SENDINBLUE_KEY]'],
|
|
101
|
+
// ── Discord ──
|
|
102
|
+
[/\b[MNO][A-Za-z0-9_-]{23,25}\.[A-Za-z0-9_-]{6}\.[A-Za-z0-9_-]{27,}/g, '[REDACTED:DISCORD_BOT_TOKEN]'],
|
|
103
|
+
[/https:\/\/(?:canary\.|ptb\.)?discord(?:app)?\.com\/api\/webhooks\/[0-9]+\/[A-Za-z0-9_-]+/g, '[REDACTED:DISCORD_WEBHOOK]'],
|
|
104
|
+
// ── AI / ML providers ──
|
|
105
|
+
[/\bhf_[A-Za-z0-9]{34,}/g, '[REDACTED:HUGGINGFACE_KEY]'],
|
|
106
|
+
[/\br8_[A-Za-z0-9]{37,}/g, '[REDACTED:REPLICATE_KEY]'],
|
|
107
|
+
// ── Package registries ──
|
|
108
|
+
[/\bnpm_[A-Za-z0-9]{36}\b/g, '[REDACTED:NPM_TOKEN]'],
|
|
109
|
+
[/\bpypi-AgEIcHlwaS[A-Za-z0-9_-]{50,}/g, '[REDACTED:PYPI_TOKEN]'],
|
|
110
|
+
[/\brubygems_[a-f0-9]{48}\b/g, '[REDACTED:RUBYGEMS_KEY]'],
|
|
111
|
+
[/\bdckr_pat_[A-Za-z0-9_-]{27,}/g, '[REDACTED:DOCKERHUB_PAT]'],
|
|
112
|
+
[/\bCLOJARS_[a-z0-9]{60}/g, '[REDACTED:CLOJARS_TOKEN]'],
|
|
113
|
+
// ── Google OAuth client secret + Firebase Cloud Messaging ──
|
|
114
|
+
[/\bGOCSPX-[A-Za-z0-9_-]{28,}/g, '[REDACTED:GCP_OAUTH_SECRET]'],
|
|
115
|
+
[/\bAAAA[A-Za-z0-9_-]{7}:APA91b[A-Za-z0-9_-]{130,}/g, '[REDACTED:FCM_SERVER_KEY]'],
|
|
116
|
+
// ── New Relic ──
|
|
117
|
+
[/\bNRAK-[A-Z0-9]{27}\b/g, '[REDACTED:NEWRELIC_USER_KEY]'],
|
|
118
|
+
[/\bNRJS-[a-f0-9]{19}\b/g, '[REDACTED:NEWRELIC_BROWSER_KEY]'],
|
|
119
|
+
[/\bNRAA-[a-f0-9]{27}\b/g, '[REDACTED:NEWRELIC_ADMIN_KEY]'],
|
|
120
|
+
[/\bNRII-[A-Za-z0-9_-]{32}\b/g, '[REDACTED:NEWRELIC_INSIGHTS_KEY]'],
|
|
121
|
+
// ── Shopify ──
|
|
122
|
+
[/\bshpat_[a-fA-F0-9]{32}\b/g, '[REDACTED:SHOPIFY_ACCESS_TOKEN]'],
|
|
123
|
+
[/\bshpss_[a-fA-F0-9]{32}\b/g, '[REDACTED:SHOPIFY_SHARED_SECRET]'],
|
|
124
|
+
[/\bshpca_[a-fA-F0-9]{32}\b/g, '[REDACTED:SHOPIFY_CUSTOM_APP]'],
|
|
125
|
+
[/\bshppa_[a-fA-F0-9]{32}\b/g, '[REDACTED:SHOPIFY_PRIVATE_APP]'],
|
|
126
|
+
// ── Linear / Notion / Airtable / Contentful / Typeform ──
|
|
127
|
+
[/\blin_api_[A-Za-z0-9]{40,}/g, '[REDACTED:LINEAR_API_KEY]'],
|
|
128
|
+
[/\blin_oauth_[A-Za-z0-9]{40,}/g, '[REDACTED:LINEAR_OAUTH]'],
|
|
129
|
+
[/\bsecret_[A-Za-z0-9]{43}\b/g, '[REDACTED:NOTION_TOKEN]'],
|
|
130
|
+
[/\bntn_[A-Za-z0-9]{46,}/g, '[REDACTED:NOTION_TOKEN]'],
|
|
131
|
+
[/\bpat[A-Za-z0-9]{14}\.[a-f0-9]{64}\b/g, '[REDACTED:AIRTABLE_PAT]'],
|
|
132
|
+
[/\bCFPAT-[A-Za-z0-9_-]{43}/g, '[REDACTED:CONTENTFUL_PAT]'],
|
|
133
|
+
[/\btfp_[A-Za-z0-9._=-]{59}/g, '[REDACTED:TYPEFORM_TOKEN]'],
|
|
134
|
+
// ── DigitalOcean ──
|
|
135
|
+
[/\bdop_v1_[a-f0-9]{64}\b/g, '[REDACTED:DIGITALOCEAN_PAT]'],
|
|
136
|
+
[/\bdoo_v1_[a-f0-9]{64}\b/g, '[REDACTED:DIGITALOCEAN_OAUTH]'],
|
|
137
|
+
[/\bdor_v1_[a-f0-9]{64}\b/g, '[REDACTED:DIGITALOCEAN_REFRESH]'],
|
|
138
|
+
// ── Grafana / Sentry ──
|
|
139
|
+
[/\bglc_[A-Za-z0-9+/]{32,}={0,2}/g, '[REDACTED:GRAFANA_CLOUD_TOKEN]'],
|
|
140
|
+
[/\bglsa_[A-Za-z0-9]{32}_[a-fA-F0-9]{8}/g, '[REDACTED:GRAFANA_SERVICE_ACCOUNT]'],
|
|
141
|
+
[/\bsntrys_[A-Za-z0-9_=+/]{32,}/g, '[REDACTED:SENTRY_ORG_TOKEN]'],
|
|
142
|
+
[/\bsntryu_[A-Za-z0-9_=+/]{32,}/g, '[REDACTED:SENTRY_USER_TOKEN]'],
|
|
143
|
+
// ── HubSpot / Postman / Pulumi / Databricks / Prefect / Readme ──
|
|
144
|
+
[/\bpat-(?:na|eu)1-[a-f0-9-]{36}/g, '[REDACTED:HUBSPOT_TOKEN]'],
|
|
145
|
+
[/\bPMAK-[a-f0-9]{24}-[a-f0-9]{34}/g, '[REDACTED:POSTMAN_KEY]'],
|
|
146
|
+
[/\bpul-[a-f0-9]{40}\b/g, '[REDACTED:PULUMI_TOKEN]'],
|
|
147
|
+
[/\bdapi[a-f0-9]{32}(?:-[0-9]+)?\b/g, '[REDACTED:DATABRICKS_TOKEN]'],
|
|
148
|
+
[/\bpnu_[A-Za-z0-9]{36}\b/g, '[REDACTED:PREFECT_KEY]'],
|
|
149
|
+
[/\brdme_[a-z0-9]{70}\b/g, '[REDACTED:README_KEY]'],
|
|
150
|
+
// ── Telegram / Dropbox / Mapbox / Supabase ──
|
|
151
|
+
[/\b[0-9]{8,10}:AA[A-Za-z0-9_-]{33}\b/g, '[REDACTED:TELEGRAM_BOT_TOKEN]'],
|
|
152
|
+
[/\bsl\.[A-Za-z0-9_-]{130,}/g, '[REDACTED:DROPBOX_SHORT_LIVED]'],
|
|
153
|
+
[/\bsk\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g, '[REDACTED:MAPBOX_SECRET_TOKEN]'],
|
|
154
|
+
[/\bsbp_[a-f0-9]{40}\b/g, '[REDACTED:SUPABASE_TOKEN]'],
|
|
155
|
+
// ── PlanetScale / Doppler / Fly.io / Tailscale ──
|
|
156
|
+
[/\bpscale_tkn_[A-Za-z0-9_=.-]{32,}/g, '[REDACTED:PLANETSCALE_TOKEN]'],
|
|
157
|
+
[/\bpscale_pw_[A-Za-z0-9_=.-]{32,}/g, '[REDACTED:PLANETSCALE_PASSWORD]'],
|
|
158
|
+
[/\bpscale_oauth_[A-Za-z0-9_=.-]{32,}/g, '[REDACTED:PLANETSCALE_OAUTH]'],
|
|
159
|
+
[/\bdp\.pt\.[A-Za-z0-9]{40,}/g, '[REDACTED:DOPPLER_TOKEN]'],
|
|
160
|
+
[/\bfo1_[A-Za-z0-9_-]{43}/g, '[REDACTED:FLYIO_TOKEN]'],
|
|
161
|
+
[/\btskey-auth-[A-Za-z0-9]+-[A-Za-z0-9]+/g, '[REDACTED:TAILSCALE_KEY]'],
|
|
162
|
+
// ── HashiCorp Vault / Terraform Cloud ──
|
|
163
|
+
[/\bhvs\.[A-Za-z0-9_-]{90,}/g, '[REDACTED:VAULT_TOKEN]'],
|
|
164
|
+
[/\bhvb\.[A-Za-z0-9_-]{90,}/g, '[REDACTED:VAULT_BATCH_TOKEN]'],
|
|
165
|
+
[/\b[A-Za-z0-9]{14}\.atlasv1\.[A-Za-z0-9_=-]{60,}/g, '[REDACTED:TERRAFORM_CLOUD_TOKEN]'],
|
|
166
|
+
// ── Dynatrace / EasyPost / Frame.io / Duffel / Flutterwave / Shippo / Plaid ──
|
|
167
|
+
[/\bdt0c01\.[A-Z0-9]{24}\.[A-Z0-9]{64}/g, '[REDACTED:DYNATRACE_TOKEN]'],
|
|
168
|
+
[/\bEZAK[A-Za-z0-9]{54}/g, '[REDACTED:EASYPOST_KEY]'],
|
|
169
|
+
[/\bfio-u-[A-Za-z0-9_=-]{64}/g, '[REDACTED:FRAMEIO_TOKEN]'],
|
|
170
|
+
[/\bduffel_(?:test|live)_[A-Za-z0-9_=-]{43}/g, '[REDACTED:DUFFEL_TOKEN]'],
|
|
171
|
+
[/\bFLWSECK_TEST-[a-h0-9]{32}-X/g, '[REDACTED:FLUTTERWAVE_KEY]'],
|
|
172
|
+
[/\bshippo_(?:live|test)_[a-fA-F0-9]{40}/g, '[REDACTED:SHIPPO_TOKEN]'],
|
|
173
|
+
[/\baccess-(?:sandbox|development|production)-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/g, '[REDACTED:PLAID_TOKEN]'],
|
|
174
|
+
// ── Misc cloud / infra prefixes ──
|
|
175
|
+
[/\bLTAI[A-Za-z0-9]{20}\b/g, '[REDACTED:ALIBABA_ACCESS_KEY]'],
|
|
176
|
+
[/\baio_[A-Za-z0-9]{28}\b/g, '[REDACTED:ADAFRUIT_IO_KEY]'],
|
|
177
|
+
[/\by0_[A-Za-z0-9_-]{20,}/g, '[REDACTED:YANDEX_OAUTH]'],
|
|
178
|
+
// ── AI / inference providers ──
|
|
179
|
+
[/\bgsk_[A-Za-z0-9]{40,}/g, '[REDACTED:GROQ_KEY]'],
|
|
180
|
+
[/\bpplx-[A-Za-z0-9]{32,}/g, '[REDACTED:PERPLEXITY_KEY]'],
|
|
181
|
+
[/\bxai-[A-Za-z0-9]{32,}/g, '[REDACTED:XAI_KEY]'],
|
|
182
|
+
[/\bfw_[A-Za-z0-9]{24,}/g, '[REDACTED:FIREWORKS_KEY]'],
|
|
183
|
+
[/\bpa-[A-Za-z0-9_-]{43}\b/g, '[REDACTED:VOYAGE_KEY]'],
|
|
184
|
+
[/\bpcsk_[A-Za-z0-9_]{30,}/g, '[REDACTED:PINECONE_KEY]'],
|
|
185
|
+
[/\blsv2_pt_[a-f0-9]{32}_[a-f0-9]{10}/g, '[REDACTED:LANGSMITH_PERSONAL]'],
|
|
186
|
+
[/\blsv2_sk_[a-f0-9]{32}_[a-f0-9]{10}/g, '[REDACTED:LANGSMITH_SERVICE]'],
|
|
187
|
+
[/\bsecret-(?:test|live)-[A-Za-z0-9_=-]{36}/g, '[REDACTED:STYTCH_SECRET]'],
|
|
188
|
+
[/\bamzn\.mws\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/g, '[REDACTED:AWS_MWS_TOKEN]'],
|
|
189
|
+
// ── More infra / dev-tool prefixes ──
|
|
190
|
+
[/\bnapi_[a-z0-9]{30,}/g, '[REDACTED:NEON_API_KEY]'],
|
|
191
|
+
[/\bre_[A-Za-z0-9_]{20,}/g, '[REDACTED:RESEND_KEY]'],
|
|
192
|
+
[/\b[0-9]\/[0-9]{16}:[a-f0-9]{32}\b/g, '[REDACTED:ASANA_TOKEN]'],
|
|
193
|
+
[/\bbkua_[a-z0-9]{40}\b/g, '[REDACTED:BUILDKITE_TOKEN]'],
|
|
194
|
+
[/\bCCIPAT_[A-Za-z0-9]{10,}/g, '[REDACTED:CIRCLECI_TOKEN]'],
|
|
195
|
+
[/\brnd_[A-Za-z0-9]{30,}/g, '[REDACTED:RENDER_KEY]'],
|
|
196
|
+
[/\bdnkey-[a-z0-9=_-]{26}-[a-z0-9=_-]{52}/g, '[REDACTED:DEFINED_NET_KEY]'],
|
|
197
|
+
[/\bAccountKey=[A-Za-z0-9+/]{86,88}={0,2}/g, '[REDACTED:AZURE_STORAGE_KEY]'],
|
|
198
|
+
[/\bwaka_[0-9a-f-]{36}\b/g, '[REDACTED:WAKATIME_KEY]'],
|
|
199
|
+
[/\bsgp_[a-fA-F0-9]{40,}/g, '[REDACTED:SOURCEGRAPH_TOKEN]'],
|
|
200
|
+
[/\bcmVmdGtuOj[A-Za-z0-9_/+=-]{64,}/g, '[REDACTED:JFROG_TOKEN]'],
|
|
201
|
+
[/https:\/\/[a-f0-9]{32}@[a-z0-9.-]+\/[0-9]+/g, '[REDACTED:SENTRY_DSN]'],
|
|
202
|
+
// ── Context-anchored generics (require a labelled key + quoted/assigned value,
|
|
203
|
+
// so they fire on `name = "value"` shapes only — low false-positive. These
|
|
204
|
+
// run LAST so specific provider prefixes above always win first. Capture
|
|
205
|
+
// groups preserve the surrounding structure; only the value is redacted) ──
|
|
206
|
+
// Provider-anchored: require the provider name + a key-name + the value, so
|
|
207
|
+
// they fire only on explicit `<provider>_api_key = "<value>"` shapes.
|
|
208
|
+
[/(datadog[_-]?api[_-]?key["']?\s*[:=]\s*["']?)([a-f0-9]{32})/gi, '$1[REDACTED:DATADOG_KEY]'],
|
|
209
|
+
[/(heroku[_-]?api[_-]?key["']?\s*[:=]\s*["']?)([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/gi, '$1[REDACTED:HEROKU_KEY]'],
|
|
210
|
+
[/(cloudflare[_-]?(?:api[_-]?)?(?:token|key)["']?\s*[:=]\s*["']?)([A-Za-z0-9_-]{37,40})/gi, '$1[REDACTED:CLOUDFLARE_TOKEN]'],
|
|
211
|
+
[/(algolia[_-]?(?:admin[_-]?)?(?:api[_-]?)?key["']?\s*[:=]\s*["']?)([a-zA-Z0-9]{32})/gi, '$1[REDACTED:ALGOLIA_KEY]'],
|
|
212
|
+
[/(facebook[_-]?(?:app[_-]?)?secret["']?\s*[:=]\s*["']?)([a-f0-9]{32})/gi, '$1[REDACTED:FACEBOOK_SECRET]'],
|
|
213
|
+
[/(linkedin[_-]?(?:client[_-]?)?secret["']?\s*[:=]\s*["']?)([A-Za-z0-9]{16,})/gi, '$1[REDACTED:LINKEDIN_SECRET]'],
|
|
214
|
+
[/(okta[_-]?(?:api[_-]?)?token["']?\s*[:=]\s*["']?)([A-Za-z0-9_-]{42})/gi, '$1[REDACTED:OKTA_TOKEN]'],
|
|
215
|
+
[/(intercom[_-]?(?:access[_-]?)?token["']?\s*[:=]\s*["']?)([A-Za-z0-9=_-]{40,})/gi, '$1[REDACTED:INTERCOM_TOKEN]'],
|
|
216
|
+
[/(fastly[_-]?(?:api[_-]?)?(?:token|key)["']?\s*[:=]\s*["']?)([A-Za-z0-9_-]{32})/gi, '$1[REDACTED:FASTLY_TOKEN]'],
|
|
217
|
+
[/(cohere[_-]?(?:api[_-]?)?key["']?\s*[:=]\s*["']?)([A-Za-z0-9]{40})/gi, '$1[REDACTED:COHERE_KEY]'],
|
|
218
|
+
[/(elevenlabs[_-]?(?:api[_-]?)?key["']?\s*[:=]\s*["']?)([a-f0-9]{32})/gi, '$1[REDACTED:ELEVENLABS_KEY]'],
|
|
219
|
+
[/(deepgram[_-]?(?:api[_-]?)?key["']?\s*[:=]\s*["']?)([a-f0-9]{40})/gi, '$1[REDACTED:DEEPGRAM_KEY]'],
|
|
220
|
+
[/(mistral[_-]?(?:api[_-]?)?key["']?\s*[:=]\s*["']?)([A-Za-z0-9]{32})/gi, '$1[REDACTED:MISTRAL_KEY]'],
|
|
221
|
+
[/(twitch[_-]?(?:client[_-]?)?secret["']?\s*[:=]\s*["']?)([a-z0-9]{30})/gi, '$1[REDACTED:TWITCH_SECRET]'],
|
|
222
|
+
[/(coinbase[_-]?(?:api[_-]?)?(?:secret|key)["']?\s*[:=]\s*["']?)([A-Za-z0-9]{32,})/gi, '$1[REDACTED:COINBASE_KEY]'],
|
|
223
|
+
[/(snyk[_-]?(?:api[_-]?)?(?:token|key)["']?\s*[:=]\s*["']?)([0-9a-f-]{36})/gi, '$1[REDACTED:SNYK_TOKEN]'],
|
|
224
|
+
[/(azure[_-]?(?:ad[_-]?)?client[_-]?secret["']?\s*[:=]\s*["']?)([A-Za-z0-9._~-]{30,})/gi, '$1[REDACTED:AZURE_AD_SECRET]'],
|
|
225
|
+
[/(mux[_-]?token[_-]?secret["']?\s*[:=]\s*["']?)([A-Za-z0-9+/=]{40,})/gi, '$1[REDACTED:MUX_SECRET]'],
|
|
226
|
+
// AWS secret access key (40-char base64 only meaningful in context)
|
|
227
|
+
[/((?:aws_?)?secret_?access_?key["']?\s*[:=]\s*["']?)([A-Za-z0-9/+]{40})/gi, '$1[REDACTED:AWS_SECRET_KEY]'],
|
|
228
|
+
// Credentials embedded in a URL (DB conn strings + HTTP basic auth):
|
|
229
|
+
// scheme://user:PASSWORD@host → keeps scheme/user/@host, redacts password.
|
|
230
|
+
// Covers postgres / mysql / mongodb+srv / redis / amqp / http(s) / etc.
|
|
231
|
+
[/\b([a-z][a-z0-9+.-]*:\/\/[^:/?#\s@]+:)([^@/?#\s]+)(@)/gi, '$1[REDACTED:URL_CREDENTIALS]$3'],
|
|
232
|
+
// Generic quoted secret/API-key/token assignments.
|
|
233
|
+
[/(\b(?:api[_-]?key|api[_-]?secret|secret[_-]?key|access[_-]?token|auth[_-]?token|client[_-]?secret|private[_-]?key)["']?\s*[:=]\s*["'])([A-Za-z0-9._+/=-]{16,})(["'])/gi, '$1[REDACTED:GENERIC_SECRET]$3'],
|
|
234
|
+
// Generic quoted password assignments (require quotes + length to limit FP).
|
|
235
|
+
[/(\b(?:password|passwd|pwd)["']?\s*[:=]\s*["'])([^"'\s]{8,})(["'])/gi, '$1[REDACTED:PASSWORD]$3'],
|
|
236
|
+
// ── Private-key PEM blocks (RSA / EC / DSA / OPENSSH / PGP / encrypted /
|
|
237
|
+
// plain, incl. GCP service-account JSON `private_key` with escaped \n) ──
|
|
238
|
+
[/-----BEGIN (?:RSA |EC |DSA |OPENSSH |PGP |ENCRYPTED )?PRIVATE KEY(?: BLOCK)?-----[\s\S]+?-----END (?:RSA |EC |DSA |OPENSSH |PGP |ENCRYPTED )?PRIVATE KEY(?: BLOCK)?-----/g, '[REDACTED:PRIVATE_KEY]'],
|
|
239
|
+
];
|
|
240
|
+
// ─── High-entropy catch-all ────────────────────────────────────────────────
|
|
241
|
+
// Last-resort net for PREFIXLESS keys the patterns miss. Deliberately
|
|
242
|
+
// CONSERVATIVE: it must never redact git SHAs, UUIDs, hex colors, file paths,
|
|
243
|
+
// hash-named files, numeric ids, build-ids / content-hashes in prose, or
|
|
244
|
+
// ordinary prose. It fires only on a key-sized window of an unbroken
|
|
245
|
+
// base64url-ish run that mixes letters AND digits, spans ≥3 character classes,
|
|
246
|
+
// is not pure hex, not a UUID, and clears an entropy floor — AND is neither a
|
|
247
|
+
// filename stem (`<run>.<ext>`) nor a path component (preceded by `/` or `\`).
|
|
248
|
+
const HEX_RE = /^[0-9a-fA-F]+$/;
|
|
249
|
+
const UUID_RE = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
250
|
+
// Candidate run: base64url + base64-pad alphabet, NO `/` or `.` (so file paths,
|
|
251
|
+
// URLs and dotted version strings break into short sub-tokens). 32–120 chars: a
|
|
252
|
+
// key-sized window — long enough to exclude most identifiers, short enough to
|
|
253
|
+
// skip multi-KB base64 data blobs (which are not credentials).
|
|
254
|
+
const ENTROPY_CANDIDATE_RE = /[A-Za-z0-9_+=-]{32,120}/g;
|
|
255
|
+
const ENTROPY_MIN_BITS = 4.0;
|
|
256
|
+
// Tail test: a candidate immediately followed by `.<short ext>` is a filename
|
|
257
|
+
// stem (a8Kd…Gh3.md, Xy7…Jk.json), not a secret. The candidate run stops at the
|
|
258
|
+
// `.`, so this matches against the text that FOLLOWS the matched run.
|
|
259
|
+
const FILE_EXT_TAIL_RE = /^\.[A-Za-z0-9]{1,8}\b/;
|
|
260
|
+
/** Shannon entropy of a string, in bits per character. */
|
|
261
|
+
function shannonEntropy(s) {
|
|
262
|
+
if (!s)
|
|
263
|
+
return 0;
|
|
264
|
+
const freq = {};
|
|
265
|
+
for (const ch of s)
|
|
266
|
+
freq[ch] = (freq[ch] || 0) + 1;
|
|
267
|
+
let bits = 0;
|
|
268
|
+
for (const ch in freq) {
|
|
269
|
+
const p = freq[ch] / s.length;
|
|
270
|
+
bits -= p * Math.log2(p);
|
|
271
|
+
}
|
|
272
|
+
return bits;
|
|
273
|
+
}
|
|
274
|
+
/** Count distinct character classes (lower / upper / digit / symbol) in a token.
|
|
275
|
+
* A real prefixless key mixes ≥3; a non-secret build-id or content hash in prose
|
|
276
|
+
* is usually 1–2 (e.g. all-lowercase + digits), so requiring ≥3 clears those
|
|
277
|
+
* false positives without dropping genuine mixed-case-plus-digit keys. */
|
|
278
|
+
function charClassCount(token) {
|
|
279
|
+
let n = 0;
|
|
280
|
+
if (/[a-z]/.test(token))
|
|
281
|
+
n++;
|
|
282
|
+
if (/[A-Z]/.test(token))
|
|
283
|
+
n++;
|
|
284
|
+
if (/[0-9]/.test(token))
|
|
285
|
+
n++;
|
|
286
|
+
if (/[_+=-]/.test(token))
|
|
287
|
+
n++;
|
|
288
|
+
return n;
|
|
289
|
+
}
|
|
290
|
+
/** True when a single token looks like a high-entropy secret (see thresholds). */
|
|
291
|
+
function isHighEntropyToken(token) {
|
|
292
|
+
if (token.length < 32 || token.length > 120)
|
|
293
|
+
return false;
|
|
294
|
+
if (HEX_RE.test(token))
|
|
295
|
+
return false; // git SHA / MD5 / hex color / UUID-sans-hyphen
|
|
296
|
+
if (UUID_RE.test(token))
|
|
297
|
+
return false; // canonical UUID
|
|
298
|
+
if (!/[A-Za-z]/.test(token))
|
|
299
|
+
return false; // pure-numeric ids
|
|
300
|
+
if (!/[0-9]/.test(token))
|
|
301
|
+
return false; // prose / camelCase words (no digit)
|
|
302
|
+
if (charClassCount(token) < 3)
|
|
303
|
+
return false; // single-/double-class ids (build-ids, content hashes in prose)
|
|
304
|
+
return shannonEntropy(token) >= ENTROPY_MIN_BITS;
|
|
305
|
+
}
|
|
306
|
+
/** Redact prefixless high-entropy tokens. The entropy catch-all (pass 3).
|
|
307
|
+
* A high-entropy run is NOT redacted when it is a filename stem (immediately
|
|
308
|
+
* followed by `.<short ext>`) or a path component (immediately preceded by `/`
|
|
309
|
+
* or `\`) — a hash-named file or a path segment is an identifier, not a secret.
|
|
310
|
+
* Those guards need the surrounding text, so they live here, not in the
|
|
311
|
+
* token-only `isHighEntropyToken`. */
|
|
312
|
+
function redactHighEntropy(text) {
|
|
313
|
+
if (!text)
|
|
314
|
+
return text;
|
|
315
|
+
return text.replace(ENTROPY_CANDIDATE_RE, (m, offset, str) => {
|
|
316
|
+
if (!isHighEntropyToken(m))
|
|
317
|
+
return m;
|
|
318
|
+
const before = offset > 0 ? str[offset - 1] : '';
|
|
319
|
+
if (before === '/' || before === '\\')
|
|
320
|
+
return m; // path component
|
|
321
|
+
if (FILE_EXT_TAIL_RE.test(str.slice(offset + m.length)))
|
|
322
|
+
return m; // filename stem
|
|
323
|
+
return '[REDACTED:HIGH_ENTROPY]';
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
// ─── Pure scrubbing API ────────────────────────────────────────────────────
|
|
327
|
+
/** Scrub a string. Three passes, in order:
|
|
328
|
+
* 1. caller-supplied value map (e.g. .env values) — exact replace, the map is
|
|
329
|
+
* expected longest-first so longer secrets win before their substrings;
|
|
330
|
+
* 2. provider SECRET_PATTERNS;
|
|
331
|
+
* 3. high-entropy catch-all.
|
|
332
|
+
* Filesystem-free — the value map is the caller's job (the CLI hook builds it
|
|
333
|
+
* from .env; the server passes none). */
|
|
334
|
+
function scrubString(text, valueMap) {
|
|
335
|
+
if (!text)
|
|
336
|
+
return text;
|
|
337
|
+
let out = text;
|
|
338
|
+
// Pass 1: caller value-map replacement (deterministic, names the var).
|
|
339
|
+
if (valueMap) {
|
|
340
|
+
for (const [value, replacement] of valueMap) {
|
|
341
|
+
if (value && out.includes(value))
|
|
342
|
+
out = out.split(value).join(replacement);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
// Pass 2: pattern-based detection for tokens not in any value map.
|
|
346
|
+
for (const [pattern, replacement] of exports.SECRET_PATTERNS) {
|
|
347
|
+
out = out.replace(pattern, replacement);
|
|
348
|
+
}
|
|
349
|
+
// Pass 3: high-entropy catch-all for prefixless keys.
|
|
350
|
+
out = redactHighEntropy(out);
|
|
351
|
+
return out;
|
|
352
|
+
}
|
|
353
|
+
/** Recursively scrub all string leaves of an object / array, preserving shape. */
|
|
354
|
+
function scrubObject(obj, valueMap) {
|
|
355
|
+
if (typeof obj === 'string')
|
|
356
|
+
return scrubString(obj, valueMap);
|
|
357
|
+
if (Array.isArray(obj))
|
|
358
|
+
return obj.map((o) => scrubObject(o, valueMap));
|
|
359
|
+
if (obj && typeof obj === 'object') {
|
|
360
|
+
const out = {};
|
|
361
|
+
for (const k of Object.keys(obj)) {
|
|
362
|
+
out[k] = scrubObject(obj[k], valueMap);
|
|
363
|
+
}
|
|
364
|
+
return out;
|
|
365
|
+
}
|
|
366
|
+
return obj;
|
|
367
|
+
}
|
|
368
|
+
//# sourceMappingURL=secret-scrubber.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secret-scrubber.js","sourceRoot":"","sources":["../src/secret-scrubber.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/skill/greprag/SKILL.md
CHANGED
|
@@ -125,18 +125,21 @@ Aliases (silent back-compat): `greprag memory briefing` → `recap` (renamed v5.
|
|
|
125
125
|
|
|
126
126
|
**Codex live inbox push requires the startup watcher.** Hooks only fire at Codex turn/tool/session boundaries. Public installs should use `greprag init --codex --tenant-id <handle> --install-watcher`; run `greprag codex doctor --wake-test` to inspect state, and use `greprag codex watch --session <id>` only for foreground testing. The sidecar listens to GrepRAG inbox SSE and wakes Codex via `codex exec resume`. If the wake-action probe fails, describe live push as notification-only and rely on turn-bound inbox steering for reliable delivery.
|
|
127
127
|
|
|
128
|
-
**ABOUT TO
|
|
128
|
+
**ABOUT TO `greprag send` TO A `@gmail.com` / `@anthropic.com` / REAL EMAIL ADDRESS? STOP — for `inbox`/`send` (internal cross-session messaging), `users.email` IS NEVER A ROUTING ADDRESS.** Use the numeric handle (`1834729@greprag.com`) or claimed vanity alias (`travis@greprag.com`). If you don't know the recipient's handle, ASK — don't guess from their email. adr: adr/numeric-handles.md. Full grammar: `docs/inbox.md § address`.
|
|
129
|
+
|
|
130
|
+
**BUT IF THE USER SAYS "EMAIL X" / "SEND A REAL EMAIL" — USE `greprag email send`, NOT `greprag send`.** `greprag email send --to <real-addr> --from <handle>@greprag.com --subject "s" (--body "t" | --body-file <f|->)` is the agent's REAL outbound SMTP mailbox — it delivers to actual inboxes (gmail, etc.), with `--attach`/`--cc`/`--bcc`/`--html-file`. `greprag send` is internal-only and never leaves greprag. The two read identically — this collision is a known trap. `email pending`/`email pull` drains inbound. Full reference: `docs/email.md`.
|
|
129
131
|
|
|
130
132
|
## Reference index
|
|
131
133
|
|
|
132
134
|
- `docs/setup.md` — codex · claude-code · opencode · auth · hooks · conventions · permissions · channels · anchor · bulk-register
|
|
133
135
|
- `docs/platforms.md` — exact platform paths for Claude Code · Codex · OpenCode
|
|
134
136
|
- `docs/per-project-flags.md` — flip `memory_capture` / `session_start_recap` / `inbox_notify`
|
|
135
|
-
- `docs/inbox.md` — `greprag send`, `greprag inbox`, address grammar, retract
|
|
137
|
+
- `docs/inbox.md` — `greprag send`, `greprag inbox`, address grammar, retract (internal messaging)
|
|
138
|
+
- `docs/email.md` — `greprag email send`/`pending`/`pull` — REAL outbound + inbound SMTP (distinct from `send`)
|
|
136
139
|
- `docs/inbox-watch.md` — SSE watcher patterns, liveness model, parent-side / post-send patterns
|
|
137
140
|
- `docs/doctor.md` — `greprag doctor` (identity drift, orphan consolidation)
|
|
138
141
|
- `docs/corpus.md` — upload + search arbitrary text (books, codebases, voice samples)
|
|
139
142
|
- `docs/discord-handoff.md` — Commander DM bridge (`greprag discord pair/handoff`)
|
|
140
143
|
- `docs/memory-advanced.md` — raw curl, `/v1/memory/by-period` `--type` values, naming note
|
|
141
|
-
- `docs/
|
|
144
|
+
- `docs/fix.md` — log + query project-specific fixes (the Mechanic's queue)
|
|
142
145
|
- `docs/discover.md` — cross-project advisor lookup (`greprag discover`)
|
|
@@ -18,6 +18,21 @@ greprag corpus upload <file-or-url> [--name "Display Name"] [--kind book]
|
|
|
18
18
|
- File or URL must be plain text or markdown. PDFs: ask user to convert first.
|
|
19
19
|
- Cost: one round-trip to ingest + N async Gemini Flash-Lite calls (one per substantive node, drained every 2 min). Read-time cost: $0.
|
|
20
20
|
|
|
21
|
+
## Recipe: ingest SPA-rendered API docs (no public OpenAPI download)
|
|
22
|
+
|
|
23
|
+
Modern API doc sites (ReadMe, Stoplight, Redoc, Mintlify, GitBook, custom SPAs) render client-side: `curl`/WebFetch return an empty shell, there's no linked `openapi.json`, and the endpoint catalog is usually NOT in the JS bundle. **Don't scrape the DOM.** Almost all of them are generated from OpenAPI and fetch it from an internal — often public — data API. Find that, ingest the spec.
|
|
24
|
+
|
|
25
|
+
1. **Find the data API.** Cold-load a reference page in the browser with network capture already armed (capture clears on cross-domain nav, so arm while on-domain then hard-reload). Inspect `performance.getEntriesByType('resource')` for an internal `/api/` JSON feed — that's the catalog/spec source. The spec is fetched once at hard load, not on SPA route changes.
|
|
26
|
+
2. **Pull the catalog**, then **the per-resource spec.** Both are often public (test with `curl`, no cookies). Watch for explicit version codes — generic `?version=latest` may 400.
|
|
27
|
+
- Procore (worked end-to-end): `GET developers.procore.com/api/v1/resource_groups` → 588 resources (each w/ `resource_name_id` + `versions`); then `GET .../api/v1/resource_groups/resource/{rid}?version=rest_v1.1` → real OpenAPI 3.0 per resource.
|
|
28
|
+
3. **Render each spec → compact markdown**: per endpoint, emit `METHOD /path`, summary, params, and request/response field schemas with **required** flags + enums + descriptions (resolve `$ref` against the spec root). Far more searchable + smaller than raw OpenAPI JSON.
|
|
29
|
+
4. **Ingest as one consolidated file.** `--index` wants an `llms.txt`/`sitemap.xml` URL the vendor doesn't publish, so concatenate all rendered specs into one file (the `llms-full.txt` model) and `greprag corpus upload <file>.md --raw --name <vendor>-docs`. Use `--raw` (reference docs — search terms already match the text).
|
|
30
|
+
5. **Refresh** = rerun the build script + re-upload (static file doesn't auto-refresh).
|
|
31
|
+
|
|
32
|
+
Keep the build script in the consuming repo (`scripts/build-<vendor>-corpus.mjs`) so refresh/rescope is one command. Scope to the resources you actually integrate with — full vendor catalogs are often huge and mostly irrelevant.
|
|
33
|
+
|
|
34
|
+
> Future greprag enhancement: teach `corpus --index` to accept a JSON catalog + a member-URL selector (JSONPath), so OpenAPI-backed doc APIs auto-refresh as proper index stores instead of static files.
|
|
35
|
+
|
|
21
36
|
## Search — chain protocol
|
|
22
37
|
|
|
23
38
|
Searches are agent-driven. You decompose the user's intent into 1–3 lexical queries.
|
|
@@ -21,7 +21,7 @@ Response shape:
|
|
|
21
21
|
}
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
`anchor: "registered"` means the project was registered via `/v1/inbox/projects/register` (has a row in the `projects` table — inbox
|
|
24
|
+
`anchor: "registered"` means the project was registered via `/v1/inbox/projects/register` (has a row in the `projects` table — so `--project <name>` filters on `greprag inbox` / `inbox watch` resolve it). A project is NOT a send target, though — project broadcasts were removed (2026-06-07); send to a session or the bare front desk.
|
|
25
25
|
|
|
26
26
|
`anchor: "fallback"` means the project is only known via memory-store metadata — still has memory, but no registry entry. Its name may be the path-derived basename and inbox-by-name resolution will not find it.
|
|
27
27
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# greprag email — the agent's real SMTP mailbox
|
|
2
|
+
|
|
3
|
+
`greprag email` is **real outbound + inbound email** from your greprag address (e.g. `travis@greprag.com`). This is distinct from `greprag send` / `greprag inbox`, which is internal cross-session messaging that never leaves greprag.
|
|
4
|
+
|
|
5
|
+
**The trap:** "email me" and "send X" sound identical. Only `greprag email send` produces an actual email in a real inbox. `greprag send --to travis@greprag.com` lands on a greprag front desk, NOT in gmail.
|
|
6
|
+
|
|
7
|
+
## Send a real email
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
greprag email send --to <real-addr> \
|
|
11
|
+
--from <handle>@greprag.com \
|
|
12
|
+
--subject "<subject>" \
|
|
13
|
+
(--body "<text>" | --body-file <file|->)
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Flags:
|
|
17
|
+
- `--from <handle>@greprag.com` — send AS one of YOUR OWN greprag handles. The server rejects any address that isn't yours — you cannot forge a From. Defaults to your tenant address if omitted.
|
|
18
|
+
- `--body-file <f>` — read body from a file; `--body-file -` reads from stdin (best for long/multiline bodies — avoids shell-quoting pain).
|
|
19
|
+
- `--html-file <f>` — HTML alternative body.
|
|
20
|
+
- `--attach <path>` — attach a local file (repeatable). Gmail's combined limit is 25 MB; larger → Drive link.
|
|
21
|
+
- `--cc <addr>` / `--bcc <addr>` — repeatable / comma-sep.
|
|
22
|
+
- `--reply-to <addr>` — Reply-To header.
|
|
23
|
+
|
|
24
|
+
Returns a `message-id` and writes an egress audit `.eml` on success.
|
|
25
|
+
|
|
26
|
+
### Long body via heredoc (the reliable pattern)
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
cat > /tmp/body.txt <<'EOF'
|
|
30
|
+
Multi-line body here.
|
|
31
|
+
No shell-quoting headaches.
|
|
32
|
+
EOF
|
|
33
|
+
greprag email send --to someone@gmail.com --from travis@greprag.com \
|
|
34
|
+
--subject "Subject" --body-file /tmp/body.txt
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Inbound
|
|
38
|
+
|
|
39
|
+
- `greprag email` or `greprag email pending` — list pending front-desk email (envelope only).
|
|
40
|
+
- `greprag email pull --id <record>` — pull ONE record's attachments to disk.
|
|
41
|
+
- `greprag email pull --all-pending [--to <dir>]` — pull every pending record's attachments. Save dir resolves: `--to` > `$GREPRAG_EMAIL_DIR` > per-project anchor `email_dir` > `~/.greprag/email/<project>`.
|
|
42
|
+
- Auto-save: set `email_autosave=true` in `.greprag/project.json` (optionally `email_dir`) — the per-turn mail hook auto-pulls new attachments each turn.
|
|
43
|
+
|
|
44
|
+
## Sending rule
|
|
45
|
+
|
|
46
|
+
Sending email is an outward-facing action — confirm with the user before sending unless they've explicitly asked for it in this turn. (Here they asked → send.)
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Fix (per-project friction queue — `greprag fix`)
|
|
2
|
+
|
|
3
|
+
A fix = a rough spot worth repairing. Project-specific emergent knowledge — gotchas, discovered constraints, drift-prone observations, plus raw smells (open fixes) awaiting digestion. Distinct from static project knowledge (which belongs in CLAUDE.md / docs / code structure). Reviewed via `/mechanic`.
|
|
4
|
+
|
|
5
|
+
Whenever you waste tokens *discovering* something a future agent shouldn't have to re-discover, log it. Discovery is fine the first time — the failure mode is repeating it every time a chip spawns or a new session opens.
|
|
6
|
+
|
|
7
|
+
## When to log
|
|
8
|
+
|
|
9
|
+
Three signatures of discovery waste:
|
|
10
|
+
|
|
11
|
+
- **Search cascade.** ≥3 `Glob` / `Grep` calls to find a path that should be obvious (where do migrations live? where's the inbox table? which file owns the CLI dispatch?). Log with `--scope chip-startup --repaired`.
|
|
12
|
+
- **Schema archaeology.** ≥3 file `Read`s to reconstruct the shape of a data type, a JSONB column, or an API request body. Log the shape (1–3 lines) with `--scope <subsystem>-touch --repaired`.
|
|
13
|
+
- **Trial-and-error env probing.** Multiple `Bash` attempts at the same env operation. Log the working invocation with `--scope env --repaired`.
|
|
14
|
+
|
|
15
|
+
## How to log
|
|
16
|
+
|
|
17
|
+
Open a raw fix (the Mechanic digests it later):
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
greprag fix log "<text>" [--project <name>]
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Record a known durable rule directly (`--repaired` skips the digestion step — use when you already know the canonical fix):
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
greprag fix log "<one-sentence learning, optionally with a file path>" --repaired --scope <scope> [--project <name>]
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Examples:
|
|
30
|
+
```bash
|
|
31
|
+
greprag fix log "Migrations live at repo root: migrations/<NNN>_<name>.sql. Apply with node scripts/apply-migration.cjs migrations/<file>.sql." --repaired --scope chip-startup
|
|
32
|
+
|
|
33
|
+
greprag fix log "Inbox storage uses JSONB metadata on nodes (store kind='inbox'). No inbox_messages table — dropped in migration 035. See packages/core/src/inbox.ts." --repaired --scope inbox-touch
|
|
34
|
+
|
|
35
|
+
greprag fix log "Build everything from repo root: npm run build (Turborepo). Forced rebuild: npm run build -- --force." --repaired --scope general
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Scope naming
|
|
39
|
+
|
|
40
|
+
Free-form strings — no enum. Check what's already in use first:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
greprag fix scopes [--project <name>]
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Conventional scopes:
|
|
47
|
+
- `chip-startup` — what a freshly-spawned chip needs in its first 5 turns. Layout, build commands, test runners, key file paths.
|
|
48
|
+
- `general` — applies to almost any session in this project.
|
|
49
|
+
- `<subsystem>-touch` — fixes that matter only when editing a specific subsystem (`inbox-touch`, `episodic-touch`, `enrichment-touch`).
|
|
50
|
+
- `env` — environment / credentials / DB connection gotchas.
|
|
51
|
+
|
|
52
|
+
Pick the narrowest scope that still applies. A fix about migration paths is `chip-startup` (every chip needs it); one about how the hourly compactor's prompt versioning works is `episodic-touch` (only relevant if you're editing that subsystem).
|
|
53
|
+
|
|
54
|
+
## Reading fixes back
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
greprag fix list # open queue (raw smells awaiting digestion)
|
|
58
|
+
greprag fix list --repaired --format markdown # durable rules grouped by scope (human review)
|
|
59
|
+
greprag fix search "how do migrations apply" --limit 5 # lexical-rank across scopes
|
|
60
|
+
greprag fix search "session routing" --scope inbox-touch # lexical-rank within a scope
|
|
61
|
+
greprag fix delete <nodeId> # prune stale entry
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`--format markdown` on `fix search` returns a numbered list with no decoration. `fix list --repaired` prints entries grouped under `## <scope>` headings (it ignores `--scope`/`--limit`/`--format markdown`) — slice the scope group you need into a `**Project Fixes**` block when spawning a chip.
|
|
65
|
+
|
|
66
|
+
## Repair (digest an open fix into a durable rule)
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
greprag fix repair <id> "<durable rule>" [--scope <s>]
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
A `repair` records the durable rule (kept, browsable via `fix list --repaired`) **and** closes the open fix in one step. To remove a fix without keeping a rule, use `fix delete`.
|
|
73
|
+
|
|
74
|
+
## Deliberate review
|
|
75
|
+
|
|
76
|
+
Fixes decay as code moves — paths change, conventions die. Run `/mechanic` periodically (especially after a refactor or rename) to audit drift, mine episodic memory for newly-emerged learnings, and promote project-agnostic entries to global rules.
|
|
77
|
+
|
|
78
|
+
## Chip-spawn pull pattern
|
|
79
|
+
|
|
80
|
+
When composing a `spawn_task` chip prompt, pull `chip-startup` fixes into the prompt before dispatching. Full convention: `~/.claude/docs/chip-spawn.md`. One-liner:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
greprag fix list --repaired --project <project> --format markdown
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Take the `## chip-startup` section and wrap it in a `**Project Fixes**` block at the top of the prompt. (Don't reach for `fix search` here: it requires a positional query that lexically *filters* — a guessed query drops entries — and the query-less `--scope` form mis-parses `--scope` as the query.) Skip the block when there is no `chip-startup` group.
|