feat(lib-core): biblioteca atomica @pulse-libs/core v1.0.0-beta.1
Esta commit conteudo a estrutura atomica completa:
- types: Result<T,E>, AsyncState<T>, Paginated<T>, SortConfig<T>
- utils: date, str, num, cn, debounce, throttle, storage, arr, obj
- validators: Zod schemas — email, password, uuid, url, phone, CPF/CNPJ, sanitizedStr, safeParse
- hooks: useToggle, useAsync, useDebounce, useLocalStorage, useMedia, useInterval, useOnClickOutside, useClipboard, useFetch
- components: Button, Input, Alert, Card, Spinner (atomic design pattern)
- build: tsup v8 ESM+CJS + DTS + sourcemaps — 0 erros
- tests: 57 testes 100% usuarios
- docker: multi-stage Dockerfile (node 20-alpine)
- config: vitest, tsup, tsconfig strict, .npmignore
Filosofia atomica:/utils ← /types ← /validators ← /hooks ← /components
Build: npm run build | Test: npm test | Publish: npm publish
🤖 Generated with Pulse (openclaw + nova-self-improver)
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
# Building a Dashboard on TaskFlow (taskflow-016)
|
||||
|
||||
TaskFlow is **UI-agnostic**. It ships data; you pick the surface.
|
||||
|
||||
The export script (`taskflow export`) writes a single, self-contained JSON document
|
||||
to stdout. That's the only contract. Everything else is up to you.
|
||||
|
||||
---
|
||||
|
||||
## The Core Loop
|
||||
|
||||
```
|
||||
TaskFlow DB → export JSON → your UI
|
||||
```
|
||||
|
||||
Run `taskflow export > /path/to/projects.json` on any schedule and your dashboard
|
||||
gets fresh data without any server, polling API, or WebSocket.
|
||||
|
||||
---
|
||||
|
||||
## How We Built Ours
|
||||
|
||||
We run a **Vite + React** single-page app that reads a static JSON file.
|
||||
The JSON is refreshed every few minutes by a macOS **LaunchAgent watchdog** that
|
||||
calls `taskflow export` and writes the result to the app's public directory.
|
||||
|
||||
No backend. No database exposed over HTTP. No caching layer.
|
||||
The browser just `fetch()`es a local file — or, in prod, an S3/CDN path.
|
||||
|
||||
The LaunchAgent entry looks like this (conceptually):
|
||||
|
||||
```
|
||||
ProgramArguments:
|
||||
/usr/bin/env node
|
||||
/path/to/taskflow/bin/taskflow export
|
||||
> /path/to/vite-app/public/data/projects.json
|
||||
StartInterval: 300 # every 5 minutes
|
||||
```
|
||||
|
||||
On the React side, a `useProjects` hook fetches `data/projects.json` at mount
|
||||
(and optionally on an interval if you want live-ish updates in dev). The rest is
|
||||
just components: progress rings, kanban columns, transition timelines.
|
||||
|
||||
---
|
||||
|
||||
## What the JSON Gives You
|
||||
|
||||
- **Every project** with its name, status, and description
|
||||
- **Task counts** in every bucket (`backlog`, `in_progress`, `pending_validation`,
|
||||
`blocked`, `done`) — ready to drive a status bar, ring, or number badge
|
||||
- **Progress percentage** pre-computed per project
|
||||
- **Last 20 transitions** across all projects — ready for an activity feed
|
||||
|
||||
See `export-schema.json` in this directory for the full JSON Schema.
|
||||
|
||||
---
|
||||
|
||||
## Why This Works Well
|
||||
|
||||
- **Static export = zero attack surface.** No live DB connection in the UI.
|
||||
- **Works offline.** The UI renders whatever is in the file, no network required.
|
||||
- **Framework-neutral.** Svelte, Vue, plain HTML + Chart.js — the JSON doesn't care.
|
||||
- **Cheap to refresh.** A 5-minute cron is fine. Sub-second latency is never needed
|
||||
for a task dashboard.
|
||||
- **Composable.** Pipe the JSON into `jq`, into a Notion integration, into a Slack
|
||||
bot — the export is just text.
|
||||
|
||||
---
|
||||
|
||||
## Other Ideas
|
||||
|
||||
| Surface | Approach |
|
||||
|---|---|
|
||||
| Terminal widget | `taskflow status` (built in) |
|
||||
| Apple Notes | `scripts/apple-notes-export.sh` (macOS) |
|
||||
| Raycast | Script Command reading the export JSON |
|
||||
| Obsidian | Dataview plugin querying the same SQLite directly |
|
||||
| Linear-style board | React + export JSON, one column per status |
|
||||
| CI badge | `taskflow sync check` exits 1 on drift — wire it to a status check |
|
||||
|
||||
---
|
||||
|
||||
The data is already there. Build whatever feels right.
|
||||
|
||||
## Validation Workflow Coupling (Projects Panel)
|
||||
|
||||
In our dashboard, validation actions are wired directly into TaskFlow task state.
|
||||
`decision-store.ts` updates `taskflow.sqlite` (`tasks_v2`) when reviewers act in the
|
||||
Projects panel:
|
||||
|
||||
- Confirm: `pending_validation` -> `done`
|
||||
- Reject: `pending_validation` -> `in_progress` (with reviewer feedback note)
|
||||
|
||||
This is an integration pattern for dashboards, not a required part of TaskFlow core.
|
||||
TaskFlow remains UI-agnostic and does not assume any specific frontend store.
|
||||
@@ -0,0 +1,118 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://clawhub.dev/schemas/taskflow/export-projects-overview/v1",
|
||||
"title": "TaskFlow Export — Projects Overview",
|
||||
"description": "Output schema for scripts/export-projects-overview.mjs (taskflow-009). Emitted to stdout as a single JSON object.",
|
||||
"type": "object",
|
||||
"required": ["exported_at", "projects", "recent_transitions"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
|
||||
"exported_at": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "ISO 8601 timestamp when the export was generated (UTC)."
|
||||
},
|
||||
|
||||
"projects": {
|
||||
"type": "array",
|
||||
"description": "All projects in the TaskFlow DB, ordered by slug.",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["id", "name", "description", "status", "task_counts", "progress_pct"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Project slug — primary key in the `projects` table.",
|
||||
"examples": ["dashboard", "trading", "smart-home"]
|
||||
},
|
||||
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Human-readable project name.",
|
||||
"examples": ["Dashboard", "Trading System"]
|
||||
},
|
||||
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "One-liner description (may be empty string)."
|
||||
},
|
||||
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": ["active", "paused", "done"],
|
||||
"description": "Project lifecycle status."
|
||||
},
|
||||
|
||||
"task_counts": {
|
||||
"type": "object",
|
||||
"description": "Count of tasks in each status bucket for this project.",
|
||||
"required": ["in_progress", "pending_validation", "backlog", "blocked", "done"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"in_progress": { "type": "integer", "minimum": 0 },
|
||||
"pending_validation": { "type": "integer", "minimum": 0 },
|
||||
"backlog": { "type": "integer", "minimum": 0 },
|
||||
"blocked": { "type": "integer", "minimum": 0 },
|
||||
"done": { "type": "integer", "minimum": 0 }
|
||||
}
|
||||
},
|
||||
|
||||
"progress_pct": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 100,
|
||||
"description": "Completion percentage: done / total * 100, rounded to 2 decimal places. 0 when there are no tasks."
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"recent_transitions": {
|
||||
"type": "array",
|
||||
"description": "Last 20 task status transitions across all projects, ordered oldest-first within the window. Sourced from task_transitions_v2.",
|
||||
"maxItems": 20,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["task_id", "to_status", "at"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
|
||||
"task_id": {
|
||||
"type": "string",
|
||||
"description": "Task identifier in <slug>-NNN format.",
|
||||
"examples": ["dashboard-025", "trading-003"]
|
||||
},
|
||||
|
||||
"from_status": {
|
||||
"type": ["string", "null"],
|
||||
"enum": ["backlog", "in_progress", "pending_validation", "blocked", "done", null],
|
||||
"description": "Previous status. Null for task creation events."
|
||||
},
|
||||
|
||||
"to_status": {
|
||||
"type": "string",
|
||||
"enum": ["backlog", "in_progress", "pending_validation", "blocked", "done"],
|
||||
"description": "Status the task transitioned into."
|
||||
},
|
||||
|
||||
"reason": {
|
||||
"type": ["string", "null"],
|
||||
"description": "Optional free-text reason recorded at transition time."
|
||||
},
|
||||
|
||||
"at": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "ISO 8601 timestamp of the transition (UTC)."
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user