Prepare 0.4.0 release

This commit is contained in:
Chris Hendrickson 2026-05-05 01:42:08 -04:00
parent 69fa044e5b
commit 1bc6a6447d
51 changed files with 225 additions and 67 deletions

View file

@ -7,6 +7,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
## [0.4.0] - 2026-05-05
### Added in 0.4.0
- Added `dew infra` for project infrastructure service discovery, validation,
configuration payloads, initialization payloads, lifecycle control, status,
logs, and cleanup.
- Added Podman Quadlet runtime support with a runtime boundary for future
container backends.
- Added manifest, configure, and init JSON Schema handling for infrastructure
services.
- Added sample infrastructure services for PostgreSQL 18, Valkey, RustFS,
Keycloak, pods, networks, volumes, and local image builds.
- Exposed every `dew infra` CLI path through Dew MCP tools, including
path-specific configure/init tools.
### Changed in 0.4.0
- Extended MCP tool discovery so commands can provide extra path-specific tools
beyond one tool per subcommand.
- Raised package versions to `0.4.0` for the infra release.
- Cleaned existing analyzer info findings in kanban and vault ahead of release.
## [0.1.0] - 2026-04-25 ## [0.1.0] - 2026-04-25
### Added ### Added
@ -86,5 +109,6 @@ Full set of kanban subcommands, each also registered as an MCP tool automaticall
- `ProjectDirs` with injectable filesystem abstraction (`package:file`) for - `ProjectDirs` with injectable filesystem abstraction (`package:file`) for
testable path resolution. testable path resolution.
[Unreleased]: https://github.com/artificerchris/dew/compare/v0.1.0...HEAD [Unreleased]: https://github.com/artificerchris/dew/compare/v0.4.0...HEAD
[0.4.0]: https://github.com/artificerchris/dew/compare/v0.3.0...v0.4.0
[0.1.0]: https://github.com/artificerchris/dew/releases/tag/v0.1.0 [0.1.0]: https://github.com/artificerchris/dew/releases/tag/v0.1.0

View file

@ -36,7 +36,10 @@ The TUI auto-refreshes when ticket files change on disk, so it stays in sync whe
### MCP Server ### MCP Server
`dew mcp serve` starts an MCP-compliant stdio server that exposes every kanban command as an MCP tool. AI agents (GitHub Copilot, Claude, etc.) can create tickets, move cards, search, and comment — using the exact same logic as the CLI. No separate tool definitions needed: every command that mixes in `DewToolCommand` is registered automatically. See the [MCP documentation](./docs/features/mcp.md). `dew mcp serve` starts an MCP-compliant stdio server that exposes Dew commands
as MCP tools. AI agents (GitHub Copilot, Claude, etc.) can create tickets, move
cards, search, comment, and manage project infrastructure through the same logic
as the CLI. See the [MCP documentation](./docs/features/mcp.md).
## Installation ## Installation

View file

@ -6,9 +6,10 @@ The Dew Model Context Protocol (MCP) Server is a feature that allows AI agents t
The MCP feature is split across two packages to keep concerns separate: The MCP feature is split across two packages to keep concerns separate:
- **`packages/core`** defines the `DewToolCommand` mixin. Any command that mixes it in is automatically - **`packages/core`** defines the `DewToolCommand` mixin and `McpToolProvider`
registered as an MCP tool — the mixin derives the tool's JSON Schema directly from the command's own interface. Commands that mix in `DewToolCommand` are automatically registered
`ArgParser`, so tools and CLI commands share a single definition. as MCP tools, and commands that implement `McpToolProvider` can expose extra
path-specific tools.
- **`packages/mcp`** implements the actual server. It reads the list of tools from `CommandRegistry` and - **`packages/mcp`** implements the actual server. It reads the list of tools from `CommandRegistry` and
serves them over stdio using the [dart\_mcp](https://pub.dev/packages/dart_mcp) package. Only the `cli` serves them over stdio using the [dart\_mcp](https://pub.dev/packages/dart_mcp) package. Only the `cli`
package depends on `packages/mcp`; feature packages like `kanban` remain decoupled from the transport layer. package depends on `packages/mcp`; feature packages like `kanban` remain decoupled from the transport layer.
@ -74,6 +75,29 @@ The following tools are registered by the `kanban` package:
| `kanban_link_tickets` | Link two tickets with a typed relationship (bidirectional) | | `kanban_link_tickets` | Link two tickets with a typed relationship (bidirectional) |
| `kanban_unlink_tickets` | Remove a link between two tickets (both sides) | | `kanban_unlink_tickets` | Remove a link between two tickets (both sides) |
The following tools are registered by the `infra` package:
| Tool | Description |
| ---------------------------- | ------------------------------------------------ |
| `infra_list_services` | List infrastructure services |
| `infra_show_service` | Show manifest and runtime details |
| `infra_validate_services` | Validate one service or all services |
| `infra_configure_service` | CLI-default configure path placeholder |
| `infra_configure_schema` | Show the configure JSON Schema |
| `infra_configure_show` | Show the active configure payload |
| `infra_configure_apply` | Apply configure payload values |
| `infra_init_service` | CLI-default init path placeholder |
| `infra_init_schema` | Show the init JSON Schema |
| `infra_init_run` | Write an initialization payload |
| `infra_install_service` | Install service Quadlets |
| `infra_uninstall_service` | Uninstall service Quadlets |
| `infra_up_service` | Install, reload, and start services |
| `infra_down_service` | Stop services |
| `infra_restart_service` | Restart services |
| `infra_status_service` | Show service runtime status |
| `infra_logs` | Read service logs |
| `infra_delete_service` | Delete declared runtime artifacts |
### Link types ### Link types
`kanban_link_tickets` requires a `--type` argument. The inverse is written automatically on the target ticket. `kanban_link_tickets` requires a `--type` argument. The inverse is written automatically on the target ticket.

View file

@ -26,7 +26,9 @@ Dew is structured as a Dart workspace with the following packages:
### How commands become MCP tools ### How commands become MCP tools
Every CLI command that mixes in `DewToolCommand` is automatically registered as an MCP tool — no separate registration needed. The mixin derives the JSON Schema for the tool's input from the command's own `ArgParser`, so argument definitions are written exactly once. Every CLI command that mixes in `DewToolCommand` is automatically registered as
an MCP tool. Commands that need more granular tool paths can also implement
`McpToolProvider` to expose additional tools.
```text ```text
ArgParser definition ArgParser definition

View file

@ -1,5 +1,13 @@
# Changelog # Changelog
## 0.4.0 — 2026-05-05
Infra release.
- Added the `dew infra` command group to the CLI.
- Registered the infra package alongside kanban, MCP, and vault packages.
- Updated CLI dependencies for the `0.4.0` package set.
## 0.1.0 — 2026-04-25 ## 0.1.0 — 2026-04-25
Initial release. Initial release.

View file

@ -1,6 +1,6 @@
name: dew name: dew
description: Command-line interface for the Dew project management tool. description: Command-line interface for the Dew project management tool.
version: 0.1.0 version: 0.4.0
repository: https://github.com/artificerchris/dew repository: https://github.com/artificerchris/dew
issue_tracker: https://github.com/artificerchris/dew/issues issue_tracker: https://github.com/artificerchris/dew/issues
resolution: workspace resolution: workspace
@ -10,11 +10,11 @@ environment:
dependencies: dependencies:
args: ^2.7.0 args: ^2.7.0
dew_core: ^0.1.0 dew_core: ^0.4.0
dew_infra: ^0.1.0 dew_infra: ^0.4.0
dew_kanban: ^0.1.0 dew_kanban: ^0.4.0
dew_vault: ^0.3.0 dew_vault: ^0.4.0
dew_mcp: ^0.1.0 dew_mcp: ^0.4.0
dev_dependencies: dev_dependencies:
lints: ^6.0.0 lints: ^6.0.0

View file

@ -1,5 +1,13 @@
# Changelog # Changelog
## 0.4.0 — 2026-05-05
Infra and MCP provider release.
- Extended `CommandRegistry.mcpTools` to collect extra tools from
`McpToolProvider` command classes.
- Kept `InitCommand` analyzer-clean with the Dart SDK `3.12` lint set.
## 0.1.0 — 2026-04-25 ## 0.1.0 — 2026-04-25
Initial release. Initial release.

View file

@ -1,6 +1,6 @@
name: dew_core name: dew_core
description: Core shared types, interfaces, and configuration for the Dew project management tool. description: Core shared types, interfaces, and configuration for the Dew project management tool.
version: 0.1.0 version: 0.4.0
repository: https://github.com/artificerchris/dew repository: https://github.com/artificerchris/dew
issue_tracker: https://github.com/artificerchris/dew/issues issue_tracker: https://github.com/artificerchris/dew/issues
resolution: workspace resolution: workspace

View file

@ -0,0 +1,13 @@
# Changelog
## 0.4.0 — 2026-05-05
Initial public release.
- Added infrastructure service discovery from `.project/infrastructure/services`.
- Added YAML service manifests with support for multiple Podman Quadlet files.
- Added manifest validation, JSON Schema validation, configuration payloads, and
initialization payloads.
- Added Podman Quadlet install, uninstall, up, down, restart, status, logs, and
delete operations behind a container runtime boundary.
- Added MCP tools for every `dew infra` CLI path.

21
packages/infra/LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Chris Gebhardt
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

22
packages/infra/README.md Normal file
View file

@ -0,0 +1,22 @@
# dew_infra
Infrastructure service management for the Dew CLI.
`dew_infra` discovers service manifests under `.project/infrastructure`,
validates referenced Quadlet files and JSON Schemas, and manages Podman
Quadlets through systemd. It is designed around a runtime boundary so Dew can
support additional container runtimes in later releases.
Most users should install the `dew` CLI package instead:
```sh
dart pub global activate dew
```
See the main Dew documentation for the `dew infra` command reference:
<https://github.com/artificerchris/dew#readme>
## License
MIT — see [LICENSE](LICENSE).

View file

@ -1,7 +1,6 @@
name: dew_infra name: dew_infra
description: Infrastructure service management package for the Dew CLI. description: Infrastructure service management package for the Dew CLI.
publish_to: none version: 0.4.0
version: 0.1.0
repository: https://github.com/artificerchris/dew repository: https://github.com/artificerchris/dew
issue_tracker: https://github.com/artificerchris/dew/issues issue_tracker: https://github.com/artificerchris/dew/issues
resolution: workspace resolution: workspace
@ -11,7 +10,7 @@ environment:
dependencies: dependencies:
args: ^2.7.0 args: ^2.7.0
dew_core: ^0.1.0 dew_core: ^0.4.0
file: ^7.0.1 file: ^7.0.1
json_schema: ^5.2.2 json_schema: ^5.2.2
path: ^1.9.0 path: ^1.9.0

View file

@ -1,5 +1,12 @@
# Changelog # Changelog
## 0.4.0 — 2026-05-05
Release alignment for the `0.4.0` Dew package set.
- Updated the `dew_core` dependency constraint for the infra release.
- Applied analyzer fixes for current Dart lint recommendations.
## 0.1.0 — 2026-04-25 ## 0.1.0 — 2026-04-25
Initial release. Initial release.

View file

@ -8,7 +8,7 @@ import '../ticket_store.dart';
class AddCommentCommand extends DewCommand with DewToolCommand { class AddCommentCommand extends DewCommand with DewToolCommand {
final FileSystem _fs; final FileSystem _fs;
AddCommentCommand({FileSystem fs = const LocalFileSystem()}) : _fs = fs { AddCommentCommand({this._fs = const LocalFileSystem()}) {
argParser argParser
..addOption( ..addOption(
'id', 'id',

View file

@ -9,7 +9,7 @@ import '../ticket_store.dart';
class ArchiveCommand extends DewCommand with DewToolCommand { class ArchiveCommand extends DewCommand with DewToolCommand {
final FileSystem _fs; final FileSystem _fs;
ArchiveCommand({FileSystem fs = const LocalFileSystem()}) : _fs = fs { ArchiveCommand({this._fs = const LocalFileSystem()}) {
argParser.addOption( argParser.addOption(
'id', 'id',
abbr: 'i', abbr: 'i',

View file

@ -9,7 +9,7 @@ import '../ticket_store.dart';
class BoardCommand extends DewCommand with DewToolCommand { class BoardCommand extends DewCommand with DewToolCommand {
final FileSystem _fs; final FileSystem _fs;
BoardCommand({FileSystem fs = const LocalFileSystem()}) : _fs = fs { BoardCommand({this._fs = const LocalFileSystem()}) {
argParser argParser
..addOption('type', abbr: 't', help: 'Filter tickets to this type.') ..addOption('type', abbr: 't', help: 'Filter tickets to this type.')
..addOption('label', help: 'Filter tickets to this label.') ..addOption('label', help: 'Filter tickets to this label.')

View file

@ -8,7 +8,7 @@ import '../ticket_store.dart';
class CreateCommand extends DewCommand with DewToolCommand { class CreateCommand extends DewCommand with DewToolCommand {
final FileSystem _fs; final FileSystem _fs;
CreateCommand({FileSystem fs = const LocalFileSystem()}) : _fs = fs { CreateCommand({this._fs = const LocalFileSystem()}) {
argParser argParser
..addOption('title', abbr: 't', mandatory: true, help: 'Ticket title.') ..addOption('title', abbr: 't', mandatory: true, help: 'Ticket title.')
..addOption( ..addOption(

View file

@ -8,7 +8,7 @@ import '../ticket_store.dart';
class DeleteCommand extends DewCommand with DewToolCommand { class DeleteCommand extends DewCommand with DewToolCommand {
final FileSystem _fs; final FileSystem _fs;
DeleteCommand({FileSystem fs = const LocalFileSystem()}) : _fs = fs { DeleteCommand({this._fs = const LocalFileSystem()}) {
argParser.addOption( argParser.addOption(
'id', 'id',
abbr: 'i', abbr: 'i',

View file

@ -9,7 +9,7 @@ import '../ticket_store.dart';
class GetCommand extends DewCommand with DewToolCommand { class GetCommand extends DewCommand with DewToolCommand {
final FileSystem _fs; final FileSystem _fs;
GetCommand({FileSystem fs = const LocalFileSystem()}) : _fs = fs { GetCommand({this._fs = const LocalFileSystem()}) {
argParser.addOption( argParser.addOption(
'id', 'id',
abbr: 'i', abbr: 'i',

View file

@ -6,7 +6,7 @@ import '../kanban_config.dart';
class GetConfigCommand extends DewCommand with DewToolCommand { class GetConfigCommand extends DewCommand with DewToolCommand {
final FileSystem _fs; final FileSystem _fs;
GetConfigCommand({FileSystem fs = const LocalFileSystem()}) : _fs = fs; GetConfigCommand({this._fs = const LocalFileSystem()});
@override @override
final String name = 'config'; final String name = 'config';

View file

@ -9,7 +9,7 @@ import '../ticket_store.dart';
class LinkCommand extends DewCommand with DewToolCommand { class LinkCommand extends DewCommand with DewToolCommand {
final FileSystem _fs; final FileSystem _fs;
LinkCommand({FileSystem fs = const LocalFileSystem()}) : _fs = fs { LinkCommand({this._fs = const LocalFileSystem()}) {
argParser argParser
..addOption('id', abbr: 'i', mandatory: true, help: 'Source ticket ID.') ..addOption('id', abbr: 'i', mandatory: true, help: 'Source ticket ID.')
..addOption( ..addOption(
@ -44,8 +44,9 @@ class LinkCommand extends DewCommand with DewToolCommand {
final targetId = '${args['target']}'.toUpperCase(); final targetId = '${args['target']}'.toUpperCase();
final type = '${args['type']}'; final type = '${args['type']}';
if (id == targetId) if (id == targetId) {
throw ArgumentError('A ticket cannot be linked to itself.'); throw ArgumentError('A ticket cannot be linked to itself.');
}
final context = await ProjectContext.find(fs: _fs); final context = await ProjectContext.find(fs: _fs);
final store = TicketStore( final store = TicketStore(

View file

@ -9,7 +9,7 @@ import '../ticket_store.dart';
class ListCommand extends DewCommand with DewToolCommand { class ListCommand extends DewCommand with DewToolCommand {
final FileSystem _fs; final FileSystem _fs;
ListCommand({FileSystem fs = const LocalFileSystem()}) : _fs = fs { ListCommand({this._fs = const LocalFileSystem()}) {
argParser argParser
..addOption( ..addOption(
'column', 'column',

View file

@ -8,7 +8,7 @@ import '../ticket_store.dart';
class MoveCommand extends DewCommand with DewToolCommand { class MoveCommand extends DewCommand with DewToolCommand {
final FileSystem _fs; final FileSystem _fs;
MoveCommand({FileSystem fs = const LocalFileSystem()}) : _fs = fs { MoveCommand({this._fs = const LocalFileSystem()}) {
argParser argParser
..addOption('id', abbr: 'i', mandatory: true, help: 'Ticket ID.') ..addOption('id', abbr: 'i', mandatory: true, help: 'Ticket ID.')
..addOption( ..addOption(

View file

@ -8,7 +8,7 @@ import '../ticket_store.dart';
class SearchCommand extends DewCommand with DewToolCommand { class SearchCommand extends DewCommand with DewToolCommand {
final FileSystem _fs; final FileSystem _fs;
SearchCommand({FileSystem fs = const LocalFileSystem()}) : _fs = fs { SearchCommand({this._fs = const LocalFileSystem()}) {
argParser argParser
..addOption( ..addOption(
'query', 'query',

View file

@ -8,7 +8,7 @@ import '../ticket_store.dart';
class StatsCommand extends DewCommand with DewToolCommand { class StatsCommand extends DewCommand with DewToolCommand {
final FileSystem _fs; final FileSystem _fs;
StatsCommand({FileSystem fs = const LocalFileSystem()}) : _fs = fs; StatsCommand({this._fs = const LocalFileSystem()});
@override @override
final String name = 'stats'; final String name = 'stats';

View file

@ -214,7 +214,7 @@ class _Cell {
class TuiCommand extends DewCommand { class TuiCommand extends DewCommand {
final FileSystem _fs; final FileSystem _fs;
TuiCommand({FileSystem fs = const LocalFileSystem()}) : _fs = fs; TuiCommand({this._fs = const LocalFileSystem()});
@override @override
final String name = 'tui'; final String name = 'tui';
@ -410,8 +410,9 @@ class TuiCommand extends DewCommand {
case ControlCharacter.arrowLeft: case ControlCharacter.arrowLeft:
if (p.relationIdx > 0) p.relationIdx--; if (p.relationIdx > 0) p.relationIdx--;
case ControlCharacter.arrowRight: case ControlCharacter.arrowRight:
if (p.relationIdx < _linkRelations.length - 1) if (p.relationIdx < _linkRelations.length - 1) {
p.relationIdx++; p.relationIdx++;
}
case ControlCharacter.enter: case ControlCharacter.enter:
try { try {
await store.linkTickets( await store.linkTickets(
@ -444,12 +445,14 @@ class TuiCommand extends DewCommand {
p.input = p.input.substring(0, p.input.length - 1); p.input = p.input.substring(0, p.input.length - 1);
} }
case ControlCharacter.arrowLeft: case ControlCharacter.arrowLeft:
if (p.kind == _PromptKind.newTitle && p.typeIdx > 0) if (p.kind == _PromptKind.newTitle && p.typeIdx > 0) {
p.typeIdx--; p.typeIdx--;
}
case ControlCharacter.arrowRight: case ControlCharacter.arrowRight:
if (p.kind == _PromptKind.newTitle && if (p.kind == _PromptKind.newTitle &&
p.typeIdx < config.ticketTypes.length - 1) p.typeIdx < config.ticketTypes.length - 1) {
p.typeIdx++; p.typeIdx++;
}
case ControlCharacter.enter: case ControlCharacter.enter:
final trimmed = p.input.trim(); final trimmed = p.input.trim();
if (p.kind == _PromptKind.linkId) { if (p.kind == _PromptKind.linkId) {
@ -712,8 +715,9 @@ class TuiCommand extends DewCommand {
case _EditorField.labels: case _EditorField.labels:
if (es.itemCursor < es.labels.length - 1) es.itemCursor++; if (es.itemCursor < es.labels.length - 1) es.itemCursor++;
case _EditorField.milestones: case _EditorField.milestones:
if (es.itemCursor < es.milestones.length - 1) if (es.itemCursor < es.milestones.length - 1) {
es.itemCursor++; es.itemCursor++;
}
default: default:
break; break;
} }
@ -1386,10 +1390,12 @@ class TuiCommand extends DewCommand {
kv('Type', ticket.type); kv('Type', ticket.type);
kv('Column', ticket.column); kv('Column', ticket.column);
kv('Created', _fmtDate(ticket.created)); kv('Created', _fmtDate(ticket.created));
if (ticket.milestones.isNotEmpty) if (ticket.milestones.isNotEmpty) {
kv('Milestones', ticket.milestones.join(', ')); kv('Milestones', ticket.milestones.join(', '));
if (ticket.labels.isNotEmpty) }
if (ticket.labels.isNotEmpty) {
kv('Labels', ticket.labels.map((l) => '#$l').join(' ')); kv('Labels', ticket.labels.map((l) => '#$l').join(' '));
}
if (ticket.links.isNotEmpty) { if (ticket.links.isNotEmpty) {
for (final link in ticket.links) { for (final link in ticket.links) {
kv(link.type, link.targetId); kv(link.type, link.targetId);

View file

@ -9,7 +9,7 @@ import '../ticket_store.dart';
class UnarchiveCommand extends DewCommand with DewToolCommand { class UnarchiveCommand extends DewCommand with DewToolCommand {
final FileSystem _fs; final FileSystem _fs;
UnarchiveCommand({FileSystem fs = const LocalFileSystem()}) : _fs = fs { UnarchiveCommand({this._fs = const LocalFileSystem()}) {
argParser argParser
..addOption( ..addOption(
'id', 'id',

View file

@ -8,7 +8,7 @@ import '../ticket_store.dart';
class UnlinkCommand extends DewCommand with DewToolCommand { class UnlinkCommand extends DewCommand with DewToolCommand {
final FileSystem _fs; final FileSystem _fs;
UnlinkCommand({FileSystem fs = const LocalFileSystem()}) : _fs = fs { UnlinkCommand({this._fs = const LocalFileSystem()}) {
argParser argParser
..addOption('id', abbr: 'i', mandatory: true, help: 'Source ticket ID.') ..addOption('id', abbr: 'i', mandatory: true, help: 'Source ticket ID.')
..addOption( ..addOption(

View file

@ -8,7 +8,7 @@ import '../ticket_store.dart';
class UpdateCommand extends DewCommand with DewToolCommand { class UpdateCommand extends DewCommand with DewToolCommand {
final FileSystem _fs; final FileSystem _fs;
UpdateCommand({FileSystem fs = const LocalFileSystem()}) : _fs = fs { UpdateCommand({this._fs = const LocalFileSystem()}) {
argParser argParser
..addOption('id', abbr: 'i', mandatory: true, help: 'Ticket ID.') ..addOption('id', abbr: 'i', mandatory: true, help: 'Ticket ID.')
..addOption('title', abbr: 't', help: 'New title.') ..addOption('title', abbr: 't', help: 'New title.')

View file

@ -8,7 +8,7 @@ import 'kanban_config.dart';
class KanbanInitHook implements DewInitHook { class KanbanInitHook implements DewInitHook {
final FileSystem _fs; final FileSystem _fs;
KanbanInitHook({FileSystem fs = const LocalFileSystem()}) : _fs = fs; KanbanInitHook({this._fs = const LocalFileSystem()});
@override @override
Future<void> onInit( Future<void> onInit(

View file

@ -218,9 +218,10 @@ class TicketStore {
await found.file.delete(); await found.file.delete();
// Clean up per-ticket attachment directory if present. // Clean up per-ticket attachment directory if present.
final attachmentsDir = fs.directory(p.join(kanbanDir, 'attachments', id)); final attachmentsDir = fs.directory(p.join(kanbanDir, 'attachments', id));
if (await attachmentsDir.exists()) if (await attachmentsDir.exists()) {
await attachmentsDir.delete(recursive: true); await attachmentsDir.delete(recursive: true);
} }
}
/// Searches all column subdirectories (one level deep) for a ticket file. /// Searches all column subdirectories (one level deep) for a ticket file.
/// Skips the [attachments] directory. Includes [archive]. /// Skips the [attachments] directory. Includes [archive].
@ -231,9 +232,10 @@ class TicketStore {
if (entity is! Directory) continue; if (entity is! Directory) continue;
if (p.basename(entity.path) == 'attachments') continue; if (p.basename(entity.path) == 'attachments') continue;
final file = fs.file(p.join(entity.path, '$id.md')); final file = fs.file(p.join(entity.path, '$id.md'));
if (await file.exists()) if (await file.exists()) {
return (file: file, column: p.basename(entity.path)); return (file: file, column: p.basename(entity.path));
} }
}
return null; return null;
} }
@ -243,8 +245,9 @@ class TicketStore {
final pattern = RegExp(r'^' + RegExp.escape(prefix) + r'-(\d+)\.md$'); final pattern = RegExp(r'^' + RegExp.escape(prefix) + r'-(\d+)\.md$');
var max = 0; var max = 0;
await for (final entity in dir.list()) { await for (final entity in dir.list()) {
if (entity is! Directory || p.basename(entity.path) == 'attachments') if (entity is! Directory || p.basename(entity.path) == 'attachments') {
continue; continue;
}
await for (final file in entity.list()) { await for (final file in entity.list()) {
final match = pattern.firstMatch(p.basename(file.path)); final match = pattern.firstMatch(p.basename(file.path));
if (match != null) { if (match != null) {

View file

@ -1,6 +1,6 @@
name: dew_kanban name: dew_kanban
description: Kanban board feature for the Dew project management tool. Implements McpToolProvider to expose board tools to the MCP server. description: Kanban board feature for the Dew project management tool. Implements McpToolProvider to expose board tools to the MCP server.
version: 0.1.0 version: 0.4.0
repository: https://github.com/artificerchris/dew repository: https://github.com/artificerchris/dew
issue_tracker: https://github.com/artificerchris/dew/issues issue_tracker: https://github.com/artificerchris/dew/issues
resolution: workspace resolution: workspace
@ -10,7 +10,7 @@ environment:
# Add regular dependencies here. # Add regular dependencies here.
dependencies: dependencies:
dew_core: ^0.1.0 dew_core: ^0.4.0
dart_console: ^4.1.2 dart_console: ^4.1.2
file: ^7.0.1 file: ^7.0.1
path: ^1.9.0 path: ^1.9.0

View file

@ -1,5 +1,13 @@
# Changelog # Changelog
## 0.4.0 — 2026-05-05
Infra MCP tool release.
- Published with `dew_core` `0.4.0`, which allows commands to expose
path-specific MCP tools through `McpToolProvider`.
- Updated test coverage for provider-supplied MCP tools.
## 0.1.0 — 2026-04-25 ## 0.1.0 — 2026-04-25
Initial release. Initial release.

View file

@ -2,7 +2,8 @@
MCP (Model Context Protocol) server package for the [Dew](https://github.com/artificerchris/dew) project management tool. MCP (Model Context Protocol) server package for the [Dew](https://github.com/artificerchris/dew) project management tool.
Exposes all Dew kanban operations as MCP tools so AI assistants (GitHub Copilot, Claude, etc.) can manage your kanban board directly. Exposes Dew operations as MCP tools so AI assistants (GitHub Copilot, Claude,
etc.) can manage your kanban board and project infrastructure directly.
## Usage ## Usage

View file

@ -5,7 +5,7 @@ import 'package:dew_core/dew_core.dart';
base class DewMcpServer extends MCPServer with ToolsSupport { base class DewMcpServer extends MCPServer with ToolsSupport {
DewMcpServer(super.channel, List<McpTool> tools) DewMcpServer(super.channel, List<McpTool> tools)
: super.fromStreamChannel( : super.fromStreamChannel(
implementation: Implementation(name: 'dew', version: '1.0.0'), implementation: Implementation(name: 'dew', version: '0.4.0'),
instructions: instructions:
'Tools for managing a Dew project (kanban tickets, etc.).', 'Tools for managing a Dew project (kanban tickets, etc.).',
) { ) {

View file

@ -1,6 +1,6 @@
name: dew_mcp name: dew_mcp
description: MCP server for the Dew project management tool. Collects and serves tools registered by feature packages via the McpToolProvider interface. description: MCP server for the Dew project management tool. Collects and serves tools registered by feature packages via the McpToolProvider interface.
version: 0.1.0 version: 0.4.0
repository: https://github.com/artificerchris/dew repository: https://github.com/artificerchris/dew
issue_tracker: https://github.com/artificerchris/dew/issues issue_tracker: https://github.com/artificerchris/dew/issues
resolution: workspace resolution: workspace
@ -10,11 +10,11 @@ environment:
# Add regular dependencies here. # Add regular dependencies here.
dependencies: dependencies:
dew_core: ^0.1.0 dew_core: ^0.4.0
dart_mcp: ^0.5.0 dart_mcp: ^0.5.0
yaml: ^3.1.0 yaml: ^3.1.0
dev_dependencies: dev_dependencies:
lints: ^6.0.0 lints: ^6.0.0
test: ^1.25.6 test: ^1.25.6
dew_kanban: ^0.1.0 dew_kanban: ^0.4.0

View file

@ -1,5 +1,14 @@
# Changelog # Changelog
## 0.4.0 — 2026-05-05
Release alignment for the `0.4.0` Dew package set.
- Updated the `dew_core` dependency constraint for the infra release.
- Published vault with the SDK floor and config path fixes from the development
branch.
- Applied analyzer fixes for current Dart lint recommendations.
## 0.3.0 — 2026-05-03 ## 0.3.0 — 2026-05-03
Implemented vault command behavior end-to-end and completed release-readiness features. Implemented vault command behavior end-to-end and completed release-readiness features.

View file

@ -12,7 +12,7 @@ String renderVaultOutput({
if (format == 'json') { if (format == 'json') {
final payload = <String, dynamic>{ final payload = <String, dynamic>{
'message': message, 'message': message,
if (json != null) ...json, ...?json,
}; };
return const JsonEncoder.withIndent(' ').convert(payload); return const JsonEncoder.withIndent(' ').convert(payload);
} }

View file

@ -9,7 +9,7 @@ import '../command_output.dart';
class DeleteCommand extends DewCommand with DewToolCommand { class DeleteCommand extends DewCommand with DewToolCommand {
final FileSystem _fs; final FileSystem _fs;
DeleteCommand({FileSystem fs = const LocalFileSystem()}) : _fs = fs { DeleteCommand({this._fs = const LocalFileSystem()}) {
argParser argParser
..addOption( ..addOption(
'name', 'name',

View file

@ -9,7 +9,7 @@ import '../vault_generators.dart';
class GenerateCommand extends DewCommand with DewToolCommand { class GenerateCommand extends DewCommand with DewToolCommand {
final FileSystem _fs; final FileSystem _fs;
GenerateCommand({FileSystem fs = const LocalFileSystem()}) : _fs = fs { GenerateCommand({this._fs = const LocalFileSystem()}) {
argParser argParser
..addOption( ..addOption(
'generator', 'generator',

View file

@ -9,7 +9,7 @@ import '../vault_store.dart';
class GetCommand extends DewCommand with DewToolCommand { class GetCommand extends DewCommand with DewToolCommand {
final FileSystem _fs; final FileSystem _fs;
GetCommand({FileSystem fs = const LocalFileSystem()}) : _fs = fs { GetCommand({this._fs = const LocalFileSystem()}) {
argParser argParser
..addOption('name', abbr: 'n', mandatory: true, help: 'Secret name.') ..addOption('name', abbr: 'n', mandatory: true, help: 'Secret name.')
..addOption( ..addOption(

View file

@ -8,7 +8,7 @@ import '../command_output.dart';
class VaultInitCommand extends DewCommand with DewToolCommand { class VaultInitCommand extends DewCommand with DewToolCommand {
final FileSystem _fs; final FileSystem _fs;
VaultInitCommand({FileSystem fs = const LocalFileSystem()}) : _fs = fs { VaultInitCommand({this._fs = const LocalFileSystem()}) {
argParser argParser
..addOption( ..addOption(
'password-file', 'password-file',

View file

@ -9,7 +9,7 @@ import '../command_output.dart';
class ListCommand extends DewCommand with DewToolCommand { class ListCommand extends DewCommand with DewToolCommand {
final FileSystem _fs; final FileSystem _fs;
ListCommand({FileSystem fs = const LocalFileSystem()}) : _fs = fs { ListCommand({this._fs = const LocalFileSystem()}) {
argParser.addOption( argParser.addOption(
'format', 'format',
defaultsTo: 'default', defaultsTo: 'default',

View file

@ -9,7 +9,7 @@ import '../vault_store.dart';
class RenameCommand extends DewCommand with DewToolCommand { class RenameCommand extends DewCommand with DewToolCommand {
final FileSystem _fs; final FileSystem _fs;
RenameCommand({FileSystem fs = const LocalFileSystem()}) : _fs = fs { RenameCommand({this._fs = const LocalFileSystem()}) {
argParser argParser
..addOption( ..addOption(
'from', 'from',

View file

@ -10,7 +10,7 @@ import '../vault_store.dart';
class RotateCommand extends DewCommand with DewToolCommand { class RotateCommand extends DewCommand with DewToolCommand {
final FileSystem _fs; final FileSystem _fs;
RotateCommand({FileSystem fs = const LocalFileSystem()}) : _fs = fs { RotateCommand({this._fs = const LocalFileSystem()}) {
argParser argParser
..addOption('name', help: 'Secret name to rotate; omit to rotate vault password.') ..addOption('name', help: 'Secret name to rotate; omit to rotate vault password.')
..addOption( ..addOption(

View file

@ -9,7 +9,7 @@ import '../vault_store.dart';
class SetCommand extends DewCommand with DewToolCommand { class SetCommand extends DewCommand with DewToolCommand {
final FileSystem _fs; final FileSystem _fs;
SetCommand({FileSystem fs = const LocalFileSystem()}) : _fs = fs { SetCommand({this._fs = const LocalFileSystem()}) {
argParser argParser
..addOption( ..addOption(
'name', 'name',

View file

@ -9,7 +9,7 @@ import '../command_output.dart';
class UpdateCommand extends DewCommand with DewToolCommand { class UpdateCommand extends DewCommand with DewToolCommand {
final FileSystem _fs; final FileSystem _fs;
UpdateCommand({FileSystem fs = const LocalFileSystem()}) : _fs = fs { UpdateCommand({this._fs = const LocalFileSystem()}) {
argParser argParser
..addOption( ..addOption(
'name', 'name',

View file

@ -6,7 +6,7 @@ import 'package:path/path.dart' as p;
class VaultInitHook implements DewInitHook { class VaultInitHook implements DewInitHook {
final FileSystem _fs; final FileSystem _fs;
VaultInitHook({FileSystem fs = const LocalFileSystem()}) : _fs = fs; VaultInitHook({this._fs = const LocalFileSystem()});
@override @override
Future<void> onInit( Future<void> onInit(

View file

@ -1,6 +1,6 @@
name: dew_vault name: dew_vault
description: Vault feature package for the Dew project management tool. description: Vault feature package for the Dew project management tool.
version: 0.3.0 version: 0.4.0
repository: https://github.com/artificerchris/dew repository: https://github.com/artificerchris/dew
issue_tracker: https://github.com/artificerchris/dew/issues issue_tracker: https://github.com/artificerchris/dew/issues
resolution: workspace resolution: workspace
@ -9,7 +9,7 @@ environment:
sdk: ^3.12.0 sdk: ^3.12.0
dependencies: dependencies:
dew_core: ^0.1.0 dew_core: ^0.4.0
file: ^7.0.1 file: ^7.0.1
pointycastle: ^4.0.0 pointycastle: ^4.0.0
path: ^1.9.0 path: ^1.9.0

View file

@ -154,7 +154,7 @@ packages:
source: hosted source: hosted
version: "4.1.4" version: "4.1.4"
dart_mcp: dart_mcp:
dependency: transitive dependency: "direct dev"
description: description:
name: dart_mcp name: dart_mcp
sha256: "92a2ee1cca577ed54fa4f3d5ae0e80ce2dd61e1892e793bb4f951b30eab9c4b1" sha256: "92a2ee1cca577ed54fa4f3d5ae0e80ce2dd61e1892e793bb4f951b30eab9c4b1"

View file

@ -1,6 +1,6 @@
name: dew_workspace name: dew_workspace
description: A Dart workspace for the dew project management tool. description: A Dart workspace for the dew project management tool.
version: 0.1.0 version: 0.4.0
repository: https://github.com/artificerchris/dew repository: https://github.com/artificerchris/dew
publish_to: none publish_to: none
@ -15,11 +15,10 @@ workspace:
- packages/vault - packages/vault
dev_dependencies: dev_dependencies:
dart_mcp: ^0.5.0
file: ^7.0.1 file: ^7.0.1
lints: ^6.0.0 lints: ^6.0.0
melos: ^7.0.0 melos: ^7.0.0
melos: melos:
scripts: scripts:
analyze: analyze: