bbdb68a6de
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)
110 lines
3.9 KiB
JavaScript
110 lines
3.9 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* export-projects-overview.mjs (taskflow-009)
|
|
*
|
|
* Reads the TaskFlow SQLite DB and emits a clean JSON summary to stdout.
|
|
*
|
|
* Output shape:
|
|
* {
|
|
* exported_at: ISO string,
|
|
* projects: [ { id, name, description, status, task_counts, progress_pct } ],
|
|
* recent_transitions: [ { task_id, from_status, to_status, reason, at } ]
|
|
* }
|
|
*
|
|
* Usage:
|
|
* node scripts/export-projects-overview.mjs
|
|
* node scripts/export-projects-overview.mjs | jq .
|
|
*/
|
|
|
|
import { DatabaseSync } from 'node:sqlite'
|
|
import path from 'node:path'
|
|
import { existsSync } from 'node:fs'
|
|
|
|
// ── DB path resolution ─────────────────────────────────────────────────────
|
|
const workspace = process.env.OPENCLAW_WORKSPACE || process.cwd()
|
|
const dbPath = path.join(workspace, 'memory', 'taskflow.sqlite')
|
|
|
|
if (!existsSync(dbPath)) {
|
|
process.stderr.write(
|
|
`[taskflow-export] DB not found at: ${dbPath}\n` +
|
|
` Run 'taskflow init' to bootstrap the database.\n`
|
|
)
|
|
process.exit(1)
|
|
}
|
|
|
|
const db = new DatabaseSync(dbPath)
|
|
db.exec('PRAGMA foreign_keys = ON')
|
|
db.exec('PRAGMA journal_mode = WAL')
|
|
|
|
// ── Fetch projects ─────────────────────────────────────────────────────────
|
|
const projects = db
|
|
.prepare('SELECT id, name, description, status FROM projects ORDER BY id')
|
|
.all()
|
|
|
|
// ── Task counts per project ────────────────────────────────────────────────
|
|
const ALL_STATUSES = ['in_progress', 'pending_validation', 'backlog', 'blocked', 'done']
|
|
|
|
const countRows = db
|
|
.prepare(`
|
|
SELECT project_id, status, COUNT(*) AS cnt
|
|
FROM tasks_v2
|
|
GROUP BY project_id, status
|
|
`)
|
|
.all()
|
|
|
|
// Build lookup: { project_id: { status: count } }
|
|
const countsByProject = {}
|
|
for (const row of countRows) {
|
|
if (!countsByProject[row.project_id]) countsByProject[row.project_id] = {}
|
|
countsByProject[row.project_id][row.status] = row.cnt
|
|
}
|
|
|
|
// ── Assemble project objects ───────────────────────────────────────────────
|
|
const projectsOut = projects.map((p) => {
|
|
const counts = countsByProject[p.id] || {}
|
|
|
|
const task_counts = {
|
|
in_progress: counts.in_progress ?? 0,
|
|
pending_validation: counts.pending_validation ?? 0,
|
|
backlog: counts.backlog ?? 0,
|
|
blocked: counts.blocked ?? 0,
|
|
done: counts.done ?? 0,
|
|
}
|
|
|
|
const total = Object.values(task_counts).reduce((s, n) => s + n, 0)
|
|
const progress_pct = total > 0
|
|
? Math.round((task_counts.done / total) * 10000) / 100 // 2 decimal places
|
|
: 0
|
|
|
|
return {
|
|
id: p.id,
|
|
name: p.name,
|
|
description: p.description,
|
|
status: p.status,
|
|
task_counts,
|
|
progress_pct,
|
|
}
|
|
})
|
|
|
|
// ── Recent transitions (last 20) ───────────────────────────────────────────
|
|
const recentTransitions = db
|
|
.prepare(`
|
|
SELECT task_id, from_status, to_status, reason, at
|
|
FROM task_transitions_v2
|
|
ORDER BY id DESC
|
|
LIMIT 20
|
|
`)
|
|
.all()
|
|
|
|
// Reverse so oldest-first within the window (more natural for consumers)
|
|
recentTransitions.reverse()
|
|
|
|
// ── Output ─────────────────────────────────────────────────────────────────
|
|
const output = {
|
|
exported_at: new Date().toISOString(),
|
|
projects: projectsOut,
|
|
recent_transitions: recentTransitions,
|
|
}
|
|
|
|
process.stdout.write(JSON.stringify(output, null, 2) + '\n')
|