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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +59 -0
  3. data/.rubocop.yml +79 -0
  4. data/.ruby-version +1 -0
  5. data/.tool-versions +1 -0
  6. data/CHANGELOG.md +173 -0
  7. data/Gemfile +21 -0
  8. data/Makefile +20 -0
  9. data/README.md +160 -32
  10. data/Rakefile +174 -0
  11. data/anki_generator.gemspec +42 -0
  12. data/bin/anki_generator +3 -2
  13. data/docs/CI_SETUP.md +120 -0
  14. data/docs/architecture/current-v1.3.0.architecture.json +315 -0
  15. data/docs/architecture/current-v1.3.0.html +14990 -0
  16. data/docs/architecture/current-v1.3.0.visual-check.json +548 -0
  17. data/docs/architecture/phase3-proposed.architecture.json +310 -0
  18. data/docs/architecture/phase3-proposed.html +15001 -0
  19. data/docs/architecture/phase3-proposed.visual-check.json +548 -0
  20. data/docs/phase3-draft.md +86 -0
  21. data/examples/example_class.rb +13 -0
  22. data/examples/manual_cards.yaml +5 -0
  23. data/examples/study_prompt.txt +3 -0
  24. data/input/input.yaml.example +3 -0
  25. data/lib/anki_generator/anki_connect_client.rb +85 -0
  26. data/lib/anki_generator/apkg_schema.rb +257 -0
  27. data/lib/anki_generator/apkg_writer.rb +149 -0
  28. data/lib/anki_generator/card.rb +83 -0
  29. data/lib/anki_generator/cli.rb +183 -0
  30. data/lib/anki_generator/client_factory.rb +20 -0
  31. data/lib/anki_generator/commands/create_ai_template.rb +39 -0
  32. data/lib/anki_generator/commands/generate_deck.rb +43 -0
  33. data/lib/anki_generator/commands/generate_yaml.rb +63 -0
  34. data/lib/anki_generator/commands/import.rb +75 -0
  35. data/lib/anki_generator/commands/prompt_based.rb +74 -0
  36. data/lib/anki_generator/commands/prompt_to_deck.rb +84 -0
  37. data/lib/anki_generator/commands/push.rb +32 -0
  38. data/lib/anki_generator/commands/serve.rb +99 -0
  39. data/lib/anki_generator/commands/test_api.rb +33 -0
  40. data/lib/anki_generator/deck_builder.rb +184 -0
  41. data/lib/anki_generator/errors.rb +22 -0
  42. data/lib/anki_generator/file_processor.rb +154 -0
  43. data/lib/anki_generator/importers/csv.rb +52 -0
  44. data/lib/anki_generator/importers/markdown.rb +72 -0
  45. data/lib/anki_generator/prompt_builder.rb +77 -0
  46. data/lib/anki_generator/server.rb +98 -0
  47. data/lib/anki_generator/ui.rb +28 -0
  48. data/lib/anki_generator/version.rb +5 -0
  49. data/lib/anki_generator.rb +18 -114
  50. data/prompt.txt +5 -0
  51. metadata +100 -43
  52. data/lib/anki_cli.rb +0 -259
  53. data/lib/file_processor.rb +0 -156
  54. 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 using OpenRouter and file attachment support.
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 OpenRouter API with multiple AI models (GPT, Claude, Llama, etc.)
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, or file attachments
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 model selection
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 OpenRouter API key:
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 OpenRouter API key
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-3-sonnet
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` - OpenRouter 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` - OpenRouter 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 OpenRouter API connection:
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
- - `OPENROUTER_API_KEY`: Your OpenRouter API key
251
- - `OPENROUTER_DEFAULT_MODEL`: Default model to use (optional)
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 OpenRouter:
354
+ The tool supports all models available through the configured provider:
256
355
 
257
- - OpenAI: `openai/gpt-4`, `openai/gpt-3.5-turbo`
258
- - Anthropic: `anthropic/claude-3-sonnet`, `anthropic/claude-3-haiku`
259
- - Meta: `meta-llama/llama-3.1-8b-instruct`
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
- │ ├── anki_generator.rb # Main generator class
389
- │ ├── openrouter_client.rb # OpenRouter API client
390
- │ ├── anki_cli.rb # CLI interface
391
- │ └── file_processor.rb # File attachment processing
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 OpenRouter to provide access to multiple AI models. You can:
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. Submit a pull request
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
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
- require_relative '../lib/anki_cli'
4
+ require_relative '../lib/anki_generator/cli'
4
5
 
5
- AnkiCLI.start(ARGV)
6
+ AnkiGenerator::CLI.start(ARGV)
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