kin: KIN-049 Кнопка Deploy на странице задачи после approve. Для каждого проекта настраивается deploy-команда (git push, scp, ssh restart). В Settings проекта.

This commit is contained in:
Gros Frumos 2026-03-16 08:21:13 +02:00
parent 860ef3f6c9
commit d50bd703ae
11 changed files with 517 additions and 61 deletions

View file

@ -53,21 +53,6 @@ def _table(headers: list[str], rows: list[list[str]], min_width: int = 6):
return "\n".join(lines)
def _auto_task_id(conn, project_id: str) -> str:
"""Generate next task ID like PROJ-001."""
prefix = project_id.upper()
existing = models.list_tasks(conn, project_id=project_id)
max_num = 0
for t in existing:
tid = t["id"]
if tid.startswith(prefix + "-"):
try:
num = int(tid.split("-", 1)[1])
max_num = max(max_num, num)
except ValueError:
pass
return f"{prefix}-{max_num + 1:03d}"
# ===========================================================================
# Root group
@ -178,18 +163,28 @@ def task():
@click.argument("title")
@click.option("--type", "route_type", type=click.Choice(["debug", "feature", "refactor", "hotfix"]), default=None)
@click.option("--priority", type=int, default=5)
@click.option("--category", "-c", default=None,
help=f"Task category: {', '.join(models.TASK_CATEGORIES)}")
@click.pass_context
def task_add(ctx, project_id, title, route_type, priority):
"""Add a task to a project. ID is auto-generated (PROJ-001)."""
def task_add(ctx, project_id, title, route_type, priority, category):
"""Add a task to a project. ID is auto-generated (PROJ-001 or PROJ-CAT-001)."""
conn = ctx.obj["conn"]
p = models.get_project(conn, project_id)
if not p:
click.echo(f"Project '{project_id}' not found.", err=True)
raise SystemExit(1)
task_id = _auto_task_id(conn, project_id)
if category:
category = category.upper()
if category not in models.TASK_CATEGORIES:
click.echo(
f"Invalid category '{category}'. Must be one of: {', '.join(models.TASK_CATEGORIES)}",
err=True,
)
raise SystemExit(1)
task_id = models.next_task_id(conn, project_id, category=category)
brief = {"route_type": route_type} if route_type else None
t = models.create_task(conn, task_id, project_id, title,
priority=priority, brief=brief)
priority=priority, brief=brief, category=category)
click.echo(f"Created task: {t['id']}{t['title']}")
@ -588,16 +583,28 @@ def run_task(ctx, task_id, dry_run, allow_write):
# Save completion_mode from PM output to task (only if not already set by user)
task_current = models.get_task(conn, task_id)
update_fields = {}
if not task_current.get("execution_mode"):
pm_completion_mode = models.validate_completion_mode(
output.get("completion_mode", "review")
)
models.update_task(conn, task_id, execution_mode=pm_completion_mode)
update_fields["execution_mode"] = pm_completion_mode
import logging
logging.getLogger("kin").info(
"PM set completion_mode=%s for task %s", pm_completion_mode, task_id
)
# Save category from PM output (only if task has no category yet)
if not task_current.get("category"):
pm_category = output.get("category")
if pm_category and isinstance(pm_category, str):
pm_category = pm_category.upper()
if pm_category in models.TASK_CATEGORIES:
update_fields["category"] = pm_category
if update_fields:
models.update_task(conn, task_id, **update_fields)
click.echo(f"\nAnalysis: {analysis}")
click.echo(f"Pipeline ({len(pipeline_steps)} steps):")
for i, step in enumerate(pipeline_steps, 1):