Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Expert guidance for building Logseq plugins compatible with the new DB architecture. Auto-invokes when users want to create Logseq plugins, work with the Logseq Plugin API, extend Logseq functionality, or need help with plugin development for DB-based graphs. Covers plugin structure, API usage, and DB-specific considerations.
.claude/skills/aiskillstore-building-logseq-plugins/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-16 | ✗→✓ | ▲ Improved | 50% | 0% |
| case-13 | ✗→✓ | ▲ Improved | 44% | 0% |
| case-17 | ✗→✓ | ▲ Improved | 66% | 0% |
| case-18 | ✗→✓ | ▲ Improved | 50% | 0% |
| case-03 | ✓→✗ | ▼ Worse | 86% | 0% |
This skill auto-invokes when:
You are an expert in Logseq plugin development, with special focus on DB-version compatibility.
Logseq plugins run in sandboxed iframes and communicate with the main app via the Plugin API.
my-logseq-plugin/
├── package.json # Plugin manifest
├── index.html # Entry point
├── index.js # Main logic (or TypeScript)
├── icon.png # Plugin icon (optional)
└── README.md # Documentationjson{ "name": "logseq-my-plugin", "version": "1.0.0", "main": "index.html", "logseq": { "id": "my-plugin-id", "title": "My Plugin", "icon": "./icon.png", "description": "Plugin description" } }
javascriptimport '@logseq/libs' async function main() { console.log('Plugin loaded') // Register commands, settings, etc. logseq.Editor.registerSlashCommand('My Command', async () => { // Command logic }) } logseq.ready(main).catch(console.error)
| Namespace | Purpose | |-----------|---------| | logseq.Editor | Block/page manipulation | | logseq.DB | Database queries | | logseq.App | App-level operations | | logseq.UI | UI components | | logseq.Settings | Plugin settings |
javascript// Get block with properties (DB version) const block = await logseq.Editor.getBlock(blockUuid, { includeChildren: true }) // Properties are structured differently in DB version // Access via block.properties object const author = block.properties?.author // Set property value await logseq.Editor.upsertBlockProperty(blockUuid, 'author', 'John Doe')
javascript// Get blocks with a specific tag/class const books = await logseq.DB.datascriptQuery(` [:find (pull ?b [*]) :where [?b :block/tags ?t] [?t :block/title "Book"]] `) // Add tag to block await logseq.Editor.upsertBlockProperty(blockUuid, 'tags', ['Book', 'Fiction'])
javascript// New APIs for DB version await logseq.Editor.getBlockProperties(blockUuid) await logseq.Editor.setBlockProperties(blockUuid, { author: 'Jane Doe', rating: 5, published: '2024-01-15' })
javascriptconst results = await logseq.DB.datascriptQuery(` [:find ?title :where [?p :block/title ?title] [?p :block/tags ?t] [?t :db/ident :logseq.class/Page]] `)
javascriptconst results = await logseq.DB.datascriptQuery(` [:find (pull ?b [*]) :in $ ?tag-name :where [?b :block/tags ?t] [?t :block/title ?tag-name]] `, ['Book'])
javascript// MD version - file-based queries const mdQuery = ` [:find ?content :where [?b :block/content ?content] [?b :block/page ?p] [?p :block/name "my-page"]] ` // DB version - entity-based queries const dbQuery = ` [:find ?title :where [?b :block/title ?title] [?b :block/tags ?t] [?t :block/title "My Tag"]] `
javascriptlogseq.Editor.registerSlashCommand('Insert Book Template', async () => { await logseq.Editor.insertAtEditingCursor(` - [[New Book]] #Book author:: rating:: status:: To Read `) })
javascriptlogseq.Editor.registerBlockContextMenuItem('Convert to Task', async (e) => { const block = await logseq.Editor.getBlock(e.uuid) await logseq.Editor.upsertBlockProperty(e.uuid, 'tags', ['Task']) await logseq.Editor.upsertBlockProperty(e.uuid, 'status', 'Todo') })
javascriptlogseq.useSettingsSchema([ { key: 'apiKey', type: 'string', title: 'API Key', description: 'Your API key for the service', default: '' }, { key: 'enableFeature', type: 'boolean', title: 'Enable Feature', default: true }, { key: 'theme', type: 'enum', title: 'Theme', enumChoices: ['light', 'dark', 'auto'], default: 'auto' } ])
When building plugins for DB version:
:block/title instead of :block/content for page namesjavascriptasync function isDBGraph() { // Check if current graph is DB-based const graph = await logseq.App.getCurrentGraph() return graph?.name?.includes('db-') || graph?.type === 'db' } async function main() { const isDB = await isDBGraph() if (isDB) { // DB-specific initialization } else { // MD-specific initialization } }
javascriptfunction formatPropertyValue(value, type) { switch (type) { case 'date': return new Date(value).toISOString().split('T')[0] case 'number': return Number(value) case 'checkbox': return Boolean(value) case 'node': return `[[${value}]]` default: return String(value) } }
npm run dev for hot reloadnpm run build for productionLogseq > Settings > Advanced > Developer Mode: ON
Logseq > Plugins > Load unpacked plugin > Select folder| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-22 | pass→pass | 11,044 | 10,367 | -6% | 1 | 1 | 0% | 1,898 | 3,702 | +95% | 0 | 0 | — |
case-03 | pass→fail | 22,353 | 22,013 | -2% | 1 | 1 | 0% | 2,731 | 5,070 | +86% | 0 | 0 | — |
case-04 | pass→pass | 15,125 | 11,522 | -24% | 1 | 1 | 0% | 1,797 | 3,133 | +74% | 0 | 0 | — |
case-05 | pass→pass | 15,135 | 15,387 | +2% | 1 | 1 | 0% | 2,558 | 4,624 | +81% | 0 | 0 | — |
case-06 | pass→pass | 14,775 | 12,366 | -16% | 1 | 1 | 0% | 1,632 | 3,138 | +92% | 0 | 0 | — |
case-02 | fail→fail | 18,866 | 16,956 | -10% | 1 | 1 | 0% | 2,883 | 3,977 | +38% | 0 | 0 | — |
case-01 | fail→fail | 16,371 | 23,955 | +46% | 1 | 1 | 0% | 2,986 | 4,195 | +40% | 0 | 0 | — |
case-16 | fail→pass | 16,024 | 17,001 | +6% | 1 | 1 | 0% | 2,745 | 4,126 | +50% | 0 | 0 | — |
case-07 | pass→pass | 11,852 | 4,470 | -62% | 1 | 1 | 0% | 1,269 | 2,621 | +107% | 0 | 0 | — |
case-08 | pass→pass | 7,367 | 9,549 | +30% | 1 | 1 | 0% | 1,327 | 2,740 | +106% | 0 | 0 | — |
case-09 | pass→pass | 10,520 | 4,308 | -59% | 1 | 1 | 0% | 1,066 | 2,674 | +151% | 0 | 0 | — |
case-10 | pass→pass | 18,859 | 15,005 | -20% | 1 | 1 | 0% | 2,416 | 3,669 | +52% | 0 | 0 | — |
case-11 | pass→pass | 10,861 | 8,496 | -22% | 1 | 1 | 0% | 1,082 | 2,512 | +132% | 0 | 0 | — |
case-12 | pass→pass | 10,882 | 5,889 | -46% | 1 | 1 | 0% | 1,635 | 2,935 | +80% | 0 | 0 | — |
case-13 | fail→pass | 17,529 | 11,262 | -36% | 1 | 1 | 0% | 2,090 | 3,012 | +44% | 0 | 0 | — |
case-14 | pass→pass | 20,182 | 9,106 | -55% | 1 | 1 | 0% | 2,590 | 2,593 | +0% | 0 | 0 | — |
case-15 | pass→pass | 18,216 | 12,145 | -33% | 1 | 1 | 0% | 2,814 | 3,773 | +34% | 0 | 0 | — |
case-17 | fail→pass | 14,672 | 10,482 | -29% | 1 | 1 | 0% | 1,686 | 2,798 | +66% | 0 | 0 | — |
case-18 | fail→pass | 19,474 | 15,090 | -23% | 1 | 1 | 0% | 2,492 | 3,741 | +50% | 0 | 0 | — |
case-19 | pass→pass | 11,332 | 9,010 | -20% | 1 | 1 | 0% | 1,092 | 2,561 | +135% | 0 | 0 | — |
case-20 | pass→pass | 21,057 | 22,918 | +9% | 1 | 1 | 0% | 3,255 | 4,589 | +41% | 0 | 0 | — |
case-21 | pass→pass | 11,468 | 7,734 | -33% | 1 | 1 | 0% | 961 | 2,280 | +137% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +14 percentage points is the difference between those two pass rates over the 22 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.