agentydragon(tasks): normalize task 18 front-matter; add manager_utils package

This commit is contained in:
Rai (Michael Pokorny)
2025-06-24 17:32:40 -07:00
parent 0e2afc0378
commit d78cfb6330
5 changed files with 168 additions and 2 deletions

View File

@@ -3,7 +3,7 @@
---
id: 18
title: Chat UI Textarea Overlay and Border Styling Fix
status: Not started # one of: Not started, Started, Needs manual review, Done, Cancelled
status: Not started
summary: Fix overlay of waiting messages and streamline borders between chat window and input area to improve visibility and reclaim terminal space.
goal: |
Adjust the TUI chat interface so that waiting/status messages no longer overlay the first line of the input textarea (ensuring user drafts remain visible), and merge/remove borders as follows:
@@ -39,4 +39,4 @@ goal: |
Run the frontmatter linter to ensure conformance:
```bash
python3 agentydragon/tools/check_task_frontmatter.py agentydragon/tasks/18-chat-ui-textarea-overlay-border-fix.md
```
```

View File

@@ -0,0 +1,4 @@
"""
agentydragon manager utilities package.
"""
__version__ = '0.1'

View File

@@ -0,0 +1,93 @@
"""
CLI for managing agentydragon tasks: status, set-status, set-deps, dispose, launch.
"""
import sys
import subprocess
from datetime import datetime
from pathlib import Path
import click
from tasklib import load_task, save_task, TaskMeta
TASK_DIR = Path(__file__).parent.parent / 'tasks'
@click.group()
def cli():
"""Manage agentydragon tasks."""
pass
@cli.command()
def status():
"""Show a table of task id, title, status, dependencies, last_updated"""
rows = []
for md in sorted(TASK_DIR.glob('[0-9][0-9]-*.md')):
meta, _ = load_task(md)
rows.append((meta.id, meta.title, meta.status,
meta.dependencies.replace('\n', ' '),
meta.last_updated.strftime('%Y-%m-%d %H:%M')))
# simple table
fmt = '{:>2} {:<50} {:<12} {:<30} {:<16}'
print(fmt.format('ID','Title','Status','Dependencies','Updated'))
for r in rows:
print(fmt.format(*r))
@cli.command()
@click.argument('task_id')
@click.argument('status')
def set_status(task_id, status):
"""Set status of TASK_ID to STATUS"""
md = TASK_DIR / f"{task_id}-*.md"
files = list(TASK_DIR.glob(f'{task_id}-*.md'))
if not files:
click.echo(f'Task {task_id} not found', err=True)
sys.exit(1)
path = files[0]
meta, body = load_task(path)
meta.status = status
meta.last_updated = datetime.utcnow()
save_task(path, meta, body)
@cli.command()
@click.argument('task_id')
@click.argument('deps', nargs=-1)
def set_deps(task_id, deps):
"""Set dependencies of TASK_ID"""
files = list(TASK_DIR.glob(f'{task_id}-*.md'))
if not files:
click.echo(f'Task {task_id} not found', err=True)
sys.exit(1)
path = files[0]
meta, body = load_task(path)
now = datetime.utcnow().isoformat()
meta.dependencies = f'as of {now}: ' + ', '.join(deps)
meta.last_updated = datetime.utcnow()
save_task(path, meta, body)
@cli.command()
@click.argument('task_id', nargs=-1)
def dispose(task_id):
"""Dispose worktree and delete branch for TASK_ID(s)"""
for tid in task_id:
branch = f'agentydragon-{tid}-*'
# remove worktree
subprocess.run(['git', 'worktree', 'remove', f'tasks/.worktrees/{tid}-*', '--force'])
# delete branch
subprocess.run(['git', 'branch', '-D', f'agentydragon-{tid}-*'])
click.echo(f'Disposed task {tid}')
@cli.command()
@click.argument('task_id', nargs=-1)
def launch(task_id):
"""Copy tmux launch one-liner for TASK_ID(s) to clipboard"""
cmd = ['create-task-worktree.sh', '--agent', '--tmux'] + list(task_id)
line = ' '.join(cmd)
# system clipboard
try:
subprocess.run(['pbcopy'], input=line.encode(), check=True)
click.echo('Copied to clipboard:')
except FileNotFoundError:
click.echo(line)
return
click.echo(line)
if __name__ == '__main__':
cli()

View File

@@ -0,0 +1,35 @@
"""
Simple library for loading and saving task metadata embedded as TOML front-matter
in task Markdown files.
"""
import re
import toml
from pathlib import Path
from datetime import datetime
from pydantic import BaseModel, Field
FRONTMATTER_RE = re.compile(r"^\+\+\+\s*(.*?)\s*\+\+\+", re.S | re.M)
class TaskMeta(BaseModel):
id: str
title: str
status: str
dependencies: str = Field(default="")
last_updated: datetime = Field(default_factory=datetime.utcnow)
def load_task(path: Path) -> (TaskMeta, str):
text = path.read_text(encoding='utf-8')
m = FRONTMATTER_RE.match(text)
if not m:
raise ValueError(f"No TOML frontmatter in {path}")
meta = toml.loads(m.group(1))
tm = TaskMeta(**meta)
body = text[m.end():].lstrip('\n')
return tm, body
def save_task(path: Path, meta: TaskMeta, body: str) -> None:
tm = meta.dict()
tm['last_updated'] = meta.last_updated.isoformat()
fm = toml.dumps(tm).strip()
content = f"+++\n{fm}\n+++\n\n{body.lstrip()}"
path.write_text(content, encoding='utf-8')

View File

@@ -0,0 +1,34 @@
import tempfile
from pathlib import Path
import toml
import pytest
from agentydragon.tools.manager_utils.tasklib import TaskMeta, load_task, save_task
SAMPLE = ''''''
++
id = "99"
title = "Sample Task"
status = "Not started"
dependencies = ""
last_updated = "2023-01-01T12:00:00"
++
# Body here
'''
def test_load_and_save(tmp_path):
md = tmp_path / '99-sample.md'
md.write_text(SAMPLE)
meta, body = load_task(md)
assert meta.id == '99'
assert 'Body here' in body
meta.status = 'Done'
save_task(md, meta, body)
text = md.read_text()
data = toml.loads(text.split('+++')[1])
assert data['status'] == 'Done'
def test_meta_model_validation():
with pytest.raises(ValueError):
TaskMeta(id='a', title='t', status='bogus', dependencies='', last_updated='bad')