issues

SQLite-backed DAG issue tracker with a MoonBit CLI and Rabbit Tea dashboard.

issues
sqlite
dag
cli
dashboard
moon add bobzhang/issues@0.1.5
Download zip
Author
Version
0.1.5
License
Apache-2.0
Last updated
2 months ago
Downloads
49
README

#bobzhang/issues

SQLite-backed DAG issue tracker with a MoonBit CLI for agents and a Rabbit Tea dashboard for humans.

#CLI

Run the CLI through Moon while developing:

moon run --target native cmd/main -- --db issues.db init moon run --target native cmd/main -- --db issues.db add -p 5 --body "Track the project plan." Build tracker moon run --target native cmd/main -- --db issues.db add Implement CLI moon run --target native cmd/main -- --db issues.db link -t contains iss_21845b479070a3c5 iss_0e62aff88401672c moon run --target native cmd/main -- --db issues.db focus iss_21845b479070a3c5 moon run --target native cmd/main -- --db issues.db next moon run --target native cmd/main -- --db issues.db todos moon run --target native cmd/main -- --db issues.db todos --ready moon run --target native cmd/main -- --db issues.db show iss_21845b479070a3c5 moon run --target native cmd/main -- --db issues.db outline moon run --target native cmd/main -- --db issues.db import-markdown plan.md moon run --target native cmd/main -- --db issues.db serve

Issue IDs are deterministic: add trims the title, hashes it with SHA-256, and uses the first 16 hex characters with an iss_ prefix. Titles are immutable; to rename an issue, create a new issue with the new title and relink it. Re-adding the same title returns the same ID. If the generated SHA ID already belongs to a different title, the CLI exits with ISSUE_TITLE_RENAME_REQUIRED so an agent can choose a more specific title.

Edges are typed:

  • contains for outline/decomposition.
  • depends_on for blocking relationships.
  • relates_to for loose references.

next returns the highest-priority unfinished, unblocked leaf under the current focus item. todos renders the current focus as a recursive Markdown task list for terminal reading. Use todos --ready, todos --blocked, or todos --all for flat filtered lists.

import-markdown batch-creates todos from a CommonMark task list using cmark.mbt:

- [ ] Build tracker - [x] Implement CLI - [ ] Implement dashboard

Nested task bullets should be indented to the parent item content column; after - [ ] Parent, that means six spaces before the child -. Use --parent <issue-id> to attach imported top-level tasks under an existing issue.

Larger-agent workflows have mutable details, notes, claims, done evidence, search, and an append-only event log:

moon run --target native cmd/main -- --db issues.db body iss_21845b479070a3c5 "Mutable details and acceptance criteria." moon run --target native cmd/main -- --db issues.db note iss_21845b479070a3c5 "Found the schema edge case." moon run --target native cmd/main -- --db issues.db claim --agent codex --ttl-minutes 60 iss_21845b479070a3c5 moon run --target native cmd/main -- --db issues.db done iss_0e62aff88401672c "Validated with moon test --target native." moon run --target native cmd/main -- --db issues.db events iss_21845b479070a3c5 moon run --target native cmd/main -- --db issues.db search schema moon run --target native cmd/main -- --db issues.db release --agent codex iss_21845b479070a3c5

The SQLite schema records migrations in schema_migrations. Current hashed-ID databases migrate additively, and old numeric-ID databases are migrated into deterministic title-hash IDs with items_legacy_numeric backup tables.

#Dashboard

Generate a static dashboard bundle from the SQLite database:

moon run --target native cmd/main -- --db issues.db dashboard --out dashboard-dist

Open dashboard-dist/index.html in a browser. The command writes data.js, builds the MoonBit/Rabbit Tea JS app, and copies it to app.js.

For a live local viewer backed by SQLite, run:

moon run --target native cmd/main -- --db issues.db serve --port 8080

Then open http://127.0.0.1:8080. The service exposes:

  • / and /index.html for the dashboard.
  • /data.js for the dashboard bootstrap data.
  • /api/graph for graph JSON.
  • /api/next for the next actionable todo as JSON.
  • /api/outline for the text outline.
  • POST /api/body to update mutable issue body text.
  • POST /api/status to update mutable status; done requires evidence.
  • POST /api/note to append an issue note.
  • /healthz for a simple health check.

The live dashboard can edit body text, status, and append-only notes. Issue titles remain immutable: the UI only displays them, and the service has no title mutation endpoint.

#Validation

moon test --target native moon test --target js moon check --target native moon check --target js

#Executable CLI Docs

The tests/scrut/issue_tracker.md document is a Scrut suite: it describes the agent workflow in Markdown and executes each scrut block against the compiled CLI with a fresh SQLite database.

Install Scrut, then run:

scripts/test-scrut.sh

#
EdgeKind

pub(all) enum EdgeKind {
Contains
DependsOn
RelatesTo
} derive(Eq, ToJson,
Debug
,
FromJson
)

#
EdgeKind::from_storage

fn EdgeKind::from_storage(value : String) -> EdgeKind raise

#
EdgeKind::to_storage

fn EdgeKind::to_storage(self : EdgeKind) -> String

#
IssueEdge

pub(all) struct IssueEdge {
from_id : String
to_id : String
kind : EdgeKind
} derive(Eq, ToJson,
Debug
,
FromJson
)

#
IssueGraph

pub(all) struct IssueGraph {
items : Array[IssueItem]
edges : Array[IssueEdge]
current_id : String?
} derive(Eq, ToJson,
Debug
,
FromJson
)

#
IssueGraph::all_todos_markdown

fn IssueGraph::all_todos_markdown(self : IssueGraph, focus : String?) -> String

#
IssueGraph::blocked_count

fn IssueGraph::blocked_count(self : IssueGraph) -> Int

#
IssueGraph::blocked_todos

fn IssueGraph::blocked_todos(self : IssueGraph, focus : String?) -> Array[IssueItem]

#
IssueGraph::blocked_todos_markdown

fn IssueGraph::blocked_todos_markdown(self : IssueGraph, focus : String?) -> String

#
IssueGraph::contains_children

fn IssueGraph::contains_children(self : IssueGraph, id : String) -> Array[IssueItem]

#
IssueGraph::dependencies

fn IssueGraph::dependencies(self : IssueGraph, id : String) -> Array[IssueItem]

#
IssueGraph::edge_count

fn IssueGraph::edge_count(self : IssueGraph, kind : EdgeKind) -> Int

#
IssueGraph::empty

fn IssueGraph::empty() -> IssueGraph

#
IssueGraph::find_item

fn IssueGraph::find_item(self : IssueGraph, id : String) -> IssueItem?

#
IssueGraph::has_open_child

fn IssueGraph::has_open_child(self : IssueGraph, id : String) -> Bool

#
IssueGraph::is_blocked

fn IssueGraph::is_blocked(self : IssueGraph, id : String) -> Bool

#
IssueGraph::next_todo

fn IssueGraph::next_todo(self : IssueGraph, focus : String?) -> IssueItem?

#
IssueGraph::open_count

fn IssueGraph::open_count(self : IssueGraph) -> Int

#
IssueGraph::outline

fn IssueGraph::outline(self : IssueGraph, focus : String?) -> String

#
IssueGraph::ready_todos

fn IssueGraph::ready_todos(self : IssueGraph, focus : String?) -> Array[IssueItem]

#
IssueGraph::ready_todos_markdown

fn IssueGraph::ready_todos_markdown(self : IssueGraph, focus : String?) -> String

#
IssueGraph::scoped_items

fn IssueGraph::scoped_items(self : IssueGraph, focus : String?) -> Array[IssueItem]

#
IssueGraph::todos_markdown

fn IssueGraph::todos_markdown(self : IssueGraph, focus : String?) -> String

#
IssueItem

pub(all) struct IssueItem {
id : String
title : String
body : String
status : Status
priority : Int
} derive(Eq, ToJson,
Debug
,
FromJson
)

#
Status

pub(all) enum Status {
Todo
InProgress
Done
} derive(Eq, ToJson,
Debug
,
FromJson
)

#
Status::from_storage

fn Status::from_storage(value : String) -> Status raise

#
Status::is_open

fn Status::is_open(self : Status) -> Bool

#
Status::to_storage

fn Status::to_storage(self : Status) -> String

Source Files

Powered by MoonBit

Site sourceReport issuePackagesBuild queueSkillsStatistics

© 2026 mooncakes.io