From 628761473f873eb62b4129616737cd1a213c5e35 Mon Sep 17 00:00:00 2001 From: Michael Chihlas Date: Sat, 21 Mar 2026 17:17:41 -0400 Subject: [PATCH] feat: add language column, AI Generated category, and mine/shared filters - Add language column (powershell/bash/python) to script_templates model and schemas - Seed 'AI Generated' script category via migration 063 - Add mine and shared query params to list_templates endpoint Co-Authored-By: Claude Opus 4.6 (1M context) --- ...dd_language_to_script_templates_and_ai_.py | 55 +++++++++++++++++++ backend/app/api/endpoints/scripts.py | 8 +++ backend/app/models/script_template.py | 4 ++ backend/app/schemas/script_template.py | 2 + 4 files changed, 69 insertions(+) create mode 100644 backend/alembic/versions/063_add_language_to_script_templates_and_ai_.py diff --git a/backend/alembic/versions/063_add_language_to_script_templates_and_ai_.py b/backend/alembic/versions/063_add_language_to_script_templates_and_ai_.py new file mode 100644 index 00000000..fef95de5 --- /dev/null +++ b/backend/alembic/versions/063_add_language_to_script_templates_and_ai_.py @@ -0,0 +1,55 @@ +"""add_language_to_script_templates_and_ai_generated_category + +Revision ID: 063 +Revises: 062 +Create Date: 2026-03-21 21:13:32.239533 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '063' +down_revision: Union[str, None] = '062' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # Add language column to script_templates + op.add_column( + 'script_templates', + sa.Column( + 'language', + sa.String(length=30), + nullable=True, + comment='Script language: powershell, bash, python', + ), + ) + + # Seed "AI Generated" category + op.execute( + sa.text(""" + INSERT INTO script_categories (id, name, slug, description, icon, sort_order, is_active, created_at, updated_at) + VALUES ( + 'a0000000-0000-0000-0000-000000000001'::uuid, + 'AI Generated', + 'ai-generated', + 'Scripts generated by the AI Script Builder', + 'sparkles', + 100, + true, + NOW(), + NOW() + ) + ON CONFLICT (slug) DO NOTHING + """) + ) + + +def downgrade() -> None: + op.execute(sa.text("DELETE FROM script_categories WHERE slug = 'ai-generated'")) + op.drop_column('script_templates', 'language') diff --git a/backend/app/api/endpoints/scripts.py b/backend/app/api/endpoints/scripts.py index 182f837e..da8d8955 100644 --- a/backend/app/api/endpoints/scripts.py +++ b/backend/app/api/endpoints/scripts.py @@ -76,6 +76,8 @@ async def list_templates( search: Optional[str] = Query(None), tags: Optional[str] = Query(None, description="Comma-separated tags"), managed: Optional[bool] = Query(None, description="If true, return only templates this user can edit"), + mine: bool = Query(False, description="If true, return only templates created by the current user"), + shared: bool = Query(False, description="If true, return only templates shared with the user's team"), ) -> list[ScriptTemplateListItem]: query = ( select(ScriptTemplate) @@ -116,6 +118,12 @@ async def list_templates( # engineers see only their own query = query.where(ScriptTemplate.created_by == current_user.id) + if mine: + query = query.where(ScriptTemplate.created_by == current_user.id) + + if shared: + query = query.where(ScriptTemplate.team_id == current_user.team_id) + result = await db.execute(query.order_by(ScriptTemplate.name)) templates = result.scalars().all() diff --git a/backend/app/models/script_template.py b/backend/app/models/script_template.py index 84ddfb19..d120d37c 100644 --- a/backend/app/models/script_template.py +++ b/backend/app/models/script_template.py @@ -52,6 +52,10 @@ class ScriptTemplate(Base): description: Mapped[Optional[str]] = mapped_column(Text, nullable=True) use_case: Mapped[Optional[str]] = mapped_column(Text, nullable=True) script_body: Mapped[str] = mapped_column(Text, nullable=False) + language: Mapped[Optional[str]] = mapped_column( + String(30), nullable=True, default="powershell", + comment="Script language: powershell, bash, python", + ) parameters_schema: Mapped[dict] = mapped_column(JSONB, nullable=False, default=dict) default_values: Mapped[dict] = mapped_column(JSONB, nullable=False, default=dict) validation_rules: Mapped[dict] = mapped_column(JSONB, nullable=False, default=dict) diff --git a/backend/app/schemas/script_template.py b/backend/app/schemas/script_template.py index de6ec106..fbaaa45d 100644 --- a/backend/app/schemas/script_template.py +++ b/backend/app/schemas/script_template.py @@ -62,6 +62,7 @@ class ScriptTemplateCreate(BaseModel): estimated_runtime: Optional[str] = None requires_elevation: bool = False requires_modules: list[str] = Field(default_factory=list) + language: str | None = None class ScriptTemplateUpdate(BaseModel): name: Optional[str] = Field(None, min_length=1, max_length=200) @@ -93,6 +94,7 @@ class ScriptTemplateListItem(BaseModel): requires_modules: list[str] is_verified: bool usage_count: int + language: str | None = None class Config: from_attributes = True