Add Auto/Review mode toggle and non-interactive runner
- GUI: Auto/Review toggle on TaskDetail and ProjectView
persisted per-project in localStorage
- Runner: noninteractive param (stdin=DEVNULL, 300s timeout)
activated by KIN_NONINTERACTIVE=1 env or param
- CLI: --allow-write flag for kin run command
- API: POST /run accepts {allow_write: bool}, sets
KIN_NONINTERACTIVE=1 and stdin=DEVNULL for subprocess
- Fixes pipeline hanging on interactive claude input (VDOL-002)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
03961500e6
commit
e755a19633
8 changed files with 174 additions and 18 deletions
14
cli/main.py
14
cli/main.py
|
|
@ -4,6 +4,7 @@ Uses core.models for all data access, never raw SQL.
|
|||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
|
@ -481,8 +482,9 @@ def approve_task(ctx, task_id, followup, decision_text):
|
|||
@cli.command("run")
|
||||
@click.argument("task_id")
|
||||
@click.option("--dry-run", is_flag=True, help="Show pipeline plan without executing")
|
||||
@click.option("--allow-write", is_flag=True, help="Allow agents to write files (skip permissions)")
|
||||
@click.pass_context
|
||||
def run_task(ctx, task_id, dry_run):
|
||||
def run_task(ctx, task_id, dry_run, allow_write):
|
||||
"""Run a task through the agent pipeline.
|
||||
|
||||
PM decomposes the task into specialist steps, then the pipeline executes.
|
||||
|
|
@ -497,6 +499,7 @@ def run_task(ctx, task_id, dry_run):
|
|||
raise SystemExit(1)
|
||||
|
||||
project_id = task["project_id"]
|
||||
is_noninteractive = os.environ.get("KIN_NONINTERACTIVE") == "1"
|
||||
click.echo(f"Task: {task['id']} — {task['title']}")
|
||||
|
||||
# Step 1: PM decomposes
|
||||
|
|
@ -504,6 +507,7 @@ def run_task(ctx, task_id, dry_run):
|
|||
pm_result = run_agent(
|
||||
conn, "pm", task_id, project_id,
|
||||
model="sonnet", dry_run=dry_run,
|
||||
allow_write=allow_write, noninteractive=is_noninteractive,
|
||||
)
|
||||
|
||||
if dry_run:
|
||||
|
|
@ -537,13 +541,17 @@ def run_task(ctx, task_id, dry_run):
|
|||
for i, step in enumerate(pipeline_steps, 1):
|
||||
click.echo(f" {i}. {step['role']} ({step.get('model', 'sonnet')}): {step.get('brief', '')}")
|
||||
|
||||
if not click.confirm("\nExecute pipeline?"):
|
||||
if is_noninteractive:
|
||||
click.echo("\n[non-interactive] Auto-executing pipeline...")
|
||||
elif not click.confirm("\nExecute pipeline?"):
|
||||
click.echo("Aborted.")
|
||||
return
|
||||
|
||||
# Step 2: Execute pipeline
|
||||
click.echo("\nExecuting pipeline...")
|
||||
result = run_pipeline(conn, task_id, pipeline_steps)
|
||||
result = run_pipeline(conn, task_id, pipeline_steps,
|
||||
allow_write=allow_write,
|
||||
noninteractive=is_noninteractive)
|
||||
|
||||
if result["success"]:
|
||||
click.echo(f"\nPipeline completed: {result['steps_completed']} steps")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue