ag-cortex 0.1.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/.agent/commands/test-browser.md +339 -0
- package/.agent/rules/00-constitution.md +46 -0
- package/.agent/rules/project-rules.md +49 -0
- package/.agent/skills/agent-browser/SKILL.md +223 -0
- package/.agent/skills/agent-native-architecture/SKILL.md +435 -0
- package/.agent/skills/agent-native-architecture/references/action-parity-discipline.md +409 -0
- package/.agent/skills/agent-native-architecture/references/agent-execution-patterns.md +467 -0
- package/.agent/skills/agent-native-architecture/references/agent-native-testing.md +582 -0
- package/.agent/skills/agent-native-architecture/references/architecture-patterns.md +478 -0
- package/.agent/skills/agent-native-architecture/references/dynamic-context-injection.md +338 -0
- package/.agent/skills/agent-native-architecture/references/files-universal-interface.md +301 -0
- package/.agent/skills/agent-native-architecture/references/from-primitives-to-domain-tools.md +359 -0
- package/.agent/skills/agent-native-architecture/references/mcp-tool-design.md +506 -0
- package/.agent/skills/agent-native-architecture/references/mobile-patterns.md +871 -0
- package/.agent/skills/agent-native-architecture/references/product-implications.md +443 -0
- package/.agent/skills/agent-native-architecture/references/refactoring-to-prompt-native.md +317 -0
- package/.agent/skills/agent-native-architecture/references/self-modification.md +269 -0
- package/.agent/skills/agent-native-architecture/references/shared-workspace-architecture.md +680 -0
- package/.agent/skills/agent-native-architecture/references/system-prompt-design.md +250 -0
- package/.agent/skills/agent-native-reviewer/SKILL.md +246 -0
- package/.agent/skills/andrew-kane-gem-writer/SKILL.md +184 -0
- package/.agent/skills/andrew-kane-gem-writer/references/database-adapters.md +231 -0
- package/.agent/skills/andrew-kane-gem-writer/references/module-organization.md +121 -0
- package/.agent/skills/andrew-kane-gem-writer/references/rails-integration.md +183 -0
- package/.agent/skills/andrew-kane-gem-writer/references/resources.md +119 -0
- package/.agent/skills/andrew-kane-gem-writer/references/testing-patterns.md +261 -0
- package/.agent/skills/ankane-readme-writer/SKILL.md +50 -0
- package/.agent/skills/architecture-strategist/SKILL.md +52 -0
- package/.agent/skills/best-practices-researcher/SKILL.md +100 -0
- package/.agent/skills/bug-reproduction-validator/SKILL.md +67 -0
- package/.agent/skills/code-simplicity-reviewer/SKILL.md +85 -0
- package/.agent/skills/coding-tutor/.claude-plugin/plugin.json +9 -0
- package/.agent/skills/coding-tutor/README.md +37 -0
- package/.agent/skills/coding-tutor/commands/quiz-me.md +1 -0
- package/.agent/skills/coding-tutor/commands/sync-tutorials.md +25 -0
- package/.agent/skills/coding-tutor/commands/teach-me.md +1 -0
- package/.agent/skills/coding-tutor/skills/coding-tutor/SKILL.md +214 -0
- package/.agent/skills/coding-tutor/skills/coding-tutor/scripts/create_tutorial.py +202 -0
- package/.agent/skills/coding-tutor/skills/coding-tutor/scripts/index_tutorials.py +203 -0
- package/.agent/skills/coding-tutor/skills/coding-tutor/scripts/quiz_priority.py +190 -0
- package/.agent/skills/coding-tutor/skills/coding-tutor/scripts/setup_tutorials.py +132 -0
- package/.agent/skills/compound-docs/SKILL.md +510 -0
- package/.agent/skills/compound-docs/assets/critical-pattern-template.md +34 -0
- package/.agent/skills/compound-docs/assets/resolution-template.md +93 -0
- package/.agent/skills/compound-docs/references/yaml-schema.md +65 -0
- package/.agent/skills/compound-docs/schema.yaml +176 -0
- package/.agent/skills/create-agent-skills/SKILL.md +299 -0
- package/.agent/skills/create-agent-skills/references/api-security.md +226 -0
- package/.agent/skills/create-agent-skills/references/be-clear-and-direct.md +531 -0
- package/.agent/skills/create-agent-skills/references/best-practices.md +404 -0
- package/.agent/skills/create-agent-skills/references/common-patterns.md +595 -0
- package/.agent/skills/create-agent-skills/references/core-principles.md +437 -0
- package/.agent/skills/create-agent-skills/references/executable-code.md +175 -0
- package/.agent/skills/create-agent-skills/references/iteration-and-testing.md +474 -0
- package/.agent/skills/create-agent-skills/references/official-spec.md +185 -0
- package/.agent/skills/create-agent-skills/references/recommended-structure.md +168 -0
- package/.agent/skills/create-agent-skills/references/skill-structure.md +372 -0
- package/.agent/skills/create-agent-skills/references/using-scripts.md +113 -0
- package/.agent/skills/create-agent-skills/references/using-templates.md +112 -0
- package/.agent/skills/create-agent-skills/references/workflows-and-validation.md +510 -0
- package/.agent/skills/create-agent-skills/templates/router-skill.md +73 -0
- package/.agent/skills/create-agent-skills/templates/simple-skill.md +33 -0
- package/.agent/skills/create-agent-skills/workflows/add-reference.md +96 -0
- package/.agent/skills/create-agent-skills/workflows/add-script.md +93 -0
- package/.agent/skills/create-agent-skills/workflows/add-template.md +74 -0
- package/.agent/skills/create-agent-skills/workflows/add-workflow.md +120 -0
- package/.agent/skills/create-agent-skills/workflows/audit-skill.md +138 -0
- package/.agent/skills/create-agent-skills/workflows/create-domain-expertise-skill.md +605 -0
- package/.agent/skills/create-agent-skills/workflows/create-new-skill.md +191 -0
- package/.agent/skills/create-agent-skills/workflows/get-guidance.md +121 -0
- package/.agent/skills/create-agent-skills/workflows/upgrade-to-router.md +161 -0
- package/.agent/skills/create-agent-skills/workflows/verify-skill.md +204 -0
- package/.agent/skills/data-integrity-guardian/SKILL.md +70 -0
- package/.agent/skills/data-migration-expert/SKILL.md +97 -0
- package/.agent/skills/deployment-verification-agent/SKILL.md +159 -0
- package/.agent/skills/design-implementation-reviewer/SKILL.md +85 -0
- package/.agent/skills/design-iterator/SKILL.md +197 -0
- package/.agent/skills/dhh-rails-reviewer/SKILL.md +45 -0
- package/.agent/skills/dhh-rails-style/SKILL.md +184 -0
- package/.agent/skills/dhh-rails-style/references/architecture.md +653 -0
- package/.agent/skills/dhh-rails-style/references/controllers.md +303 -0
- package/.agent/skills/dhh-rails-style/references/frontend.md +510 -0
- package/.agent/skills/dhh-rails-style/references/gems.md +266 -0
- package/.agent/skills/dhh-rails-style/references/models.md +359 -0
- package/.agent/skills/dhh-rails-style/references/testing.md +338 -0
- package/.agent/skills/dspy-ruby/SKILL.md +594 -0
- package/.agent/skills/dspy-ruby/assets/config-template.rb +359 -0
- package/.agent/skills/dspy-ruby/assets/module-template.rb +326 -0
- package/.agent/skills/dspy-ruby/assets/signature-template.rb +143 -0
- package/.agent/skills/dspy-ruby/references/core-concepts.md +265 -0
- package/.agent/skills/dspy-ruby/references/optimization.md +623 -0
- package/.agent/skills/dspy-ruby/references/providers.md +305 -0
- package/.agent/skills/every-style-editor/SKILL.md +134 -0
- package/.agent/skills/every-style-editor/references/EVERY_WRITE_STYLE.md +529 -0
- package/.agent/skills/figma-design-sync/SKILL.md +166 -0
- package/.agent/skills/file-todos/SKILL.md +251 -0
- package/.agent/skills/file-todos/assets/todo-template.md +155 -0
- package/.agent/skills/framework-docs-researcher/SKILL.md +83 -0
- package/.agent/skills/frontend-design/SKILL.md +42 -0
- package/.agent/skills/gemini-imagegen/SKILL.md +237 -0
- package/.agent/skills/gemini-imagegen/requirements.txt +2 -0
- package/.agent/skills/gemini-imagegen/scripts/compose_images.py +168 -0
- package/.agent/skills/gemini-imagegen/scripts/edit_image.py +157 -0
- package/.agent/skills/gemini-imagegen/scripts/gemini_images.py +265 -0
- package/.agent/skills/gemini-imagegen/scripts/generate_image.py +147 -0
- package/.agent/skills/gemini-imagegen/scripts/multi_turn_chat.py +215 -0
- package/.agent/skills/git-history-analyzer/SKILL.md +42 -0
- package/.agent/skills/git-worktree/SKILL.md +302 -0
- package/.agent/skills/git-worktree/scripts/worktree-manager.sh +345 -0
- package/.agent/skills/julik-frontend-races-reviewer/SKILL.md +222 -0
- package/.agent/skills/kieran-python-reviewer/SKILL.md +104 -0
- package/.agent/skills/kieran-rails-reviewer/SKILL.md +86 -0
- package/.agent/skills/kieran-typescript-reviewer/SKILL.md +95 -0
- package/.agent/skills/lint/SKILL.md +16 -0
- package/.agent/skills/pattern-recognition-specialist/SKILL.md +57 -0
- package/.agent/skills/performance-oracle/SKILL.md +110 -0
- package/.agent/skills/pr-comment-resolver/SKILL.md +69 -0
- package/.agent/skills/rclone/SKILL.md +150 -0
- package/.agent/skills/rclone/scripts/check_setup.sh +60 -0
- package/.agent/skills/repo-research-analyst/SKILL.md +113 -0
- package/.agent/skills/security-sentinel/SKILL.md +93 -0
- package/.agent/skills/skill-creator/SKILL.md +209 -0
- package/.agent/skills/skill-creator/scripts/init_skill.py +304 -0
- package/.agent/skills/skill-creator/scripts/package_skill.py +112 -0
- package/.agent/skills/skill-creator/scripts/quick_validate.py +72 -0
- package/.agent/skills/spec-flow-analyzer/SKILL.md +113 -0
- package/.agent/skills/test-agent/SKILL.md +4 -0
- package/.agent/workflows/agent-native-audit.md +277 -0
- package/.agent/workflows/ask-user-question.md +21 -0
- package/.agent/workflows/changelog.md +137 -0
- package/.agent/workflows/compound.md +202 -0
- package/.agent/workflows/create-agent-skill.md +8 -0
- package/.agent/workflows/deepen-plan-research.md +334 -0
- package/.agent/workflows/deepen-plan-synthesis.md +182 -0
- package/.agent/workflows/deepen-plan.md +79 -0
- package/.agent/workflows/feature-video.md +342 -0
- package/.agent/workflows/generate-command.md +162 -0
- package/.agent/workflows/heal-skill.md +142 -0
- package/.agent/workflows/lfg.md +20 -0
- package/.agent/workflows/plan-analysis.md +67 -0
- package/.agent/workflows/plan-next-steps.md +63 -0
- package/.agent/workflows/plan-review.md +33 -0
- package/.agent/workflows/plan-synthesis.md +106 -0
- package/.agent/workflows/plan.md +49 -0
- package/.agent/workflows/report-bug.md +150 -0
- package/.agent/workflows/reproduce-bug.md +99 -0
- package/.agent/workflows/resolve-parallel.md +34 -0
- package/.agent/workflows/resolve-pr-parallel.md +49 -0
- package/.agent/workflows/resolve-todo-parallel.md +35 -0
- package/.agent/workflows/review-analysis.md +145 -0
- package/.agent/workflows/review-synthesis.md +262 -0
- package/.agent/workflows/review.md +64 -0
- package/.agent/workflows/ship.md +90 -0
- package/.agent/workflows/test-command.md +3 -0
- package/.agent/workflows/triage.md +310 -0
- package/.agent/workflows/work.md +157 -0
- package/.agent/workflows/xcode-test.md +332 -0
- package/LICENSE +22 -0
- package/README.md +49 -0
- package/bin/ag-cortex.js +54 -0
- package/lib/core.js +165 -0
- package/package.json +31 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Sync Coding Tutor Tutorials
|
|
2
|
+
|
|
3
|
+
Commit and push your tutorials to the GitHub repository for backup and mobile reading.
|
|
4
|
+
|
|
5
|
+
## Instructions
|
|
6
|
+
|
|
7
|
+
1. **Go to the tutorials repo**: `cd ~/coding-tutor-tutorials`
|
|
8
|
+
|
|
9
|
+
2. **Check for changes**: Run `git status` to see what's new or modified
|
|
10
|
+
|
|
11
|
+
3. **If there are changes**:
|
|
12
|
+
- Stage all changes: `git add -A`
|
|
13
|
+
- Create a commit with a message summarizing what was added/updated (e.g., "Add tutorial on React hooks" or "Update quiz scores")
|
|
14
|
+
- Push to origin: `git push`
|
|
15
|
+
|
|
16
|
+
4. **If no GitHub remote exists**:
|
|
17
|
+
- Create the repo: `gh repo create coding-tutor-tutorials --private --source=. --push`
|
|
18
|
+
|
|
19
|
+
5. **Report results**: Tell the user what was synced or that everything is already up to date
|
|
20
|
+
|
|
21
|
+
## Notes
|
|
22
|
+
|
|
23
|
+
- The tutorials repo is at: `~/coding-tutor-tutorials/`
|
|
24
|
+
- Always use `--private` when creating the GitHub repo
|
|
25
|
+
- This is your personal learning journey - keep it backed up!
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Teach me something using the coding-tutor skill
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: coding-tutor
|
|
3
|
+
description: Personalized coding tutorials that build on your existing knowledge and use your actual codebase for examples. Creates a persistent learning trail that compounds over time using the power of AI, spaced repetition and quizes.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
This skill creates personalized coding tutorials that evolve with the learner. Each tutorial builds on previous ones, uses real examples from the current codebase, and maintains a persistent record of concepts mastered.
|
|
7
|
+
|
|
8
|
+
The user asks to learn something - either a specific concept or an open "teach me something new" request.
|
|
9
|
+
|
|
10
|
+
## Welcome New Learners
|
|
11
|
+
|
|
12
|
+
If `~/coding-tutor-tutorials/` does not exist, this is a new learner. Before running setup, introduce yourself:
|
|
13
|
+
|
|
14
|
+
> I'm your personal coding tutor. I create tutorials tailored to you - using real code from your projects, building on what you already know, and tracking your progress over time.
|
|
15
|
+
>
|
|
16
|
+
> All your tutorials live in one central library (`~/coding-tutor-tutorials/`) that works across all your projects. Use `/teach-me` to learn something new, `/quiz-me` to test your retention with spaced repetition.
|
|
17
|
+
|
|
18
|
+
Then proceed with setup and onboarding.
|
|
19
|
+
|
|
20
|
+
## Setup: Ensure Tutorials Repo Exists
|
|
21
|
+
|
|
22
|
+
**Before doing anything else**, run the setup script to ensure the central tutorials repository exists:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
python3 ${ANTIGRAVITY_PLUGIN_ROOT}/skills/coding-tutor/scripts/setup_tutorials.py
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
This creates `~/coding-tutor-tutorials/` if it doesn't exist. All tutorials and the learner profile are stored there, shared across all your projects.
|
|
29
|
+
|
|
30
|
+
## First Step: Know Your Learner
|
|
31
|
+
|
|
32
|
+
**Always start by reading `~/coding-tutor-tutorials/learner_profile.md` if it exists.** This profile contains crucial context about who you're teaching - their background, goals, and personality. Use it to calibrate everything: what analogies will land, how fast to move, what examples resonate.
|
|
33
|
+
|
|
34
|
+
If no tutorials exist in `~/coding-tutor-tutorials/` AND no learner profile exists at `~/coding-tutor-tutorials/learner_profile.md`, this is a brand new learner. Before teaching anything, you need to understand who you're teaching.
|
|
35
|
+
|
|
36
|
+
**Onboarding Interview:**
|
|
37
|
+
|
|
38
|
+
Ask these three questions, one at a time. Wait for each answer before asking the next.
|
|
39
|
+
|
|
40
|
+
1. **Prior exposure**: What's your background with programming? - Understand if they've built anything before, followed tutorials, or if this is completely new territory.
|
|
41
|
+
|
|
42
|
+
2. **Ambitious goal**: This is your private AI tutor whose goal is to make you a top 1% programmer. Where do you want this to take you? - Understand what success looks like for them: a million-dollar product, a job at a company they admire, or something else entirely.
|
|
43
|
+
|
|
44
|
+
3. **Who are you**: Tell me a bit about yourself - imagine we just met at a coworking space. - Get context that shapes how to teach them.
|
|
45
|
+
|
|
46
|
+
4. **Optional**: Based on the above answers, you may ask upto one optional 4th question if it will make your understanding of the learner richer.
|
|
47
|
+
|
|
48
|
+
After gathering responses, create `~/coding-tutor-tutorials/learner_profile.md` and put the interview Q&A there (along with your commentary):
|
|
49
|
+
|
|
50
|
+
```yaml
|
|
51
|
+
---
|
|
52
|
+
created: DD-MM-YYYY
|
|
53
|
+
last_updated: DD-MM-YYYY
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
**Q1. <insert question you asked>**
|
|
57
|
+
**Answer**. <insert user's answer>
|
|
58
|
+
**your internal commentary**
|
|
59
|
+
|
|
60
|
+
**Q2. <insert question you asked>**
|
|
61
|
+
**Answer**. <insert user's answer>
|
|
62
|
+
**your internal commentary**
|
|
63
|
+
|
|
64
|
+
**Q3. <insert question you asked>**
|
|
65
|
+
**Answer**. <insert user's answer>
|
|
66
|
+
**your internal commentary**
|
|
67
|
+
|
|
68
|
+
**Q4. <optional>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Teaching Philosophy
|
|
72
|
+
|
|
73
|
+
Our general goal is to take the user from newbie to a senior engineer in record time. One at par with engineers at companies like 37 Signals or Vercel.
|
|
74
|
+
|
|
75
|
+
Before creating a tutorial, make a plan by following these steps:
|
|
76
|
+
|
|
77
|
+
- **Load learner context**: Read `~/coding-tutor-tutorials/learner_profile.md` to understand who you're teaching - their background, goals, and personality.
|
|
78
|
+
- **Survey existing knowledge**: Run `python3 ${ANTIGRAVITY_PLUGIN_ROOT}/skills/coding-tutor/scripts/index_tutorials.py` to understand what concepts have been covered, at what depth, and how well they landed (understanding scores). Optionally, dive into particular tutorials in `~/coding-tutor-tutorials/` to read them.
|
|
79
|
+
- **Identify the gap**: What's the next concept that would be most valuable? Consider both what they've asked for AND what naturally follows from their current knowledge. Think of a curriculum that would get them from their current point to Senior Engineer - what should be the next 3 topics they need to learn to advance their programming knowledge in this direction?
|
|
80
|
+
- **Find the anchor**: Locate real examples in the codebase that demonstrate this concept. Learning from abstract examples is forgettable; learning from YOUR code is sticky.
|
|
81
|
+
- **(Optional) Use ask-user-question tool**: Ask clarifying questions to the learner to understand their intent, goals or expectations if it'll help you make a better plan.
|
|
82
|
+
|
|
83
|
+
Then show this curriculum plan of **next 3 TUTORIALS** to the user and proceed to the tutorial creation step only if the user approves. If the user rejects, create a new plan using steps mentioned above.
|
|
84
|
+
|
|
85
|
+
## Tutorial Creation
|
|
86
|
+
|
|
87
|
+
Each tutorial is a markdown file in `~/coding-tutor-tutorials/` with this structure:
|
|
88
|
+
```yaml
|
|
89
|
+
---
|
|
90
|
+
concepts: [primary_concept, related_concept_1, related_concept_2]
|
|
91
|
+
source_repo: my-app # Auto-detected: which repo this tutorial's examples come from
|
|
92
|
+
description: One-paragraph summary of what this tutorial covers
|
|
93
|
+
understanding_score: null # null until quizzed, then 1-10 based on quiz performance
|
|
94
|
+
last_quizzed: null # null until first quiz, then DD-MM-YYYY
|
|
95
|
+
prerequisites: [~/coding-tutor-tutorials/tutorial_1_name.md, ~/coding-tutor-tutorials/tutorial_2_name.md, (upto 3 other existing tutorials)]
|
|
96
|
+
created: DD-MM-YYYY
|
|
97
|
+
last_updated: DD-MM-YYYY
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
Full contents of tutorial go here
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Q&A
|
|
105
|
+
|
|
106
|
+
Cross-questions during learning go here.
|
|
107
|
+
|
|
108
|
+
## Quiz History
|
|
109
|
+
|
|
110
|
+
Quiz sessions recorded here.
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Run `scripts/create_tutorial.py` like this to create a new tutorial with template:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
python3 ${ANTIGRAVITY_PLUGIN_ROOT}/skills/coding-tutor/scripts/create_tutorial.py "Topic Name" --concepts "Concept1,Concept2"
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
This creates an empty template of the tutorial. Then you should edit the newly created file to write in the actual tutorial.
|
|
120
|
+
Qualities of a great tutorial should:
|
|
121
|
+
|
|
122
|
+
- **Start with the "why"**: Not "here's how callbacks work" but "here's the problem in your code that callbacks solve"
|
|
123
|
+
- **Use their code**: Every concept demonstrated with examples pulled from the actual codebase. Reference specific files and line numbers.
|
|
124
|
+
- **Build mental models**: Diagrams, analogies, the underlying "shape" of the concept - not just syntax, ELI5
|
|
125
|
+
- **Predict confusion**: Address the questions they're likely to ask before they ask them, don't skim over things, don't write in a notes style
|
|
126
|
+
- **End with a challenge**: A small exercise they could try in this codebase to cement understanding
|
|
127
|
+
|
|
128
|
+
### Tutorial Writing Style
|
|
129
|
+
|
|
130
|
+
Write personal tutorials like the best programming educators: Julia Evans, Dan Abramov. Not like study notes or documentation. There's a difference between a well-structured tutorial and one that truly teaches.
|
|
131
|
+
|
|
132
|
+
- Show the struggle - "Here's what you might try... here's why it doesn't work... here's the insight that unlocks it."
|
|
133
|
+
- Fewer concepts, more depth - A tutorial that teaches 3 things deeply beats one that mentions 10 things.
|
|
134
|
+
- Tell stories - a great tutorial is one coherent story, dives deep into a single concept, using storytelling techniques that engage readers
|
|
135
|
+
|
|
136
|
+
We should make the learner feel like Julia Evans or Dan Abramov is their private tutor.
|
|
137
|
+
|
|
138
|
+
Note: If you're not sure about a fact or capability or new features/APIs, do web research, look at documentation to make sure you're teaching accurate up-to-date things. NEVER commit the sin of teaching something incorrect.
|
|
139
|
+
|
|
140
|
+
## The Living Tutorial
|
|
141
|
+
|
|
142
|
+
Tutorials aren't static documents - they evolve:
|
|
143
|
+
|
|
144
|
+
- **Q&A is mandatory**: When the learner asks ANY clarifying question about a tutorial, you MUST append it to the tutorial's `## Q&A` section. This is not optional - these exchanges are part of their personalized learning record and improve future teaching.
|
|
145
|
+
- If the learner says they can't follow the tutorial or need you to take a different approach, update the tutorial like they ask
|
|
146
|
+
- Update `last_updated` timestamp
|
|
147
|
+
- If a question reveals a gap in prerequisites, note it for future tutorial planning
|
|
148
|
+
|
|
149
|
+
Note: `understanding_score` is only updated through Quiz Mode, not during teaching.
|
|
150
|
+
|
|
151
|
+
## What Makes Great Teaching
|
|
152
|
+
**DO**: Meet them where they are. Use their vocabulary. Reference their past struggles. Make connections to concepts they already own. Be encouraging but honest about complexity.
|
|
153
|
+
|
|
154
|
+
**DON'T**: Assume knowledge not demonstrated in previous tutorials. Use generic blog-post examples when codebase examples exist. Overwhelm with every edge case upfront. Be condescending about gaps.
|
|
155
|
+
|
|
156
|
+
**CALIBRATE**: A learner with 3 tutorials is different from one with 30. Early tutorials need more scaffolding and encouragement. Later tutorials can move faster and reference the shared history you've built.
|
|
157
|
+
|
|
158
|
+
Remember: The goal isn't to teach programming in the abstract. It's to teach THIS person, using THEIR code, building on THEIR specific journey. Every tutorial should feel like it was written specifically for them - because it was.
|
|
159
|
+
|
|
160
|
+
## Quiz Mode
|
|
161
|
+
|
|
162
|
+
Tutorials teach. Quizzes verify. The score should reflect what the learner actually retained, not what was presented to them.
|
|
163
|
+
|
|
164
|
+
**Triggers:**
|
|
165
|
+
- Explicit: "Quiz me on React hooks" → quiz that specific concept
|
|
166
|
+
- Open: "Quiz me on something" → run `python3 ${ANTIGRAVITY_PLUGIN_ROOT}/skills/coding-tutor/scripts/quiz_priority.py` to get a prioritized list based on spaced repetition, then choose what to quiz
|
|
167
|
+
|
|
168
|
+
**Spaced Repetition:**
|
|
169
|
+
|
|
170
|
+
When the user requests an open quiz, the priority script uses spaced repetition intervals to surface:
|
|
171
|
+
- Never-quizzed tutorials (need baseline assessment)
|
|
172
|
+
- Low-scored concepts that are overdue for review
|
|
173
|
+
- High-scored concepts whose review interval has elapsed
|
|
174
|
+
|
|
175
|
+
The script uses Fibonacci-ish intervals: score 1 = review in 2 days, score 5 = 13 days, score 8 = 55 days, score 10 = 144 days. This means weak concepts get drilled frequently while mastered ones fade into long-term review.
|
|
176
|
+
|
|
177
|
+
The script gives you an ordered list with `understanding_score` and `last_quizzed` for each tutorial. Use this to make an informed choice about what to quiz, and explain to the learner why you picked that concept ("You learned callbacks 5 days ago but scored 4/10 - let's see if it's sticking better now").
|
|
178
|
+
|
|
179
|
+
**Philosophy:**
|
|
180
|
+
|
|
181
|
+
A quiz isn't an exam - it's a conversation that reveals understanding. Ask questions that expose mental models, not just syntax recall. The goal is to find the edges of their knowledge: where does solid understanding fade into uncertainty?
|
|
182
|
+
|
|
183
|
+
**Ask only 1 question at a time.** Wait for the learner's answer before asking the next question.
|
|
184
|
+
|
|
185
|
+
Mix question types based on what the concept demands:
|
|
186
|
+
- Conceptual ("when would you use X over Y?")
|
|
187
|
+
- Code reading ("what does this code in your app do?")
|
|
188
|
+
- Code writing ("write a scope that does X")
|
|
189
|
+
- Debugging ("what's wrong here?")
|
|
190
|
+
|
|
191
|
+
Use their codebase for examples whenever possible. "What does line 47 of `app/models/user.rb` do?" is more valuable than abstract snippets.
|
|
192
|
+
|
|
193
|
+
**Scoring:**
|
|
194
|
+
|
|
195
|
+
After the quiz, update `understanding_score` honestly:
|
|
196
|
+
- **1-3**: Can't recall the concept, needs re-teaching
|
|
197
|
+
- **4-5**: Vague memory, partial answers
|
|
198
|
+
- **6-7**: Solid understanding, minor gaps
|
|
199
|
+
- **8-9**: Strong grasp, handles edge cases
|
|
200
|
+
- **10**: Could teach this to someone else
|
|
201
|
+
|
|
202
|
+
Also update `last_quizzed: DD-MM-YYYY` in the frontmatter.
|
|
203
|
+
|
|
204
|
+
**Recording:**
|
|
205
|
+
|
|
206
|
+
Append to the tutorial's `## Quiz History` section:
|
|
207
|
+
```
|
|
208
|
+
### Quiz - DD-MM-YYYY
|
|
209
|
+
**Q:** [Question asked]
|
|
210
|
+
**A:** [Brief summary of their response and what it revealed about understanding]
|
|
211
|
+
Score updated: 5 → 7
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
This history helps future quizzes avoid repetition and track progression over time.
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Create a new coding tutorial template with proper frontmatter.
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
python create_tutorial.py "React Hooks"
|
|
7
|
+
python create_tutorial.py "State Management" --concepts "Redux,Context,State"
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import argparse
|
|
11
|
+
import subprocess
|
|
12
|
+
import sys
|
|
13
|
+
from datetime import datetime
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def get_tutorials_repo_path():
|
|
18
|
+
"""Get the path for the tutorials repo (~/coding-tutor-tutorials/)."""
|
|
19
|
+
return Path.home() / "coding-tutor-tutorials"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def get_repo_name():
|
|
23
|
+
"""Get the current git repository name."""
|
|
24
|
+
try:
|
|
25
|
+
result = subprocess.run(
|
|
26
|
+
["git", "rev-parse", "--show-toplevel"], capture_output=True, text=True
|
|
27
|
+
)
|
|
28
|
+
if result.returncode == 0:
|
|
29
|
+
return result.stdout.strip().split("/")[-1]
|
|
30
|
+
except Exception:
|
|
31
|
+
pass
|
|
32
|
+
return "unknown"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def check_uncommitted_changes():
|
|
36
|
+
"""Check for uncommitted changes and print a warning if any exist."""
|
|
37
|
+
try:
|
|
38
|
+
result = subprocess.run(
|
|
39
|
+
["git", "status", "--porcelain"], capture_output=True, text=True
|
|
40
|
+
)
|
|
41
|
+
if result.returncode == 0 and result.stdout.strip():
|
|
42
|
+
lines = result.stdout.strip().split("\n")
|
|
43
|
+
print(
|
|
44
|
+
f"WARNING: You have {len(lines)} uncommitted change(s). Commit and push before proceeding."
|
|
45
|
+
)
|
|
46
|
+
print(result.stdout)
|
|
47
|
+
except Exception:
|
|
48
|
+
pass
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def slugify(text):
|
|
52
|
+
"""Convert text to URL-friendly slug."""
|
|
53
|
+
return text.lower().replace(" ", "-").replace("_", "-")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def create_tutorial(topic, concepts=None, output_dir=None):
|
|
57
|
+
"""
|
|
58
|
+
Create a new tutorial template file.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
topic: Main topic of the tutorial
|
|
62
|
+
concepts: Comma-separated concepts (defaults to topic)
|
|
63
|
+
output_dir: Directory to save tutorial (defaults to ~/coding-tutor-tutorials/)
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
Path to created tutorial file
|
|
67
|
+
"""
|
|
68
|
+
# Default output directory is the central tutorials repo (sibling to git root)
|
|
69
|
+
if output_dir is None:
|
|
70
|
+
output_dir = get_tutorials_repo_path()
|
|
71
|
+
else:
|
|
72
|
+
output_dir = Path(output_dir)
|
|
73
|
+
|
|
74
|
+
# Create output directory if it doesn't exist
|
|
75
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
76
|
+
|
|
77
|
+
# Generate filename: YYYY-MM-DD-topic-slug.md
|
|
78
|
+
date_str_filename = datetime.now().strftime("%Y-%m-%d")
|
|
79
|
+
date_str_frontmatter = datetime.now().strftime("%d-%m-%Y")
|
|
80
|
+
slug = slugify(topic)
|
|
81
|
+
filename = f"{date_str_filename}-{slug}.md"
|
|
82
|
+
filepath = output_dir / filename
|
|
83
|
+
|
|
84
|
+
# Default concepts to topic if not provided
|
|
85
|
+
if concepts is None:
|
|
86
|
+
concepts = topic
|
|
87
|
+
|
|
88
|
+
# Get current repo name
|
|
89
|
+
repo_name = get_repo_name()
|
|
90
|
+
|
|
91
|
+
# Create tutorial template with YAML frontmatter and embedded guidance
|
|
92
|
+
template = f"""---
|
|
93
|
+
concepts: {concepts}
|
|
94
|
+
source_repo: {repo_name}
|
|
95
|
+
description: [TODO: Fill after completing tutorial - one paragraph summary]
|
|
96
|
+
understanding_score: null
|
|
97
|
+
last_quizzed: null
|
|
98
|
+
prerequisites: []
|
|
99
|
+
created: {date_str_frontmatter}
|
|
100
|
+
last_updated: {date_str_frontmatter}
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
# {topic}
|
|
104
|
+
|
|
105
|
+
[TODO: Opening paragraph - Start with the WHY. What problem does this concept solve? Why should the learner care about this? Connect it to their goal of becoming a senior engineer.
|
|
106
|
+
|
|
107
|
+
NOTE: Update the frontmatter 'prerequisites' field with up to 3 relevant past tutorials if this builds on previous concepts (e.g., [coding-tutor-tutorials/2025-11-20-basics.md]). Leave as empty array [] if this is foundational.]
|
|
108
|
+
|
|
109
|
+
## The Problem
|
|
110
|
+
|
|
111
|
+
[TODO: Describe a real scenario from this codebase where this concept matters. Make it concrete - not "X is useful for Y" but "look at this code in src/components/User.tsx where we need to do Y - that's the problem this concept solves"]
|
|
112
|
+
|
|
113
|
+
## Key Concepts
|
|
114
|
+
|
|
115
|
+
[TODO: Build mental models, not just definitions. Use:
|
|
116
|
+
- Analogies that connect to things they already understand
|
|
117
|
+
- ASCII diagrams if helpful for visualizing relationships
|
|
118
|
+
- ELI5 explanations that get to the essence
|
|
119
|
+
- Break complex concepts into digestible pieces
|
|
120
|
+
- Predict and address likely points of confusion
|
|
121
|
+
|
|
122
|
+
Remember: Teach the "shape" of the concept, not just the syntax.]
|
|
123
|
+
|
|
124
|
+
## Examples from Codebase
|
|
125
|
+
|
|
126
|
+
[TODO: Include 2-4 real examples from this repository. For each example:
|
|
127
|
+
|
|
128
|
+
### Example 1: [Brief description]
|
|
129
|
+
**Location:** src/components/User.tsx:25-30
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
# Paste the relevant code snippet here
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**What this demonstrates:** [Explain what's happening and why this is a good example of the concept]
|
|
136
|
+
|
|
137
|
+
Repeat for each example. Use actual file paths and line numbers. The more specific, the stickier the learning.]
|
|
138
|
+
|
|
139
|
+
## Try It Yourself
|
|
140
|
+
|
|
141
|
+
[TODO: Suggest a small exercise the learner could try in this codebase to practice the concept. Make it:
|
|
142
|
+
- Achievable in 10-15 minutes
|
|
143
|
+
- Directly related to the codebase they're working in
|
|
144
|
+
- Something that would genuinely improve their understanding
|
|
145
|
+
|
|
146
|
+
Delete this section if no practical exercise makes sense for this concept.]
|
|
147
|
+
|
|
148
|
+
## Summary
|
|
149
|
+
|
|
150
|
+
[TODO: Key takeaways - what should stick in their mind after this tutorial? 3-5 bullet points capturing:
|
|
151
|
+
- The core concept in one sentence
|
|
152
|
+
- When to use it
|
|
153
|
+
- Common pitfalls to avoid
|
|
154
|
+
- How it connects to their broader learning journey]
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Q&A
|
|
159
|
+
|
|
160
|
+
[Questions and answers will be added here as the learner asks them during the tutorial]
|
|
161
|
+
|
|
162
|
+
## Quiz History
|
|
163
|
+
|
|
164
|
+
[Quiz sessions will be recorded here after the learner is quizzed on this topic]
|
|
165
|
+
"""
|
|
166
|
+
|
|
167
|
+
# Write template to file
|
|
168
|
+
filepath.write_text(template)
|
|
169
|
+
|
|
170
|
+
return filepath
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def main():
|
|
174
|
+
parser = argparse.ArgumentParser(
|
|
175
|
+
description="Create a new coding tutorial template"
|
|
176
|
+
)
|
|
177
|
+
parser.add_argument("topic", help="Topic of the tutorial (e.g., 'React Hooks')")
|
|
178
|
+
parser.add_argument(
|
|
179
|
+
"--concepts", help="Comma-separated concepts (defaults to topic)", default=None
|
|
180
|
+
)
|
|
181
|
+
parser.add_argument(
|
|
182
|
+
"--output-dir",
|
|
183
|
+
help="Output directory for tutorial (defaults to ~/coding-tutor-tutorials/)",
|
|
184
|
+
default=None,
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
args = parser.parse_args()
|
|
188
|
+
|
|
189
|
+
check_uncommitted_changes()
|
|
190
|
+
|
|
191
|
+
try:
|
|
192
|
+
filepath = create_tutorial(args.topic, args.concepts, args.output_dir)
|
|
193
|
+
print(f"Created tutorial template: {filepath}")
|
|
194
|
+
print("Edit the file to add content and update the frontmatter")
|
|
195
|
+
return 0
|
|
196
|
+
except Exception as e:
|
|
197
|
+
print(f"Error creating tutorial: {e}", file=sys.stderr)
|
|
198
|
+
return 1
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
if __name__ == "__main__":
|
|
202
|
+
sys.exit(main())
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Index all tutorials by extracting their YAML frontmatter.
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
python index_tutorials.py
|
|
7
|
+
python index_tutorials.py --tutorials-dir /path/to/tutorials
|
|
8
|
+
python index_tutorials.py --format json
|
|
9
|
+
python index_tutorials.py --format human
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import argparse
|
|
13
|
+
import json
|
|
14
|
+
import re
|
|
15
|
+
import sys
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_tutorials_directory():
|
|
20
|
+
"""Get the tutorials directory (~/coding-tutor-tutorials/)."""
|
|
21
|
+
return Path.home() / "coding-tutor-tutorials"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def extract_frontmatter(filepath):
|
|
25
|
+
"""
|
|
26
|
+
Extract YAML frontmatter from a markdown file.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
filepath: Path to markdown file
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
dict with frontmatter fields or None if no frontmatter found
|
|
33
|
+
"""
|
|
34
|
+
content = filepath.read_text()
|
|
35
|
+
|
|
36
|
+
# Match YAML frontmatter between --- delimiters
|
|
37
|
+
match = re.match(r"^---\s*\n(.*?)\n---\s*\n", content, re.DOTALL)
|
|
38
|
+
if not match:
|
|
39
|
+
return None
|
|
40
|
+
|
|
41
|
+
frontmatter_text = match.group(1)
|
|
42
|
+
frontmatter = {}
|
|
43
|
+
|
|
44
|
+
# Parse simple YAML key: value pairs
|
|
45
|
+
for line in frontmatter_text.split("\n"):
|
|
46
|
+
line = line.strip()
|
|
47
|
+
if ":" in line:
|
|
48
|
+
key, value = line.split(":", 1)
|
|
49
|
+
key = key.strip()
|
|
50
|
+
value = value.strip()
|
|
51
|
+
|
|
52
|
+
# Convert understanding_score to int, or None if "null"
|
|
53
|
+
if key == "understanding_score":
|
|
54
|
+
if value == "null" or not value:
|
|
55
|
+
value = None
|
|
56
|
+
else:
|
|
57
|
+
try:
|
|
58
|
+
value = int(value)
|
|
59
|
+
except ValueError:
|
|
60
|
+
pass
|
|
61
|
+
|
|
62
|
+
# Handle null values for last_quizzed
|
|
63
|
+
if key == "last_quizzed" and value == "null":
|
|
64
|
+
value = None
|
|
65
|
+
|
|
66
|
+
# Handle list/array values for prerequisites
|
|
67
|
+
if key == "prerequisites" and value.startswith("["):
|
|
68
|
+
# Simple list parsing - extract items between brackets
|
|
69
|
+
value = value.strip("[]").strip()
|
|
70
|
+
if value:
|
|
71
|
+
frontmatter[key] = [item.strip() for item in value.split(",")]
|
|
72
|
+
else:
|
|
73
|
+
frontmatter[key] = []
|
|
74
|
+
else:
|
|
75
|
+
frontmatter[key] = value
|
|
76
|
+
|
|
77
|
+
return frontmatter
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def index_tutorials(tutorials_dir=None):
|
|
81
|
+
"""
|
|
82
|
+
Index all tutorials from the tutorials directory.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
tutorials_dir: Path to tutorials directory (defaults to ~/coding-tutor-tutorials/)
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
list of dicts with tutorial metadata
|
|
89
|
+
"""
|
|
90
|
+
tutorials = []
|
|
91
|
+
|
|
92
|
+
if tutorials_dir is not None:
|
|
93
|
+
tutorials_path = Path(tutorials_dir)
|
|
94
|
+
else:
|
|
95
|
+
tutorials_path = get_tutorials_directory()
|
|
96
|
+
|
|
97
|
+
if not tutorials_path.exists():
|
|
98
|
+
return tutorials
|
|
99
|
+
|
|
100
|
+
# Find all .md files in tutorials directory
|
|
101
|
+
for filepath in sorted(tutorials_path.glob("*.md")):
|
|
102
|
+
frontmatter = extract_frontmatter(filepath)
|
|
103
|
+
|
|
104
|
+
if frontmatter:
|
|
105
|
+
tutorials.append(
|
|
106
|
+
{
|
|
107
|
+
"filename": filepath.name,
|
|
108
|
+
"filepath": str(filepath),
|
|
109
|
+
"concepts": frontmatter.get("concepts", ""),
|
|
110
|
+
"source_repo": frontmatter.get("source_repo", ""),
|
|
111
|
+
"description": frontmatter.get("description", ""),
|
|
112
|
+
"understanding_score": frontmatter.get("understanding_score"),
|
|
113
|
+
"last_quizzed": frontmatter.get("last_quizzed"),
|
|
114
|
+
"prerequisites": frontmatter.get("prerequisites", []),
|
|
115
|
+
"created": frontmatter.get("created", ""),
|
|
116
|
+
"last_updated": frontmatter.get("last_updated", ""),
|
|
117
|
+
}
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
return tutorials
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def format_human_readable(tutorials):
|
|
124
|
+
"""Format tutorials as human-readable text."""
|
|
125
|
+
if not tutorials:
|
|
126
|
+
return "No tutorials found. Check if ~/coding-tutor-tutorials/learner_profile.md exists - if not, onboard the learner first. If it exists, create their first tutorial using their profile context."
|
|
127
|
+
|
|
128
|
+
output = []
|
|
129
|
+
output.append(f"Found {len(tutorials)} tutorial(s):\n")
|
|
130
|
+
|
|
131
|
+
for tutorial in tutorials:
|
|
132
|
+
output.append(f" {tutorial['filename']}")
|
|
133
|
+
output.append(f" Concepts: {tutorial['concepts']}")
|
|
134
|
+
if tutorial.get("source_repo"):
|
|
135
|
+
output.append(f" Source repo: {tutorial['source_repo']}")
|
|
136
|
+
if tutorial["description"]:
|
|
137
|
+
output.append(f" Description: {tutorial['description']}")
|
|
138
|
+
score = tutorial["understanding_score"]
|
|
139
|
+
if score is None:
|
|
140
|
+
output.append(" Understanding: not quizzed yet")
|
|
141
|
+
else:
|
|
142
|
+
output.append(f" Understanding: {score}/10")
|
|
143
|
+
if tutorial.get("last_quizzed"):
|
|
144
|
+
output.append(f" Last quizzed: {tutorial['last_quizzed']}")
|
|
145
|
+
if tutorial.get("created"):
|
|
146
|
+
output.append(f" Created: {tutorial['created']}")
|
|
147
|
+
if tutorial.get("prerequisites") and tutorial["prerequisites"]:
|
|
148
|
+
prereqs = (
|
|
149
|
+
", ".join(tutorial["prerequisites"])
|
|
150
|
+
if isinstance(tutorial["prerequisites"], list)
|
|
151
|
+
else tutorial["prerequisites"]
|
|
152
|
+
)
|
|
153
|
+
output.append(f" Prerequisites: {prereqs}")
|
|
154
|
+
output.append("")
|
|
155
|
+
|
|
156
|
+
return "\n".join(output)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def main():
|
|
160
|
+
parser = argparse.ArgumentParser(
|
|
161
|
+
description="Index all tutorials by extracting frontmatter"
|
|
162
|
+
)
|
|
163
|
+
parser.add_argument(
|
|
164
|
+
"--tutorials-dir",
|
|
165
|
+
help="Path to tutorials directory (defaults to ~/coding-tutor-tutorials/)",
|
|
166
|
+
default=None,
|
|
167
|
+
)
|
|
168
|
+
parser.add_argument(
|
|
169
|
+
"--format",
|
|
170
|
+
choices=["json", "human"],
|
|
171
|
+
default="json",
|
|
172
|
+
help="Output format (default: json)",
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
args = parser.parse_args()
|
|
176
|
+
|
|
177
|
+
try:
|
|
178
|
+
tutorials = index_tutorials(args.tutorials_dir)
|
|
179
|
+
|
|
180
|
+
if args.format == "json":
|
|
181
|
+
if not tutorials:
|
|
182
|
+
print(
|
|
183
|
+
json.dumps(
|
|
184
|
+
{
|
|
185
|
+
"tutorials": [],
|
|
186
|
+
"message": "No tutorials found. Check if ~/coding-tutor-tutorials/learner_profile.md exists - if not, onboard the learner first. If it exists, create their first tutorial using their profile context.",
|
|
187
|
+
},
|
|
188
|
+
indent=2,
|
|
189
|
+
)
|
|
190
|
+
)
|
|
191
|
+
else:
|
|
192
|
+
print(json.dumps({"tutorials": tutorials}, indent=2))
|
|
193
|
+
else:
|
|
194
|
+
print(format_human_readable(tutorials))
|
|
195
|
+
|
|
196
|
+
return 0
|
|
197
|
+
except Exception as e:
|
|
198
|
+
print(f"Error indexing tutorials: {e}", file=sys.stderr)
|
|
199
|
+
return 1
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
if __name__ == "__main__":
|
|
203
|
+
sys.exit(main())
|