@kurokeita/add-skill 1.20.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -8
- package/dist/bin/cli.js +576 -792
- package/dist/skills/git-commit/SKILL.md +101 -37
- package/dist/skills/handoff/SKILL.md +16 -0
- package/dist/skills/init-agents-md/SKILL.md +51 -0
- package/dist/skills/init-agents-md/references/agents_template.md +42 -0
- package/dist/skills/init-agents-md/references/patterns_template.md +19 -0
- package/dist/skills/koreader-plugin-development/SKILL.md +141 -0
- package/dist/skills/koreader-plugin-development/examples/2-example-patch.lua +17 -0
- package/dist/skills/koreader-plugin-development/examples/_meta.lua +6 -0
- package/dist/skills/koreader-plugin-development/examples/helloworld.lua +36 -0
- package/dist/skills/koreader-plugin-development/references/debugging.md +92 -0
- package/dist/skills/koreader-plugin-development/references/events-and-widgets.md +102 -0
- package/dist/skills/koreader-plugin-development/references/patches.md +118 -0
- package/dist/skills/koreader-plugin-development/references/plugin-anatomy.md +97 -0
- package/dist/skills/statusline-setup/SKILL.md +211 -66
- package/dist/skills/universalize-agents/SKILL.md +245 -0
- package/dist/skills/universalize-agents/reference/hook-templates/README.md +30 -0
- package/dist/skills/universalize-agents/reference/hook-templates/claude-code.json +15 -0
- package/dist/skills/universalize-agents/reference/hook-templates/codex.toml +10 -0
- package/dist/skills/universalize-agents/reference/hook-templates/copilot.json +9 -0
- package/dist/skills/universalize-agents/reference/hook-templates/gemini.json +11 -0
- package/dist/skills/universalize-agents/reference/mapping.md +100 -0
- package/dist/skills/universalize-agents/scripts/agent-setup.ps1 +83 -0
- package/dist/skills/universalize-agents/scripts/agent-setup.sh +98 -0
- package/dist/skills/update-agents-md/SKILL.md +43 -0
- package/package.json +2 -5
|
@@ -1,22 +1,95 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: git-commit
|
|
3
|
-
description:
|
|
4
|
-
version:
|
|
3
|
+
description: Use BEFORE running any write-side git or gh command (git commit, git commit --amend, git push --force / --tags, git reset --hard, git rebase, gh pr create, gh pr merge), and whenever the user asks to commit changes, write a commit message, or follow conventional commits. Provides the hard pre-commit protocol (stop, summarize, propose, ask, wait) plus the Conventional Commits format used for the proposed message.
|
|
4
|
+
version: 2.0.0
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Git Commit
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
This skill enforces the pre-commit protocol AND specifies the commit-message format. Both are mandatory before any gated git/gh write operation runs.
|
|
10
10
|
|
|
11
|
-
## When to Use
|
|
11
|
+
## When to Use
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
- Learning the Conventional Commits specification
|
|
15
|
-
- Ensuring consistency across the repository's history
|
|
13
|
+
Invoke this skill BEFORE calling Bash for any of:
|
|
16
14
|
|
|
17
|
-
|
|
15
|
+
- `git commit` (any form, including `-m`, `-am`, no-args, heredoc, chained via `&&`/`;`)
|
|
16
|
+
- `git commit --amend` (any form)
|
|
17
|
+
- `git push --force` / `git push -f` / `git push --force-with-lease`
|
|
18
|
+
- `git push --tags`
|
|
19
|
+
- `git reset --hard`
|
|
20
|
+
- `git rebase` (any form, interactive or non-interactive)
|
|
21
|
+
- `gh pr create`
|
|
22
|
+
- `gh pr merge`
|
|
18
23
|
|
|
19
|
-
|
|
24
|
+
Also invoke when the user asks to commit changes, write a commit message, or follow conventional commits.
|
|
25
|
+
|
|
26
|
+
The `permissions.ask` rule in `~/.claude/settings.json` will surface a permission prompt for these commands regardless. This skill ensures the prompt arrives with a proper proposal already presented to the user.
|
|
27
|
+
|
|
28
|
+
## Pre-Commit Protocol (HARD RULE)
|
|
29
|
+
|
|
30
|
+
Every gated command, every time. No exceptions for "tiny" or "obvious" changes. Each commit gets its own gate even within the same session. Plan approval, prior "go ahead" signals, and approvals of earlier commits do NOT carry over.
|
|
31
|
+
|
|
32
|
+
### Step 1 — STOP
|
|
33
|
+
|
|
34
|
+
Do not retry the command yet. The first attempt is your trigger, not your green light.
|
|
35
|
+
|
|
36
|
+
### Step 2 — Emit a detailed technical summary
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
### Technical summary
|
|
40
|
+
- Scope: <files added/modified/deleted, one-line purpose each>
|
|
41
|
+
- Behavior change: <user-visible / API-visible effect, or "none" for pure refactors>
|
|
42
|
+
- Architecture/contract impact: <new exports, removed symbols, changed signatures, new dependencies, or "none">
|
|
43
|
+
- Tests: <what was added/updated, what was run, the result>
|
|
44
|
+
- Risk notes: <edge cases, deferred follow-ups, impact-analysis severity>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Fill every bullet. "None" is a valid answer; an omitted bullet is not.
|
|
48
|
+
|
|
49
|
+
### Step 3 — Propose a commit message
|
|
50
|
+
|
|
51
|
+
Format per the Conventional Commits section below. No `Co-Authored-By: Claude` trailer. Imperative present tense, no trailing period in the subject. When a body is included, it explains the why.
|
|
52
|
+
|
|
53
|
+
**Match the message size to the change.** For small commits — single file, trivial scope, narrow type change, doc tweak, small revert, one-line config flip, renamed prop — propose a title-only message. No body, no bullets. The subject alone conveys intent and a body adds friction without value.
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
### Proposed commit message
|
|
57
|
+
<type>(<scope>): <imperative subject>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Reserve the full proposal format (body + bullets) for commits that touch multiple files, change behavior in subtle ways, or introduce risk worth flagging:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
### Proposed commit message
|
|
64
|
+
<type>(<scope>): <imperative subject>
|
|
65
|
+
|
|
66
|
+
<body paragraph(s) explaining motivation and behavior change>
|
|
67
|
+
|
|
68
|
+
- bullet of notable change
|
|
69
|
+
- bullet of notable change
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Step 4 — Ask the user verbatim
|
|
73
|
+
|
|
74
|
+
Print exactly: **"Commit as-is, edit the message, or skip?"**
|
|
75
|
+
|
|
76
|
+
### Step 5 — Wait for explicit approval
|
|
77
|
+
|
|
78
|
+
Only after the user replies with an affirmative answer may you re-attempt the command. The OS permission prompt will then surface for final confirmation.
|
|
79
|
+
|
|
80
|
+
If the user declines or asks to edit, do not commit. Update the proposal and re-ask.
|
|
81
|
+
|
|
82
|
+
## Multi-commit work
|
|
83
|
+
|
|
84
|
+
If you have several logical commits ready, emit one full proposal per commit and gate each independently. Do not batch proposals. Do not commit any of them until each is individually approved.
|
|
85
|
+
|
|
86
|
+
## Subagent delegation
|
|
87
|
+
|
|
88
|
+
If you dispatch a subagent that will end in a gated command, you (the orchestrator) own the proposal step. Either instruct the subagent to stop before the command and report back, or require it to wait for an "approved" signal you forward only after the user approves the proposal. A subagent that commits on its own initiative is a rule violation.
|
|
89
|
+
|
|
90
|
+
## Conventional Commits format
|
|
91
|
+
|
|
92
|
+
Format the proposed message per the [Conventional Commits](https://www.conventionalcommits.org/) specification.
|
|
20
93
|
|
|
21
94
|
### Structure
|
|
22
95
|
|
|
@@ -30,36 +103,27 @@ Format commit messages according to the [Conventional Commits](https://www.conve
|
|
|
30
103
|
|
|
31
104
|
### Types
|
|
32
105
|
|
|
33
|
-
- `feat`:
|
|
34
|
-
- `fix`:
|
|
35
|
-
- `docs`:
|
|
36
|
-
- `style`:
|
|
37
|
-
- `refactor`:
|
|
38
|
-
- `perf`:
|
|
39
|
-
- `test`:
|
|
40
|
-
- `build`:
|
|
41
|
-
- `ci`:
|
|
42
|
-
- `chore`:
|
|
43
|
-
- `revert`:
|
|
106
|
+
- `feat`: a new feature
|
|
107
|
+
- `fix`: a bug fix
|
|
108
|
+
- `docs`: documentation only changes
|
|
109
|
+
- `style`: changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc.)
|
|
110
|
+
- `refactor`: a code change that neither fixes a bug nor adds a feature
|
|
111
|
+
- `perf`: a code change that improves performance
|
|
112
|
+
- `test`: adding missing tests or correcting existing tests
|
|
113
|
+
- `build`: changes that affect the build system or external dependencies
|
|
114
|
+
- `ci`: changes to CI configuration files and scripts
|
|
115
|
+
- `chore`: other changes that do not modify src or test files
|
|
116
|
+
- `revert`: reverts a previous commit
|
|
44
117
|
|
|
45
118
|
### Guidelines
|
|
46
119
|
|
|
47
|
-
- **Description**:
|
|
48
|
-
- **Case**:
|
|
49
|
-
- **Scope**:
|
|
50
|
-
- **Body**:
|
|
51
|
-
- **Footer**:
|
|
52
|
-
|
|
53
|
-
## Additional Resources
|
|
54
|
-
|
|
55
|
-
### Reference Files
|
|
56
|
-
|
|
57
|
-
For detailed patterns and advanced git workflows, consult:
|
|
58
|
-
|
|
59
|
-
- **`references/conventional-commits.md`** - Detailed Conventional Commits specification.
|
|
60
|
-
|
|
61
|
-
### Example Files
|
|
120
|
+
- **Description**: imperative, present tense ("add", not "added" or "adds")
|
|
121
|
+
- **Case**: lower-case or sentence-case, no trailing period
|
|
122
|
+
- **Scope**: noun in parentheses describing a section of the codebase (e.g., `feat(auth): add login validation`)
|
|
123
|
+
- **Body**: explain the what and why, not the how
|
|
124
|
+
- **Footer**: reference issues (`Closes #123`) or breaking changes (`BREAKING CHANGE: ...`)
|
|
125
|
+
- **Breaking changes**: indicate with `!` after the type/scope OR a `BREAKING CHANGE:` footer
|
|
62
126
|
|
|
63
|
-
|
|
127
|
+
### Reference files
|
|
64
128
|
|
|
65
|
-
|
|
129
|
+
For detailed patterns and edge cases, see `references/conventional-commits.md`.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: handoff
|
|
3
|
+
description: Compact the current conversation into a handoff document for another agent to pick up.
|
|
4
|
+
argument-hint: "What will the next session be used for?"
|
|
5
|
+
disable-model-invocation: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Write a handoff document summarising the current conversation so a fresh agent can continue the work. Save to the temporary directory of the user's OS - not the current workspace.
|
|
9
|
+
|
|
10
|
+
Include a "suggested skills" section in the document, which suggests skills that the agent should invoke.
|
|
11
|
+
|
|
12
|
+
Do not duplicate content already captured in other artifacts (PRDs, plans, ADRs, issues, commits, diffs). Reference them by path or URL instead.
|
|
13
|
+
|
|
14
|
+
Redact any sensitive information, such as API keys, passwords, or personally identifiable information.
|
|
15
|
+
|
|
16
|
+
If the user passed arguments, treat them as a description of what the next session will focus on and tailor the doc accordingly.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: init-agents-md
|
|
3
|
+
description: Analyze a codebase to initialize or update its AGENTS.md (or CLAUDE.md) file, extract architectural patterns to docs/architectural_patterns.md, and procedural skills to .agents/skills/. Use when initializing a project's agent instructions, updating CLAUDE.md/AGENTS.md, or documenting project structure and workflows.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Initialize Agent Instructions & Patterns
|
|
7
|
+
|
|
8
|
+
Use this skill to analyze a workspace and create or update standard developer onboarding and workflow guidelines.
|
|
9
|
+
|
|
10
|
+
## Workflow
|
|
11
|
+
|
|
12
|
+
### 1. Analyze Codebase
|
|
13
|
+
|
|
14
|
+
Explore the codebase to identify:
|
|
15
|
+
|
|
16
|
+
- **WHAT**: Core technology stack, languages, frameworks, main libraries.
|
|
17
|
+
- **WHY**: Core business logic or project purpose.
|
|
18
|
+
- **HOW**: Scripts and CLI commands (e.g., in package.json, Makefile, etc.).
|
|
19
|
+
- **Files/Docs**: Existing documentation, configuration files, and key source directories.
|
|
20
|
+
- **Workflows**: Critical operational workflows (specifically: build, test, release).
|
|
21
|
+
|
|
22
|
+
### 2. Generate or Update AGENTS.md
|
|
23
|
+
|
|
24
|
+
Create or update the `AGENTS.md` (or `CLAUDE.md`) file in the project root.
|
|
25
|
+
|
|
26
|
+
- Keep the file **under 150 lines** (optimum is 100–150 lines).
|
|
27
|
+
- Reference [agents_template.md](references/agents_template.md) for the structure.
|
|
28
|
+
- Follow these constraints strictly:
|
|
29
|
+
1. Cover: WHAT (tech stack), WHY (purpose), and HOW (commands).
|
|
30
|
+
2. Index all major documentation files or folders using progressive disclosure (one-line description each).
|
|
31
|
+
3. Use **file:line references** (e.g., `src/main.ts:L42`) instead of embedding code snippets.
|
|
32
|
+
4. Document exactly 2–3 critical workflows (e.g., build, test, release) as numbered steps.
|
|
33
|
+
5. Do not include formatting or code styling rules (assume linters handle them).
|
|
34
|
+
6. Always include these two lines under rules/guidelines:
|
|
35
|
+
- `Be extremely concise. Sacrifice grammar for concision.`
|
|
36
|
+
- `At the end of each plan, list unresolved questions.`
|
|
37
|
+
|
|
38
|
+
### 3. Extract Architectural Patterns
|
|
39
|
+
|
|
40
|
+
Identify recurring design patterns, folder structure conventions, or key abstractions in the codebase.
|
|
41
|
+
|
|
42
|
+
- Extract these into `docs/architectural_patterns.md`.
|
|
43
|
+
- Reference [patterns_template.md](references/patterns_template.md) for the structure.
|
|
44
|
+
- Provide file:line references for concrete code examples of these patterns.
|
|
45
|
+
|
|
46
|
+
### 4. Extract Procedural Know-How
|
|
47
|
+
|
|
48
|
+
Identify step-by-step procedures, deployment configurations, setup guides, or domain-specific instructions that would clutter the main `AGENTS.md` file.
|
|
49
|
+
|
|
50
|
+
- Extract these into `.agents/skills/<name>/SKILL.md` under the project scope.
|
|
51
|
+
- Reference the newly created skills in `AGENTS.md` or `docs/architectural_patterns.md` where relevant.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
## Purpose & Tech Stack
|
|
4
|
+
|
|
5
|
+
- **WHAT**: [Tech stack, e.g., Next.js, TypeScript, TailwindCSS]
|
|
6
|
+
- **WHY**: [Core project purpose, target users, problem solved]
|
|
7
|
+
|
|
8
|
+
## Commands
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pnpm dev # Run local dev server
|
|
12
|
+
pnpm build # Build project for production
|
|
13
|
+
pnpm test # Run test suite
|
|
14
|
+
pnpm lint # Check types and linting
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Documentation Index
|
|
18
|
+
|
|
19
|
+
- [docs/architecture.md](file:///path/to/docs/architecture.md) — One-line overview of architecture
|
|
20
|
+
- [docs/api.md](file:///path/to/docs/api.md) — One-line overview of API design
|
|
21
|
+
|
|
22
|
+
## Critical Workflows
|
|
23
|
+
|
|
24
|
+
### 1. Development & Build
|
|
25
|
+
|
|
26
|
+
1. Run local environment: `command`
|
|
27
|
+
2. Validate changes: `command`
|
|
28
|
+
|
|
29
|
+
### 2. Testing
|
|
30
|
+
|
|
31
|
+
1. Run unit tests: `command`
|
|
32
|
+
2. Run integration tests: `command`
|
|
33
|
+
|
|
34
|
+
### 3. Release/Deployment
|
|
35
|
+
|
|
36
|
+
1. Build assets: `command`
|
|
37
|
+
2. Deploy command or CI pipeline trigger: `command`
|
|
38
|
+
|
|
39
|
+
## Guidelines
|
|
40
|
+
|
|
41
|
+
- Be extremely concise. Sacrifice grammar for concision.
|
|
42
|
+
- At the end of each plan, list unresolved questions.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Architectural & Design Patterns
|
|
2
|
+
|
|
3
|
+
This document details the recurring architectural, design, and structural patterns found in this codebase.
|
|
4
|
+
|
|
5
|
+
## 1. [Pattern Name]
|
|
6
|
+
|
|
7
|
+
- **Intent**: [What problem does this pattern solve?]
|
|
8
|
+
- **Location/Example**: [e.g., src/utils/paths.ts:L10-L40]
|
|
9
|
+
- **Implementation Details**:
|
|
10
|
+
- [Key implementation note 1]
|
|
11
|
+
- [Key implementation note 2]
|
|
12
|
+
|
|
13
|
+
## 2. [Pattern Name]
|
|
14
|
+
|
|
15
|
+
- **Intent**: [What problem does this pattern solve?]
|
|
16
|
+
- **Location/Example**: [e.g., src/commands/add.ts:L45-L90]
|
|
17
|
+
- **Implementation Details**:
|
|
18
|
+
- [Key implementation note 1]
|
|
19
|
+
- [Key implementation note 2]
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: koreader-plugin-development
|
|
3
|
+
description: This skill should be used when the user asks to "create a KOReader plugin", "write a koplugin", "build a KOReader patch", "userpatch", "modify KOReader behavior", "add a menu item to KOReader", "develop for KOReader", or mentions ".koplugin", "koreader/patches/", `WidgetContainer`, `UIManager`, or KOReader event handlers. Provides plugin and patch development guidance for the KOReader e-reader application (Lua, LuaJIT).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# KOReader Plugin and Patch Development
|
|
7
|
+
|
|
8
|
+
KOReader is a document viewer for e-ink readers, written in Lua on LuaJIT. It exposes two main extension mechanisms:
|
|
9
|
+
|
|
10
|
+
- **Plugins** — self-contained `.koplugin/` directories loaded by `pluginloader` from `DEFAULT_PLUGIN_PATH` or `extra_plugin_paths`. Use for new features, menu entries, or background tasks.
|
|
11
|
+
- **Patches** — Lua files in `koreader/patches/` applied at startup by the `userpatch` module. Use for runtime monkey-patching of KOReader internals when a plugin is too heavy or the change must run before plugin load.
|
|
12
|
+
|
|
13
|
+
Choose plugins when the change is a feature you can ship as a folder. Choose patches for surgical fixes to existing modules, behavior overrides, or hotfixes that target a specific KOReader version.
|
|
14
|
+
|
|
15
|
+
## Mental Model
|
|
16
|
+
|
|
17
|
+
KOReader's UI is a tree of `WidgetContainer` instances (subclasses of `EventListener`) managed by a top-level `UIManager`. Communication happens via `Event` objects with a `handler` name and `args`. `WidgetContainer:handleEvent` propagates events to children first; if no child returns `true`, the container's own `on<EventName>` method runs.
|
|
18
|
+
|
|
19
|
+
Plugins are typically classes built via `WidgetContainer:extend{...}` and returned from `main.lua`; they register themselves to the UI host (`ReaderUI` or `FileManager`) via `addToMainMenu`, dispatcher actions, or by listening to events.
|
|
20
|
+
|
|
21
|
+
Patches manipulate already-loaded modules (`require("apps.reader.readerui")`, `require("ui.widget.infomessage")`, etc.) to change methods, add hooks, or replace functions before or after the UI is built. Module paths must be the full namespaced form KOReader uses internally — short forms silently no-op.
|
|
22
|
+
|
|
23
|
+
## Workflow
|
|
24
|
+
|
|
25
|
+
### To create a plugin
|
|
26
|
+
|
|
27
|
+
1. Create a directory `MyPlugin.koplugin/` containing at least:
|
|
28
|
+
- `main.lua` — module returning a `WidgetContainer`-derived class
|
|
29
|
+
- `_meta.lua` — table with `name`, `fullname`, `description`, optional `version`
|
|
30
|
+
2. Place it under KOReader's `plugins/` directory (or set `extra_plugin_paths`).
|
|
31
|
+
3. Implement the plugin class. Required pieces:
|
|
32
|
+
- `local MyPlugin = WidgetContainer:extend{ name = "myplugin" }` at the top of `main.lua`, with `return MyPlugin` at the bottom (return the class, not an instance — the loader instantiates it per host)
|
|
33
|
+
- `function MyPlugin:init() ... end` to wire dispatcher actions and event handlers
|
|
34
|
+
- `function MyPlugin:addToMainMenu(menu_items) ... end` if the plugin needs a menu entry
|
|
35
|
+
4. Surface UI via `UIManager:show(InfoMessage:new{ text = "..." })` or custom widgets.
|
|
36
|
+
5. React to events via `onEventName(self, ...)` methods. Return `true` to consume.
|
|
37
|
+
6. Restart KOReader (or re-enter file manager) to pick up the plugin.
|
|
38
|
+
|
|
39
|
+
See `examples/helloworld.lua` for a minimal plugin and `references/plugin-anatomy.md` for full structure rules and lifecycle.
|
|
40
|
+
|
|
41
|
+
### To create a patch
|
|
42
|
+
|
|
43
|
+
1. Create `koreader/patches/2-my-fix.lua` (the leading number sets priority; see priorities below).
|
|
44
|
+
2. `require` the target module and replace or wrap methods on it.
|
|
45
|
+
3. Keep the patch small and version-pin it via a header comment that names the KOReader version it targets — patches break across releases.
|
|
46
|
+
4. Restart KOReader. Errors during patch application surface in `crash.log`.
|
|
47
|
+
|
|
48
|
+
Priority is encoded in the filename prefix passed by `userpatch.applyPatches(priority)`. Common values:
|
|
49
|
+
|
|
50
|
+
| Prefix | Phase | Use for |
|
|
51
|
+
|--------|-------|---------|
|
|
52
|
+
| `1-` | early-once (very early) | Startup-only configuration, env tweaks |
|
|
53
|
+
| `2-` | early (before UI) | Patch core modules before UI builds |
|
|
54
|
+
| `3-` | late (after UI) | Override running widgets, add menu items |
|
|
55
|
+
|
|
56
|
+
See `references/patches.md` for monkey-patch idioms, version-guards, and pitfalls.
|
|
57
|
+
|
|
58
|
+
## Common Tasks
|
|
59
|
+
|
|
60
|
+
### Add a menu item
|
|
61
|
+
|
|
62
|
+
Implement `addToMainMenu(self, menu_items)` in a plugin and push an entry into the proper sub-table (`menu_items.tools`, `menu_items.plugins`, etc.). The host UI calls this once when the menu is built.
|
|
63
|
+
|
|
64
|
+
### Listen for an event
|
|
65
|
+
|
|
66
|
+
Add `onEventName(self, arg1, arg2)` to the plugin class. Returning `true` stops further propagation; returning `nil`/`false` lets sibling widgets see the event. Common reader events include `PosUpdate`, `UpdatePos`, `PageUpdate`, `ReaderReady`, `CloseDocument`. See `references/events-and-widgets.md` for the wider catalog.
|
|
67
|
+
|
|
68
|
+
### Show something to the user
|
|
69
|
+
|
|
70
|
+
Quick info: `UIManager:show(InfoMessage:new{ text = _("Done") })`.
|
|
71
|
+
Transient toast: `Notification:notify("Saved")`.
|
|
72
|
+
Custom UI: subclass `WidgetContainer` (or use `ButtonDialog`, `InputDialog`), then `UIManager:show(self)`. Always pair with `UIManager:close(self)` when dismissing.
|
|
73
|
+
|
|
74
|
+
### Run code on a schedule
|
|
75
|
+
|
|
76
|
+
`UIManager:scheduleIn(seconds, function() ... end)` and `UIManager:unschedule(callback)`. For repeating background work, prefer subclassing `BackgroundTaskPlugin` (`ui.plugin.background_task_plugin`).
|
|
77
|
+
|
|
78
|
+
### Persist plugin state
|
|
79
|
+
|
|
80
|
+
Use `LuaSettings` (`require("luasettings")`) backed by a file under `DataStorage:getSettingsDir()`. For document-scoped state, write to `self.ui.doc_settings` so it follows the document.
|
|
81
|
+
|
|
82
|
+
## Debugging
|
|
83
|
+
|
|
84
|
+
- Enable debug mode (`./kodev run --debug` in dev, or `Settings -> Developer options -> Enable debug logging` on device) to get stack traces for event handlers and `logger.dbg(...)` output.
|
|
85
|
+
- `logger.dbg("label", value)` prints to stdout and to `koreader/crash.log`. Lua arguments evaluate eagerly — guard heavy expressions with `if dbg.is_on then ... end`.
|
|
86
|
+
- Read `crash.log` first when something fails silently; missing menu entries usually trace back to a load-time error in the plugin.
|
|
87
|
+
- Iterate on widget code without booting the reader: `./kodev wbuilder` spins up a minimal UI host. Add `UIManager:show(MyWidget:new{...})` lines to `tools/wbuilder.lua` to preview.
|
|
88
|
+
|
|
89
|
+
See `references/debugging.md` for emulator setup, asserts, and breakpoint strategies.
|
|
90
|
+
|
|
91
|
+
## Important Gotchas
|
|
92
|
+
|
|
93
|
+
- **Module identity:** patches must target the exact module path KOReader uses (`require("apps.reader.readerui")`, not `require("readerui")` from outside the source root). Mismatched paths silently no-op.
|
|
94
|
+
- **Event return contract:** forgetting to `return true` causes events to keep propagating, often manifesting as duplicated actions.
|
|
95
|
+
- **String translation:** wrap user-visible strings in `_(...)` from `gettext` so they participate in translations.
|
|
96
|
+
- **Reader vs. FileManager host:** plugins can run under either. Check `self.ui.name == "ReaderUI"` or use `if self.ui.document then` to branch.
|
|
97
|
+
- **Version drift:** KOReader internals change between releases. Both patches and plugins that touch private fields must declare a target version in the header and degrade gracefully if internals shift.
|
|
98
|
+
- **No pcall around plugin init:** an error during `init` disables the plugin without obvious user feedback. Keep `init` defensive and short; defer heavy work to lazy methods or first-event.
|
|
99
|
+
|
|
100
|
+
## File Layout Reference
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
koreader/
|
|
104
|
+
├── plugins/
|
|
105
|
+
│ └── MyPlugin.koplugin/
|
|
106
|
+
│ ├── _meta.lua
|
|
107
|
+
│ ├── main.lua
|
|
108
|
+
│ └── (assets, sub-modules, README.md)
|
|
109
|
+
├── patches/
|
|
110
|
+
│ ├── 2-fix-something.lua
|
|
111
|
+
│ └── 3-override-menu.lua
|
|
112
|
+
└── crash.log
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Source-of-Truth Links
|
|
116
|
+
|
|
117
|
+
These pages back the rules above. Verify against them when behavior diverges.
|
|
118
|
+
|
|
119
|
+
- Development guide (frontend layout): <https://koreader.rocks/doc/topics/Development_guide.md.html>
|
|
120
|
+
- Events guide (propagation, builtin events): <https://koreader.rocks/doc/topics/Events.md.html>
|
|
121
|
+
- Hacking guide (debugging, wbuilder): <https://koreader.rocks/doc/topics/Hacking.md.html>
|
|
122
|
+
- `pluginloader` module: <https://koreader.rocks/doc/modules/pluginloader.html>
|
|
123
|
+
- `userpatch` module: <https://koreader.rocks/doc/modules/userpatch.html>
|
|
124
|
+
- HelloWorld example plugin: <https://github.com/koreader/koreader/tree/master/plugins/helloworld.koplugin>
|
|
125
|
+
|
|
126
|
+
## Additional Resources
|
|
127
|
+
|
|
128
|
+
### Reference Files
|
|
129
|
+
|
|
130
|
+
- `references/plugin-anatomy.md` — `_meta.lua`, `main.lua`, lifecycle, `addToMainMenu`, dispatcher integration, plugin disable/enable settings.
|
|
131
|
+
- `references/events-and-widgets.md` — `WidgetContainer` propagation rules, common reader/filemanager events, `UIManager` lifecycle.
|
|
132
|
+
- `references/patches.md` — patch priorities, monkey-patch idioms, version-guard patterns, removal/cleanup.
|
|
133
|
+
- `references/debugging.md` — `logger`, `dbg`, `crash.log`, `wbuilder`, emulator workflow.
|
|
134
|
+
|
|
135
|
+
### Examples
|
|
136
|
+
|
|
137
|
+
- `examples/helloworld.lua` — minimal plugin `main.lua` showing menu registration and `InfoMessage`.
|
|
138
|
+
- `examples/_meta.lua` — minimal metadata file.
|
|
139
|
+
- `examples/2-example-patch.lua` — early-phase patch that wraps an existing method.
|
|
140
|
+
|
|
141
|
+
When information conflicts, the source-of-truth links above take precedence over this skill — verify against them before changing production code.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
-- koreader/patches/2-example-patch.lua
|
|
2
|
+
-- Early-phase patch: wrap InfoMessage:init to prefix every message with "[patched]".
|
|
3
|
+
-- Targets KOReader v2025.05+. Remove if upstream changes the InfoMessage API.
|
|
4
|
+
|
|
5
|
+
local Version = require("version")
|
|
6
|
+
if Version:getCurrentRevision() < "v2025.05" then return end
|
|
7
|
+
|
|
8
|
+
local InfoMessage = require("ui.widget.infomessage")
|
|
9
|
+
local original_init = InfoMessage.init
|
|
10
|
+
|
|
11
|
+
function InfoMessage:init()
|
|
12
|
+
if self.text and not self._patched then
|
|
13
|
+
self.text = "[patched] " .. self.text
|
|
14
|
+
self._patched = true
|
|
15
|
+
end
|
|
16
|
+
return original_init(self)
|
|
17
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
-- Minimal KOReader plugin: HelloWorld.koplugin/main.lua
|
|
2
|
+
-- Add to plugins/HelloWorld.koplugin/ alongside _meta.lua, then restart KOReader.
|
|
3
|
+
|
|
4
|
+
local InfoMessage = require("ui.widget.infomessage")
|
|
5
|
+
local UIManager = require("ui.uimanager")
|
|
6
|
+
local WidgetContainer = require("ui.widget.container.widgetcontainer")
|
|
7
|
+
local logger = require("logger")
|
|
8
|
+
local _ = require("gettext")
|
|
9
|
+
|
|
10
|
+
local HelloWorld = WidgetContainer:extend{
|
|
11
|
+
name = "helloworld",
|
|
12
|
+
is_doc_only = false,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function HelloWorld:init()
|
|
16
|
+
self.ui.menu:registerToMainMenu(self)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
function HelloWorld:addToMainMenu(menu_items)
|
|
20
|
+
menu_items.helloworld = {
|
|
21
|
+
text = _("Hello World"),
|
|
22
|
+
sorting_hint = "tools",
|
|
23
|
+
callback = function()
|
|
24
|
+
UIManager:show(InfoMessage:new{
|
|
25
|
+
text = _("Hello, plugin world."),
|
|
26
|
+
})
|
|
27
|
+
end,
|
|
28
|
+
}
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
function HelloWorld:onPageUpdate(new_pageno)
|
|
32
|
+
logger.dbg("HelloWorld saw PageUpdate", new_pageno)
|
|
33
|
+
-- Do not return true; let other widgets handle the event too.
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
return HelloWorld
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Debugging KOReader Plugins and Patches
|
|
2
|
+
|
|
3
|
+
## Quick logger
|
|
4
|
+
|
|
5
|
+
```lua
|
|
6
|
+
local logger = require("logger")
|
|
7
|
+
logger.dbg("foo", { a = 1 }) -- prefixed with DEBUG
|
|
8
|
+
logger.info("startup ok")
|
|
9
|
+
logger.warn("recoverable issue")
|
|
10
|
+
logger.err("failure", err)
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
`logger.dbg` only prints when debug logging is enabled. Lua evaluates arguments eagerly, so wrap heavy work:
|
|
14
|
+
|
|
15
|
+
```lua
|
|
16
|
+
local dbg = require("dbg")
|
|
17
|
+
if dbg.is_on then
|
|
18
|
+
logger.dbg("expensive view", buildSnapshot())
|
|
19
|
+
end
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Where logs go
|
|
23
|
+
|
|
24
|
+
- Desktop emulator: stdout of the `./kodev run` process.
|
|
25
|
+
- On device: `koreader/crash.log`. Lines tagged `04/06/17-21:44:53 DEBUG …`.
|
|
26
|
+
|
|
27
|
+
`crash.log` is the first place to look when a plugin "doesn't load" or a patch "didn't run". Plugin-load errors land here with the plugin path.
|
|
28
|
+
|
|
29
|
+
## Enable debug mode
|
|
30
|
+
|
|
31
|
+
- Dev: `./kodev run --debug`
|
|
32
|
+
- On device: `Settings -> Developer options -> Enable debug logging`. Optionally `Enable verbose debug logging` for finer detail. Restart KOReader for the change to take effect.
|
|
33
|
+
|
|
34
|
+
In debug mode the loader logs stack traces from event handlers — this is how you find which plugin or patch raised an exception during event dispatch.
|
|
35
|
+
|
|
36
|
+
## Emulator workflow
|
|
37
|
+
|
|
38
|
+
`./kodev` is the dev driver. Useful targets:
|
|
39
|
+
|
|
40
|
+
- `./kodev run` — start the SDL emulator with the bundled plugins.
|
|
41
|
+
- `./kodev run --debug` — same with debug logging.
|
|
42
|
+
- `./kodev wbuilder` — minimal UI host for prototyping a single widget without booting reader/file-manager. Append `UIManager:show(MyWidget:new{...})` lines to `tools/wbuilder.lua`.
|
|
43
|
+
- `./kodev test` — run the unit suite (`busted`).
|
|
44
|
+
- `./kodev clean` / `./kodev fetch-thirdparty` — when builds drift.
|
|
45
|
+
|
|
46
|
+
For out-of-tree plugin development, point KOReader at your folder via the `extra_plugin_paths` setting (UI: Plugin manager) so you do not have to copy files on each iteration.
|
|
47
|
+
|
|
48
|
+
## Inspecting state
|
|
49
|
+
|
|
50
|
+
- `dbg.dump(any_value)` — pretty-prints a Lua table to the log.
|
|
51
|
+
- `require("dump")(any_value)` — alternative dumper used elsewhere in the codebase.
|
|
52
|
+
- `UIManager._window_stack` — the live widget stack, top of stack is the topmost visible widget.
|
|
53
|
+
- `self.ui` from inside a plugin gives access to the host (`ReaderUI` or `FileManager`) and all sibling modules (`self.ui.menu`, `self.ui.document`, `self.ui.dictionary`, etc.).
|
|
54
|
+
|
|
55
|
+
## Asserts and fail-fast
|
|
56
|
+
|
|
57
|
+
Plugin `init` errors disable the plugin silently in production; let them propagate during development:
|
|
58
|
+
|
|
59
|
+
```lua
|
|
60
|
+
function MyPlugin:init()
|
|
61
|
+
assert(self.ui, "no host UI")
|
|
62
|
+
-- ...
|
|
63
|
+
end
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Wrap the assert in `if dbg.is_on then` if you need a forgiving build.
|
|
67
|
+
|
|
68
|
+
## Common diagnostics
|
|
69
|
+
|
|
70
|
+
| Symptom | First check |
|
|
71
|
+
|---------|-------------|
|
|
72
|
+
| Plugin not in plugin manager | `crash.log` for load error; verify `_meta.lua` returns a table |
|
|
73
|
+
| Menu entry missing | `addToMainMenu` actually called? `self.ui.menu:registerToMainMenu(self)` in `init`? |
|
|
74
|
+
| Event handler not firing | Event name typo; `is_doc_only` true while in FileManager; child widget consuming first |
|
|
75
|
+
| Patch silently no-op | Wrong module path passed to `require`; another patch already replaced the same method |
|
|
76
|
+
| UI not redrawing | Missing `UIManager:setDirty(widget, "ui")` after mutating widget state |
|
|
77
|
+
|
|
78
|
+
## Reproducing on device vs emulator
|
|
79
|
+
|
|
80
|
+
Emulator runs the same Lua but uses SDL for framebuffer and lacks real Wi-Fi, real e-ink refresh quirks, and some device-specific paths. Behaviors that depend on the framebuffer driver (waveform modes, partial refresh) only repro on hardware. Path-sensitive logic (case-sensitive filesystems, mount points) usually shows up on Linux but not on macOS.
|
|
81
|
+
|
|
82
|
+
## Reading the source effectively
|
|
83
|
+
|
|
84
|
+
When the docs are silent (most plugin internals), grep:
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
git grep "Event:new(\"PageUpdate\""
|
|
88
|
+
git grep "registerToMainMenu"
|
|
89
|
+
git grep "applyPatches"
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The KOReader codebase is the authoritative reference; treat upstream code as documentation.
|