@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.
Files changed (27) hide show
  1. package/README.md +17 -8
  2. package/dist/bin/cli.js +576 -792
  3. package/dist/skills/git-commit/SKILL.md +101 -37
  4. package/dist/skills/handoff/SKILL.md +16 -0
  5. package/dist/skills/init-agents-md/SKILL.md +51 -0
  6. package/dist/skills/init-agents-md/references/agents_template.md +42 -0
  7. package/dist/skills/init-agents-md/references/patterns_template.md +19 -0
  8. package/dist/skills/koreader-plugin-development/SKILL.md +141 -0
  9. package/dist/skills/koreader-plugin-development/examples/2-example-patch.lua +17 -0
  10. package/dist/skills/koreader-plugin-development/examples/_meta.lua +6 -0
  11. package/dist/skills/koreader-plugin-development/examples/helloworld.lua +36 -0
  12. package/dist/skills/koreader-plugin-development/references/debugging.md +92 -0
  13. package/dist/skills/koreader-plugin-development/references/events-and-widgets.md +102 -0
  14. package/dist/skills/koreader-plugin-development/references/patches.md +118 -0
  15. package/dist/skills/koreader-plugin-development/references/plugin-anatomy.md +97 -0
  16. package/dist/skills/statusline-setup/SKILL.md +211 -66
  17. package/dist/skills/universalize-agents/SKILL.md +245 -0
  18. package/dist/skills/universalize-agents/reference/hook-templates/README.md +30 -0
  19. package/dist/skills/universalize-agents/reference/hook-templates/claude-code.json +15 -0
  20. package/dist/skills/universalize-agents/reference/hook-templates/codex.toml +10 -0
  21. package/dist/skills/universalize-agents/reference/hook-templates/copilot.json +9 -0
  22. package/dist/skills/universalize-agents/reference/hook-templates/gemini.json +11 -0
  23. package/dist/skills/universalize-agents/reference/mapping.md +100 -0
  24. package/dist/skills/universalize-agents/scripts/agent-setup.ps1 +83 -0
  25. package/dist/skills/universalize-agents/scripts/agent-setup.sh +98 -0
  26. package/dist/skills/update-agents-md/SKILL.md +43 -0
  27. package/package.json +2 -5
@@ -0,0 +1,102 @@
1
+ # Events and Widgets
2
+
3
+ ## Class hierarchy
4
+
5
+ ```
6
+ EventListener -- ui.widget.eventlistener (handleEvent)
7
+ └── Widget -- ui.widget.widget
8
+ └── WidgetContainer -- ui.widget.container.widgetcontainer
9
+ ├── FrameContainer, CenterContainer, BottomContainer, ...
10
+ ├── InputContainer (gesture handling)
11
+ ├── ReaderUI / FileManager (top-level hosts)
12
+ └── any plugin extending WidgetContainer
13
+ ```
14
+
15
+ All widgets inherit `handleEvent`. `WidgetContainer` overrides it to first propagate to children; if no child returns `true`, the container's own `on<EventName>` runs.
16
+
17
+ ## Sending an event
18
+
19
+ ```lua
20
+ widget:handleEvent(Event:new("Timeout")) -- direct, may be unsafe if widget destroys itself
21
+ UIManager:sendEvent(Event:new("PageUpdate", 5)) -- from topmost widget down
22
+ UIManager:broadcastEvent(Event:new("CloseDocument")) -- to every widget on the stack
23
+ ```
24
+
25
+ `Event:new(name, arg1, arg2, ...)` packs args; the receiver implements `on<Name>(self, arg1, arg2, ...)`.
26
+
27
+ ## The event consumption contract
28
+
29
+ ```lua
30
+ function MyPlugin:onPageUpdate(new_pageno)
31
+ if not self.enabled then return end -- do not consume
32
+ self:doWork(new_pageno)
33
+ return true -- stop propagation
34
+ end
35
+ ```
36
+
37
+ - `return true` — consume; sibling widgets and the container's own handler are skipped.
38
+ - `return nil`/`false` — let propagation continue.
39
+
40
+ Consumption is the most common source of "my plugin runs but the reader stops responding to taps" bugs. Consume only events whose semantics you fully replace.
41
+
42
+ ## Common reader events
43
+
44
+ | Event | When | Args |
45
+ |-------|------|------|
46
+ | `ReaderReady` | After document opens and modules initialized | `doc_settings` |
47
+ | `CloseDocument` | Before tearing down ReaderUI | — |
48
+ | `PosUpdate` | Reader rolling module signals scroll position change | `pos` |
49
+ | `UpdatePos` | Typesetting changed; recompute layout | — |
50
+ | `PageUpdate` | Page number changed | `new_pageno` |
51
+ | `PageChangeAnimation` | Animated page turn | `forward` |
52
+ | `BookMetadataChanged` | Metadata edited | `prop_updated` |
53
+ | `Suspend` / `Resume` | Device sleep cycle | — |
54
+ | `NetworkConnected` / `NetworkDisconnected` | Network state | — |
55
+
56
+ ## Common file-manager events
57
+
58
+ | Event | When |
59
+ |-------|------|
60
+ | `PathChanged` | Directory navigated |
61
+ | `BookmarkAdded` / `BookmarkRemoved` | Bookmark mutated |
62
+ | `SetupShowReader` | About to launch Reader for a file |
63
+
64
+ For exhaustive coverage, search KOReader source for `Event:new("` — every emit is grep-able.
65
+
66
+ ## UIManager lifecycle for widgets
67
+
68
+ ```lua
69
+ UIManager:show(widget) -- push onto _window_stack, schedule paint
70
+ UIManager:setDirty(widget, "ui") -- request redraw
71
+ UIManager:close(widget) -- pop, schedule repaint of stack below
72
+ UIManager:scheduleIn(0.5, fn) -- run after 500ms
73
+ UIManager:unschedule(fn)
74
+ UIManager:nextTick(fn) -- defer to next event loop tick
75
+ ```
76
+
77
+ Always pair `show` with `close`. Widgets registered with `is_always_active = true` keep receiving events even when not at the top of the stack.
78
+
79
+ ## Useful built-in widgets
80
+
81
+ | Widget | Purpose |
82
+ |--------|---------|
83
+ | `InfoMessage` | Modal info text with auto-close |
84
+ | `ConfirmBox` | Yes/no prompt with callbacks |
85
+ | `ButtonDialog` | Multi-button dialog from a 2D grid |
86
+ | `InputDialog` | Text input with buttons |
87
+ | `Notification` | Transient toast (no user dismissal) |
88
+ | `Menu` | Scrollable list of items |
89
+ | `KeyValuePage` | Two-column property display |
90
+ | `TrapWidget` | Block input while a long task runs |
91
+
92
+ All are under `ui.widget.*` and accept a table of properties to `:new{...}`.
93
+
94
+ ## Draw-page code path (for performance work)
95
+
96
+ 1. `ReaderView:recalculate` flags itself dirty.
97
+ 2. UIManager main loop calls `ReaderView:paintTo`.
98
+ 3. `ReaderView:paintTo` calls `document:drawPage`.
99
+ 4. `document:drawPage` checks the cache; if hit, returns the cached buffer.
100
+ 5. On miss, `document:renderPage` calls `_document:openPage`, `page:draw`, then caches the buffer.
101
+
102
+ Hooking `ReaderView:paintTo` from a plugin can layer custom overlays per page; do it sparingly and never block.
@@ -0,0 +1,118 @@
1
+ # Patches (`koreader/patches/`)
2
+
3
+ User patches are arbitrary Lua files in `koreader/patches/` that the `userpatch` module applies during startup. Use them when:
4
+
5
+ - A plugin would be overkill for a one-line behavior change.
6
+ - The change must run before plugins or before the UI builds.
7
+ - Distributing a hotfix to users who can drop a single file into a directory.
8
+
9
+ ## Priority ordering
10
+
11
+ `userpatch.applyPatches(priority)` is called multiple times during boot. Each call processes files whose name starts with the matching numeric prefix, sorted lexicographically.
12
+
13
+ | Filename prefix | Phase | Run point | Typical use |
14
+ |-----------------|-------|-----------|-------------|
15
+ | `1-` | early-once | Very early, before most modules load | Set globals, env vars, monkey-patch core utilities |
16
+ | `2-` | early | After core modules load, before UI | Override module methods that the UI then uses |
17
+ | `3-` | late | After UI is built | Replace running widgets, mutate menus |
18
+
19
+ A patch named `2-fix-readerfooter.lua` runs in the early phase; `3-add-menu.lua` runs late.
20
+
21
+ ## Patch skeleton
22
+
23
+ ```lua
24
+ -- 2-stay-on-cover.lua
25
+ -- Targets KOReader v2025.05+; remove if behavior is fixed upstream.
26
+
27
+ local ReaderRolling = require("apps.reader.modules.readerrolling")
28
+ local original_onReadSettings = ReaderRolling.onReadSettings
29
+
30
+ function ReaderRolling:onReadSettings(config)
31
+ local res = original_onReadSettings(self, config)
32
+ if config:nilOrTrue("stay_on_cover") then
33
+ self:gotoPos(0)
34
+ end
35
+ return res
36
+ end
37
+ ```
38
+
39
+ Always:
40
+
41
+ 1. Capture the original function before replacement.
42
+ 2. Call the original from inside your override unless the goal is total replacement.
43
+ 3. Pass `self` and all args through unchanged.
44
+
45
+ ## Patch idioms
46
+
47
+ ### Add a method
48
+
49
+ ```lua
50
+ local M = require("apps.reader.modules.readerbookmark")
51
+ function M:exportAll()
52
+ -- ...
53
+ end
54
+ ```
55
+
56
+ ### Wrap an existing method
57
+
58
+ ```lua
59
+ local M = require("ui.widget.infomessage")
60
+ local orig = M.init
61
+ function M:init()
62
+ orig(self)
63
+ self.text = "[patched] " .. (self.text or "")
64
+ end
65
+ ```
66
+
67
+ ### Replace a method
68
+
69
+ ```lua
70
+ local M = require("apps.reader.modules.readerfooter")
71
+ function M:onTapFooter() return false end
72
+ ```
73
+
74
+ ### Add a menu entry from a patch
75
+
76
+ A patch can mutate the menu builder used by the UI — the safer option is to attach to an existing module's `addToMainMenu`:
77
+
78
+ ```lua
79
+ local M = require("apps.reader.modules.readermenu")
80
+ local orig = M.setUpdateItemTable
81
+ function M:setUpdateItemTable()
82
+ orig(self)
83
+ self.menu_items.tools.sub_item_table[#self.menu_items.tools.sub_item_table + 1] = {
84
+ text = "Patched entry",
85
+ callback = function() end,
86
+ }
87
+ end
88
+ ```
89
+
90
+ For complex menu work, prefer a plugin.
91
+
92
+ ## Version guarding
93
+
94
+ KOReader internals shift between releases. A patch that worked on v2025.04 may crash on v2025.07.
95
+
96
+ ```lua
97
+ local Version = require("version")
98
+ local current = Version:getCurrentRevision()
99
+ if current < "v2025.05" then return end -- skip if older
100
+ ```
101
+
102
+ Always include a target-version comment header so users (and you, six months later) can audit at a glance.
103
+
104
+ ## Removal
105
+
106
+ To disable a patch, delete it or rename it so the prefix no longer matches a known phase (e.g. `_off-2-foo.lua`). Patches do not auto-clean their effects on next boot — KOReader simply does not reapply them.
107
+
108
+ Patches that allocate scheduled tasks or register listeners must clean up on `CloseDocument` / `Exit` if the user might disable them mid-session. Easier path: design patches to be idempotent and applied only at startup.
109
+
110
+ ## Failure mode
111
+
112
+ A patch that errors during application aborts only that patch; remaining patches and the rest of boot continue. The traceback lands in `crash.log` with the patch filename. Always test with debug logging on (`./kodev run --debug`) before shipping.
113
+
114
+ ## When NOT to use a patch
115
+
116
+ - Adding substantial new UI → write a plugin.
117
+ - Anything users should be able to toggle → write a plugin.
118
+ - Patching something that has an official extension hook → use the hook (events, dispatcher, `addToMainMenu`).
@@ -0,0 +1,97 @@
1
+ # Plugin Anatomy
2
+
3
+ ## Directory layout
4
+
5
+ A plugin is a directory whose name matches `.+%.koplugin`. KOReader scans `DEFAULT_PLUGIN_PATH` (the bundled `plugins/` directory) plus any path listed in the `extra_plugin_paths` setting. The user setting `plugins_disabled` is a table of plugin names to skip.
6
+
7
+ ```
8
+ MyPlugin.koplugin/
9
+ ├── _meta.lua # plugin metadata (required for menu integration)
10
+ ├── main.lua # returns the plugin class
11
+ ├── README.md # optional, surfaced in the plugin manager UI
12
+ ├── i18n/ # optional Crowdin-style translations
13
+ └── *.lua # additional sub-modules
14
+ ```
15
+
16
+ ## `_meta.lua`
17
+
18
+ Returns a plain table that pluginloader reads before instantiating `main.lua`. Minimum fields:
19
+
20
+ ```lua
21
+ return {
22
+ name = "myplugin", -- internal id, must match WidgetContainer name
23
+ fullname = _("My Plugin"), -- shown in plugin manager
24
+ description = _([[One-paragraph description.]]),
25
+ }
26
+ ```
27
+
28
+ Optional fields recognized by the loader: `version`, `author`. The translation function `_` comes from `gettext` and is available in `_meta.lua` evaluation scope.
29
+
30
+ ## `main.lua` skeleton
31
+
32
+ ```lua
33
+ local WidgetContainer = require("ui.widget.container.widgetcontainer")
34
+ local InfoMessage = require("ui.widget.infomessage")
35
+ local UIManager = require("ui.uimanager")
36
+ local _ = require("gettext")
37
+
38
+ local MyPlugin = WidgetContainer:extend{
39
+ name = "myplugin",
40
+ is_doc_only = false, -- true: only load inside ReaderUI
41
+ }
42
+
43
+ function MyPlugin:init()
44
+ self.ui.menu:registerToMainMenu(self)
45
+ end
46
+
47
+ function MyPlugin:addToMainMenu(menu_items)
48
+ menu_items.myplugin = {
49
+ text = _("My plugin"),
50
+ sorting_hint = "tools",
51
+ callback = function()
52
+ UIManager:show(InfoMessage:new{ text = _("Hello from MyPlugin") })
53
+ end,
54
+ }
55
+ end
56
+
57
+ return MyPlugin
58
+ ```
59
+
60
+ Key conventions:
61
+
62
+ - `name` must be unique and match the directory stem.
63
+ - `is_doc_only = true` restricts the plugin to ReaderUI; omit for both hosts.
64
+ - `init` runs after the host UI built `self.ui`. Treat it as the plugin's constructor — register menus, dispatcher actions, and event listeners here.
65
+ - Returning the class (not an instance) lets pluginloader instantiate per host.
66
+
67
+ ## Lifecycle and host wiring
68
+
69
+ `pluginloader` instantiates plugins for the active UI host (`FileManager` or `ReaderUI`). Each instance is appended as a child of that host's `WidgetContainer`, so it receives every event the host receives via standard propagation.
70
+
71
+ To register hooks beyond the menu:
72
+
73
+ - Dispatcher actions (gestures, profiles): `Dispatcher:registerAction("my_action", { category = "none", event = "MyAction", title = _("My action"), general = true })` then handle `onMyAction`.
74
+ - Background work: extend `ui.plugin.background_task_plugin`.
75
+ - Toggle behavior: extend `ui.plugin.switch_plugin`.
76
+
77
+ ## Event handlers in plugins
78
+
79
+ Implement `onFoo(self, ...)` to handle event `Foo`. The host UI broadcasts events down the tree; plugins receive them after host modules. Returning `true` stops propagation. Avoid swallowing events the reader needs (`PageUpdate`, `PosUpdate`) unless that is the goal.
80
+
81
+ ## Persistence
82
+
83
+ - App-wide settings: `local LuaSettings = require("luasettings"); local s = LuaSettings:open(DataStorage:getSettingsDir() .. "/myplugin.lua")`.
84
+ - Per-document state: `self.ui.doc_settings:saveSetting("myplugin_state", value)`. Saved with the document's metadata.
85
+ - Always handle the case where the file does not yet exist; LuaSettings degrades to an empty table.
86
+
87
+ ## Internationalization
88
+
89
+ Wrap user-visible strings: `local _ = require("gettext"); _("Save")`. For plurals, `gettext.ngettext("%1 page", "%1 pages", n)`. Translations live under KOReader's main `l10n/` tree; for in-tree plugins, use the project's translation pipeline. For out-of-tree plugins, you may ship strings English-only or vendor your own gettext catalog.
90
+
91
+ ## Loading errors
92
+
93
+ Errors during plugin load disable the plugin silently for the user but log to `crash.log`. Defensive techniques:
94
+
95
+ - Wrap optional dependencies (e.g. plugins that integrate with another plugin) in `pcall(require, "...")`.
96
+ - Keep `_meta.lua` side-effect free — it loads in a plain `dofile` context.
97
+ - Avoid top-level `require` of modules that only exist in newer KOReader versions; gate with `pcall` and fall back gracefully.