kin: KIN-048 Post-pipeline hook: автокоммит после успешного завершения задачи. git add -A && git commit -m 'kin: TASK_ID TITLE'. Срабатывает автоматически как rebuild-frontend.
This commit is contained in:
parent
8a6f280cbd
commit
ae21e48b65
13 changed files with 1554 additions and 65 deletions
|
|
@ -110,6 +110,7 @@ def _slim_project(project: dict) -> dict:
|
|||
"path": project["path"],
|
||||
"tech_stack": project.get("tech_stack"),
|
||||
"language": project.get("language", "ru"),
|
||||
"execution_mode": project.get("execution_mode"),
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
49
core/db.py
49
core/db.py
|
|
@ -216,12 +216,61 @@ def _migrate(conn: sqlite3.Connection):
|
|||
conn.execute("ALTER TABLE tasks ADD COLUMN blocked_reason TEXT")
|
||||
conn.commit()
|
||||
|
||||
if "autocommit_enabled" not in proj_cols:
|
||||
conn.execute("ALTER TABLE projects ADD COLUMN autocommit_enabled INTEGER DEFAULT 0")
|
||||
conn.commit()
|
||||
|
||||
# Rename legacy 'auto' → 'auto_complete' (KIN-063)
|
||||
conn.execute(
|
||||
"UPDATE projects SET execution_mode = 'auto_complete' WHERE execution_mode = 'auto'"
|
||||
)
|
||||
conn.execute(
|
||||
"UPDATE tasks SET execution_mode = 'auto_complete' WHERE execution_mode = 'auto'"
|
||||
)
|
||||
conn.commit()
|
||||
|
||||
|
||||
def _seed_default_hooks(conn: sqlite3.Connection):
|
||||
"""Seed default hooks for the kin project (idempotent).
|
||||
|
||||
Creates rebuild-frontend hook only when:
|
||||
- project 'kin' exists in the projects table
|
||||
- the hook doesn't already exist (no duplicate)
|
||||
"""
|
||||
kin_exists = conn.execute(
|
||||
"SELECT 1 FROM projects WHERE id = 'kin'"
|
||||
).fetchone()
|
||||
if not kin_exists:
|
||||
return
|
||||
|
||||
exists = conn.execute(
|
||||
"SELECT 1 FROM hooks"
|
||||
" WHERE project_id = 'kin'"
|
||||
" AND name = 'rebuild-frontend'"
|
||||
" AND event = 'pipeline_completed'"
|
||||
).fetchone()
|
||||
if not exists:
|
||||
conn.execute(
|
||||
"""INSERT INTO hooks (project_id, name, event, command, enabled)
|
||||
VALUES ('kin', 'rebuild-frontend', 'pipeline_completed',
|
||||
'cd /Users/grosfrumos/projects/kin/web/frontend && npm run build',
|
||||
1)"""
|
||||
)
|
||||
conn.commit()
|
||||
|
||||
# Enable autocommit for kin project (opt-in, idempotent)
|
||||
conn.execute(
|
||||
"UPDATE projects SET autocommit_enabled=1 WHERE id='kin' AND autocommit_enabled=0"
|
||||
)
|
||||
conn.commit()
|
||||
|
||||
|
||||
def init_db(db_path: Path = DB_PATH) -> sqlite3.Connection:
|
||||
conn = get_connection(db_path)
|
||||
conn.executescript(SCHEMA)
|
||||
conn.commit()
|
||||
_migrate(conn)
|
||||
_seed_default_hooks(conn)
|
||||
return conn
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,15 @@ VALID_TASK_STATUSES = [
|
|||
"blocked", "decomposed", "cancelled",
|
||||
]
|
||||
|
||||
VALID_COMPLETION_MODES = {"auto_complete", "review"}
|
||||
|
||||
|
||||
def validate_completion_mode(value: str) -> str:
|
||||
"""Validate completion mode from LLM output. Falls back to 'review' if invalid."""
|
||||
if value in VALID_COMPLETION_MODES:
|
||||
return value
|
||||
return "review"
|
||||
|
||||
|
||||
def _row_to_dict(row: sqlite3.Row | None) -> dict | None:
|
||||
"""Convert sqlite3.Row to dict with JSON fields decoded."""
|
||||
|
|
@ -220,6 +229,32 @@ def add_decision(
|
|||
return _row_to_dict(row)
|
||||
|
||||
|
||||
def add_decision_if_new(
|
||||
conn: sqlite3.Connection,
|
||||
project_id: str,
|
||||
type: str,
|
||||
title: str,
|
||||
description: str,
|
||||
category: str | None = None,
|
||||
tags: list | None = None,
|
||||
task_id: str | None = None,
|
||||
) -> dict | None:
|
||||
"""Add a decision only if no existing one matches (project_id, type, normalized title).
|
||||
|
||||
Returns the new decision dict, or None if skipped as duplicate.
|
||||
"""
|
||||
existing = conn.execute(
|
||||
"""SELECT id FROM decisions
|
||||
WHERE project_id = ? AND type = ?
|
||||
AND lower(trim(title)) = lower(trim(?))""",
|
||||
(project_id, type, title),
|
||||
).fetchone()
|
||||
if existing:
|
||||
return None
|
||||
return add_decision(conn, project_id, type, title, description,
|
||||
category=category, tags=tags, task_id=task_id)
|
||||
|
||||
|
||||
def get_decisions(
|
||||
conn: sqlite3.Connection,
|
||||
project_id: str,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue