Add bootstrap command — auto-detect project stack, modules, decisions
kin bootstrap <path> --id <id> --name <name> [--vault <path>] Detects: package.json, requirements.txt, go.mod, config files → tech_stack. Scans src/app/lib/frontend/backend dirs → modules with type detection. Parses CLAUDE.md for GOTCHA/WORKAROUND/FIXME/ВАЖНО → decisions. Scans Obsidian vault for kanban tasks, checkboxes, and decisions. Preview before save, -y to skip confirmation. 18 bootstrap tests, 57 total passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
432cfd55d4
commit
da4a8aae72
4 changed files with 957 additions and 0 deletions
69
cli/main.py
69
cli/main.py
|
|
@ -14,6 +14,10 @@ sys.path.insert(0, str(Path(__file__).parent.parent))
|
|||
|
||||
from core.db import init_db
|
||||
from core import models
|
||||
from agents.bootstrap import (
|
||||
detect_tech_stack, detect_modules, extract_decisions_from_claude_md,
|
||||
find_vault_root, scan_obsidian, format_preview, save_to_db,
|
||||
)
|
||||
|
||||
DEFAULT_DB = Path.home() / ".kin" / "kin.db"
|
||||
|
||||
|
|
@ -404,6 +408,71 @@ def cost(ctx, period):
|
|||
click.echo(f"\nTotal: ${total:.4f}")
|
||||
|
||||
|
||||
# ===========================================================================
|
||||
# bootstrap
|
||||
# ===========================================================================
|
||||
|
||||
@cli.command("bootstrap")
|
||||
@click.argument("path", type=click.Path(exists=True))
|
||||
@click.option("--id", "project_id", required=True, help="Short project ID (e.g. vdol)")
|
||||
@click.option("--name", required=True, help="Project display name")
|
||||
@click.option("--vault", "vault_path", type=click.Path(), default=None,
|
||||
help="Obsidian vault path (auto-detected if omitted)")
|
||||
@click.option("-y", "--yes", is_flag=True, help="Skip confirmation")
|
||||
@click.pass_context
|
||||
def bootstrap(ctx, path, project_id, name, vault_path, yes):
|
||||
"""Auto-detect project stack, modules, decisions and import into Kin."""
|
||||
conn = ctx.obj["conn"]
|
||||
project_path = Path(path).expanduser().resolve()
|
||||
|
||||
# Check if project already exists
|
||||
existing = models.get_project(conn, project_id)
|
||||
if existing:
|
||||
click.echo(f"Project '{project_id}' already exists. Use 'kin project show {project_id}'.", err=True)
|
||||
raise SystemExit(1)
|
||||
|
||||
# Detect everything
|
||||
click.echo(f"Scanning {project_path} ...")
|
||||
tech_stack = detect_tech_stack(project_path)
|
||||
modules = detect_modules(project_path)
|
||||
decisions = extract_decisions_from_claude_md(project_path)
|
||||
|
||||
# Obsidian
|
||||
obsidian = None
|
||||
vault_root = find_vault_root(Path(vault_path) if vault_path else None)
|
||||
if vault_root:
|
||||
dir_name = project_path.name
|
||||
obsidian = scan_obsidian(vault_root, project_id, name, dir_name)
|
||||
if not obsidian["tasks"] and not obsidian["decisions"]:
|
||||
obsidian = None # Nothing found, don't clutter output
|
||||
|
||||
# Preview
|
||||
click.echo("")
|
||||
click.echo(format_preview(
|
||||
project_id, name, str(project_path), tech_stack,
|
||||
modules, decisions, obsidian,
|
||||
))
|
||||
click.echo("")
|
||||
|
||||
if not yes:
|
||||
if not click.confirm("Save to kin.db?"):
|
||||
click.echo("Aborted.")
|
||||
return
|
||||
|
||||
save_to_db(conn, project_id, name, str(project_path),
|
||||
tech_stack, modules, decisions, obsidian)
|
||||
|
||||
# Summary
|
||||
task_count = 0
|
||||
dec_count = len(decisions)
|
||||
if obsidian:
|
||||
task_count += len(obsidian.get("tasks", []))
|
||||
dec_count += len(obsidian.get("decisions", []))
|
||||
|
||||
click.echo(f"Saved: 1 project, {len(modules)} modules, "
|
||||
f"{dec_count} decisions, {task_count} tasks.")
|
||||
|
||||
|
||||
# ===========================================================================
|
||||
# Entry point
|
||||
# ===========================================================================
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue