greptor 0.5.1 → 0.7.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/README.md +179 -85
- package/dist/cli/app.d.ts +3 -0
- package/dist/cli/app.d.ts.map +1 -0
- package/dist/cli/app.js +28 -0
- package/dist/cli/app.js.map +1 -0
- package/dist/cli/commands/generate/index.d.ts +2 -0
- package/dist/cli/commands/generate/index.d.ts.map +1 -0
- package/dist/cli/commands/generate/index.js +13 -0
- package/dist/cli/commands/generate/index.js.map +1 -0
- package/dist/cli/commands/generate/skills/command.d.ts +2 -0
- package/dist/cli/commands/generate/skills/command.d.ts.map +1 -0
- package/dist/cli/commands/generate/skills/command.js +134 -0
- package/dist/cli/commands/generate/skills/command.js.map +1 -0
- package/dist/cli/commands/generate/skills/generator.d.ts +8 -0
- package/dist/cli/commands/generate/skills/generator.d.ts.map +1 -0
- package/dist/cli/commands/generate/skills/generator.js +151 -0
- package/dist/cli/commands/generate/skills/generator.js.map +1 -0
- package/dist/cli/commands/generate/skills/prompt-template.d.ts +16 -0
- package/dist/cli/commands/generate/skills/prompt-template.d.ts.map +1 -0
- package/dist/cli/commands/generate/skills/prompt-template.js +247 -0
- package/dist/cli/commands/generate/skills/prompt-template.js.map +1 -0
- package/dist/cli/commands/generate/skills/types.d.ts +16 -0
- package/dist/cli/commands/generate/skills/types.d.ts.map +1 -0
- package/dist/cli/commands/generate/skills/types.js.map +1 -0
- package/dist/cli/commands/generate/tags/command.d.ts +2 -0
- package/dist/cli/commands/generate/tags/command.d.ts.map +1 -0
- package/dist/cli/commands/generate/tags/command.js +113 -0
- package/dist/cli/commands/generate/tags/command.js.map +1 -0
- package/dist/cli/commands/generate/tags/generator.d.ts +3 -0
- package/dist/cli/commands/generate/tags/generator.d.ts.map +1 -0
- package/dist/cli/commands/generate/tags/generator.js +44 -0
- package/dist/cli/commands/generate/tags/generator.js.map +1 -0
- package/dist/cli/commands/login.d.ts +2 -0
- package/dist/cli/commands/login.d.ts.map +1 -0
- package/dist/cli/commands/login.js +108 -0
- package/dist/cli/commands/login.js.map +1 -0
- package/dist/cli/types.d.ts +24 -0
- package/dist/cli/types.d.ts.map +1 -0
- package/dist/{storage → cli}/types.js.map +1 -1
- package/dist/cli/utils/auth.d.ts +5 -0
- package/dist/cli/utils/auth.d.ts.map +1 -0
- package/dist/cli/utils/auth.js +26 -0
- package/dist/cli/utils/auth.js.map +1 -0
- package/dist/index.d.ts +1 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -2
- package/dist/index.js.map +1 -1
- package/dist/{tag-schema/types.d.ts → lib/config.d.ts} +10 -1
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +69 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/greptor.d.ts +11 -0
- package/dist/lib/greptor.d.ts.map +1 -0
- package/dist/lib/greptor.js +88 -0
- package/dist/lib/greptor.js.map +1 -0
- package/dist/lib/index.d.ts +7 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +4 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/llm/llm-factory.d.ts +7 -0
- package/dist/lib/llm/llm-factory.d.ts.map +1 -0
- package/dist/lib/llm/llm-factory.js +52 -0
- package/dist/lib/llm/llm-factory.js.map +1 -0
- package/dist/{processing → lib/processing}/processor.d.ts +11 -7
- package/dist/lib/processing/processor.d.ts.map +1 -0
- package/dist/lib/processing/processor.js +230 -0
- package/dist/lib/processing/processor.js.map +1 -0
- package/dist/{storage → lib/storage}/file-storage.d.ts +5 -2
- package/dist/lib/storage/file-storage.d.ts.map +1 -0
- package/dist/{storage → lib/storage}/file-storage.js +47 -13
- package/dist/lib/storage/file-storage.js.map +1 -0
- package/dist/lib/storage/index.d.ts.map +1 -0
- package/dist/lib/storage/index.js.map +1 -0
- package/dist/lib/storage/types.d.ts.map +1 -0
- package/dist/lib/storage/types.js +2 -0
- package/dist/lib/storage/types.js.map +1 -0
- package/dist/lib/types.d.ts +91 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +2 -0
- package/dist/lib/types.js.map +1 -0
- package/dist/lib/utils/file.d.ts.map +1 -0
- package/dist/lib/utils/file.js.map +1 -0
- package/package.json +26 -2
- package/dist/greptor.d.ts +0 -7
- package/dist/greptor.d.ts.map +0 -1
- package/dist/greptor.js +0 -99
- package/dist/greptor.js.map +0 -1
- package/dist/processing/processor.d.ts.map +0 -1
- package/dist/processing/processor.js +0 -260
- package/dist/processing/processor.js.map +0 -1
- package/dist/skills/skill-generator.d.ts +0 -16
- package/dist/skills/skill-generator.d.ts.map +0 -1
- package/dist/skills/skill-generator.js +0 -347
- package/dist/skills/skill-generator.js.map +0 -1
- package/dist/storage/file-storage.d.ts.map +0 -1
- package/dist/storage/file-storage.js.map +0 -1
- package/dist/storage/index.d.ts.map +0 -1
- package/dist/storage/index.js.map +0 -1
- package/dist/storage/types.d.ts.map +0 -1
- package/dist/tag-schema/generate.d.ts +0 -4
- package/dist/tag-schema/generate.d.ts.map +0 -1
- package/dist/tag-schema/generate.js +0 -44
- package/dist/tag-schema/generate.js.map +0 -1
- package/dist/tag-schema/initialize.d.ts +0 -5
- package/dist/tag-schema/initialize.d.ts.map +0 -1
- package/dist/tag-schema/initialize.js +0 -28
- package/dist/tag-schema/initialize.js.map +0 -1
- package/dist/tag-schema/types.d.ts.map +0 -1
- package/dist/tag-schema/types.js +0 -22
- package/dist/tag-schema/types.js.map +0 -1
- package/dist/types.d.ts +0 -95
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/utils/file.d.ts.map +0 -1
- package/dist/utils/file.js.map +0 -1
- package/dist/utils/hash.d.ts +0 -2
- package/dist/utils/hash.d.ts.map +0 -1
- package/dist/utils/hash.js +0 -5
- package/dist/utils/hash.js.map +0 -1
- /package/dist/{storage → cli/commands/generate/skills}/types.js +0 -0
- /package/dist/{types.js → cli/types.js} +0 -0
- /package/dist/{storage → lib/storage}/index.d.ts +0 -0
- /package/dist/{storage → lib/storage}/index.js +0 -0
- /package/dist/{storage → lib/storage}/types.d.ts +0 -0
- /package/dist/{utils → lib/utils}/file.d.ts +0 -0
- /package/dist/{utils → lib/utils}/file.js +0 -0
package/README.md
CHANGED
|
@@ -34,23 +34,71 @@ bun add greptor
|
|
|
34
34
|
|
|
35
35
|
### Step 2: Initialize
|
|
36
36
|
|
|
37
|
-
Create a Greptor instance with your base
|
|
37
|
+
Create a Greptor instance with your base path, topic, and model config.
|
|
38
38
|
|
|
39
39
|
```typescript
|
|
40
40
|
import { createGreptor } from 'greptor';
|
|
41
|
-
import { openai } from "@ai-sdk/openai";
|
|
42
41
|
|
|
43
42
|
// Create Greptor instance
|
|
44
43
|
const greptor = await createGreptor({
|
|
45
|
-
|
|
44
|
+
basePath: './projects/investing/content',
|
|
46
45
|
topic: 'Investing, stock market, financial, and macroeconomics',
|
|
47
|
-
|
|
46
|
+
tagSchema: YOUR_TAG_SCHEMA, // Required. See "Tag Schemas" below.
|
|
47
|
+
model: {
|
|
48
|
+
provider: "@ai-sdk/openai",
|
|
49
|
+
model: "gpt-5-mini",
|
|
50
|
+
},
|
|
48
51
|
});
|
|
52
|
+
|
|
53
|
+
// Start background processing workers
|
|
54
|
+
await greptor.start();
|
|
49
55
|
```
|
|
50
56
|
|
|
51
|
-
- **`
|
|
57
|
+
- **`basePath`**: Base directory where data will be stored.
|
|
52
58
|
- **`topic`**: Helps Greptor understand your data better and generate a relevant tag schema.
|
|
53
|
-
- **`
|
|
59
|
+
- **`tagSchema`**: Required. Define your tag fields (or generate them with `greptor generate tags`).
|
|
60
|
+
- **`model`**: A config object with `provider`, `model`, and optional `options` for the [Vercel AI SDK](https://ai-sdk.dev).
|
|
61
|
+
|
|
62
|
+
Greptor will automatically create and manage the following structure in your basePath:
|
|
63
|
+
- `raw/` - immediate raw content writes
|
|
64
|
+
- `processed/` - enriched/processed content from background workers
|
|
65
|
+
|
|
66
|
+
#### Model Config
|
|
67
|
+
|
|
68
|
+
Greptor uses an LLM (via [Greptor](https://github.com/greptorio/greptor)) to process content. You'll need to:
|
|
69
|
+
|
|
70
|
+
1. **Choose a provider** from the [AI SDK ecosystem](https://sdk.vercel.ai/providers/ai-sdk-providers):
|
|
71
|
+
- `@ai-sdk/openai` - OpenAI (GPT-4, GPT-4o, etc.)
|
|
72
|
+
- `@ai-sdk/anthropic` - Anthropic (Claude)
|
|
73
|
+
- `@ai-sdk/groq` - Groq (fast inference)
|
|
74
|
+
- `@ai-sdk/openai-compatible` - OpenAI-compatible endpoints (NVIDIA NIM, OpenRouter, etc.)
|
|
75
|
+
- And [many more](https://sdk.vercel.ai/providers/ai-sdk-providers)...
|
|
76
|
+
|
|
77
|
+
2. **Get an API key** from your provider and set it as an environment variable:
|
|
78
|
+
```bash
|
|
79
|
+
export OPENAI_API_KEY="sk-..."
|
|
80
|
+
# or add to ~/.bashrc, ~/.zshrc, etc.
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
3. **Provide it in the model config** when creating Greptor.
|
|
84
|
+
```typescript
|
|
85
|
+
const greptor = await createGreptor({
|
|
86
|
+
basePath: './projects/investing/content',
|
|
87
|
+
topic: 'Investing, stock market, financial, and macroeconomics',
|
|
88
|
+
tagSchema: YOUR_TAG_SCHEMA,
|
|
89
|
+
model: {
|
|
90
|
+
provider: "@ai-sdk/openai-compatible",
|
|
91
|
+
model: "z-ai/glm4.7",
|
|
92
|
+
name: "nvidia",
|
|
93
|
+
options: {
|
|
94
|
+
baseURL: "https://integrate.api.nvidia.com/v1",
|
|
95
|
+
apiKey: process.env.NVIDIA_API_KEY,
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
await greptor.start();
|
|
101
|
+
```
|
|
54
102
|
|
|
55
103
|
### Step 3: Start Feeding Documents
|
|
56
104
|
|
|
@@ -87,52 +135,58 @@ await greptor.eat({
|
|
|
87
135
|
|
|
88
136
|
### Step 4: Wait for Background Processing
|
|
89
137
|
|
|
90
|
-
Greptor
|
|
138
|
+
Greptor writes your input to a raw Markdown file immediately. After you call `await greptor.start()`, background workers run enrichment (LLM cleaning + chunking + tagging) and write a processed Markdown file. You can grep the raw files right away, and the processed files will appear shortly after.
|
|
91
139
|
|
|
92
|
-
### Step 5: Generate a
|
|
140
|
+
### Step 5: Generate a Skill (CLI)
|
|
93
141
|
|
|
94
|
-
|
|
95
|
-
|
|
142
|
+
Navigate to your workspace directory and run:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
greptor generate skills
|
|
96
146
|
```
|
|
97
147
|
|
|
98
|
-
|
|
148
|
+
The CLI will prompt you to pick an agent type (`claude code`, `codex`, or `opencode`)
|
|
149
|
+
|
|
150
|
+
Then it writes the appropriate skill file for your chosen agent.
|
|
99
151
|
|
|
100
152
|
The skill is customized for the sources you provide and includes search tips based on the tag schema. You can always customize it manually further for better results.
|
|
101
153
|
|
|
102
154
|
### Step 6: Run the Agent
|
|
103
155
|
|
|
104
|
-
By this point, you should have the following structure in your `
|
|
156
|
+
By this point, you should have the following structure in your `basePath`:
|
|
105
157
|
|
|
106
158
|
```
|
|
107
|
-
./projects/investing/
|
|
159
|
+
./projects/investing/content/
|
|
108
160
|
.claude/
|
|
109
161
|
skills/
|
|
110
162
|
search-youtube-reddit/
|
|
111
163
|
SKILL.md
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
2025-12
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
2025-12
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
2025-12
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
2025-12
|
|
130
|
-
2025-12-03-Tesla-reports-418227-deliveries-for-the-fourth-quarter-down-16.md
|
|
164
|
+
raw/
|
|
165
|
+
youtube/
|
|
166
|
+
JosephCarlsonShow/
|
|
167
|
+
2025-12/
|
|
168
|
+
2025-12-01-Top-Five-AI-Stocks-Im-Buying-Now.md
|
|
169
|
+
reddit/
|
|
170
|
+
investing/
|
|
171
|
+
2025-12/
|
|
172
|
+
2025-12-03-Tesla-reports-418227-deliveries-for-the-fourth-quarter-down-16.md
|
|
173
|
+
processed/
|
|
174
|
+
youtube/
|
|
175
|
+
JosephCarlsonShow/
|
|
176
|
+
2025-12/
|
|
177
|
+
2025-12-01-Top-Five-AI-Stocks-Im-Buying-Now.md
|
|
178
|
+
reddit/
|
|
179
|
+
investing/
|
|
180
|
+
2025-12/
|
|
181
|
+
2025-12-03-Tesla-reports-418227-deliveries-for-the-fourth-quarter-down-16.md
|
|
131
182
|
```
|
|
132
183
|
|
|
133
|
-
|
|
184
|
+
If you chose Codex or OpenCode, the skill file will be written to:
|
|
185
|
+
|
|
186
|
+
- `.codex/skills/search-*.md` (Codex)
|
|
187
|
+
- `.opencode/skills/search-*.md` (OpenCode)
|
|
134
188
|
|
|
135
|
-
|
|
189
|
+
Now run your chosen agent in this folder and ask questions about your data or perform research tasks!
|
|
136
190
|
|
|
137
191
|
**For better results**:
|
|
138
192
|
1. Connect MCP servers like Yahoo Finance or other relevant financial/stock market MCP servers for up-to-date information.
|
|
@@ -296,86 +350,124 @@ rg -n -C 6 "narrative=ev_transition" content/processed/ | rg "sentiment=bearish"
|
|
|
296
350
|
|
|
297
351
|
## Configuration
|
|
298
352
|
|
|
299
|
-
###
|
|
353
|
+
### Custom Processing Prompts
|
|
300
354
|
|
|
301
|
-
|
|
355
|
+
You can override the default processing prompt for specific sources to tailor how content is processed:
|
|
302
356
|
|
|
303
357
|
```typescript
|
|
304
358
|
const greptor = await createGreptor({
|
|
305
|
-
|
|
359
|
+
basePath: './projects/investing/content',
|
|
306
360
|
topic: 'Investing, stock market, financial, and macroeconomics',
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
361
|
+
tagSchema: YOUR_TAG_SCHEMA,
|
|
362
|
+
model: {
|
|
363
|
+
provider: "@ai-sdk/openai",
|
|
364
|
+
model: "gpt-5-mini",
|
|
365
|
+
},
|
|
366
|
+
customProcessingPrompts: {
|
|
367
|
+
// Custom prompt for Twitter/X content
|
|
368
|
+
'twitter': `
|
|
369
|
+
# INSTRUCTIONS
|
|
370
|
+
Process this Twitter/X content for investment research. Focus on:
|
|
371
|
+
- Investment signals, predictions, or analysis
|
|
372
|
+
- Key metrics and numbers mentioned
|
|
373
|
+
- Influencer sentiment and conviction level
|
|
374
|
+
|
|
375
|
+
# CONTENT TO PROCESS:
|
|
376
|
+
{CONTENT}
|
|
377
|
+
`,
|
|
317
378
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
const processed = successful + failed;
|
|
330
|
-
const status = success ? '✓' : '✗';
|
|
331
|
-
console.log(
|
|
332
|
-
`[${processed}/${queueSize}] ${status} ${label} (${elapsedMs}ms, ${totalTokens} tokens)`
|
|
333
|
-
);
|
|
334
|
-
},
|
|
379
|
+
// Custom prompt for SEC filings
|
|
380
|
+
'sec_filing': `
|
|
381
|
+
# INSTRUCTIONS
|
|
382
|
+
Process this SEC filing with extreme precision:
|
|
383
|
+
- Preserve all financial figures, dates, and legal language exactly
|
|
384
|
+
- Extract key financial metrics and risk factors
|
|
385
|
+
- Maintain formal, factual tone throughout
|
|
386
|
+
|
|
387
|
+
# CONTENT TO PROCESS:
|
|
388
|
+
{CONTENT}
|
|
389
|
+
`,
|
|
335
390
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
391
|
+
// Custom prompt for earnings transcripts
|
|
392
|
+
'earnings': `
|
|
393
|
+
# INSTRUCTIONS
|
|
394
|
+
Process this earnings call transcript:
|
|
395
|
+
- Extract forward-looking statements and guidance
|
|
396
|
+
- Preserve exact numbers, percentages, and ranges
|
|
397
|
+
- Capture management sentiment and key Q&A points
|
|
398
|
+
|
|
399
|
+
# CONTENT TO PROCESS:
|
|
400
|
+
{CONTENT}
|
|
401
|
+
`,
|
|
402
|
+
},
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
await greptor.start();
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
**Usage notes**:
|
|
409
|
+
- Use `{CONTENT}` as a placeholder where the raw content will be inserted
|
|
410
|
+
- Each custom prompt should include the placeholder exactly once
|
|
411
|
+
- If no custom prompt is defined for a source, Greptor falls back to the default processing prompt
|
|
412
|
+
- Custom prompts are matched against the document's `source` field (e.g., `youtube`, `reddit`, `twitter`)
|
|
413
|
+
|
|
414
|
+
### Event Hooks
|
|
415
|
+
|
|
416
|
+
Greptor provides optional hooks to monitor document processing. These are useful for logging, metrics, progress tracking, or building custom UIs.
|
|
417
|
+
|
|
418
|
+
```typescript
|
|
419
|
+
const greptor = await createGreptor({
|
|
420
|
+
basePath: './projects/investing/content',
|
|
421
|
+
topic: 'Investing, stock market, financial, and macroeconomics',
|
|
422
|
+
tagSchema: YOUR_TAG_SCHEMA,
|
|
423
|
+
model: {
|
|
424
|
+
provider: "@ai-sdk/openai",
|
|
425
|
+
model: "gpt-5-mini",
|
|
426
|
+
},
|
|
427
|
+
hooks: {
|
|
428
|
+
onDocumentProcessingStarted: ({ source, publisher, label, documentsCount }) => {
|
|
429
|
+
const count = documentsCount[source] || { fetched: 0, processed: 0 };
|
|
430
|
+
console.log(`Processing: ${source}/${publisher}/${label} (${count.fetched} fetched, ${count.processed} processed)`);
|
|
344
431
|
},
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
if (
|
|
348
|
-
|
|
432
|
+
|
|
433
|
+
onDocumentProcessingCompleted: (event) => {
|
|
434
|
+
if (event.success) {
|
|
435
|
+
const { source, publisher, label, documentsCount, elapsedMs, totalTokens } = event;
|
|
436
|
+
const count = documentsCount[source] || { fetched: 0, processed: 0 };
|
|
437
|
+
console.log(`✓ Completed: ${source}/${publisher}/${label} (${elapsedMs}ms, ${totalTokens} tokens, ${count.processed}/${count.fetched} processed)`);
|
|
349
438
|
} else {
|
|
350
|
-
|
|
439
|
+
const { source, publisher, label, error } = event;
|
|
440
|
+
console.error(`✗ Failed: ${source}/${publisher}/${label} - ${error}`);
|
|
351
441
|
}
|
|
352
442
|
},
|
|
353
443
|
},
|
|
354
444
|
});
|
|
445
|
+
|
|
446
|
+
await greptor.start();
|
|
355
447
|
```
|
|
356
448
|
|
|
357
449
|
#### Available Hooks
|
|
358
450
|
|
|
359
451
|
| Hook | When Called | Event Data |
|
|
360
452
|
|------|-------------|------------|
|
|
361
|
-
| `
|
|
362
|
-
| `
|
|
363
|
-
| `onDocumentProcessingCompleted` | After processing succeeds or fails | `success`, `source`, `publisher`, `label`, `successful`, `failed`, `queueSize`, `elapsedMs`, `inputTokens`, `outputTokens`, `totalTokens` |
|
|
364
|
-
| `onProcessingRunCompleted` | When all queued documents are processed | `successful`, `failed`, `elapsedMs` |
|
|
365
|
-
| `onError` | When errors occur during processing or ingestion | `error`, `context` (with optional `source`, `publisher`, `label`, `ref`) |
|
|
453
|
+
| `onDocumentProcessingStarted` | Before processing each document | `source`, `publisher?`, `label`, `documentsCount: SourceCounts` |
|
|
454
|
+
| `onDocumentProcessingCompleted` | After processing succeeds or fails | Union type:<br/>• **Success**: `success: true`, `source`, `publisher?`, `label`, `documentsCount`, `elapsedMs`, `inputTokens`, `outputTokens`, `totalTokens`<br/>• **Failure**: `success: false`, `error: string`, `source`, `publisher?`, `label` |
|
|
366
455
|
|
|
367
456
|
|
|
368
457
|
## Tag Schemas
|
|
369
458
|
|
|
370
|
-
|
|
459
|
+
Greptor requires a tag schema. For best results, provide a custom tag schema (or generate one with `greptor generate tags`).
|
|
371
460
|
|
|
372
461
|
Here's a comprehensive example for investment research:
|
|
373
462
|
|
|
374
463
|
```typescript
|
|
375
464
|
const greptor = await createGreptor({
|
|
376
|
-
|
|
465
|
+
basePath: './projects/investing/content',
|
|
377
466
|
topic: 'Investing, stock market, financial, and macroeconomics',
|
|
378
|
-
model:
|
|
467
|
+
model: {
|
|
468
|
+
provider: "@ai-sdk/openai",
|
|
469
|
+
model: "gpt-5-mini",
|
|
470
|
+
},
|
|
379
471
|
tagSchema: [
|
|
380
472
|
{
|
|
381
473
|
name: 'company',
|
|
@@ -446,6 +538,8 @@ const greptor = await createGreptor({
|
|
|
446
538
|
},
|
|
447
539
|
],
|
|
448
540
|
});
|
|
541
|
+
|
|
542
|
+
await greptor.start();
|
|
449
543
|
```
|
|
450
544
|
|
|
451
545
|
## License
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/cli/app.ts"],"names":[],"mappings":";AAgBA,eAAO,MAAM,GAAG,6EAKd,CAAC"}
|
package/dist/cli/app.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { buildApplication, buildRouteMap } from "@stricli/core";
|
|
3
|
+
import { run } from "@stricli/core";
|
|
4
|
+
import { generateRoutes } from "./commands/generate/index.js";
|
|
5
|
+
import { loginCommand } from "./commands/login.js";
|
|
6
|
+
const routes = buildRouteMap({
|
|
7
|
+
routes: {
|
|
8
|
+
login: loginCommand,
|
|
9
|
+
generate: generateRoutes,
|
|
10
|
+
},
|
|
11
|
+
docs: {
|
|
12
|
+
brief: "Greptor CLI - Transform unstructured text into grep-friendly data",
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
export const app = buildApplication(routes, {
|
|
16
|
+
name: "greptor",
|
|
17
|
+
versionInfo: {
|
|
18
|
+
currentVersion: "0.6.0",
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
await run(app, process.argv.slice(2), {
|
|
22
|
+
process: {
|
|
23
|
+
stdout: process.stdout,
|
|
24
|
+
stderr: process.stderr,
|
|
25
|
+
exitCode: process.exitCode ?? null,
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
//# sourceMappingURL=app.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../../src/cli/app.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,MAAM,MAAM,GAAG,aAAa,CAAC;IAC5B,MAAM,EAAE;QACP,KAAK,EAAE,YAAY;QACnB,QAAQ,EAAE,cAAc;KACxB;IACD,IAAI,EAAE;QACL,KAAK,EAAE,mEAAmE;KAC1E;CACD,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,GAAG,GAAG,gBAAgB,CAAC,MAAM,EAAE;IAC3C,IAAI,EAAE,SAAS;IACf,WAAW,EAAE;QACZ,cAAc,EAAE,OAAO;KACvB;CACD,CAAC,CAAC;AAEH,MAAM,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;IACrC,OAAO,EAAE;QACR,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,IAAI;KAClC;CACD,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/generate/index.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,cAAc,0EAQzB,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { buildRouteMap } from "@stricli/core";
|
|
2
|
+
import { skillsCommand } from "./skills/command.js";
|
|
3
|
+
import { tagsCommand } from "./tags/command.js";
|
|
4
|
+
export const generateRoutes = buildRouteMap({
|
|
5
|
+
routes: {
|
|
6
|
+
tags: tagsCommand,
|
|
7
|
+
skills: skillsCommand,
|
|
8
|
+
},
|
|
9
|
+
docs: {
|
|
10
|
+
brief: "Generate tags or skills",
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/cli/commands/generate/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,MAAM,CAAC,MAAM,cAAc,GAAG,aAAa,CAAC;IAC3C,MAAM,EAAE;QACP,IAAI,EAAE,WAAW;QACjB,MAAM,EAAE,aAAa;KACrB;IACD,IAAI,EAAE;QACL,KAAK,EAAE,yBAAyB;KAChC;CACD,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/generate/skills/command.ts"],"names":[],"mappings":"AAgKA,eAAO,MAAM,aAAa,yEASxB,CAAC"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { readdir } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { cancel, intro, isCancel, log, outro, select, spinner, } from "@clack/prompts";
|
|
4
|
+
import { buildCommand } from "@stricli/core";
|
|
5
|
+
import { findConfigFile, readConfig } from "../../../../lib/config.js";
|
|
6
|
+
import { generateSkill } from "./generator.js";
|
|
7
|
+
import { PROCESSED_DIR_NAME, RAW_DIR_NAME, } from "../../../../lib/storage/file-storage.js";
|
|
8
|
+
async function findGreptorPaths(workspacePath) {
|
|
9
|
+
let rawPath;
|
|
10
|
+
let processedPath;
|
|
11
|
+
const configPath = await findConfigFile(".");
|
|
12
|
+
const queue = [workspacePath];
|
|
13
|
+
const visited = new Set();
|
|
14
|
+
while (queue.length > 0) {
|
|
15
|
+
if (rawPath && processedPath) {
|
|
16
|
+
break;
|
|
17
|
+
}
|
|
18
|
+
const current = queue.shift();
|
|
19
|
+
if (!current || visited.has(current)) {
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
visited.add(current);
|
|
23
|
+
try {
|
|
24
|
+
const entries = await readdir(current, { withFileTypes: true });
|
|
25
|
+
for (const entry of entries) {
|
|
26
|
+
if (entry.isDirectory()) {
|
|
27
|
+
const fullPath = path.join(current, entry.name);
|
|
28
|
+
if (entry.name === RAW_DIR_NAME && !rawPath) {
|
|
29
|
+
rawPath = fullPath;
|
|
30
|
+
}
|
|
31
|
+
else if (entry.name === PROCESSED_DIR_NAME && !processedPath) {
|
|
32
|
+
processedPath = fullPath;
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
queue.push(fullPath);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
catch { }
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
configPath: configPath,
|
|
44
|
+
rawContentPath: rawPath,
|
|
45
|
+
processedContentPath: processedPath,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
async function findSources(processedPath) {
|
|
49
|
+
const entries = await readdir(processedPath, { withFileTypes: true });
|
|
50
|
+
const sources = [];
|
|
51
|
+
for (const entry of entries) {
|
|
52
|
+
if (entry.isDirectory()) {
|
|
53
|
+
sources.push(entry.name);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return sources;
|
|
57
|
+
}
|
|
58
|
+
async function generateSkillsCommand() {
|
|
59
|
+
console.clear();
|
|
60
|
+
intro("greptor generate skills");
|
|
61
|
+
const s = spinner();
|
|
62
|
+
try {
|
|
63
|
+
// Step 1: Find greptor content directories for the skill
|
|
64
|
+
s.start("Scanning workspace for greptor paths...");
|
|
65
|
+
const greptorPaths = await findGreptorPaths(".");
|
|
66
|
+
s.stop();
|
|
67
|
+
if (!greptorPaths.rawContentPath ||
|
|
68
|
+
!greptorPaths.processedContentPath ||
|
|
69
|
+
!greptorPaths.configPath) {
|
|
70
|
+
cancel("The current directory doesn't contain greptor content.");
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
// Step 2: Select agent type
|
|
74
|
+
const agent = await select({
|
|
75
|
+
message: "Select agent type:",
|
|
76
|
+
options: [
|
|
77
|
+
{
|
|
78
|
+
value: "claude-code",
|
|
79
|
+
label: "Claude Code",
|
|
80
|
+
hint: "Anthropic Claude Code agent",
|
|
81
|
+
},
|
|
82
|
+
{ value: "codex", label: "Codex", hint: "OpenAI Codex CLI agent" },
|
|
83
|
+
{ value: "opencode", label: "OpenCode", hint: "OpenCode agent" },
|
|
84
|
+
],
|
|
85
|
+
});
|
|
86
|
+
if (isCancel(agent)) {
|
|
87
|
+
cancel("Cancelled");
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
// Step 4: Load config.yaml from workspace
|
|
91
|
+
s.start("Loading config.yaml...");
|
|
92
|
+
const config = await readConfig(greptorPaths.configPath);
|
|
93
|
+
if (!config?.domain || !config.tagSchema || config.tagSchema.length === 0) {
|
|
94
|
+
s.stop("Invalid config");
|
|
95
|
+
cancel("Invalid configuration");
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
s.stop("Config loaded");
|
|
99
|
+
// Step 5: Find content sources from the processed content
|
|
100
|
+
s.start("Finding content sources for the skill...");
|
|
101
|
+
const sources = await findSources(greptorPaths.processedContentPath);
|
|
102
|
+
s.stop(`Found ${sources.length} sources`);
|
|
103
|
+
// Step 6: Generate skills
|
|
104
|
+
s.start("Generating skills...");
|
|
105
|
+
const { skillPath } = await generateSkill({
|
|
106
|
+
domain: config.domain,
|
|
107
|
+
sources,
|
|
108
|
+
basePath: ".",
|
|
109
|
+
greptorPaths: greptorPaths,
|
|
110
|
+
tagsSchema: config.tagSchema,
|
|
111
|
+
agent,
|
|
112
|
+
});
|
|
113
|
+
const relativePath = path.relative(process.cwd(), skillPath);
|
|
114
|
+
s.stop(`Skill file generated at ${relativePath}`);
|
|
115
|
+
outro("Skill generation complete!");
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
s.stop("Error");
|
|
119
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
120
|
+
log.error(`Failed to generate skill: ${message}`);
|
|
121
|
+
cancel("Generation failed");
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
export const skillsCommand = buildCommand({
|
|
125
|
+
func: generateSkillsCommand,
|
|
126
|
+
parameters: {
|
|
127
|
+
flags: {},
|
|
128
|
+
positional: { kind: "tuple", parameters: [] },
|
|
129
|
+
},
|
|
130
|
+
docs: {
|
|
131
|
+
brief: "Generate skills file for AI coding agents",
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
//# sourceMappingURL=command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command.js","sourceRoot":"","sources":["../../../../../src/cli/commands/generate/skills/command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,OAAO,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EACN,MAAM,EACN,KAAK,EACL,QAAQ,EACR,GAAG,EACH,KAAK,EACL,MAAM,EACN,OAAO,GACP,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAG/C,OAAO,EACN,kBAAkB,EAClB,YAAY,GACZ,MAAM,yCAAyC,CAAC;AAEjD,KAAK,UAAU,gBAAgB,CAAC,aAAqB;IACpD,IAAI,OAA2B,CAAC;IAChC,IAAI,aAAiC,CAAC;IACtC,MAAM,UAAU,GAAG,MAAM,cAAc,CAAC,GAAG,CAAC,CAAC;IAE7C,MAAM,KAAK,GAAG,CAAC,aAAa,CAAC,CAAC;IAC9B,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAElC,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,IAAI,OAAO,IAAI,aAAa,EAAE,CAAC;YAC9B,MAAM;QACP,CAAC;QAED,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;QAC9B,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YACtC,SAAS;QACV,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAErB,IAAI,CAAC;YACJ,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;YAEhE,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;gBAC7B,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;oBACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;oBAEhD,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,IAAI,CAAC,OAAO,EAAE,CAAC;wBAC7C,OAAO,GAAG,QAAQ,CAAC;oBACpB,CAAC;yBAAM,IAAI,KAAK,CAAC,IAAI,KAAK,kBAAkB,IAAI,CAAC,aAAa,EAAE,CAAC;wBAChE,aAAa,GAAG,QAAQ,CAAC;oBAC1B,CAAC;yBAAM,CAAC;wBACP,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;oBACtB,CAAC;gBACF,CAAC;YACF,CAAC;QACF,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IACX,CAAC;IAED,OAAO;QACN,UAAU,EAAE,UAAU;QACtB,cAAc,EAAE,OAAO;QACvB,oBAAoB,EAAE,aAAa;KACnC,CAAC;AACH,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,aAAqB;IAC/C,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,aAAa,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IACtE,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACzB,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;IACF,CAAC;IAED,OAAO,OAAO,CAAC;AAChB,CAAC;AAED,KAAK,UAAU,qBAAqB;IACnC,OAAO,CAAC,KAAK,EAAE,CAAC;IAChB,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAEjC,MAAM,CAAC,GAAG,OAAO,EAAE,CAAC;IAEpB,IAAI,CAAC;QACJ,yDAAyD;QACzD,CAAC,CAAC,KAAK,CAAC,yCAAyC,CAAC,CAAC;QACnD,MAAM,YAAY,GAAG,MAAM,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACjD,CAAC,CAAC,IAAI,EAAE,CAAC;QAET,IACC,CAAC,YAAY,CAAC,cAAc;YAC5B,CAAC,YAAY,CAAC,oBAAoB;YAClC,CAAC,YAAY,CAAC,UAAU,EACvB,CAAC;YACF,MAAM,CAAC,wDAAwD,CAAC,CAAC;YACjE,OAAO;QACR,CAAC;QAED,4BAA4B;QAC5B,MAAM,KAAK,GAAG,MAAM,MAAM,CAAY;YACrC,OAAO,EAAE,oBAAoB;YAC7B,OAAO,EAAE;gBACR;oBACC,KAAK,EAAE,aAAa;oBACpB,KAAK,EAAE,aAAa;oBACpB,IAAI,EAAE,6BAA6B;iBACnC;gBACD,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,wBAAwB,EAAE;gBAClE,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,gBAAgB,EAAE;aAChE;SACD,CAAC,CAAC;QAEH,IAAI,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YACrB,MAAM,CAAC,WAAW,CAAC,CAAC;YACpB,OAAO;QACR,CAAC;QAED,0CAA0C;QAC1C,CAAC,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;QAClC,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC;QACzD,IAAI,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3E,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;YACzB,MAAM,CAAC,uBAAuB,CAAC,CAAC;YAChC,OAAO;QACR,CAAC;QAED,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QAExB,0DAA0D;QAC1D,CAAC,CAAC,KAAK,CAAC,0CAA0C,CAAC,CAAC;QACpD,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,YAAY,CAAC,oBAAoB,CAAC,CAAC;QACrE,CAAC,CAAC,IAAI,CAAC,SAAS,OAAO,CAAC,MAAM,UAAU,CAAC,CAAC;QAE1C,0BAA0B;QAC1B,CAAC,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;QAEhC,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,aAAa,CAAC;YACzC,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,OAAO;YACP,QAAQ,EAAE,GAAG;YACb,YAAY,EAAE,YAAY;YAC1B,UAAU,EAAE,MAAM,CAAC,SAAS;YAC5B,KAAK;SACL,CAAC,CAAC;QAEH,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,SAAS,CAAC,CAAC;QAC7D,CAAC,CAAC,IAAI,CAAC,2BAA2B,YAAY,EAAE,CAAC,CAAC;QAElD,KAAK,CAAC,4BAA4B,CAAC,CAAC;IACrC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAChB,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvE,GAAG,CAAC,KAAK,CAAC,6BAA6B,OAAO,EAAE,CAAC,CAAC;QAClD,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAC7B,CAAC;AACF,CAAC;AAED,MAAM,CAAC,MAAM,aAAa,GAAG,YAAY,CAAC;IACzC,IAAI,EAAE,qBAAqB;IAC3B,UAAU,EAAE;QACX,KAAK,EAAE,EAAE;QACT,UAAU,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE,EAAE;KAC7C;IACD,IAAI,EAAE;QACL,KAAK,EAAE,2CAA2C;KAClD;CACD,CAAC,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { SkillGeneratorOptions } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Generate a skill file for the give options and agent type.
|
|
4
|
+
*/
|
|
5
|
+
export declare function generateSkill(options: SkillGeneratorOptions): Promise<{
|
|
6
|
+
skillPath: string;
|
|
7
|
+
}>;
|
|
8
|
+
//# sourceMappingURL=generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/generate/skills/generator.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AA2LxD;;GAEG;AACH,wBAAsB,aAAa,CAClC,OAAO,EAAE,qBAAqB,GAC5B,OAAO,CAAC;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAWhC"}
|