โ† Back to Productivity & Tasks
Productivity & Tasks by @amirbrooks

task

Tasker docstore task management via tool-dispatch

0
Source Code

Route task-related requests to tasker_cmd (raw args only, no leading tasker).

  • For natural language, translate the request into CLI args.
  • For /task ..., pass the args through unchanged.
  • Prefer human-readable output. Avoid --stdout-json/--stdout-ndjson unless explicitly requested.
  • For chat-friendly output (Telegram/WhatsApp), add --format telegram. Use --all only when done/archived are explicitly requested.
  • This is the natural-language profile. For slash-only, use skills/task-slash/.
  • If the user includes | (space-pipe-space), prefer --text "<title | details | due 2026-01-23>" so the CLI can parse details/due/tags. Only split on explicit | to avoid corrupting titles.
  • Do not guess separators like "but" or "โ€”"; only split on explicit |.
  • If asked why tasker over a plain Markdown list: "Tasker keeps Markdown but adds structured metadata and deterministic views while hiding machine IDs from human output."
  • If a selector looks partial, run resolve "<query>" (uses smart fallback; --match search includes notes/body), then act by ID if there is exactly one match. Never show IDs in human output.
  • For notes, prefer note add <selector...> -- <text...> to avoid ambiguity; without --, tasker will attempt to infer the split.

Common mappings:

  • "tasks today" / "overdue" -> tasks --open --format telegram (today + overdue)
  • "what's our week" -> week --days 7 --format telegram
  • "show tasks for Work" -> tasks --project Work --format telegram
  • "show board" -> board --project <name> --format telegram
  • "add today" -> add "<task>" --today [--project <name>] --format telegram
  • "add |
    " -> add --text "<task> | <details>" --format telegram
  • "capture " -> capture "<text>" --format telegram
  • "mark done" -> <code>done "<title>"</code></li> <li>"show config" -> <code>config show</code></li> </ul> </div> </div> </article> <aside class="skill-page-sidebar"> <div class="sidebar-card"> <div class="sidebar-title">Actions</div> <a href="https://github.com/openclaw/skills/blob/main/skills/amirbrooks/task/SKILL.md" target="_blank" rel="noopener" class="sidebar-btn sidebar-btn-primary"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/></svg> View on GitHub </a> <a href="https://clawdhub.com/amirbrooks/task" target="_blank" rel="noopener" class="sidebar-btn sidebar-btn-clawdhub"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/><line x1="2" y1="12" x2="22" y2="12"/></svg> View on ClawdHub </a> <a href="https://raw.githubusercontent.com/openclaw/skills/main/skills/amirbrooks/task/SKILL.md" download class="sidebar-btn sidebar-btn-secondary" onclick="trackDownload()"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg> Download Source </a> <button onclick="sendToSecurityAuditor()" class="sidebar-btn sidebar-btn-security"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg> Security Check </button> </div> <div class="sidebar-card"> <div class="sidebar-title">Deploy With</div> <a href="/deploy-openclaw-railway/" class="sidebar-btn sidebar-btn-secondary" style="flex-direction: column; align-items: flex-start; gap: 4px; text-align: left; padding: 12px 16px;"> <span style="font-weight: 600; font-size: 14px;">Deploy on Railway โ€” Get $20 Free</span> <span style="font-size: 12px; color: var(--text-muted); font-weight: 400;">Sign up and get $20 in Railway credits. Deploy in under 10 minutes.</span> </a> <a href="/deploy-openclaw-hostinger/" class="sidebar-btn sidebar-btn-secondary" style="flex-direction: column; align-items: flex-start; gap: 4px; text-align: left; padding: 12px 16px;"> <span style="font-weight: 600; font-size: 14px;">Deploy on Hostinger โ€” 20% Off</span> <span style="font-size: 12px; color: var(--text-muted); font-weight: 400;">Get 20% off Hostinger VPS. 1-click OpenClaw deploy, no SSH required.</span> </a> </div> <div class="sidebar-card"> <div class="sidebar-title">Details</div> <div class="sidebar-info"> <div class="sidebar-info-item"> <span class="sidebar-info-label">Author</span> <span class="sidebar-info-value">@amirbrooks</span> </div> <div class="sidebar-info-item"> <span class="sidebar-info-label">Category</span> <span class="sidebar-info-value">Productivity & Tasks</span> </div> </div> </div> </aside> </div> <footer class="footer"> <div class="footer-inner"> <p class="footer-text" style="margin-bottom: 8px;"><a href="https://github.com/neonone123/moltdirectory" target="_blank" rel="noopener" style="color: var(--accent); text-decoration: none;">View on GitHub</a> ยท <a href="/advertise/" style="color: var(--accent); text-decoration: none;">Advertise</a></p> <p class="footer-text" style="opacity: 0.6; font-size: 13px;">OpenClawDirectory.com is a community-run project and is not affiliated with the official OpenClaw team or Peter Steinberger. Created by <a href="https://x.com/neonone1234" target="_blank" rel="noopener" style="color: var(--accent); text-decoration: none;">@neonone1234</a>.</p> <p class="footer-text" style="opacity: 0.5; font-size: 12px; margin-top: 8px;"><a href="/terms-of-service/" style="color: var(--text-muted); text-decoration: none;">Terms of Service</a> ยท <a href="/privacy-policy/" style="color: var(--text-muted); text-decoration: none;">Privacy Policy</a> ยท <a href="/refund-policy/" style="color: var(--text-muted); text-decoration: none;">Refund Policy</a></p> </div> </footer> <script> function toggleTheme() { const html = document.documentElement; const currentTheme = html.getAttribute('data-theme'); const newTheme = currentTheme === 'light' ? 'dark' : 'light'; if (newTheme === 'light') { html.setAttribute('data-theme', 'light'); } else { html.removeAttribute('data-theme'); } localStorage.setItem('theme', newTheme); } function copySkillContent(btn) { const wrapper = btn.closest('.skill-source-wrapper'); const content = wrapper.querySelector('.markdown-content'); if (content) { const text = content.innerText || content.textContent; navigator.clipboard.writeText(text).then(() => { btn.classList.add('copied'); btn.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg> Copied!'; setTimeout(() => { btn.classList.remove('copied'); btn.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg> Copy'; }, 2000); // Track as download trackDownload(); }); } } function trackDownload() { const voteWidget = document.querySelector('.vote-widget.skill-page-vote[data-category-id][data-tool-id]'); if (!voteWidget) return; const categoryId = voteWidget.dataset.categoryId; const toolId = voteWidget.dataset.toolId; if (!categoryId || !toolId) return; fetch('/api/v1/download', { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ categoryId, toolId }) }).then(r => r.json()).then(data => { if (data && typeof data.downloads === 'number') { document.querySelectorAll('.download-count[data-tool-id="' + toolId + '"]').forEach(el => { el.textContent = 'โ†“ ' + formatDownloads(data.downloads); }); } }).catch(() => {}); } function formatDownloads(n) { if (n >= 1000) return (n / 1000).toFixed(1).replace(/.0$/, '') + 'k'; return String(n); } async function sendToSecurityAuditor() { try { const contentElement = document.querySelector('.markdown-content'); if (!contentElement) throw new Error('Could not find markdown content'); const skillContent = contentElement.innerText || contentElement.textContent; localStorage.setItem('skillAuditContent', skillContent); window.open('/security-auditor', '_blank'); } catch (error) { alert('Error: ' + error.message); } } function updateVoteWidget(widget, toolData) { if (!widget || !toolData) return; const upBtn = widget.querySelector('[data-vote="1"]'); const downBtn = widget.querySelector('[data-vote="-1"]'); const countNode = widget.querySelector('.vote-count'); widget.dataset.rankScore = String(toolData.rankScore ?? 0); widget.dataset.viewerVote = String(toolData.viewerVote ?? 0); if (countNode) { const net = (toolData.upvotes || 0) - (toolData.downvotes || 0); countNode.textContent = net === 0 ? '0' : (net > 0 ? '+' + net : String(net)); } if (upBtn) { upBtn.classList.toggle('is-active', Number(toolData.viewerVote) === 1); } if (downBtn) { downBtn.classList.toggle('is-active', Number(toolData.viewerVote) === -1); } } function setWidgetBusy(widget, busy) { widget.dataset.busy = busy ? '1' : '0'; widget.querySelectorAll('.vote-btn').forEach((btn) => { btn.disabled = busy; }); } function applySortMode(grid, mode) { const cards = [...grid.querySelectorAll('.skill-card[data-tool-id]')]; cards.sort((a, b) => { if (mode === 'popularity') { const scoreDiff = Number(b.dataset.rankScore || 0) - Number(a.dataset.rankScore || 0); if (scoreDiff !== 0) return scoreDiff; } else if (mode === 'downloads') { const dlDiff = Number(b.dataset.downloads || 0) - Number(a.dataset.downloads || 0); if (dlDiff !== 0) return dlDiff; } return Number(a.dataset.originalIndex || 0) - Number(b.dataset.originalIndex || 0); }); cards.forEach((card) => grid.appendChild(card)); } function bindSortToggle(toolbar, grid) { const buttons = toolbar.querySelectorAll('.sort-toggle-btn'); const status = toolbar.querySelector('.sort-status'); buttons.forEach((btn) => { btn.addEventListener('click', () => { const mode = btn.dataset.sortMode; buttons.forEach((b) => b.classList.toggle('is-active', b === btn)); applySortMode(grid, mode); if (mode === 'downloads') { status.textContent = 'Sorted by downloads'; } else { status.textContent = 'Sorted by popularity'; } }); }); } async function fetchScores(categoryId, toolIds) { const params = new URLSearchParams({ categoryId, toolIds: toolIds.join(',') }); const response = await fetch('/api/v1/scores?' + params.toString(), { method: 'GET', credentials: 'same-origin' }); if (!response.ok) { throw new Error('Unable to fetch vote scores'); } return response.json(); } async function submitVote(widget, voteValue) { const categoryId = widget.dataset.categoryId; const toolId = widget.dataset.toolId; setWidgetBusy(widget, true); try { const response = await fetch('/api/v1/vote', { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ categoryId, toolId, vote: voteValue }) }); const payload = await response.json().catch(() => ({})); if (!response.ok) { throw new Error(payload.error || 'Vote request failed'); } if (payload.tool) { updateVoteWidget(widget, payload.tool); const card = widget.closest('.skill-card[data-tool-id]'); if (card) { card.dataset.rankScore = String(payload.tool.rankScore ?? 0); } } } catch (error) { console.error(error); } finally { setWidgetBusy(widget, false); } } document.addEventListener('DOMContentLoaded', async () => { const widgets = [...document.querySelectorAll('.vote-widget[data-category-id][data-tool-id]')]; if (widgets.length === 0) return; const grouped = new Map(); widgets.forEach((widget) => { const key = widget.dataset.categoryId; if (!grouped.has(key)) grouped.set(key, new Map()); grouped.get(key).set(widget.dataset.toolId, widget); }); for (const [categoryId, widgetMap] of grouped.entries()) { const toolIds = [...widgetMap.keys()]; try { const data = await fetchScores(categoryId, toolIds); const byTool = new Map((data.tools || []).map((tool) => [tool.toolId, tool])); for (const [toolId, widget] of widgetMap.entries()) { const toolData = byTool.get(toolId); if (toolData) { updateVoteWidget(widget, toolData); const card = widget.closest('.skill-card[data-tool-id]'); if (card) { card.dataset.rankScore = String(toolData.rankScore ?? 0); card.dataset.downloads = String(toolData.downloads ?? 0); } // Update download counts on any matching elements document.querySelectorAll('.download-count[data-tool-id="' + toolId + '"]').forEach(function(el) { el.textContent = 'โ†“ ' + formatDownloads(toolData.downloads || 0); }); } } } catch (error) { console.error(error); } } widgets.forEach((widget) => { widget.querySelectorAll('.vote-btn').forEach((btn) => { btn.addEventListener('click', (event) => { event.preventDefault(); event.stopPropagation(); if (widget.dataset.busy === '1') return; const voteValue = Number(btn.dataset.vote); submitVote(widget, voteValue).then(() => { const grid = widget.closest('.skills-grid[data-category-id]'); const toolbar = document.querySelector('.sort-toolbar[data-category-id="' + widget.dataset.categoryId + '"]'); const popularityBtn = toolbar ? toolbar.querySelector('.sort-toggle-btn[data-sort-mode="popularity"]') : null; if (grid && popularityBtn && popularityBtn.classList.contains('is-active')) { applySortMode(grid, 'popularity'); } }); }); }); }); document.querySelectorAll('.skills-grid[data-category-id]').forEach((grid) => { const categoryId = grid.dataset.categoryId; const toolbar = document.querySelector('.sort-toolbar[data-category-id="' + categoryId + '"]'); if (!toolbar) return; bindSortToggle(toolbar, grid); applySortMode(grid, 'popularity'); }); }); </script> </body> </html>