- Add _EditorField enum and _EditorState class for mutable field tracking
- Add _Mode.editor and wire 'e' key in board+detail modes
- Editor supports: title (inline text edit), type (◀/▶ selector),
column (◀/▶ selector), labels (chips + add/delete), milestones (same),
body (preview + launches $VISUAL/$EDITOR/vi in raw terminal)
- j/k and arrow keys navigate between fields
- h/l cycle selector values and move item cursor in multi-value lists
- d removes selected label/milestone
- s saves all fields via store.update(), returns to board with focus on ticket
- Esc/q discards, returns to board
- _renderEditor: centered modal overlay (max 76 wide), dim background,
double-line border in column accent colour, 'unsaved' indicator when dirty
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the full-width ▔▔▔ underline bar with ┌──┐ as the top border of
the ticket box. The ▔ row had no │ side characters so the box corners were
open/disconnected when the ticket area sides started on the next row.
Now the box is closed: ┌──┐ top, │ │ sides, ╘══╛/└──┘ bottom.
The pill name row above it still provides the visual tab header.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Inactive column header name was brightBlack (dark gray) — indistinguishable
from the dim separator line beneath it. Change to white so column names are
legible even when not selected; the ─ underline bar stays brightBlack to
keep the visual hierarchy.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
After < or > move, find the ticket's index in the destination column's
filtered list via indexWhere instead of jumping to nt.length - 1.
indexWhere returns -1 if not found (e.g. filtered out), max(0,...) clamps
that safely to the first position.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Column header redesign (3 rows → 2 rows):
- Active column: ▌ NAME (n) ▐ full-width bold in column accent color
- Inactive column: lowercase dimmed name, thin ─ separator
- ▔▔▔▔▔ underline bar replaces the old ╞════╡ box separator
- Saves one terminal row per column, exposes more ticket content
Inline action prompts (bottom bar, Esc to cancel):
- [n] new ticket — prompts for title, creates in current column
using the first configured ticket type as default
- [e] edit title — prefills current title for in-place editing
- [c] add comment — single-line comment appended to selected ticket
- [a] archive — shows 'Archive TICKET-ID? [y/N]' confirm prompt
After each mutation the board reloads and ticketIdx is clamped so
the cursor stays valid. Errors surface as yellow status messages.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Escape on board home quits the app; if a filter/status message is active
it clears that first (consistent with Esc-to-clear UX elsewhere)
- Side-border cells (fillers, scroll-indicator blanks) in the selected column
now use the column accent color instead of brightBlack, so the highlight
box closes all the way around
- Empty-state '··· empty ···' label uses the column accent color (bold when
selected) / white on non-selected, replacing the invisible brightBlack dots
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Replace blocking readKey() loop with async stdin stream + StreamController
event bus. This fixes Escape (lone 0x1b batch vs ESC-sequence batch) and
unlocks concurrent async work.
- Add _parseKeys() helper that decodes raw byte batches into Key values using
the same logic as Console.readKey() but without blocking the event loop.
- Watch the kanban directory with Directory.watch() and debounce (350ms) to
auto-refresh the board when tickets change on disk; replaces manual r reload.
- Change live-filter key from / to ? to avoid conflicts with shell history.
- Handle Ctrl+C as a clean quit in both board and detail mode.
- Seal _TuiEvent/_TuiKey/_TuiRefresh event hierarchy for type-safe dispatch.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add an interactive Trello-style kanban TUI powered by dart_console.
Features:
- Side-by-side column layout, adapts to terminal width
- Keyboard navigation: j/k (up/down), h/l or ←/→ (switch column)
- Move selected ticket between columns with < and >
- Ticket cards show ID, type badge (colour-coded), title, labels/milestone
- Scroll indicators (↑ N above / ↓ N below) when a column overflows
- Column headers highlighted in their configured colour; active column
uses filled background + double-line border
- Live filter with / (fuzzy search across id, title, type, labels, body)
- Detail view (Enter): full ticket info, body and comments with word-wrap,
scrollable with j/k
- r to reload tickets from disk without leaving the TUI
- Graceful terminal restoration on exit (Esc / q)
- Requires an interactive terminal; prints a clear error otherwise
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- core: Add ProjectDirs helper class on ProjectContext (context.dirs)
with project getter for .project/ dir; feature packages extend it
via extension methods to expose their own dirs
- kanban: Add KanbanDirs extension on ProjectDirs exposing .kanban;
replace all 14 p.join(context.root, '.project', 'kanban') call
sites with context.dirs.kanban; drop now-unused path imports
- kanban: Add clock: DateTime Function() field to TicketStore
(defaults to DateTime.now); use clock().toUtc() in create() for
deterministic timestamps in tests
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace dart:io File/Directory with package:file abstractions so that
tests can use MemoryFileSystem instead of mutating the process-global
Directory.current.
- Add file: ^7.0.1 to core and kanban dependencies
- ProjectContext.find() accepts FileSystem fs parameter
- TicketStore, KanbanInitHook, InitCommand, all kanban commands accept
FileSystem fs (defaulting to LocalFileSystem())
- KanbanCommand and registerCommands() thread fs to subcommands
- Tests rewritten to use MemoryFileSystem() — no Directory.current mutation
- Remove dart_test.yaml (concurrency: 1 no longer needed)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1. get shows milestones/labels: GetCommand._format() now shows Milestones:/Labels:
lines when non-empty, between Created: and Links:
2. unarchive command: kanban unarchive --id <id> [--column <col>] restores a
ticket from archive/ back to a column (default: first configured column);
registered as 'kanban_unarchive_ticket' MCP tool (15 tools total)
3. Test isolation: add dart_test.yaml (concurrency: 1) — Directory.current is
a process-global OS chdir(); concurrent test files in the same process
would race. Now dart test packages/core packages/kanban passes cleanly.
4. update empty multi-option fix: --milestone '' / --label '' with empty
strings now filters them out (treats as 'clear to empty') rather than
writing spurious empty-string YAML list entries
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- ArchiveCommand (kanban archive --id <id>): moves ticket file from its
current column dir to .project/kanban/archive/; attachments stay put
(under attachments/<id>/); registered as 'kanban_archive_ticket' MCP tool
- TicketStore.list() gains includeArchived param (default false);
archive/ dir skipped unless includeArchived=true
- ListCommand: --include-archived flag
- SearchCommand: --include-archived flag
- Test: list excludes/includes archive correctly
- 14 MCP tools; 'archive' added to expected subcommands list
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- ListCommand: when no --column filter, groups output by column with counts;
with --column filter, returns flat list (no redundant column header)
- BoardCommand (kanban board): ASCII box per column in config order,
self-sizing to longest ticket line; supports --type/--label/--milestone filters
registered as 'kanban_board' MCP tool
- Test counts updated: 13 MCP tools, 'board' in subcommands list
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
DewConfig in core is now a thin YamlMap wrapper. Feature-specific config
classes live in their own packages and expose themselves via Dart extensions:
- KanbanConfig, ColumnConfig, TicketTypeConfig + KanbanDewConfig extension
moved to packages/kanban/lib/src/kanban_config.dart
- McpConfig + McpDewConfig extension added to
packages/mcp/lib/src/mcp_config.dart
- DewConfig.fromYaml() now trivially wraps the raw YamlMap
- All call sites unchanged: context.config.kanban.* / context.config.mcp.*
- Added yaml dependency to dew_mcp pubspec
- Updated core test to validate raw yaml instead of typed fields
- Fixed cross-suite Directory.current isolation (existing issue, not introduced)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Link types: blocks/is_blocked_by, relates_to (symmetric),
duplicates/is_duplicated_by, parent_of/child_of
- Add TicketLink(targetId, type) class and linkTypeInverses map to
ticket.dart
- Update Ticket.links from List<String> to List<TicketLink>
- Update toFileContent/fromFileContent for new {id, type} YAML format
- Update TicketStore.linkTickets to accept a type, write the forward
link and automatically write the inverse on the target ticket
- Update TicketStore.unlinkTickets to remove both sides
- Update LinkCommand with mandatory --type/-y option (enum of all valid
types); describe inverse in output message
- Update GetCommand to display typed links in ticket output
- Update all tests: typed roundtrip, bidirectional store tests for
blocks, relates_to (symmetric), parent_of/child_of, unlink both sides
- 29 tests pass, dart analyze clean
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add DewToolCommand mixin that auto-derives MCP tool JSON Schema from
ArgParser, eliminating the need to define CLI commands and MCP tools
separately
- Add schemaFromArgParser() to generate JSON Schema from ArgParser options
- Add CommandRegistry.mcpTools recursive collector for all DewToolCommand
subcommands
- Refactor all kanban subcommands to use the mixin; switch get/update/delete
from positional rest args to --id / -i option for schema compatibility
- Promote list to a proper CLI subcommand (was MCP-only before)
- Add search, comment, and config subcommands (CLI + MCP tools)
- Add TicketStore.addComment() for non-destructive comment appending
- Simplify mcp.registerCommands() to take only CommandRegistry
- Simplify CLI entry point (no more KanbanToolProvider/McpToolRegistry)
- Delete stale files: kanban_tool_provider.dart, mcp_tool_provider.dart,
mcp_tool_registry.dart (superseded by DewToolCommand mixin)
- Add tools/mcp_client.dart debug client for manual MCP server testing
- Update .vscode/mcp.json with correct server config
All 26 tests pass, dart analyze clean.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Flesh out docs/index.md, docs/config.md, docs/features/mcp.md,
docs/features/kanban.md with full content and aligned tables
- Fix .markdownlint-cli2.mjs config (rules were outside 'config' key)
- Add packages/mcp as a standalone MCP server package
- Update all pubspec descriptions
- Implement DewCommand + CommandRegistry in core
- Implement KanbanCommand and McpCommand stubs with registerCommands()
- Wire CLI entry point using CommandRunner + CommandRegistry
- Add 'melos run dew' script for running the CLI from workspace root
- Update tests across core, kanban, mcp packages
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>