Added proper agents config for mcp servers and skills.
Added the menu category delete route. Fix: Show blank vouchers in tables Fix: In rare cases, the old settements can stick around. fixed it.
This commit is contained in:
31
.agents/mcp_config.json
Normal file
31
.agents/mcp_config.json
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"postgres-db": {
|
||||
"command": "npx",
|
||||
"args": [
|
||||
"-y",
|
||||
"@modelcontextprotocol/server-postgres",
|
||||
"${POSTGRES_URL}"
|
||||
],
|
||||
"env": {
|
||||
"POSTGRES_URL": "${env:SQLALCHEMY_DATABASE_URI}"
|
||||
}
|
||||
},
|
||||
"filesystem": {
|
||||
"command": "npx",
|
||||
"args": [
|
||||
"-y",
|
||||
"@modelcontextprotocol/server-filesystem",
|
||||
"."
|
||||
]
|
||||
},
|
||||
"angular-cli": {
|
||||
"command": "npx",
|
||||
"args": [
|
||||
"-y",
|
||||
"@angular/cli",
|
||||
"mcp"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
30
.agents/rules.md
Normal file
30
.agents/rules.md
Normal file
@ -0,0 +1,30 @@
|
||||
# Project Architecture & Guidelines
|
||||
|
||||
## Monorepo Layout
|
||||
- **Backend**: `./barker` (Python, managed via `uv`)
|
||||
- **Frontend**: `./bookie` (Angular, managed via `npm`/`ng`)
|
||||
- **Environment**: Root `.env` file containing shared/database variables.
|
||||
|
||||
---
|
||||
|
||||
## Execution Rules & Commands
|
||||
|
||||
### Backend (`/barker`)
|
||||
- **Working Directory**: Always execute backend commands within `./barker` OR pass `--directory barker` to `uv`.
|
||||
- **Package & Script Management**:
|
||||
- Use `uv` exclusively. Never run bare `pip` or global `python`.
|
||||
- Run scripts/tests: `uv run python <script>` or `uv run pytest` from inside `./barker`.
|
||||
- Manage dependencies: `uv add <package>` or `uv remove <package>`.
|
||||
- **Environment**: The root `.env` file (`../.env` relative to `barker`) contains the database connection string and secret keys.
|
||||
|
||||
### Frontend (`/bookie`)
|
||||
- **Working Directory**: Always execute frontend commands within `./bookie`.
|
||||
- **Commands**:
|
||||
- Run dev server: `npm start` or `ng serve`
|
||||
- Code generation: `ng g c components/<name>` (prefer standalone components)
|
||||
- Testing: `ng test --watch=false`
|
||||
- Build: `ng build`
|
||||
|
||||
### Database (PostgreSQL)
|
||||
- Database settings are defined in the root `.env`.
|
||||
- Inspect schema, run migrations, or check types strictly via database tools or `uv run` scripts inside `./barker`.
|
||||
17
.agents/skills/angular-signals/SKILL.md
Normal file
17
.agents/skills/angular-signals/SKILL.md
Normal file
@ -0,0 +1,17 @@
|
||||
---
|
||||
name: angular-signals
|
||||
description: Writes modern Angular code using Standalone Components and Signals. Use whenever generating frontend UI or state logic.
|
||||
---
|
||||
# Modern Angular Standards
|
||||
|
||||
## Architecture
|
||||
- NEVER generate or use `NgModule`.
|
||||
- ALL components, directives, and pipes must be `standalone: true`.
|
||||
|
||||
## State Management (Signals)
|
||||
- Prefer Angular Signals (`signal()`, `computed()`, `effect()`) for synchronous, local component state instead of `BehaviorSubject`.
|
||||
- Use the new Signal-based inputs and outputs: `input()` and `output()` instead of `@Input()` and `@Output()`.
|
||||
- Use standard RxJS strictly for asynchronous streams (e.g., HTTP requests, router events).
|
||||
|
||||
## Control Flow
|
||||
- Use the new built-in control flow (`@if`, `@for`, `@switch`) instead of structural directives (`*ngIf`, `*ngFor`).
|
||||
16
.agents/skills/fastapi-pydantic-v2/SKILL.md
Normal file
16
.agents/skills/fastapi-pydantic-v2/SKILL.md
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
name: fastapi-pydantic-v2
|
||||
description: Writes modern FastAPI endpoints using Pydantic v2 syntax and Annotated dependencies. Use whenever generating or refactoring API routes.
|
||||
---
|
||||
# FastAPI & Pydantic v2 Standards
|
||||
|
||||
## Pydantic v2 Rules
|
||||
- NEVER use `.dict()` or `.json()`. ALWAYS use `.model_dump()` and `.model_dump_json()`.
|
||||
- NEVER use `@validator` or `@root_validator`. ALWAYS use `@field_validator` and `@model_validator(mode="before|after")`.
|
||||
- NEVER use `schema_extra`. Use `json_schema_extra` inside the `model_config` dict.
|
||||
- Use `pydantic_settings.BaseSettings` for all environment variable configurations.
|
||||
|
||||
## FastAPI Rules
|
||||
- ALWAYS use `typing.Annotated` for dependency injection (e.g., `db: Annotated[AsyncSession, Depends(get_db)]`).
|
||||
- Do not use `def` for endpoints that perform I/O; always use `async def`.
|
||||
- Raise `fastapi.HTTPException` for expected client errors instead of returning standard dictionaries.
|
||||
19
.agents/skills/sqlalchemy-async/SKILL.md
Normal file
19
.agents/skills/sqlalchemy-async/SKILL.md
Normal file
@ -0,0 +1,19 @@
|
||||
---
|
||||
name: sqlalchemy-async
|
||||
description: Writes SQLAlchemy 2.0 async database queries using Psycopg3. Use whenever interacting with the PostgreSQL database.
|
||||
---
|
||||
# SQLAlchemy 2.0 Async Standards
|
||||
|
||||
## Model Definitions
|
||||
- ALWAYS use SQLAlchemy 2.0 Declarative Base with `Mapped` and `mapped_column`.
|
||||
- Example: `id: Mapped[int] = mapped_column(primary_key=True)`
|
||||
- NEVER use the old `Column(Integer, primary_key=True)` syntax.
|
||||
|
||||
## Querying
|
||||
- NEVER use `session.query()`. This is legacy 1.x syntax.
|
||||
- ALWAYS use `sqlalchemy.select()` combined with `await session.execute(stmt)`.
|
||||
- Extract results using `.scalars().all()` or `.scalar_one_or_none()`.
|
||||
|
||||
## Session Management
|
||||
- Use `sqlalchemy.ext.asyncio.AsyncSession`.
|
||||
- Ensure all database I/O is awaited.
|
||||
30
.agents/skills/strict-mypy/SKILL.md
Normal file
30
.agents/skills/strict-mypy/SKILL.md
Normal file
@ -0,0 +1,30 @@
|
||||
---
|
||||
name: strict-mypy
|
||||
description: Enforces strict MyPy type checking conventions matching the project's pyproject.toml configuration. Use whenever writing or modifying Python code.
|
||||
---
|
||||
# Strict MyPy Typing Standards
|
||||
|
||||
This project uses a highly strict MyPy configuration (`strict = true`). When writing or refactoring Python code, you MUST adhere to the following rules to ensure the code passes type checking:
|
||||
|
||||
## 1. Absolute Signature Completeness
|
||||
- EVERY function and method must have fully typed arguments and a return type (`disallow_untyped_defs`, `disallow_incomplete_defs`).
|
||||
- ALWAYS specify `-> None` for functions and methods that do not return a value.
|
||||
- NEVER leave type hints partially defined (e.g., typing only some arguments).
|
||||
- Decorators must be completely type-hinted using `typing.Callable` or `typing.ParamSpec` (`disallow_untyped_decorators`).
|
||||
|
||||
## 2. No Implicit Optionals
|
||||
- `arg: str = None` is strictly forbidden.
|
||||
- You MUST explicitly union with None: `arg: str | None = None` (`no_implicit_optional`).
|
||||
|
||||
## 3. Strict Generics
|
||||
- NEVER use bare collections as types.
|
||||
- ALWAYS specify the inner types: use `list[str]`, `dict[str, int]`, or `tuple[int, ...]` instead of bare `list`, `dict`, or `tuple` (`disallow_any_generics`).
|
||||
|
||||
## 4. Restrictions on `Any` and Subclassing
|
||||
- Do not subclass from untyped external libraries or `Any` (`disallow_subclassing_any`).
|
||||
- Avoid using external types that have no stubs and resolve to `Any` (`disallow_any_unimported`).
|
||||
- Refrain from returning `Any` to avoid contaminating the type inference of calling functions (`warn_return_any`).
|
||||
|
||||
## 5. Pydantic Strictness
|
||||
- `pydantic.mypy` is active with strict initialization.
|
||||
- When instantiating Pydantic models, ensure you are passing exactly the expected typed arguments, as extra or untyped arguments will fail type checks (`init_typed = true`, `init_forbid_extra = true`).
|
||||
4
.vscode/launch.json
vendored
4
.vscode/launch.json
vendored
@ -6,14 +6,14 @@
|
||||
"configurations": [
|
||||
{
|
||||
"name": "ng serve",
|
||||
"type": "pwa-chrome",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "npm: start",
|
||||
"url": "http://localhost:4200/"
|
||||
},
|
||||
{
|
||||
"name": "Python: FastAPI",
|
||||
"type": "python",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"module": "barker",
|
||||
"args": [],
|
||||
|
||||
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@ -9,7 +9,7 @@
|
||||
"python.analysis.extraPaths": [
|
||||
"./barker"
|
||||
],
|
||||
// Ruff (you have ruff installed via poetry)
|
||||
// Ruff (you have ruff installed via uv)
|
||||
"editor.formatOnSave": true,
|
||||
"[python]": {
|
||||
"editor.defaultFormatter": "charliermarsh.ruff"
|
||||
|
||||
3
.vscode/tasks.json
vendored
3
.vscode/tasks.json
vendored
@ -27,6 +27,9 @@
|
||||
"type": "npm",
|
||||
"script": "test",
|
||||
"isBackground": true,
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/bookie"
|
||||
},
|
||||
"problemMatcher": {
|
||||
"owner": "typescript",
|
||||
"pattern": "$tsc",
|
||||
|
||||
11
Dockerfile
11
Dockerfile
@ -32,8 +32,8 @@ RUN \
|
||||
FROM python:3.14-slim AS runner
|
||||
LABEL maintainer="Amritanshu <docker@tanshu.com>"
|
||||
|
||||
RUN apt update \
|
||||
&& apt install -y --no-install-recommends curl \
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends curl \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install uv
|
||||
@ -70,16 +70,13 @@ COPY --from=builder /frontend/browser /app/static
|
||||
# Sync the project
|
||||
# Ref: https://docs.astral.sh/uv/guides/integration/docker/#intermediate-layers
|
||||
RUN --mount=type=cache,target=/root/.cache/uv \
|
||||
--mount=type=bind,source=barker/uv.lock,target=uv.lock \
|
||||
--mount=type=bind,source=barker/pyproject.toml,target=pyproject.toml \
|
||||
uv sync --locked
|
||||
|
||||
ENV PYTHONPATH=/app
|
||||
EXPOSE 80
|
||||
|
||||
RUN chmod 777 /app/docker-entrypoint.sh \
|
||||
&& ln -s /app/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh \
|
||||
&& ln -s /app/docker-entrypoint.sh /
|
||||
RUN chmod +x /app/docker-entrypoint.sh \
|
||||
&& ln -s /app/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
|
||||
ENTRYPOINT ["docker-entrypoint.sh"]
|
||||
|
||||
# at the end of your Dockerfile, before CMD or after EXPOSE
|
||||
|
||||
3
Makefile
3
Makefile
@ -20,8 +20,9 @@ build-check: ## Multi-arch build without push (compile check)
|
||||
.PHONY: build-check-local
|
||||
build-check-local: ## Multi-arch build without push (compile check)
|
||||
@git archive --format=tar HEAD | docker buildx build \
|
||||
--platform linux/amd64,linux/arm64/v8 \
|
||||
--platform linux/amd64 \
|
||||
--tag barker:test \
|
||||
--pull \
|
||||
--progress=plain \
|
||||
--load \
|
||||
-
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# Installation (Ubuntu)
|
||||
# Installation linux
|
||||
|
||||
This project uses **pyenv** for Python version management and **Poetry** for dependency and virtual environment management.
|
||||
This project uses **uv** for Python version, dependency and virtual environment management.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
"""inculded roles
|
||||
"""role includes
|
||||
|
||||
Revision ID: 5cb65066be86
|
||||
Revises: 367ecf7b898f
|
||||
@ -4,9 +4,8 @@ import uvicorn
|
||||
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.responses import JSONResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from sqlalchemy.exc import IntegrityError, SQLAlchemyError
|
||||
|
||||
# from fastapi.staticfiles import StaticFiles
|
||||
from starlette.middleware.sessions import SessionMiddleware
|
||||
|
||||
from .core.config import settings
|
||||
@ -149,7 +148,7 @@ app.include_router(split.router, prefix="/api", tags=["voucher"])
|
||||
app.include_router(change.router, prefix="/api/voucher", tags=["voucher"])
|
||||
|
||||
app.include_router(health.router, prefix="/health", tags=["health"])
|
||||
# app.mount("/static", StaticFiles(directory="static"), name="static")
|
||||
app.mount("/static", StaticFiles(directory="static"), name="static")
|
||||
|
||||
|
||||
def init() -> None:
|
||||
|
||||
@ -4,7 +4,7 @@ from datetime import date
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Security, status
|
||||
from sqlalchemy import distinct, select, update
|
||||
from sqlalchemy import distinct, func, select, update
|
||||
|
||||
from ..core.security import get_current_active_user as get_user
|
||||
from ..db.session import SessionDep
|
||||
@ -64,21 +64,26 @@ def update_route(
|
||||
return menu_category_info(item)
|
||||
|
||||
|
||||
@router.delete("/{id_}", response_model=schemas.MenuCategoryBlank)
|
||||
@router.delete("/{id_}", response_model=None)
|
||||
def delete_route(
|
||||
id_: uuid.UUID, user: Annotated[UserToken, Security(get_user, scopes=["products"])], db: SessionDep
|
||||
) -> schemas.MenuCategoryBlank:
|
||||
) -> None:
|
||||
item: MenuCategory = db.execute(select(MenuCategory).where(MenuCategory.id == id_)).scalar_one()
|
||||
if item.is_fixture:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_423_LOCKED,
|
||||
detail=f"{item.name} is a fixture and cannot be edited or deleted.",
|
||||
)
|
||||
else:
|
||||
product_count = db.execute(
|
||||
select(func.count(distinct(SkuVersion.sku_id))).where(SkuVersion.menu_category_id == id_)
|
||||
).scalar_one()
|
||||
if product_count > 0:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_501_NOT_IMPLEMENTED,
|
||||
detail="Menu Category deletion not implemented",
|
||||
detail="Menu Category has products and cannot be deleted.",
|
||||
)
|
||||
db.delete(item)
|
||||
db.commit()
|
||||
|
||||
|
||||
@router.get("", response_model=schemas.MenuCategoryBlank)
|
||||
|
||||
@ -4,7 +4,7 @@ from datetime import datetime
|
||||
from decimal import Decimal
|
||||
|
||||
from fastapi import HTTPException, status
|
||||
from sqlalchemy import ColumnElement, func, select
|
||||
from sqlalchemy import ColumnElement, delete, func, select
|
||||
from sqlalchemy.orm import Session, contains_eager
|
||||
from sqlalchemy.sql import expression
|
||||
|
||||
@ -126,9 +126,11 @@ def do_update_settlements(voucher: Voucher, others: list[SettleSchema], db: Sess
|
||||
voucher.settlements.append(ns)
|
||||
db.add(ns)
|
||||
|
||||
for removable in (os for os in voucher.settlements if os.settled not in [x.id_ for x in settlements]):
|
||||
voucher.settlements.remove(removable)
|
||||
db.delete(removable)
|
||||
db.execute(
|
||||
delete(Settlement).where(
|
||||
Settlement.voucher_id == voucher.id, Settlement.settled.notin_([x.id_ for x in settlements])
|
||||
)
|
||||
)
|
||||
return fully_settled
|
||||
|
||||
|
||||
@ -199,12 +201,12 @@ def get_voucher(
|
||||
select(Voucher)
|
||||
.join(Voucher.food_table)
|
||||
.join(Voucher.customer, isouter=True)
|
||||
.join(Voucher.kots)
|
||||
.join(Kot.inventories)
|
||||
.join(Inventory.sku)
|
||||
.join(SkuVersion, onclause=sku_version_onclause)
|
||||
.join(StockKeepingUnit.product)
|
||||
.join(ProductVersion, onclause=product_version_onclause)
|
||||
.join(Voucher.kots, isouter=True)
|
||||
.join(Kot.inventories, isouter=True)
|
||||
.join(Inventory.sku, isouter=True)
|
||||
.join(SkuVersion, onclause=sku_version_onclause, isouter=True)
|
||||
.join(StockKeepingUnit.product, isouter=True)
|
||||
.join(ProductVersion, onclause=product_version_onclause, isouter=True)
|
||||
.where(Voucher.id == voucher_id)
|
||||
.order_by(Kot.code, Inventory.sort_order)
|
||||
.options(
|
||||
|
||||
@ -82,12 +82,12 @@ def from_bill(id_: str, user: Annotated[UserToken, Security(get_user)], db: Sess
|
||||
.join(Bill.regime)
|
||||
.join(Voucher.food_table)
|
||||
.join(Voucher.customer, isouter=True)
|
||||
.join(Voucher.kots)
|
||||
.join(Kot.inventories)
|
||||
.join(Inventory.sku)
|
||||
.join(SkuVersion, onclause=sku_version_onclause)
|
||||
.join(StockKeepingUnit.product)
|
||||
.join(ProductVersion, onclause=product_version_onclause)
|
||||
.join(Voucher.kots, isouter=True)
|
||||
.join(Kot.inventories, isouter=True)
|
||||
.join(Inventory.sku, isouter=True)
|
||||
.join(SkuVersion, onclause=sku_version_onclause, isouter=True)
|
||||
.join(StockKeepingUnit.product, isouter=True)
|
||||
.join(ProductVersion, onclause=product_version_onclause, isouter=True)
|
||||
.where(Regime.prefix == match.group(1), Bill.bill_number == int(match.group(2)))
|
||||
.order_by(Kot.code)
|
||||
.options(
|
||||
|
||||
@ -1,41 +0,0 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-jasmine-html-reporter'),
|
||||
require('karma-coverage'),
|
||||
require('@angular-devkit/build-angular/plugins/karma'),
|
||||
],
|
||||
client: {
|
||||
jasmine: {
|
||||
// you can add configuration options for Jasmine here
|
||||
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
|
||||
// for example, you can disable the random execution with `random: false`
|
||||
// or set a specific seed with `seed: 4321`
|
||||
},
|
||||
clearContext: false, // leave Jasmine Spec Runner output visible in browser
|
||||
},
|
||||
jasmineHtmlReporter: {
|
||||
suppressAll: true, // removes the duplicated traces
|
||||
},
|
||||
coverageReporter: {
|
||||
dir: require('path').join(__dirname, './coverage/bookie'),
|
||||
subdir: '.',
|
||||
reporters: [{ type: 'html' }, { type: 'text-summary' }],
|
||||
},
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
singleRun: false,
|
||||
restartOnFileChange: true,
|
||||
});
|
||||
};
|
||||
10
lint.sh
10
lint.sh
@ -9,8 +9,8 @@ npx prettier --write src/app
|
||||
npx ng lint --fix
|
||||
|
||||
cd "$parent_path/barker" || exit
|
||||
ruff format .
|
||||
ruff check --fix .
|
||||
bandit --recursive barker
|
||||
bandit --recursive .
|
||||
safety check
|
||||
uv run ruff format .
|
||||
uv run ruff check .
|
||||
uv run bandit --recursive barker
|
||||
uv run bandit --recursive .
|
||||
uv run safety check
|
||||
|
||||
Reference in New Issue
Block a user