Skip to content

andrewmarconi/fms

Repository files navigation

fms-core

Monorepo for the FMS collection of independent, reusable Django apps. Each module under packages/ is a standalone pip-installable library that solves one concern (identity, billing, auditing, etc.); consumer SaaS products pick modules à la carte. There is no central runtime and no cross-module imports.

See docs/specs/2026-04-24-shared-modules-prd.md for the full PRD.

Workspace layout

fms-core/
├── packages/                       # polyglot: Python (uv) + Node (pnpm)
│   ├── _template/                  # Python module template — copy for new fms-<module>
│   ├── _template-node/             # Node lib template — copy for new @fms/<lib>
│   └── (future: ui, shell, i18n, field, api, forms, testing — see frontend spec)
├── verticals/                      # Nuxt 4 apps, one per vertical product
│   ├── _template/                  # vertical app template — copy for new vertical
│   └── (future: bookkeeping-close, hr-1099, hvac-dispatch, funeral-intake, trucking-ifta, insurance-renewals)
├── examples/
│   └── saas-sampler/               # Django reference consumer composing ≥5 Python modules
├── docs/
│   ├── specs/                      # PRDs and ADRs
│   ├── superpowers/                # plans, specs, skill content (process docs)
│   └── playbook/                   # Obsidian vault — opportunity research, schema, design system specs
├── .github/workflows/              # CI (Python: ruff/mypy/pytest/import-linter; Node: TBD)
├── pyproject.toml                  # uv workspace root
├── pnpm-workspace.yaml             # pnpm workspace root
├── package.json                    # private root, pins pnpm via packageManager
├── .npmrc                          # engine-strict, prefer-workspace-packages
├── importlinter.toml               # "no cross-module imports" contract (Python only)
├── .pre-commit-config.yaml         # ruff + mypy + import-linter + bare-python guard
└── README.md

Python packages live under packages/<dist-name>/ (PyPI-style fms-<module>) with source at src/fms_<module>/ (importable, underscored). Node packages live under packages/<lib>/ with package.json#name = "@fms/<lib>" and use pnpm, not uv. Vertical Nuxt apps live under verticals/<slug>/ with package.json#name = "@fms/vertical-<slug>". The two workspaces (uv and pnpm) coexist because each selects members by config-file presence — uv reads pyproject.toml (with Node-only directories listed under [tool.uv.workspace].exclude), pnpm reads package.json.

The frontend tech-stack architecture (Nuxt 4, layer precedence, design tokens, clusters, Storybook) is specified in docs/superpowers/specs/2026-04-27-frontend-tech-stack-design.md. The product/strategy vault lives at docs/playbook/ (Obsidian).

Operational rules (non-negotiable)

Python side (uv)

  1. Always uv run. Never invoke python, python3, or pip directly. Use uv run <cmd> and uv add <pkg>. CI fails on bare invocations (PRD §2.1).
  2. No cross-module imports between Python packages. Modules reach shared concepts (tenant, user, contact) through Django settings + apps.get_model() lazy lookups, the way Django resolves AUTH_USER_MODEL. import-linter enforces this in CI (PRD §2.2 / §4).
  3. Latest stable, always. Dependencies are unpinned in pyproject.toml so uv resolves to the latest stable at install time. Never copy a version number from this repo, the PRD, or stale documentation; verify against PyPI / pypi.org / python.org / nodejs.org at install time (PRD §2.3).

Node side (pnpm)

  1. Always pnpm --filter <name>. Never invoke node, npx, tsc, or nuxt outside package.json scripts. Run package commands as pnpm --filter @fms/<lib> <script> or vertical commands as pnpm --filter @fms/vertical-<slug> <script>.
  2. Layer precedence drives override location. The Nuxt layer order (lowest → highest priority) is @fms/ui → @fms/i18n → @fms/shell → @fms/field → <vertical>. Vertical-specific overrides live in the vertical's own layer at verticals/<slug>/ — never in shared packages. See docs/superpowers/specs/2026-04-27-frontend-tech-stack-design.md §"Layer precedence".
  3. Latest stable, always. Use unpinned ^ ranges in package.json and let pnpm resolve to current latest within the major. Never copy version numbers from documentation; verify against npmjs.com at install time. Pin only packageManager (in root package.json) and engines (Node + pnpm major versions).

Quickstart

Initial setup (run once):

# Python side
uv sync --all-packages --all-extras --dev
uv run pre-commit install

# Node side
corepack enable
pnpm install

Day-to-day commands:

# Python
uv run pytest                                            # tests across the Python workspace
uv run ruff check . && uv run ruff format --check .      # lint + format check
uv run mypy packages examples                            # type-check
uv run lint-imports --config importlinter.toml           # cross-module-import contract
uv run pre-commit run --all-files                        # all pre-commit hooks

# Node
pnpm -r typecheck                                        # type-check across the Node workspace
pnpm --filter @fms/vertical-<slug> dev                   # boot a vertical Nuxt app
pnpm --filter @fms/<lib> build                           # build a shared Node package

Adding a new module

Python (fms-<module>)

  1. cp -r packages/_template packages/fms-<module> and rename src/fms_template/ to src/fms_<module>/.
  2. Update name, AppConfig, and namespaced FMS_<MODULE>_* settings per PRD §5.
  3. Append the importable name (fms_<module>) to both lists in importlinter.tomlroot_packages and the "FMS modules are independent" contract.
  4. Add the workspace source to the root pyproject.toml under [tool.uv.sources].
  5. Run uv sync, then uv run pytest (per package) and uv run lint-imports --config importlinter.toml to verify.

Node lib (@fms/<lib>)

  1. cp -r packages/_template-node packages/<lib> and update package.json#name to @fms/<lib>.
  2. Replace src/index.ts with the package's real public exports.
  3. Append packages/<lib> to [tool.uv.workspace].exclude in root pyproject.toml so uv ignores the Node-only directory.
  4. Run pnpm install from repo root, then pnpm --filter @fms/<lib> typecheck to verify.

Vertical Nuxt app (@fms/vertical-<slug>)

  1. cp -r verticals/_template verticals/<slug> where <slug> matches the roster in docs/superpowers/specs/2026-04-27-frontend-tech-stack-design.md.
  2. Update package.json#name to @fms/vertical-<slug> and set runtimeConfig.public.fms.cluster in nuxt.config.ts.
  3. (After Plan 2 lands) wire the extends: chain in nuxt.config.ts to @fms/ui etc.
  4. Run pnpm install, then pnpm --filter @fms/vertical-<slug> dev to verify the app boots.

Packages

Python

Package Purpose Status
packages/_template Reference layout for new Python modules scaffolded
fms-identity Multi-tenancy, orgs, invites, RBAC primitives planned
fms-audit Append-only change log; WORM-ready planned
fms-notifications Unified email/SMS/push dispatch planned
fms-files Pre-signed S3/R2 uploads + PDF generation planned
fms-billing Stripe subscriptions + usage reporting planned
fms-integrations OAuth framework + reference QBO provider planned
fms-importer CSV/Excel import with pydantic validation planned
fms-reporting Saved queries + PDF/CSV export planned
fms-jobs Scheduling primitives + recurrence planned
fms-contacts Tenant-scoped CRM-lite planned
fms-forms JSON-Schema-driven forms planned
fms-messaging SMS/email thread history planned
fms-payments Stripe Invoicing for one-off revenue planned

Node (Nuxt 4 layers and shared libs — see frontend spec)

Package Purpose Status
packages/_template-node Reference layout for new Node libs scaffolded
@fms/ui Design tokens (incl. clusters), Reka primitives, Storybook planned
@fms/shell Nav shell, auth (Nitro BFF), tenant resolver, dashboard planned
@fms/i18n Label aliasing, vertical-namespaced messages planned
@fms/field PWA layer, offline queue, mobile primitives planned
@fms/api Generated OpenAPI types + typed client planned
@fms/forms JSON-Schema-driven form renderer (Draft 2020-12) planned
@fms/testing Shared Vitest/Playwright utilities planned

Verticals

Vertical Nuxt 4 apps. Newsroom-style slug encodes vertical + workflow wedge. Cluster determines emotional register (typography, density, voice).

Slug Vertical Workflow wedge Cluster Status
verticals/_template (operational default) scaffolded
bookkeeping-close Small bookkeeping QBO close-week AR triage operational planned
hr-1099 HR 1099 contractor classification risk compliance-anxious planned
hvac-dispatch HVAC / Plumbing Field dispatch blue-collar planned
funeral-intake Family funeral homes Multi-portal intake calm-institutional planned
trucking-ifta Owner-operator trucking IFTA filing blue-collar planned
insurance-renewals Independent insurance Policy renewals compliance-anxious planned

Examples

Path Description
examples/saas-sampler Reference Django project. Will compose ≥5 modules by §10 acceptance.

Phase status

This bootstrap satisfies Phase 0 of PRD §7: workspace, CI, importlinter, pre-commit, package template, and consumer skeleton. Phase 1 (fms-identity, fms-audit, fms-notifications, fms-files) is next on the Python side. The frontend foundation (pnpm workspace, vertical/Node templates, vault rename, spec amendment) is complete; Plans 2–8 (per docs/superpowers/plans/2026-04-27-fms-frontend-foundation.md) build the Node packages and verticals.

About

Five59 Modules System for MicroSaaS Platforms

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors