anki_generator 1.1.0 → 1.4.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.
- checksums.yaml +4 -4
- data/.gitignore +59 -0
- data/.rubocop.yml +79 -0
- data/.ruby-version +1 -0
- data/.tool-versions +1 -0
- data/CHANGELOG.md +173 -0
- data/Gemfile +21 -0
- data/Makefile +20 -0
- data/README.md +160 -32
- data/Rakefile +174 -0
- data/anki_generator.gemspec +42 -0
- data/bin/anki_generator +3 -2
- data/docs/CI_SETUP.md +120 -0
- data/docs/architecture/current-v1.3.0.architecture.json +315 -0
- data/docs/architecture/current-v1.3.0.html +14990 -0
- data/docs/architecture/current-v1.3.0.visual-check.json +548 -0
- data/docs/architecture/phase3-proposed.architecture.json +310 -0
- data/docs/architecture/phase3-proposed.html +15001 -0
- data/docs/architecture/phase3-proposed.visual-check.json +548 -0
- data/docs/phase3-draft.md +86 -0
- data/examples/example_class.rb +13 -0
- data/examples/manual_cards.yaml +5 -0
- data/examples/study_prompt.txt +3 -0
- data/input/input.yaml.example +3 -0
- data/lib/anki_generator/anki_connect_client.rb +85 -0
- data/lib/anki_generator/apkg_schema.rb +257 -0
- data/lib/anki_generator/apkg_writer.rb +149 -0
- data/lib/anki_generator/card.rb +83 -0
- data/lib/anki_generator/cli.rb +183 -0
- data/lib/anki_generator/client_factory.rb +20 -0
- data/lib/anki_generator/commands/create_ai_template.rb +39 -0
- data/lib/anki_generator/commands/generate_deck.rb +43 -0
- data/lib/anki_generator/commands/generate_yaml.rb +63 -0
- data/lib/anki_generator/commands/import.rb +75 -0
- data/lib/anki_generator/commands/prompt_based.rb +74 -0
- data/lib/anki_generator/commands/prompt_to_deck.rb +84 -0
- data/lib/anki_generator/commands/push.rb +32 -0
- data/lib/anki_generator/commands/serve.rb +99 -0
- data/lib/anki_generator/commands/test_api.rb +33 -0
- data/lib/anki_generator/deck_builder.rb +184 -0
- data/lib/anki_generator/errors.rb +22 -0
- data/lib/anki_generator/file_processor.rb +154 -0
- data/lib/anki_generator/importers/csv.rb +52 -0
- data/lib/anki_generator/importers/markdown.rb +72 -0
- data/lib/anki_generator/prompt_builder.rb +77 -0
- data/lib/anki_generator/server.rb +98 -0
- data/lib/anki_generator/ui.rb +28 -0
- data/lib/anki_generator/version.rb +5 -0
- data/lib/anki_generator.rb +18 -114
- data/prompt.txt +5 -0
- metadata +100 -43
- data/lib/anki_cli.rb +0 -259
- data/lib/file_processor.rb +0 -156
- data/lib/openrouter_client.rb +0 -158
data/README.md
CHANGED
|
@@ -1,31 +1,40 @@
|
|
|
1
1
|
# Anki Generator
|
|
2
2
|
|
|
3
|
-
A Ruby tool to generate Anki .apkg files from YAML definitions with AI-powered content generation
|
|
3
|
+
A Ruby tool to generate Anki .apkg files from YAML definitions, Markdown notes, or CSV files — with AI-powered content generation (Gemini, OpenAI, Anthropic, OpenRouter, or local Ollama via [ruby_llm](https://github.com/crmne/ruby_llm)), cloze deletions, tags, and a built-in web editor.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- **AI-Powered Generation**: Create flashcards using
|
|
7
|
+
- **AI-Powered Generation**: Create flashcards using any LLM provider supported by [ruby_llm](https://github.com/crmne/ruby_llm) — Gemini, OpenAI, Anthropic, OpenRouter, or a local Ollama server — with multiple AI models
|
|
8
|
+
- **Markdown & CSV Import**: Turn `Q:`/`A:` study notes or spreadsheets into decks; Markdown headings become tags
|
|
9
|
+
- **Cloze Deletion Cards**: `{{cN::...}}` deletions export as real Anki cloze notes (one card per deletion)
|
|
10
|
+
- **Tags & Reverse Cards**: Per-card tags in Anki, plus `--reverse` for recognition + recall pairs
|
|
11
|
+
- **Web UI**: `anki_generator serve` opens a localhost editor — paste notes, generate with AI, export `.apkg`
|
|
12
|
+
- **AnkiConnect Push**: Send decks straight into a running Anki with the AnkiConnect addon
|
|
13
|
+
- **Parallel Generation**: `--jobs N` fans one API call per topic out across threads
|
|
14
|
+
- **Reliable AI Output**: Structured generation via ruby_llm Schematist schemas — cards come back as validated JSON, not prompt-honoured text
|
|
8
15
|
- **File Attachment Support**: Attach code files, documentation, or entire directories for context-aware generation
|
|
9
16
|
- **Prompt File Support**: Use text files as prompts for better organization and reusability
|
|
10
|
-
- **Multiple Input Methods**: Generate from YAML files, direct prompts,
|
|
17
|
+
- **Multiple Input Methods**: Generate from YAML files, direct prompts, file attachments, Markdown, or CSV
|
|
11
18
|
- **Intelligent Content Processing**: Automatic text file detection, size limits, and binary file filtering
|
|
12
19
|
- **Direct Prompt-to-Deck Generation**: Create decks in one command without intermediate files
|
|
13
20
|
- **Sync Functionality**: Merge new AI-generated cards with existing decks
|
|
14
|
-
- **Flexible Configuration**: Multiple difficulty levels, context settings, and
|
|
21
|
+
- **Flexible Configuration**: Multiple difficulty levels, context settings, model and provider selection
|
|
15
22
|
- **Comprehensive CLI**: Full command-line interface with extensive options
|
|
16
23
|
|
|
17
24
|
## Installation
|
|
18
25
|
|
|
26
|
+
**Requirements**: Ruby 3.1 or higher
|
|
27
|
+
|
|
19
28
|
1. Clone the repository
|
|
20
29
|
2. Install dependencies:
|
|
21
30
|
```bash
|
|
22
31
|
bundle install
|
|
23
32
|
```
|
|
24
33
|
|
|
25
|
-
3. Set up your
|
|
34
|
+
3. Set up your LLM API key (e.g. Google Gemini):
|
|
26
35
|
```bash
|
|
27
36
|
cp .env.example .env
|
|
28
|
-
# Edit .env and add your
|
|
37
|
+
# Edit .env and add your GOOGLE_API_KEY (https://aistudio.google.com/apikey)
|
|
29
38
|
```
|
|
30
39
|
|
|
31
40
|
## Usage
|
|
@@ -56,6 +65,39 @@ Generate a deck from a YAML file:
|
|
|
56
65
|
./bin/anki_generator generate "My Deck" input/input.yaml my_deck.apkg
|
|
57
66
|
```
|
|
58
67
|
|
|
68
|
+
### Import Markdown or CSV Notes
|
|
69
|
+
|
|
70
|
+
Turn existing study notes into a deck:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# Markdown: Q:/A: pairs or "- **question** — answer" bullets; headings become tags
|
|
74
|
+
./bin/anki_generator import "Biology" notes.md biology.apkg
|
|
75
|
+
|
|
76
|
+
# Add reversed (recall) cards for every basic card
|
|
77
|
+
./bin/anki_generator import "Biology" notes.md biology.apkg --reverse
|
|
78
|
+
|
|
79
|
+
# CSV: front,back[,tags] with pipe-separated tags
|
|
80
|
+
./bin/anki_generator import "Capitals" capitals.csv capitals.apkg
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Web Editor
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
./bin/anki_generator serve # http://127.0.0.1:8787
|
|
87
|
+
./bin/anki_generator serve --port 9000
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Paste Markdown/YAML on the left, generate cards with AI, edit the table, export `.apkg`.
|
|
91
|
+
|
|
92
|
+
### Push to a Running Anki
|
|
93
|
+
|
|
94
|
+
With the [AnkiConnect](https://ankiweb.net/shared/info/2055492159) addon installed in Anki:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
./bin/anki_generator push "My Deck" cards.yaml
|
|
98
|
+
./bin/anki_generator push "My Deck" cards.yaml --url http://localhost:8765
|
|
99
|
+
```
|
|
100
|
+
|
|
59
101
|
### AI-Powered Generation
|
|
60
102
|
|
|
61
103
|
Create an AI generation template:
|
|
@@ -74,7 +116,7 @@ Generate a deck with AI content:
|
|
|
74
116
|
|
|
75
117
|
```bash
|
|
76
118
|
# Use a specific AI model
|
|
77
|
-
./bin/anki_generator prompt_to_deck "Python basics" "Python Deck" python.apkg --model anthropic/claude-
|
|
119
|
+
./bin/anki_generator prompt_to_deck "Python basics" "Python Deck" python.apkg --model anthropic/claude-sonnet-4
|
|
78
120
|
|
|
79
121
|
# Set difficulty and count
|
|
80
122
|
./bin/anki_generator generate_yaml "Advanced algorithms" algo.yaml --difficulty hard --count 20
|
|
@@ -102,6 +144,15 @@ Generate a deck with AI content:
|
|
|
102
144
|
|
|
103
145
|
# Test API connection
|
|
104
146
|
./bin/anki_generator test_api --api_key YOUR_API_KEY
|
|
147
|
+
|
|
148
|
+
# Use a local Ollama model instead of a cloud provider (no API key needed)
|
|
149
|
+
./bin/anki_generator prompt_to_deck "Ruby basics" "Ruby" ruby.apkg --provider ollama --model llama3.2
|
|
150
|
+
|
|
151
|
+
# Generate one API call per topic, 4 at a time
|
|
152
|
+
./bin/anki_generator generate "AI Deck" ai.yaml out.apkg --api_key KEY --jobs 4
|
|
153
|
+
|
|
154
|
+
# Basic + reversed cards from a YAML deck
|
|
155
|
+
./bin/anki_generator generate "My Deck" input.yaml out.apkg --reverse
|
|
105
156
|
```
|
|
106
157
|
|
|
107
158
|
## CLI Commands
|
|
@@ -116,7 +167,7 @@ anki_generator prompt_to_deck PROMPT DECK_NAME OUTPUT_FILE [options]
|
|
|
116
167
|
- `--attach FILE_OR_DIR [FILE_OR_DIR...]` - Attach files or directories for context
|
|
117
168
|
- `--prompt-file` - Treat PROMPT as a file path to read from
|
|
118
169
|
- `--save-yaml` - Save intermediate YAML file
|
|
119
|
-
- `--api-key API_KEY` -
|
|
170
|
+
- `--api-key API_KEY` - Provider API key (requires `--provider`)
|
|
120
171
|
- `--model MODEL` - AI model to use
|
|
121
172
|
- `--difficulty LEVEL` - Difficulty level (easy, medium, hard)
|
|
122
173
|
- `--count N` - Number of flashcards to generate
|
|
@@ -131,7 +182,7 @@ anki_generator generate_yaml PROMPT OUTPUT_YAML [options]
|
|
|
131
182
|
**Options:**
|
|
132
183
|
- `--attach FILE_OR_DIR [FILE_OR_DIR...]` - Attach files or directories for context
|
|
133
184
|
- `--prompt-file` - Treat PROMPT as a file path to read from
|
|
134
|
-
- `--api-key API_KEY` -
|
|
185
|
+
- `--api-key API_KEY` - Provider API key (requires `--provider`)
|
|
135
186
|
- `--model MODEL` - AI model to use
|
|
136
187
|
- `--difficulty LEVEL` - Difficulty level (easy, medium, hard)
|
|
137
188
|
- `--count N` - Number of flashcards to generate
|
|
@@ -143,6 +194,37 @@ Generate an Anki deck from an existing YAML file:
|
|
|
143
194
|
anki_generator generate DECK_NAME YAML_FILE OUTPUT_FILE [options]
|
|
144
195
|
```
|
|
145
196
|
|
|
197
|
+
**Options:**
|
|
198
|
+
- `--reverse` - Append a front↔back copy of every basic card
|
|
199
|
+
- `--jobs N` - Generate AI topics in parallel across N threads
|
|
200
|
+
- `--api-key`, `--model`, `--provider` (gemini/openai/anthropic/openrouter/ollama/...), `--sync_with`
|
|
201
|
+
|
|
202
|
+
### `import` - Import Markdown or CSV
|
|
203
|
+
Turn study notes into a deck:
|
|
204
|
+
```bash
|
|
205
|
+
anki_generator import DECK_NAME INPUT_FILE OUTPUT_FILE [options]
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
**Options:**
|
|
209
|
+
- `--reverse` - Append reversed copies of basic cards
|
|
210
|
+
|
|
211
|
+
Supports `.md`/`.markdown` (`Q:`/`A:` pairs, `- **front** — back` bullets, headings → tags) and `.csv` (`front,back[,tags]`, tags pipe-separated).
|
|
212
|
+
|
|
213
|
+
### `push` - Push to Anki
|
|
214
|
+
Send a YAML deck straight into a running Anki via AnkiConnect:
|
|
215
|
+
```bash
|
|
216
|
+
anki_generator push DECK_NAME YAML_FILE [options]
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**Options:**
|
|
220
|
+
- `--url URL` - AnkiConnect endpoint (default `http://localhost:8765`)
|
|
221
|
+
|
|
222
|
+
### `serve` - Web Editor
|
|
223
|
+
Start the localhost editor UI:
|
|
224
|
+
```bash
|
|
225
|
+
anki_generator serve [--port PORT]
|
|
226
|
+
```
|
|
227
|
+
|
|
146
228
|
### `create_ai_template` - Create Template
|
|
147
229
|
Create a template YAML file for AI generation:
|
|
148
230
|
```bash
|
|
@@ -150,7 +232,7 @@ anki_generator create_ai_template TEMPLATE_FILE
|
|
|
150
232
|
```
|
|
151
233
|
|
|
152
234
|
### `test_api` - Test Connection
|
|
153
|
-
Test your
|
|
235
|
+
Test your LLM connection:
|
|
154
236
|
```bash
|
|
155
237
|
anki_generator test_api [options]
|
|
156
238
|
```
|
|
@@ -220,9 +302,14 @@ The tool automatically processes text-based files including:
|
|
|
220
302
|
```yaml
|
|
221
303
|
- front: "What is Big O notation?"
|
|
222
304
|
back: "Big O notation describes the limiting behavior of a function..."
|
|
305
|
+
tags: [complexity, cs]
|
|
223
306
|
|
|
224
307
|
- front: "Define a graph"
|
|
225
308
|
back: "A graph is a collection of vertices connected by edges"
|
|
309
|
+
|
|
310
|
+
# Cloze deletion — one card per {{cN::...}} ordinal
|
|
311
|
+
- cloze: "{{c1::Paris}} is the capital of {{c2::France}}"
|
|
312
|
+
tags: [geography]
|
|
226
313
|
```
|
|
227
314
|
|
|
228
315
|
### AI Generation Format
|
|
@@ -247,17 +334,29 @@ cards:
|
|
|
247
334
|
|
|
248
335
|
### Environment Variables
|
|
249
336
|
|
|
250
|
-
- `
|
|
251
|
-
- `
|
|
337
|
+
- `GOOGLE_API_KEY` / `GEMINI_API_KEY`: Your Google Gemini API key (default provider)
|
|
338
|
+
- `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `OPENROUTER_API_KEY`: keys for the other cloud providers
|
|
339
|
+
- `OLLAMA_URL`: Ollama server URL (default `http://localhost:11434`)
|
|
340
|
+
- `ANKI_GENERATOR_MODEL`: Default model to use (optional, or pass `--model`)
|
|
341
|
+
|
|
342
|
+
### Providers
|
|
343
|
+
|
|
344
|
+
AI generation is backed by the [ruby_llm](https://github.com/crmne/ruby_llm) gem, so any provider it supports works. Omit `--provider` to auto-resolve from the model name, or pin one explicitly:
|
|
345
|
+
|
|
346
|
+
- **Gemini** (default model `gemini-3.8-flash`, no `--provider` needed): Google's models, key from `GOOGLE_API_KEY`/`GEMINI_API_KEY`
|
|
347
|
+
- **OpenAI** (`--provider openai`): `OPENAI_API_KEY`
|
|
348
|
+
- **Anthropic** (`--provider anthropic`): `ANTHROPIC_API_KEY`
|
|
349
|
+
- **OpenRouter** (`--provider openrouter`): cloud aggregator, `OPENROUTER_API_KEY`
|
|
350
|
+
- **Ollama** (`--provider ollama`): free, local, private — run any GGUF model on your machine with no API key
|
|
252
351
|
|
|
253
352
|
### Supported Models
|
|
254
353
|
|
|
255
|
-
The tool supports all models available through
|
|
354
|
+
The tool supports all models available through the configured provider:
|
|
256
355
|
|
|
257
|
-
-
|
|
258
|
-
-
|
|
259
|
-
-
|
|
260
|
-
- And many more
|
|
356
|
+
- Google: `gemini-3.8-flash` (default), `gemini-3.8-pro`
|
|
357
|
+
- OpenAI: `gpt-5`, `gpt-4o`
|
|
358
|
+
- Anthropic: `claude-sonnet-4`, `claude-haiku-4`
|
|
359
|
+
- And many more (any model id the provider supports)
|
|
261
360
|
|
|
262
361
|
## Examples
|
|
263
362
|
|
|
@@ -320,40 +419,54 @@ See the `input/` directory for example YAML files, or create examples with `rake
|
|
|
320
419
|
|
|
321
420
|
## Development
|
|
322
421
|
|
|
323
|
-
## Development
|
|
324
|
-
|
|
325
422
|
### Quick Start for Developers
|
|
326
423
|
|
|
424
|
+
The repo pins Ruby via `.ruby-version` (3.3). With rbenv/asdf installed:
|
|
425
|
+
|
|
327
426
|
```bash
|
|
328
427
|
# Clone and setup
|
|
329
428
|
git clone <repository-url>
|
|
330
429
|
cd anki_generator
|
|
331
430
|
rake setup # Install dependencies and create examples
|
|
332
431
|
|
|
333
|
-
# Run tests
|
|
432
|
+
# Run tests (coverage report in coverage/, 75% line floor enforced)
|
|
334
433
|
rake test # Run all tests
|
|
335
434
|
rake test_file[cli] # Run specific test
|
|
336
435
|
|
|
436
|
+
# Lint (zero offenses enforced — CI fails otherwise)
|
|
437
|
+
rake lint
|
|
438
|
+
rake lint_fix # Auto-correct safe offenses
|
|
439
|
+
|
|
337
440
|
# Try the examples
|
|
338
441
|
rake demo_api # Test API connection
|
|
339
442
|
rake examples # Create example files
|
|
340
443
|
rake demo_attachments # Demo with file attachments
|
|
341
444
|
```
|
|
342
445
|
|
|
446
|
+
### GitHub Actions CI/CD
|
|
447
|
+
|
|
448
|
+
The project includes GitHub Actions workflows:
|
|
449
|
+
|
|
450
|
+
- **`ruby.yml`** - Main CI pipeline: tests on Ruby 3.1–3.4 plus a RuboCop lint gate
|
|
451
|
+
- **`release.yml`** - Automated releases when tags are pushed
|
|
452
|
+
- **`manual-test.yml`** - Manual workflow for testing specific scenarios
|
|
453
|
+
|
|
454
|
+
To trigger a release:
|
|
455
|
+
```bash
|
|
456
|
+
git tag v1.2.0
|
|
457
|
+
git push origin v1.2.0
|
|
458
|
+
```
|
|
459
|
+
|
|
343
460
|
### Running Tests
|
|
344
461
|
|
|
345
462
|
```bash
|
|
346
|
-
# Run all tests
|
|
463
|
+
# Run all tests (SimpleCov runs by default; the suite fails below 75% line coverage)
|
|
347
464
|
rake test
|
|
348
465
|
|
|
349
466
|
# Run specific test file
|
|
350
467
|
rake test_file[cli] # runs tests/test_cli.rb
|
|
351
468
|
rake test_file[file_processor] # runs tests/test_file_processor.rb
|
|
352
|
-
|
|
353
|
-
# Run tests with coverage
|
|
354
|
-
rake test_coverage
|
|
355
469
|
```
|
|
356
|
-
|
|
357
470
|
### Development Commands
|
|
358
471
|
|
|
359
472
|
```bash
|
|
@@ -378,17 +491,27 @@ rake demo_api # Test API connection
|
|
|
378
491
|
# Utilities
|
|
379
492
|
rake help # Show CLI help
|
|
380
493
|
rake version # Show version info
|
|
494
|
+
rake changelog # Show changelog for current version
|
|
381
495
|
rake release_prep # Prepare for release
|
|
382
496
|
```
|
|
383
497
|
|
|
384
498
|
### Project Structure
|
|
385
499
|
|
|
386
500
|
```
|
|
387
|
-
├── lib/
|
|
388
|
-
│ ├──
|
|
389
|
-
│ ├──
|
|
390
|
-
│ ├──
|
|
391
|
-
│
|
|
501
|
+
├── lib/anki_generator/
|
|
502
|
+
│ ├── deck_builder.rb # Deck assembly: YAML loading, AI generation, sync
|
|
503
|
+
│ ├── card.rb # Card value object (basic + cloze, tags, reverse)
|
|
504
|
+
│ ├── apkg_writer.rb # Native .apkg (zip + SQLite) writer
|
|
505
|
+
│ ├── apkg_schema.rb # Anki collection schema and model JSON
|
|
506
|
+
│ ├── cli.rb # Thor CLI (thin shell over Commands::*)
|
|
507
|
+
│ ├── commands/ # One service object per CLI command
|
|
508
|
+
│ ├── importers/ # Markdown and CSV note importers
|
|
509
|
+
│ ├── llm_client.rb # Provider-agnostic LLM client (ruby_llm gem)
|
|
510
|
+
│ ├── client_factory.rb # Builds the LLM client (any ruby_llm provider)
|
|
511
|
+
│ ├── anki_connect_client.rb # Push decks into a running Anki
|
|
512
|
+
│ ├── server.rb # WEBrick servlets for the web editor
|
|
513
|
+
│ ├── prompt_builder.rb # Prompt construction
|
|
514
|
+
│ └── file_processor.rb # File attachment processing
|
|
392
515
|
├── bin/
|
|
393
516
|
│ └── anki_generator # CLI executable
|
|
394
517
|
├── tests/ # Test files
|
|
@@ -398,7 +521,7 @@ rake release_prep # Prepare for release
|
|
|
398
521
|
|
|
399
522
|
## API Integration
|
|
400
523
|
|
|
401
|
-
The tool integrates with
|
|
524
|
+
The tool integrates with the ruby_llm gem to provide access to multiple AI providers and models. You can:
|
|
402
525
|
|
|
403
526
|
1. Generate flashcards on any topic
|
|
404
527
|
2. Specify difficulty levels
|
|
@@ -421,7 +544,12 @@ The sync feature allows you to:
|
|
|
421
544
|
2. Create a feature branch
|
|
422
545
|
3. Add tests for new functionality
|
|
423
546
|
4. Ensure all tests pass
|
|
424
|
-
5.
|
|
547
|
+
5. Update CHANGELOG.md with your changes
|
|
548
|
+
6. Submit a pull request
|
|
549
|
+
|
|
550
|
+
## Changelog
|
|
551
|
+
|
|
552
|
+
See [CHANGELOG.md](CHANGELOG.md) for detailed version history and changes.
|
|
425
553
|
|
|
426
554
|
## License
|
|
427
555
|
|
data/Rakefile
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rake/testtask'
|
|
4
|
+
require 'fileutils'
|
|
5
|
+
|
|
6
|
+
# Test tasks
|
|
7
|
+
Rake::TestTask.new(:test) do |t|
|
|
8
|
+
t.libs << 'lib'
|
|
9
|
+
t.test_files = FileList['tests/test_*.rb'].exclude('tests/test_helper.rb')
|
|
10
|
+
t.verbose = true
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
desc 'Run all tests (with coverage report and floor enforced via test_helper)'
|
|
14
|
+
task test_all: :test
|
|
15
|
+
|
|
16
|
+
desc 'Run specific test file'
|
|
17
|
+
task :test_file, [:file] do |_t, args|
|
|
18
|
+
if args[:file]
|
|
19
|
+
ruby "-I lib tests/test_#{args[:file]}.rb"
|
|
20
|
+
else
|
|
21
|
+
puts 'Usage: rake test_file[file_name] (without test_ prefix)'
|
|
22
|
+
puts 'Example: rake test_file[cli] runs tests/test_cli.rb'
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Development tasks
|
|
27
|
+
desc 'Install dependencies'
|
|
28
|
+
task :install do
|
|
29
|
+
sh 'bundle install'
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
desc 'Build gem'
|
|
33
|
+
task :build do
|
|
34
|
+
sh 'gem build anki_generator.gemspec'
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
desc 'Install gem locally'
|
|
38
|
+
task install_local: :build do
|
|
39
|
+
gem_file = Dir['anki_generator-*.gem'].last
|
|
40
|
+
sh "gem install #{gem_file}"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
desc 'Clean build artifacts'
|
|
44
|
+
task :clean do
|
|
45
|
+
FileUtils.rm_f(Dir['*.gem'])
|
|
46
|
+
FileUtils.rm_f(Dir['*.apkg'])
|
|
47
|
+
FileUtils.rm_f(Dir['temp_*.yaml'])
|
|
48
|
+
puts 'Cleaned build artifacts'
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Example and demo tasks
|
|
52
|
+
desc 'Create example files'
|
|
53
|
+
task :examples do
|
|
54
|
+
FileUtils.mkdir_p('examples')
|
|
55
|
+
|
|
56
|
+
File.write('examples/study_prompt.txt', <<~PROMPT)
|
|
57
|
+
Create flashcards about Ruby programming fundamentals.
|
|
58
|
+
Focus on basic syntax, data types, control structures, and object-oriented concepts.
|
|
59
|
+
Make the questions practical and suitable for beginners.
|
|
60
|
+
PROMPT
|
|
61
|
+
|
|
62
|
+
File.write('examples/example_class.rb', <<~'RUBY')
|
|
63
|
+
class Calculator
|
|
64
|
+
attr_reader :history
|
|
65
|
+
|
|
66
|
+
def initialize
|
|
67
|
+
@history = []
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def add(a, b)
|
|
71
|
+
result = a + b
|
|
72
|
+
@history << "#{a} + #{b} = #{result}"
|
|
73
|
+
result
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
RUBY
|
|
77
|
+
|
|
78
|
+
File.write('examples/manual_cards.yaml', <<~YAML)
|
|
79
|
+
- front: "What is a Ruby class?"
|
|
80
|
+
back: "A class is a blueprint for creating objects with shared attributes and methods"
|
|
81
|
+
|
|
82
|
+
- front: "How do you define a method in Ruby?"
|
|
83
|
+
back: "Use the 'def' keyword followed by the method name and optional parameters"
|
|
84
|
+
YAML
|
|
85
|
+
|
|
86
|
+
puts 'Created example files in examples/ directory'
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
desc 'Demo: Generate cards from prompt'
|
|
90
|
+
task :demo_prompt do
|
|
91
|
+
puts 'Demo: Generating flashcards from a simple prompt...'
|
|
92
|
+
puts 'Note: This requires a configured LLM API key (e.g. GOOGLE_API_KEY)'
|
|
93
|
+
|
|
94
|
+
sh 'ruby -I lib bin/anki_generator generate_yaml "Ruby basics: variables, methods, classes" ' \
|
|
95
|
+
'demo_output.yaml --count 5 --difficulty easy'
|
|
96
|
+
puts 'Generated demo_output.yaml'
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
desc 'Demo: Generate cards with file attachments'
|
|
100
|
+
task demo_attachments: :examples do
|
|
101
|
+
puts 'Demo: Generating flashcards with file attachments...'
|
|
102
|
+
puts 'Note: This requires a configured LLM API key (e.g. GOOGLE_API_KEY)'
|
|
103
|
+
|
|
104
|
+
sh 'ruby -I lib bin/anki_generator prompt_to_deck examples/study_prompt.txt "Ruby Study Demo" ' \
|
|
105
|
+
'demo_deck.apkg --prompt-file --attach examples/example_class.rb --count 8'
|
|
106
|
+
puts 'Generated demo_deck.apkg with file attachments'
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
desc 'Demo: Test API connection'
|
|
110
|
+
task :demo_api do
|
|
111
|
+
puts 'Testing LLM connection...'
|
|
112
|
+
sh 'ruby -I lib bin/anki_generator test_api'
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Utility tasks
|
|
116
|
+
desc 'Show CLI help'
|
|
117
|
+
task :help do
|
|
118
|
+
sh 'ruby -I lib bin/anki_generator help'
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
desc 'Show version info'
|
|
122
|
+
task :version do
|
|
123
|
+
require_relative 'lib/anki_generator/version'
|
|
124
|
+
puts "Anki Generator version: #{AnkiGenerator::VERSION}"
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
desc 'Show changelog for current version'
|
|
128
|
+
task :changelog do
|
|
129
|
+
version = File.read('lib/anki_generator/version.rb')[/VERSION = '([^']+)'/, 1]
|
|
130
|
+
|
|
131
|
+
if File.exist?('CHANGELOG.md')
|
|
132
|
+
changelog = File.read('CHANGELOG.md')
|
|
133
|
+
|
|
134
|
+
version_section = changelog.match(/## \[#{Regexp.escape(version)}\].*?(?=## \[|\z)/m)
|
|
135
|
+
|
|
136
|
+
if version_section
|
|
137
|
+
puts "Changelog for version #{version}:"
|
|
138
|
+
puts '=' * 40
|
|
139
|
+
puts version_section[0]
|
|
140
|
+
else
|
|
141
|
+
puts "No changelog entry found for version #{version}"
|
|
142
|
+
puts 'Please update CHANGELOG.md'
|
|
143
|
+
end
|
|
144
|
+
else
|
|
145
|
+
puts 'CHANGELOG.md not found'
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
desc 'Lint code with RuboCop'
|
|
150
|
+
task :lint do
|
|
151
|
+
sh 'rubocop --format simple --fail-level warning'
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
desc 'Auto-fix linting issues'
|
|
155
|
+
task :lint_fix do
|
|
156
|
+
sh 'rubocop --auto-correct-all --format simple'
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Combined tasks
|
|
160
|
+
desc 'Full development setup'
|
|
161
|
+
task setup: %i[install examples] do
|
|
162
|
+
puts 'Development environment ready!'
|
|
163
|
+
puts "Run 'rake test' to run tests"
|
|
164
|
+
puts "Run 'rake demo_api' to test API connection"
|
|
165
|
+
puts "Run 'rake help' to see CLI options"
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
desc 'Prepare for release'
|
|
169
|
+
task release_prep: %i[clean lint test build] do
|
|
170
|
+
puts 'Release preparation complete!'
|
|
171
|
+
puts 'Gem built and tests passed'
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
task default: :test
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'lib/anki_generator/version'
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = 'anki_generator'
|
|
7
|
+
spec.version = AnkiGenerator::VERSION
|
|
8
|
+
spec.authors = ['Ceb']
|
|
9
|
+
spec.email = ['ceeb.developer@gmail.com']
|
|
10
|
+
spec.summary = 'AI-powered Anki flashcard generator with file attachment support'
|
|
11
|
+
spec.description = 'A command-line tool that generates Anki flashcard decks (.apkg) from YAML files, direct prompts, or file attachments. Features AI-powered content generation via the provider-agnostic ruby_llm gem (Gemini, OpenAI, Anthropic, OpenRouter, Ollama, and more), file and directory attachment processing for context-aware generation, prompt file support, intelligent content filtering, and flexible deck management with sync capabilities.'
|
|
12
|
+
spec.homepage = 'https://github.com/pinkfloydsito/anki_generator'
|
|
13
|
+
spec.license = 'MIT'
|
|
14
|
+
|
|
15
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |file|
|
|
16
|
+
file.start_with?('tests/', 'scripts/', '.github/', '.kiro/') ||
|
|
17
|
+
file.match?(/\.gem\z/) || file == '.env.example'
|
|
18
|
+
end
|
|
19
|
+
spec.bindir = 'bin'
|
|
20
|
+
spec.executables = ['anki_generator']
|
|
21
|
+
spec.require_paths = ['lib']
|
|
22
|
+
|
|
23
|
+
spec.add_dependency 'dotenv', '~> 2.8'
|
|
24
|
+
spec.add_dependency 'json', '~> 2.0'
|
|
25
|
+
spec.add_dependency 'ruby_llm', '~> 2.0'
|
|
26
|
+
spec.add_dependency 'rubyzip', '>= 2.3', '< 3.0'
|
|
27
|
+
spec.add_dependency 'sinatra', '~> 4.0'
|
|
28
|
+
spec.add_dependency 'sqlite3', '>= 1.6', '< 3.0'
|
|
29
|
+
spec.add_dependency 'thor', '~> 1.2'
|
|
30
|
+
spec.add_dependency 'webrick', '>= 1.8'
|
|
31
|
+
|
|
32
|
+
spec.required_ruby_version = '>= 3.1.0'
|
|
33
|
+
|
|
34
|
+
spec.metadata = {
|
|
35
|
+
'homepage_uri' => spec.homepage,
|
|
36
|
+
'source_code_uri' => spec.homepage,
|
|
37
|
+
'changelog_uri' => "#{spec.homepage}/blob/main/CHANGELOG.md",
|
|
38
|
+
'bug_tracker_uri' => "#{spec.homepage}/issues",
|
|
39
|
+
'documentation_uri' => "#{spec.homepage}/blob/main/README.md",
|
|
40
|
+
'rubygems_mfa_required' => 'true'
|
|
41
|
+
}
|
|
42
|
+
end
|
data/bin/anki_generator
CHANGED
data/docs/CI_SETUP.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# CI/CD Setup Guide
|
|
2
|
+
|
|
3
|
+
This document explains the minimal GitHub Actions workflows and Ruby setup.
|
|
4
|
+
|
|
5
|
+
## Workflows Overview
|
|
6
|
+
|
|
7
|
+
### 1. `ruby.yml` - Main CI Pipeline
|
|
8
|
+
- **Trigger**: Push/PR to main branch
|
|
9
|
+
- **Ruby Versions**: 3.1, 3.2, 3.3, 3.4
|
|
10
|
+
- **Lint**: RuboCop gate (zero offenses enforced)
|
|
11
|
+
- **Actions**: Test, lint, and build gem
|
|
12
|
+
- **Status**: ✅ Minimal and reliable
|
|
13
|
+
|
|
14
|
+
### 2. `release.yml` - Automated Releases
|
|
15
|
+
- **Trigger**: Git tags (v*)
|
|
16
|
+
- **Actions**: Test, build, create GitHub release
|
|
17
|
+
- **Artifacts**: Uploads gem file to release
|
|
18
|
+
|
|
19
|
+
### 3. `manual-test.yml` - Manual Testing
|
|
20
|
+
- **Trigger**: Manual workflow dispatch
|
|
21
|
+
- **Features**:
|
|
22
|
+
- Choose Ruby version (3.1, 3.2, 3.3)
|
|
23
|
+
- Optional demo runs
|
|
24
|
+
- Full CLI testing
|
|
25
|
+
|
|
26
|
+
## Ruby Version Support
|
|
27
|
+
|
|
28
|
+
### Supported Versions
|
|
29
|
+
- ✅ **Ruby 3.1**: Fully supported and tested
|
|
30
|
+
- ✅ **Ruby 3.2**: Fully supported and tested
|
|
31
|
+
- ✅ **Ruby 3.3**: Fully supported and tested
|
|
32
|
+
|
|
33
|
+
### Requirements
|
|
34
|
+
- Minimum Ruby version: 3.1.0
|
|
35
|
+
- Minitest: ~> 5.20 (for Ruby 3.3+ compatibility)
|
|
36
|
+
- Additional gems for Ruby 3.3+: mutex_m (automatically included)
|
|
37
|
+
|
|
38
|
+
## Ruby Setup Issues
|
|
39
|
+
|
|
40
|
+
### Problem: Self-Hosted Runner Detection
|
|
41
|
+
```
|
|
42
|
+
Error: The current runner (ubuntu-24.04-x64) was detected as self-hosted
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Solutions:
|
|
46
|
+
|
|
47
|
+
#### Option 1: Use Updated Workflow (Recommended)
|
|
48
|
+
The `ruby.yml` workflow uses `ruby/setup-ruby@v1` which handles newer Ubuntu versions.
|
|
49
|
+
|
|
50
|
+
#### Option 2: Manual Ruby Installation (Self-Hosted Runners)
|
|
51
|
+
```bash
|
|
52
|
+
# Run the setup script
|
|
53
|
+
./scripts/setup-ruby-self-hosted.sh 3.3.0
|
|
54
|
+
|
|
55
|
+
# Or manually:
|
|
56
|
+
ruby-build 3.3.0 /opt/hostedtoolcache/Ruby/3.3.0/x64
|
|
57
|
+
touch /opt/hostedtoolcache/Ruby/3.3.0/x64.complete
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
#### Option 3: Use Different Runner
|
|
61
|
+
```yaml
|
|
62
|
+
runs-on: ubuntu-22.04 # Instead of ubuntu-latest
|
|
63
|
+
```
|
|
64
|
+
## Testing Locally
|
|
65
|
+
|
|
66
|
+
Before pushing, test locally:
|
|
67
|
+
```bash
|
|
68
|
+
# Run all tests
|
|
69
|
+
bundle exec rake test
|
|
70
|
+
|
|
71
|
+
# Build gem
|
|
72
|
+
bundle exec rake build
|
|
73
|
+
|
|
74
|
+
# Install and test CLI
|
|
75
|
+
bundle exec rake install_local
|
|
76
|
+
anki_generator help
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Release Process
|
|
80
|
+
|
|
81
|
+
1. **Update version** in `anki_generator.gemspec`
|
|
82
|
+
2. **Update CHANGELOG.md** with new version
|
|
83
|
+
3. **Commit changes**:
|
|
84
|
+
```bash
|
|
85
|
+
git add .
|
|
86
|
+
git commit -m "Bump version to 1.2.0"
|
|
87
|
+
```
|
|
88
|
+
4. **Create and push tag**:
|
|
89
|
+
```bash
|
|
90
|
+
git tag v1.2.0
|
|
91
|
+
git push origin v1.2.0
|
|
92
|
+
```
|
|
93
|
+
5. **GitHub Actions will**:
|
|
94
|
+
- Run tests on Ruby 3.1, 3.2, 3.3
|
|
95
|
+
- Build gem
|
|
96
|
+
- Create GitHub release
|
|
97
|
+
- Upload gem artifact
|
|
98
|
+
|
|
99
|
+
## Troubleshooting
|
|
100
|
+
|
|
101
|
+
### Ruby Version Issues
|
|
102
|
+
- Supported: Ruby 3.1, 3.2, 3.3
|
|
103
|
+
- Use `ruby scripts/debug-ruby-version.rb` to diagnose issues
|
|
104
|
+
- Ensure bundler compatibility
|
|
105
|
+
|
|
106
|
+
### Dependency Issues
|
|
107
|
+
- Run `bundle update` to update dependencies
|
|
108
|
+
- Check for security vulnerabilities: `bundle audit`
|
|
109
|
+
|
|
110
|
+
### Build Issues
|
|
111
|
+
- Ensure all files are included in gemspec
|
|
112
|
+
- Check for missing dependencies
|
|
113
|
+
- Verify executable permissions on scripts
|
|
114
|
+
|
|
115
|
+
## Best Practices
|
|
116
|
+
|
|
117
|
+
1. **Test locally** before pushing
|
|
118
|
+
2. **Use supported Ruby versions** (3.1-3.3)
|
|
119
|
+
3. **Update CHANGELOG.md** for each release
|
|
120
|
+
4. **Monitor CI pipeline** health regularly
|