Compare commits

..

No commits in common. "main" and "renovate/lock-file-maintenance" have entirely different histories.

39 changed files with 390 additions and 953 deletions

4
.envrc
View file

@ -1,3 +1 @@
#!/usr/bin/env bash use flake .#impure
eval "$(devenv direnvrc)"
use devenv

View file

@ -5,7 +5,7 @@ on:
workflow_dispatch: workflow_dispatch:
jobs: jobs:
check: build-and-lint:
name: Lint, Check & Test name: Lint, Check & Test
runs-on: nix runs-on: nix
@ -16,12 +16,8 @@ jobs:
- run: nix --version - run: nix --version
- run: nix flake check - run: nix flake check
build: - name: Install the project
name: Build Package run: 'nix develop .#impure --command bash -c "uv sync --locked --all-extras --dev"'
runs-on: nix
steps: - name: Test with PyTest
- name: Check out Repository run: nix develop .#impure --command bash -c "uv run pytest --junit-xml test-report.xml"
uses: https://git.konstantinfickel.de/actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- run: nix build

5
.gitignore vendored
View file

@ -177,8 +177,3 @@ pyrightconfig.json
.direnv .direnv
test-report.xml test-report.xml
.devenv
.devenv.flake.nix
.pre-commit-config.yaml
result

View file

@ -1 +1 @@
3.14 3.14.3

View file

@ -1,36 +1,7 @@
# strea.md # streamer
![The Strea.md-Logo: A tag on an endless paper roll](./streamd.svg) Searching for `@tags` in time-based [streams](https://www.cs.yale.edu/homes/freeman/lifestreams.html).
Strea.md is a personal knowledge management and time-tracking CLI tool. It organizes time-ordered markdown files using `@tag` annotations, letting you manage tasks, track time, and query your notes from the terminal. # Usage
## Core Concepts Running `streamer` finds all lines with @Task
- **Shards** — Sections of markdown files, organized hierarchically by headings. Each shard can contain markers, tags, and nested child shards.
- **Markers** — Special `@tags` like `@Task`, `@Done`, `@Waiting`, or `@Timesheet` that give shards semantic meaning and place them into dimensions.
- **Dimensions** — Classification axes (e.g. task state, project, timesheet) that categorize shards. Some dimensions propagate to child shards.
## File Format
Markdown files are named with a timestamp: `YYYYMMDD-HHMMSS [markers].md`
For example: `20260131-210000 Task Streamd.md`
Within files, `@`-prefixed markers at the beginning of paragraphs or headings define how a shard is categorized.
## Commands
- `streamd` / `streamd new` — Create a new timestamped markdown entry, opening your editor
- `streamd todo` — Show all open tasks (shards with `@Task` markers)
- `streamd edit [number]` — Edit a stream file by index (most recent first)
- `streamd timesheet` — Generate time reports from `@Timesheet` markers
## Configuration
Streamd reads its configuration from `~/.config/streamd/config.yaml` (XDG standard). The main setting is `base_folder`, which points to the directory containing your stream files (defaults to the current working directory).
## Usage
Running `streamd` opens your editor to create a new entry. After saving, the file is renamed based on its timestamp and any markers found in the content.
Running `streamd todo` finds all shards marked as open tasks and displays them as rich-formatted panels in your terminal.

View file

@ -1,123 +0,0 @@
{
"nodes": {
"devenv": {
"locked": {
"dir": "src/modules",
"lastModified": 1771157881,
"owner": "cachix",
"repo": "devenv",
"rev": "b0b3dfa70ec90fa49f672e579f186faf4f61bd4b",
"type": "github"
},
"original": {
"dir": "src/modules",
"owner": "cachix",
"repo": "devenv",
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1767039857,
"owner": "NixOS",
"repo": "flake-compat",
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "flake-compat",
"type": "github"
}
},
"git-hooks": {
"inputs": {
"flake-compat": "flake-compat",
"gitignore": "gitignore",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1770726378,
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "5eaaedde414f6eb1aea8b8525c466dc37bba95ae",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "git-hooks.nix",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"git-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1762808025,
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "cb5e3fdca1de58ccbc3ef53de65bd372b48f567c",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"nixpkgs": {
"inputs": {
"nixpkgs-src": "nixpkgs-src"
},
"locked": {
"lastModified": 1770434727,
"owner": "cachix",
"repo": "devenv-nixpkgs",
"rev": "8430f16a39c27bdeef236f1eeb56f0b51b33d348",
"type": "github"
},
"original": {
"owner": "cachix",
"ref": "rolling",
"repo": "devenv-nixpkgs",
"type": "github"
}
},
"nixpkgs-src": {
"flake": false,
"locked": {
"lastModified": 1769922788,
"narHash": "sha256-H3AfG4ObMDTkTJYkd8cz1/RbY9LatN5Mk4UF48VuSXc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "207d15f1a6603226e1e223dc79ac29c7846da32e",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"devenv": "devenv",
"git-hooks": "git-hooks",
"nixpkgs": "nixpkgs",
"pre-commit-hooks": [
"git-hooks"
]
}
}
},
"root": "root",
"version": 7
}

View file

@ -1,28 +0,0 @@
{
pkgs,
...
}:
{
languages = {
python = {
enable = true;
uv.enable = true;
};
};
packages = [
pkgs.basedpyright
];
git-hooks.hooks = {
basedpyright = {
enable = true;
entry = "${pkgs.basedpyright}/bin/basedpyright";
files = "\\.py$";
types = [ "file" ];
};
ruff.enable = true;
ruff-format.enable = true;
commitizen.enable = true;
};
}

View file

@ -1,6 +0,0 @@
inputs:
git-hooks:
url: github:cachix/git-hooks.nix
inputs:
nixpkgs:
follows: nixpkgs

82
flake.lock generated
View file

@ -1,75 +1,16 @@
{ {
"nodes": { "nodes": {
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1767039857,
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
"owner": "NixOS",
"repo": "flake-compat",
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "flake-compat",
"type": "github"
}
},
"git-hooks": {
"inputs": {
"flake-compat": "flake-compat",
"gitignore": "gitignore",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1770726378,
"narHash": "sha256-kck+vIbGOaM/dHea7aTBxdFYpeUl/jHOy5W3eyRvVx8=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "5eaaedde414f6eb1aea8b8525c466dc37bba95ae",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "git-hooks.nix",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"git-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1771008912, "lastModified": 1769789167,
"narHash": "sha256-gf2AmWVTs8lEq7z/3ZAsgnZDhWIckkb+ZnAo5RzSxJg=", "narHash": "sha256-kKB3bqYJU5nzYeIROI82Ef9VtTbu4uA3YydSk/Bioa8=",
"owner": "nixos", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "a82ccc39b39b621151d6732718e3e250109076fa", "rev": "62c8382960464ceb98ea593cb8321a2cf8f9e3e5",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "nixos", "owner": "NixOS",
"ref": "nixos-unstable", "ref": "nixos-unstable",
"repo": "nixpkgs", "repo": "nixpkgs",
"type": "github" "type": "github"
@ -88,11 +29,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1771039651, "lastModified": 1763662255,
"narHash": "sha256-WZOfX4APbc6vmL14ZWJXgBeRfEER8H+OIX0D0nSmv0M=", "narHash": "sha256-4bocaOyLa3AfiS8KrWjZQYu+IAta05u3gYZzZ6zXbT0=",
"owner": "pyproject-nix", "owner": "pyproject-nix",
"repo": "build-system-pkgs", "repo": "build-system-pkgs",
"rev": "69bc2b53b79cbd6ce9f66f506fc962b45b5e68b9", "rev": "042904167604c681a090c07eb6967b4dd4dae88c",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -123,7 +64,6 @@
}, },
"root": { "root": {
"inputs": { "inputs": {
"git-hooks": "git-hooks",
"nixpkgs": "nixpkgs", "nixpkgs": "nixpkgs",
"pyproject-build-systems": "pyproject-build-systems", "pyproject-build-systems": "pyproject-build-systems",
"pyproject-nix": "pyproject-nix", "pyproject-nix": "pyproject-nix",
@ -140,11 +80,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1770770348, "lastModified": 1769957392,
"narHash": "sha256-A2GzkmzdYvdgmMEu5yxW+xhossP+txrYb7RuzRaqhlg=", "narHash": "sha256-6PkqwwYf5K2CHi2V+faI/9pqjfz/HxUkI/MVid6hlOY=",
"owner": "pyproject-nix", "owner": "pyproject-nix",
"repo": "uv2nix", "repo": "uv2nix",
"rev": "5d1b2cb4fe3158043fbafbbe2e46238abbc954b0", "rev": "d18bc50ae1c3d4be9c41c2d94ea765524400af75",
"type": "github" "type": "github"
}, },
"original": { "original": {

329
flake.nix
View file

@ -1,8 +1,8 @@
{ {
description = "Using Markdown Files to organize your life as a @Tag-Stream"; description = "Hello world flake using uv2nix";
inputs = { inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
pyproject-nix = { pyproject-nix = {
url = "github:pyproject-nix/pyproject.nix"; url = "github:pyproject-nix/pyproject.nix";
@ -21,215 +21,204 @@
inputs.uv2nix.follows = "uv2nix"; inputs.uv2nix.follows = "uv2nix";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
git-hooks = {
url = "github:cachix/git-hooks.nix";
inputs.nixpkgs.follows = "nixpkgs";
};
}; };
outputs = outputs =
{ {
self, self,
nixpkgs, nixpkgs,
pyproject-nix,
uv2nix, uv2nix,
pyproject-nix,
pyproject-build-systems, pyproject-build-systems,
git-hooks,
... ...
}: }:
let let
inherit (nixpkgs) lib; inherit (nixpkgs) lib;
forAllSystems = lib.genAttrs lib.systems.flakeExposed;
# Load a uv workspace from a workspace root.
# Uv2nix treats all uv projects as workspace projects.
workspace = uv2nix.lib.workspace.loadWorkspace { workspaceRoot = ./.; }; workspace = uv2nix.lib.workspace.loadWorkspace { workspaceRoot = ./.; };
# Create package overlay from workspace.
overlay = workspace.mkPyprojectOverlay { overlay = workspace.mkPyprojectOverlay {
sourcePreference = "wheel"; # Prefer prebuilt binary wheels as a package source.
# Sdists are less likely to "just work" because of the metadata missing from uv.lock.
# Binary wheels are more likely to, but may still require overrides for library dependencies.
sourcePreference = "wheel"; # or sourcePreference = "sdist";
# Optionally customise PEP 508 environment
# environ = {
# platform_release = "5.10.65";
# };
}; };
editableOverlay = workspace.mkEditablePyprojectOverlay { # Extend generated overlay with build fixups
root = "$REPO_ROOT"; #
# Uv2nix can only work with what it has, and uv.lock is missing essential metadata to perform some builds.
# This is an additional overlay implementing build fixups.
# See:
# - https://pyproject-nix.github.io/uv2nix/FAQ.html
pyprojectOverrides = _final: _prev: {
# Implement build fixups here.
# Note that uv2nix is _not_ using Nixpkgs buildPythonPackage.
# It's using https://pyproject-nix.github.io/pyproject.nix/build.html
}; };
pythonSets = forAllSystems ( # This example is only using x86_64-linux
system: pkgs = nixpkgs.legacyPackages.x86_64-linux;
let
pkgs = nixpkgs.legacyPackages.${system};
inherit (pkgs) stdenv;
baseSet = pkgs.callPackage pyproject-nix.build.packages { # Use Python 3.14 from nixpkgs
python = pkgs.python313; python = pkgs.python314;
};
pyprojectOverrides = _final: prev: { # Construct package set
streamd = prev.streamd.overrideAttrs (old: { pythonSet =
passthru = old.passthru // { # Use base package set from pyproject.nix builders
tests = (old.passthru.tests or { }) // { (pkgs.callPackage pyproject-nix.build.packages {
pytest = stdenv.mkDerivation { inherit python;
name = "${_final.streamd.name}-pytest"; }).overrideScope
inherit (_final.streamd) src; (
nativeBuildInputs = [ lib.composeManyExtensions [
(_final.mkVirtualEnv "streamd-pytest-env" { pyproject-build-systems.overlays.default
streamd = [ "dev" ]; overlay
}) pyprojectOverrides
]; ]
dontConfigure = true; );
buildPhase = ''
runHook preBuild
# Exit code 5 means no tests collected — allow it so the
# check succeeds on an empty test suite.
pytest || [ $? -eq 5 ]
runHook postBuild
'';
installPhase = ''
runHook preInstall
touch $out
runHook postInstall
'';
};
};
};
});
};
in
baseSet.overrideScope (
lib.composeManyExtensions [
pyproject-build-systems.overlays.default
overlay
pyprojectOverrides
]
)
);
mkGitHooksCheck =
system:
let
pkgs = nixpkgs.legacyPackages.${system};
pythonSet = pythonSets.${system};
venv = pythonSet.mkVirtualEnv "streamd-check-env" workspace.deps.all;
in
git-hooks.lib.${system}.run {
src = ./.;
hooks = {
basedpyright = {
enable = true;
entry = "${pkgs.basedpyright}/bin/basedpyright --pythonpath ${venv}/bin/python --project ${
pkgs.writeText "pyrightconfig.json" (
builtins.toJSON {
reportMissingTypeStubs = false;
reportUnnecessaryTypeIgnoreComment = false;
}
)
}";
files = "\\.py$";
types = [ "file" ];
};
ruff.enable = true;
ruff-format.enable = true;
commitizen.enable = true;
};
};
in in
{ {
packages = forAllSystems ( # Package a virtual environment as our main application.
system: #
# Enable no optional dependencies for production build.
packages.x86_64-linux =
let let
pythonSet = pythonSets.${system}; streamer = pythonSet.mkVirtualEnv "streamer-env" workspace.deps.default;
pkgs = nixpkgs.legacyPackages.${system};
inherit (pkgs.callPackages pyproject-nix.build.util { }) mkApplication;
in
rec {
streamd = mkApplication {
venv = pythonSet.mkVirtualEnv "streamd-env" workspace.deps.default;
package = pythonSet.streamd;
};
default = streamd;
}
);
homeManagerModules.default =
{
lib,
config,
pkgs,
...
}:
let
cfg = config.programs.streamd;
in in
{ {
options.programs.streamd = { inherit streamer;
enable = lib.mkEnableOption "streamd"; default = streamer;
base-folder = lib.mkOption {
type = lib.types.str;
description = "Base Folder of streamd";
};
package = lib.mkOption {
type = lib.types.package;
default = self.packages.${pkgs.system}.streamd;
defaultText = lib.literalExpression "inputs.streamd.packages.\${pkgs.system}.streamd";
description = "The package to use for the streamd binary.";
};
};
config = lib.mkIf cfg.enable {
assertions = [
(lib.hm.assertions.assertPlatform "programs.streamd" pkgs lib.platforms.linux)
];
home.packages = [ cfg.package ];
xdg.configFile."streamd/config.yaml".source =
(pkgs.formats.yaml { }).generate "streamd-configuration"
{
base_folder = cfg.base-folder;
};
home.shellAliases.s = "streamd";
};
}; };
checks = forAllSystems ( # Make streamer runnable with `nix run`
system: apps.x86_64-linux = {
let default = {
pythonSet = pythonSets.${system}; type = "app";
in program = "${self.packages.x86_64-linux.default}/bin/streamer";
{ };
inherit (pythonSet.streamd.passthru.tests) pytest; };
pre-commit = mkGitHooksCheck system;
}
);
devShells = forAllSystems ( # This example provides two different modes of development:
system: # - Impurely using uv to manage virtual environments
let # - Pure development using uv2nix to manage virtual environments
pkgs = nixpkgs.legacyPackages.${system}; devShells.x86_64-linux = {
pythonSet = pythonSets.${system}.overrideScope editableOverlay; # It is of course perfectly OK to keep using an impure virtualenv workflow and only use uv2nix to build packages.
virtualenv = pythonSet.mkVirtualEnv "streamd-dev-env" workspace.deps.all; # This devShell simply adds Python and undoes the dependency leakage done by Nixpkgs Python infrastructure.
in impure = pkgs.mkShell {
{ packages = with pkgs; [
default = pkgs.mkShell { python
packages = [ uv
pre-commit
bashInteractive
];
env = {
# Prevent uv from managing Python downloads
UV_PYTHON_DOWNLOADS = "never";
# Force uv to use nixpkgs Python interpreter
UV_PYTHON = python.interpreter;
}
// lib.optionalAttrs pkgs.stdenv.isLinux {
# Python libraries often load native shared objects using dlopen(3).
# Setting LD_LIBRARY_PATH makes the dynamic library loader aware of libraries without using RPATH for lookup.
LD_LIBRARY_PATH = lib.makeLibraryPath pkgs.pythonManylinuxPackages.manylinux1;
};
shellHook = ''
unset PYTHONPATH
'';
};
# This devShell uses uv2nix to construct a virtual environment purely from Nix, using the same dependency specification as the application.
# The notable difference is that we also apply another overlay here enabling editable mode ( https://setuptools.pypa.io/en/latest/userguide/development_mode.html ).
#
# This means that any changes done to your local files do not require a rebuild.
#
# Note: Editable package support is still unstable and subject to change.
uv2nix =
let
# Create an overlay enabling editable mode for all local dependencies.
editableOverlay = workspace.mkEditablePyprojectOverlay {
# Use environment variable
root = "$REPO_ROOT";
# Optional: Only enable editable for these packages
# members = [ "streamer" ];
};
# Override previous set with our overrideable overlay.
editablePythonSet = pythonSet.overrideScope (
lib.composeManyExtensions [
editableOverlay
# Apply fixups for building an editable package of your workspace packages
(final: prev: {
streamer = prev.streamer.overrideAttrs (old: {
# It's a good idea to filter the sources going into an editable build
# so the editable package doesn't have to be rebuilt on every change.
src = lib.fileset.toSource {
root = old.src;
fileset = lib.fileset.unions [
(old.src + "/pyproject.toml")
(old.src + "/README.md")
(old.src + "/src/streamer/__init__.py")
];
};
# Hatchling (our build system) has a dependency on the `editables` package when building editables.
#
# In normal Python flows this dependency is dynamically handled, and doesn't need to be explicitly declared.
# This behaviour is documented in PEP-660.
#
# With Nix the dependency needs to be explicitly declared.
nativeBuildInputs =
old.nativeBuildInputs
++ final.resolveBuildSystem {
editables = [ ];
};
});
})
]
);
# Build virtual environment, with local packages being editable.
#
# Enable all optional dependencies for development.
virtualenv = editablePythonSet.mkVirtualEnv "streamer-dev-env" workspace.deps.all;
in
pkgs.mkShell {
packages = with pkgs; [
virtualenv virtualenv
pkgs.uv uv
pre-commit
bashInteractive
]; ];
env = { env = {
# Don't create venv using uv
UV_NO_SYNC = "1"; UV_NO_SYNC = "1";
UV_PYTHON = pythonSet.python.interpreter;
# Force uv to use Python interpreter from venv
UV_PYTHON = "${virtualenv}/bin/python";
# Prevent uv from downloading managed Python's
UV_PYTHON_DOWNLOADS = "never"; UV_PYTHON_DOWNLOADS = "never";
}; };
shellHook = '' shellHook = ''
# Undo dependency propagation by nixpkgs.
unset PYTHONPATH unset PYTHONPATH
# Get repository root using git. This is expanded at runtime by the editable `.pth` machinery.
export REPO_ROOT=$(git rev-parse --show-toplevel) export REPO_ROOT=$(git rev-parse --show-toplevel)
${(mkGitHooksCheck system).shellHook}
''; '';
}; };
} };
);
}; };
} }

View file

@ -1,21 +1,21 @@
[project] [project]
name = "streamd" name = "streamer"
version = "0.1.0" version = "0.1.0"
description = "Searching for tags in streams" description = "Searching for tags in streams"
readme = "README.md" readme = "README.md"
requires-python = ">=3.13" requires-python = ">=3.12"
dependencies = [ dependencies = [
"click==8.3.1", "click==8.3.1",
"mistletoe==1.5.1", "mistletoe==1.5.1",
"pydantic==2.12.5", "pydantic==2.12.5",
"pydantic-settings[yaml]==2.13.1", "pydantic-settings[yaml]==2.12.0",
"rich==14.3.3", "rich==14.3.2",
"typer==0.24.1", "typer==0.21.2",
"xdg-base-dirs==6.0.2", "xdg-base-dirs==6.0.2",
] ]
[project.scripts] [project.scripts]
streamd = "streamd:app" streamer = "streamer:app"
[build-system] [build-system]
requires = ["hatchling"] requires = ["hatchling"]
@ -23,8 +23,8 @@ build-backend = "hatchling.build"
[dependency-groups] [dependency-groups]
dev = [ dev = [
"basedpyright==1.38.2", "faker==40.4.0",
"faker==40.11.0", "pyright==1.1.408",
"pytest==9.0.2", "pytest==9.0.2",
"ruff==0.15.6", "ruff==0.15.0",
] ]

View file

@ -1,23 +0,0 @@
import re
from typing import cast
from mistletoe.markdown_renderer import Fragment, MarkdownRenderer
from mistletoe.span_token import SpanToken
class Tag(SpanToken):
parse_inner: bool = False
pattern: re.Pattern[str] = re.compile(r"@([^\s*\x60~\[\]]+)")
class TagMarkdownRenderer(MarkdownRenderer):
def __init__(self) -> None:
super().__init__(Tag) # pyright: ignore[reportUnknownMemberType]
def render_tag(self, token: Tag):
content = cast(str, token.content)
yield Fragment("@")
yield Fragment(content)
__all__ = ["Tag", "TagMarkdownRenderer"]

View file

@ -1,9 +1,8 @@
import glob import glob
import os import os
from collections.abc import Generator
from datetime import datetime from datetime import datetime
from shutil import move from shutil import move
from typing import Annotated from typing import Annotated, Generator
import click import click
import typer import typer
@ -11,17 +10,18 @@ from rich import print
from rich.markdown import Markdown from rich.markdown import Markdown
from rich.panel import Panel from rich.panel import Panel
from streamd.localize import ( from streamer.localize import (
LocalizedShard, LocalizedShard,
RepositoryConfiguration, RepositoryConfiguration,
localize_stream_file, localize_stream_file,
) )
from streamd.localize.preconfigured_configurations import TaskConfiguration from streamer.localize.preconfigured_configurations import TaskConfiguration
from streamd.parse import parse_markdown_file from streamer.parse import parse_markdown_file
from streamd.query import find_shard_by_position from streamer.query import find_shard_by_position
from streamd.settings import Settings from streamer.query.find import find_shard_by_set_dimension
from streamd.timesheet.configuration import BasicTimesheetConfiguration from streamer.settings import Settings
from streamd.timesheet.extract import extract_timesheets from streamer.timesheet.configuration import BasicTimesheetConfiguration
from streamer.timesheet.extract import extract_timesheets
app = typer.Typer() app = typer.Typer()
@ -89,11 +89,11 @@ def timesheet() -> None:
@app.command() @app.command()
def new() -> None: def new() -> None:
streamd_directory = Settings().base_folder streamer_directory = Settings().base_folder
timestamp = datetime.now().strftime("%Y%m%d-%H%M%S") timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
preliminary_file_name = f"{timestamp}_wip.md" preliminary_file_name = f"{timestamp}_wip.md"
prelimary_path = os.path.join(streamd_directory, preliminary_file_name) prelimary_path = os.path.join(streamer_directory, preliminary_file_name)
content = "# " content = "# "
with open(prelimary_path, "w") as file: with open(prelimary_path, "w") as file:
@ -111,7 +111,7 @@ def new() -> None:
): ):
final_file_name = f"{timestamp} {' '.join(markers)}.md" final_file_name = f"{timestamp} {' '.join(markers)}.md"
final_path = os.path.join(streamd_directory, final_file_name) final_path = os.path.join(streamer_directory, final_file_name)
_ = move(prelimary_path, final_path) _ = move(prelimary_path, final_path)
print(f"Saved as [yellow]{final_file_name}") print(f"Saved as [yellow]{final_file_name}")

View file

@ -1,6 +1,6 @@
from datetime import datetime from datetime import datetime
from streamd.parse.shard import Shard, StreamFile from streamer.parse.shard import Shard, StreamFile
from .extract_datetime import ( from .extract_datetime import (
extract_datetime_from_file_name, extract_datetime_from_file_name,
@ -47,10 +47,7 @@ def localize_shard(
position.update(private_position) position.update(private_position)
return LocalizedShard( return LocalizedShard(
markers=shard.markers, **shard.model_dump(exclude={"children"}),
tags=shard.tags,
start_line=shard.start_line,
end_line=shard.end_line,
location=position, location=position,
children=children, children=children,
moment=adjusted_moment, moment=adjusted_moment,

View file

@ -2,7 +2,7 @@ from __future__ import annotations
from datetime import datetime from datetime import datetime
from streamd.parse.shard import Shard from streamer.parse.shard import Shard
class LocalizedShard(Shard): class LocalizedShard(Shard):

View file

@ -1,4 +1,4 @@
from streamd.localize.repository_configuration import ( from streamer.localize.repository_configuration import (
Dimension, Dimension,
Marker, Marker,
MarkerPlacement, MarkerPlacement,

View file

@ -1,11 +1,13 @@
from __future__ import annotations from __future__ import annotations
from typing import Optional
from pydantic import BaseModel from pydantic import BaseModel
class Dimension(BaseModel): class Dimension(BaseModel):
display_name: str display_name: str
comment: str | None = None comment: Optional[str] = None
propagate: bool = False propagate: bool = False
@ -27,7 +29,7 @@ class RepositoryConfiguration(BaseModel):
def merge_single_dimension(base: Dimension, second: Dimension) -> Dimension: def merge_single_dimension(base: Dimension, second: Dimension) -> Dimension:
second_fields_set: set[str] = getattr(second, "model_fields_set", set()) second_fields_set = getattr(second, "model_fields_set", set())
return Dimension( return Dimension(
display_name=second.display_name or base.display_name, display_name=second.display_name or base.display_name,

View file

@ -1,9 +1,7 @@
import re import re
from collections.abc import Iterable from typing import Iterable
from typing import cast
from mistletoe.block_token import BlockToken from mistletoe.block_token import BlockToken
from mistletoe.span_token import Emphasis, Link, RawText, Strikethrough, Strong from mistletoe.span_token import Emphasis, RawText, Strikethrough, Strong, Link
from mistletoe.token import Token from mistletoe.token import Token
from .markdown_tag import Tag from .markdown_tag import Tag
@ -14,21 +12,18 @@ def extract_markers_and_tags_from_single_token(
marker_boundary_encountered: bool, marker_boundary_encountered: bool,
return_at_first_marker: bool = False, return_at_first_marker: bool = False,
) -> tuple[list[str], list[str], bool]: ) -> tuple[list[str], list[str], bool]:
result_markers: list[str] = [] result_markers, result_tags = [], []
result_tags: list[str] = []
result_marker_boundary_encountered = marker_boundary_encountered result_marker_boundary_encountered = marker_boundary_encountered
if isinstance(token, Tag): if isinstance(token, Tag):
content = cast(str, token.content)
if marker_boundary_encountered: if marker_boundary_encountered:
result_tags.append(content) result_tags.append(token.content)
else: else:
result_markers.append(content) result_markers.append(token.content)
elif isinstance(token, (Emphasis, Strong, Strikethrough, Link)): elif isinstance(token, (Emphasis, Strong, Strikethrough, Link)):
children = list(token.children or [])
markers, tags, child_marker_boundary_encountered = ( markers, tags, child_marker_boundary_encountered = (
extract_markers_and_tags_from_tokens( extract_markers_and_tags_from_tokens(
children, token.children or [],
marker_boundary_encountered, marker_boundary_encountered,
return_at_first_marker, return_at_first_marker,
) )
@ -38,10 +33,8 @@ def extract_markers_and_tags_from_single_token(
result_marker_boundary_encountered = ( result_marker_boundary_encountered = (
marker_boundary_encountered or child_marker_boundary_encountered marker_boundary_encountered or child_marker_boundary_encountered
) )
elif isinstance(token, RawText): elif isinstance(token, RawText) and re.match(r"^[\s]*$", token.content):
content_raw = cast(str, token.content) pass
if not re.match(r"^[\s]*$", content_raw):
result_marker_boundary_encountered = True
else: else:
result_marker_boundary_encountered = True result_marker_boundary_encountered = True
@ -53,8 +46,7 @@ def extract_markers_and_tags_from_tokens(
marker_boundary_encountered: bool, marker_boundary_encountered: bool,
return_at_first_marker: bool = False, return_at_first_marker: bool = False,
) -> tuple[list[str], list[str], bool]: ) -> tuple[list[str], list[str], bool]:
result_markers: list[str] = [] result_markers, result_tags = [], []
result_tags: list[str] = []
result_marker_boundary_encountered = marker_boundary_encountered result_marker_boundary_encountered = marker_boundary_encountered
for child in tokens: for child in tokens:
@ -76,15 +68,15 @@ def extract_markers_and_tags_from_tokens(
def extract_markers_and_tags(block_token: BlockToken) -> tuple[list[str], list[str]]: def extract_markers_and_tags(block_token: BlockToken) -> tuple[list[str], list[str]]:
children = list(block_token.children or []) markers, tags, _ = extract_markers_and_tags_from_tokens(
markers, tags, _ = extract_markers_and_tags_from_tokens(children, False) block_token.children or [], False
)
return markers, tags return markers, tags
def has_markers(block_token: BlockToken) -> bool: def has_markers(block_token: BlockToken) -> bool:
children = list(block_token.children or [])
markers, _, _ = extract_markers_and_tags_from_tokens( markers, _, _ = extract_markers_and_tags_from_tokens(
children, False, return_at_first_marker=True block_token.children or [], False, return_at_first_marker=True
) )
return len(markers) > 0 return len(markers) > 0

View file

@ -0,0 +1,20 @@
import re
from mistletoe.markdown_renderer import Fragment, MarkdownRenderer
from mistletoe.span_token import SpanToken
class Tag(SpanToken):
parse_inner = False
pattern = re.compile(r"@([^\s*\x60~\[\]]+)")
class TagMarkdownRenderer(MarkdownRenderer):
def __init__(self):
super().__init__(Tag)
def render_tag(self, token: Tag):
yield Fragment("@")
yield Fragment(token.content)
__all__ = ["Tag", "TagMarkdownRenderer"]

View file

@ -1,5 +1,4 @@
from collections import Counter from collections import Counter
from typing import cast
from mistletoe.block_token import ( from mistletoe.block_token import (
BlockToken, BlockToken,
@ -17,20 +16,16 @@ from .shard import Shard, StreamFile
def get_line_number(block_token: BlockToken) -> int: def get_line_number(block_token: BlockToken) -> int:
return cast(int, block_token.line_number) # pyright: ignore[reportAttributeAccessIssue] return block_token.line_number # type: ignore
def build_shard( def build_shard(
start_line: int, start_line: int,
end_line: int, end_line: int,
markers: list[str] | None = None, markers: list[str] = [],
tags: list[str] | None = None, tags: list[str] = [],
children: list[Shard] | None = None, children: list[Shard] = [],
) -> Shard: ) -> Shard:
markers = markers or []
tags = tags or []
children = children or []
if ( if (
len(children) == 1 len(children) == 1
and len(tags) == 0 and len(tags) == 0
@ -50,17 +45,14 @@ def build_shard(
def merge_into_first_shard( def merge_into_first_shard(
shards: list[Shard], shards: list[Shard], start_line: int, end_line: int, additional_tags: list[str] = []
start_line: int, ):
end_line: int,
additional_tags: list[str] | None = None,
) -> Shard:
return shards[0].model_copy( return shards[0].model_copy(
update={ update={
"start_line": start_line, "start_line": start_line,
"end_line": end_line, "end_line": end_line,
"children": shards[1:], "children": shards[1:],
"tags": shards[0].tags + (additional_tags or []), "tags": shards[0].tags + additional_tags,
} }
) )
@ -73,18 +65,13 @@ def find_paragraph_shard_positions(block_tokens: list[BlockToken]) -> list[int]:
] ]
def _heading_level(heading: Heading) -> int:
return cast(int, heading.level)
def find_headings_by_level( def find_headings_by_level(
block_tokens: list[BlockToken], header_level: int block_tokens: list[BlockToken], header_level: int
) -> list[int]: ) -> list[int]:
return [ return [
index index
for index, block_token in enumerate(block_tokens) for index, block_token in enumerate(block_tokens)
if isinstance(block_token, Heading) if isinstance(block_token, Heading) and block_token.level == header_level
and _heading_level(block_token) == header_level
] ]
@ -96,8 +83,8 @@ def calculate_heading_level_for_next_split(
If only the first token is a heading with a marker, then return None. If only the first token is a heading with a marker, then return None.
Otherwise: Return the heading level with the lowest level (h1 < h2), of which there are two or which has a marker (and doesn't stem from first) Otherwise: Return the heading level with the lowest level (h1 < h2), of which there are two or which has a marker (and doesn't stem from first)
""" """
level_of_headings_without_first_with_marker: list[int] = [ level_of_headings_without_first_with_marker = [
_heading_level(token) token.level
for token in block_tokens[1:] for token in block_tokens[1:]
if isinstance(token, Heading) and has_markers(token) if isinstance(token, Heading) and has_markers(token)
] ]
@ -105,8 +92,8 @@ def calculate_heading_level_for_next_split(
if len(level_of_headings_without_first_with_marker) == 0: if len(level_of_headings_without_first_with_marker) == 0:
return None return None
heading_level_counter: Counter[int] = Counter( heading_level_counter = Counter(
[_heading_level(token) for token in block_tokens if isinstance(token, Heading)] [token.level for token in block_tokens if isinstance(token, Heading)]
) )
return min( return min(
@ -118,12 +105,10 @@ def calculate_heading_level_for_next_split(
def parse_single_block_shards( def parse_single_block_shards(
block_token: BlockToken, start_line: int, end_line: int block_token: BlockToken, start_line: int, end_line: int
) -> tuple[Shard | None, list[str]]: ) -> tuple[Shard | None, list[str]]:
markers: list[str] = [] markers, tags, children = [], [], []
tags: list[str] = []
children: list[Shard] = []
if isinstance(block_token, List): if isinstance(block_token, List):
list_items: list[ListItem] = ( # pyright: ignore[reportAssignmentType] list_items: list[ListItem] = ( # type: ignore
list(block_token.children) if block_token.children is not None else [] list(block_token.children) if block_token.children is not None else []
) )
for index, list_item in enumerate(list_items): for index, list_item in enumerate(list_items):
@ -134,7 +119,7 @@ def parse_single_block_shards(
else end_line else end_line
) )
list_item_shard, list_item_tags = parse_multiple_block_shards( list_item_shard, list_item_tags = parse_multiple_block_shards(
list_item.children, # pyright: ignore[reportArgumentType] list_item.children, # type: ignore
list_item_start_line, list_item_start_line,
list_item_end_line, list_item_end_line,
) )
@ -164,8 +149,7 @@ def parse_multiple_block_shards(
) )
paragraph_positions = find_paragraph_shard_positions(block_tokens) paragraph_positions = find_paragraph_shard_positions(block_tokens)
children: list[Shard] = [] children, tags = [], []
tags: list[str] = []
is_first_block_only_with_marker = False is_first_block_only_with_marker = False
@ -217,7 +201,7 @@ def parse_header_shards(
block_tokens_split_by_heading = split_at(block_tokens, heading_positions) block_tokens_split_by_heading = split_at(block_tokens, heading_positions)
children: list[Shard] = [] children = []
for i, child_blocks in enumerate(block_tokens_split_by_heading): for i, child_blocks in enumerate(block_tokens_split_by_heading):
child_start_line = get_line_number(child_blocks[0]) child_start_line = get_line_number(child_blocks[0])
child_end_line = ( child_end_line = (
@ -245,7 +229,7 @@ def parse_markdown_file(file_name: str, file_content: str) -> StreamFile:
with TagMarkdownRenderer(): with TagMarkdownRenderer():
ast = Document(file_content) ast = Document(file_content)
block_tokens: list[BlockToken] = ast.children # pyright: ignore[reportAssignmentType] block_tokens: list[BlockToken] = ast.children # type: ignore
if len(block_tokens) > 0: if len(block_tokens) > 0:
if parsed_shard := parse_header_shards( if parsed_shard := parse_header_shards(
block_tokens, shard.start_line, shard.end_line block_tokens, shard.start_line, shard.end_line

View file

@ -1,12 +1,12 @@
from typing import Callable from typing import Callable
from streamd.localize import LocalizedShard from streamer.localize import LocalizedShard
def find_shard( def find_shard(
shards: list[LocalizedShard], query_function: Callable[[LocalizedShard], bool] shards: list[LocalizedShard], query_function: Callable[[LocalizedShard], bool]
) -> list[LocalizedShard]: ) -> list[LocalizedShard]:
found_shards: list[LocalizedShard] = [] found_shards = []
for shard in shards: for shard in shards:
if query_function(shard): if query_function(shard):
@ -21,9 +21,8 @@ def find_shard_by_position(
) -> list[LocalizedShard]: ) -> list[LocalizedShard]:
return find_shard( return find_shard(
shards, shards,
lambda shard: ( lambda shard: dimension in shard.location
dimension in shard.location and shard.location[dimension] == value and shard.location[dimension] == value,
),
) )

View file

@ -1,6 +1,4 @@
import os import os
from typing import ClassVar, override
from pydantic_settings import ( from pydantic_settings import (
BaseSettings, BaseSettings,
PydanticBaseSettingsSource, PydanticBaseSettingsSource,
@ -9,18 +7,15 @@ from pydantic_settings import (
) )
from xdg_base_dirs import xdg_config_home from xdg_base_dirs import xdg_config_home
SETTINGS_FILE = xdg_config_home() / "streamd" / "config.yaml" SETTINGS_FILE = xdg_config_home() / "streamer" / "config.yaml"
class Settings(BaseSettings): class Settings(BaseSettings):
model_config: ClassVar[SettingsConfigDict] = SettingsConfigDict( model_config = SettingsConfigDict(env_file_encoding="utf-8")
env_file_encoding="utf-8"
)
base_folder: str = os.getcwd() base_folder: str = os.getcwd()
@classmethod @classmethod
@override
def settings_customise_sources( def settings_customise_sources(
cls, cls,
settings_cls: type[BaseSettings], settings_cls: type[BaseSettings],

View file

@ -1,7 +1,7 @@
from enum import StrEnum from enum import StrEnum
from streamd.localize import RepositoryConfiguration from streamer.localize import RepositoryConfiguration
from streamd.localize.repository_configuration import ( from streamer.localize.repository_configuration import (
Dimension, Dimension,
Marker, Marker,
MarkerPlacement, MarkerPlacement,

View file

@ -2,8 +2,9 @@ from datetime import datetime
from itertools import groupby from itertools import groupby
from pydantic import BaseModel from pydantic import BaseModel
from streamd.localize import LocalizedShard
from streamd.query.find import find_shard_by_set_dimension from streamer.localize import LocalizedShard
from streamer.query.find import find_shard_by_set_dimension
from .configuration import TIMESHEET_DIMENSION_NAME, TimesheetPointType from .configuration import TIMESHEET_DIMENSION_NAME, TimesheetPointType
from .timecard import SpecialDayType, Timecard, Timesheet from .timecard import SpecialDayType, Timecard, Timesheet

View file

@ -1,283 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="192"
height="192"
viewBox="0 0 192 192"
version="1.1"
id="svg5"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
sodipodi:docname="streamd.svg"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<title
id="title1">Streamd</title>
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:document-units="px"
showgrid="true"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:snap-bbox-midpoints="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:bbox-nodes="true"
inkscape:object-paths="true"
inkscape:snap-intersection-paths="true"
inkscape:snap-object-midpoints="true"
inkscape:snap-midpoints="true"
inkscape:snap-smooth-nodes="true"
inkscape:zoom="4"
inkscape:cx="58.375"
inkscape:cy="112.25"
inkscape:window-width="2560"
inkscape:window-height="1416"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="layer3"
inkscape:snap-grids="true"
inkscape:snap-page="true"
inkscape:showpageshadow="2"
inkscape:deskcolor="#d1d1d1">
<inkscape:grid
type="xygrid"
id="grid824"
empspacing="4"
originx="0"
originy="0"
spacingy="1"
spacingx="1"
units="px" />
</sodipodi:namedview>
<defs
id="defs2">
<linearGradient
inkscape:collect="always"
id="linearGradient3586">
<stop
style="stop-color:#ffffff;stop-opacity:0.1"
offset="0"
id="stop3582" />
<stop
style="stop-color:#ffffff;stop-opacity:0"
offset="1"
id="stop3584" />
</linearGradient>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1598">
<ellipse
style="fill:#bbdefb"
id="ellipse1600"
cx="160"
cy="40"
rx="12"
ry="20" />
</clipPath>
<filter
inkscape:collect="always"
style="color-interpolation-filters:sRGB"
id="filter1774"
x="-0.043319996"
y="-0.043320001"
width="1.08664"
height="1.08664">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="1.5060667"
id="feGaussianBlur1776" />
</filter>
<filter
inkscape:collect="always"
style="color-interpolation-filters:sRGB"
id="filter1839"
x="-0.047999999"
y="-0.047999999"
width="1.096"
height="1.096">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="3.04"
id="feGaussianBlur1841" />
</filter>
<filter
inkscape:collect="always"
style="color-interpolation-filters:sRGB"
id="filter1886"
x="-0.030315789"
y="-0.1152"
width="1.0606316"
height="1.2304">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="1.92"
id="feGaussianBlur1888" />
</filter>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1932">
<path
style="fill:#e3f2fd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 32,44 v 116 c 0,8 4,12 12,12 40,0 68,-28 115.63984,-28.34001 C 168.99976,143.59319 172,140 172,132 V 44 Z"
id="path1934"
sodipodi:nodetypes="cccsccc" />
</clipPath>
<filter
inkscape:collect="always"
style="color-interpolation-filters:sRGB"
id="filter1956"
x="-0.030857142"
y="-0.108"
width="1.0617143"
height="1.216">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="1.8"
id="feGaussianBlur1958" />
</filter>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1980">
<ellipse
style="fill:#bbdefb"
id="ellipse1982"
cx="160"
cy="40"
rx="12"
ry="20" />
</clipPath>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3586"
id="linearGradient3588"
x1="0"
y1="0"
x2="192"
y2="192"
gradientUnits="userSpaceOnUse" />
</defs>
<g
inkscape:label="Paper"
inkscape:groupmode="layer"
id="layer1"
style="display:inline"
sodipodi:insensitive="true">
<path
id="path1814"
style="opacity:0.2;fill:#000000;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter1839)"
d="M 32,20 A 12,20 0 0 0 20,40 12,20 0 0 0 32,60 v 100 c 0,8 4,12 12,12 40,0 68.00079,-27.99983 115.64062,-28.33984 C 169.00055,143.59336 172,140 172,132 V 44 h -0.26758 A 12,20 0 0 0 172,40 12,20 0 0 0 160,20 Z m 125.57812,0.455078 a 12,20 0 0 0 -0.38867,0.128906 12,20 0 0 1 0.38867,-0.128906 z m -2.58984,1.410156 a 12,20 0 0 0 -0.4043,0.333985 12,20 0 0 1 0.4043,-0.333985 z m -2.40039,2.423828 a 12,20 0 0 0 -0.21484,0.314454 12,20 0 0 1 0.21484,-0.314454 z m -1.82031,2.990235 a 12,20 0 0 0 -0.19727,0.373047 12,20 0 0 1 0.19727,-0.373047 z m -1.44531,3.685547 a 12,20 0 0 0 -0.20508,0.697265 12,20 0 0 1 0.20508,-0.697265 z m -0.97266,4.349609 a 12,20 0 0 0 -0.0762,0.650391 12,20 0 0 1 0.0762,-0.650391 z" />
<path
style="fill:#e0f7fa;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 32,40 v 120 c 0,8 4,12 12,12 40,0 68,-28 115.63984,-28.34001 C 168.99976,143.59319 172,140 172,132 V 40 Z"
id="path2678"
sodipodi:nodetypes="cccsccc" />
<path
id="path2431"
style="opacity:0.2;fill:#000000;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 172,131 c 0,8 -2.99945,11.59336 -12.35938,11.66016 C 112.00079,143.00017 84,171 44,171 36,171 32,167 32,159 v 1 c 0,8 4,12 12,12 40,0 68.00079,-27.99983 115.64062,-28.33984 C 169.00055,143.59336 172,140 172,132 Z" />
<path
id="path1863"
style="opacity:0.2;fill:#000000;stroke-width:0.470751;filter:url(#filter1886)"
d="M 32,20 A 12,20 0 0 0 20,40 12,20 0 0 0 32,60 H 160 A 12,20 0 0 0 172,40 12,20 0 0 0 160,20 Z m 125.57812,0.455078 a 12,20 0 0 0 -0.38867,0.128906 12,20 0 0 1 0.38867,-0.128906 z m -2.58984,1.410156 a 12,20 0 0 0 -0.4043,0.333985 12,20 0 0 1 0.4043,-0.333985 z m -2.40039,2.423828 a 12,20 0 0 0 -0.21484,0.314454 12,20 0 0 1 0.21484,-0.314454 z m -1.82031,2.990235 a 12,20 0 0 0 -0.19727,0.373047 12,20 0 0 1 0.19727,-0.373047 z m -1.44531,3.685547 a 12,20 0 0 0 -0.20508,0.697265 12,20 0 0 1 0.20508,-0.697265 z m -0.97266,4.349609 a 12,20 0 0 0 -0.0762,0.650391 12,20 0 0 1 0.0762,-0.650391 z m -0.0762,8.720703 a 12,20 0 0 0 0.0762,0.650391 12,20 0 0 1 -0.0762,-0.650391 z m 0.84375,4.302735 a 12,20 0 0 0 0.20508,0.697265 12,20 0 0 1 -0.20508,-0.697265 z m 1.45312,4.009765 a 12,20 0 0 0 0.19727,0.373047 12,20 0 0 1 -0.19727,-0.373047 z m 1.80274,3.048828 a 12,20 0 0 0 0.21484,0.314454 12,20 0 0 1 -0.21484,-0.314454 z m 2.21093,2.404297 a 12,20 0 0 0 0.4043,0.333985 12,20 0 0 1 -0.4043,-0.333985 z m 2.60547,1.615235 a 12,20 0 0 0 0.38867,0.128906 12,20 0 0 1 -0.38867,-0.128906 z"
clip-path="url(#clipPath1932)" />
<ellipse
style="fill:#f2f2f2"
id="ellipse2158"
cx="160"
cy="40"
rx="12"
ry="20" />
<path
id="rect1442"
style="fill:#4dd0e1;stroke-width:0.470751"
d="M 32,20 A 12,20 0 0 0 20,40 12,20 0 0 0 32,60 H 160 A 12,20 0 0 1 148,40 12,20 0 0 1 160,20 Z" />
<path
id="path1936"
style="opacity:0.2;fill:#000000;stroke-width:0.470751;filter:url(#filter1956)"
d="M 32,20 A 12,20 0 0 0 20,40 12,20 0 0 0 32,60 H 160 A 12,20 0 0 1 148,40 12,20 0 0 1 160,20 Z"
clip-path="url(#clipPath1980)" />
<path
id="rect1406"
style="opacity:1;fill:#80deea"
d="M 32,20 A 12,20 0 0 0 20,40 h 128 a 12,20 0 0 1 12,-20 z" />
<path
id="path2390"
style="opacity:0.2;fill:#ffffff"
d="M 32,20 A 12,20 0 0 0 20,40 h 0.06055 A 12,20 0 0 1 32,20.900391 H 156.64648 A 12,20 0 0 1 160,20 Z" />
<path
id="path2600"
style="opacity:0.2;fill:#000000;stroke-width:0.470751"
d="M 20.033203,39.5 C 20.020878,39.666428 20.00981,39.833108 20,40 c 0,11.045695 5.372583,20 12,20 h 128 c -1.1985,-0.03799 -2.38682,-0.375069 -3.52539,-1 H 32 C 25.500897,58.973414 20.195661,50.328485 20.033203,39.5 Z"
sodipodi:nodetypes="ccscccc" />
</g>
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="Tag"
style="display:inline">
<path
id="path1772"
style="opacity:0.2;fill:#000000;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter1774)"
d="M 91.53554,89.53554 C 89.41422,91.65686 88,94.48528 88,97.31371 v 16.97056 c 0,4.24264 0,5.65686 2.82843,8.48529 l 42.42641,42.42639 c 5.65685,5.65686 11.31371,5.65686 16.97056,0 l 16.97056,-16.97056 c 5.65686,-5.65686 5.65686,-11.3137 0,-16.97056 l -42.4264,-42.4264 C 121.94113,86 120.52692,86 116.28428,86 H 99.31371 c -2.82842,0 -5.65685,1.41422 -7.77817,3.53554 z m 4.94975,4.94974 a 8,8 0 0 1 11.3137,0 8,8 0 0 1 0,11.31371 8,8 0 0 1 -11.3137,0 8,8 0 0 1 0,-11.31371 z" />
<path
id="path7522"
style="fill:#d84315;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
d="M 91.53554,89.53554 C 89.41422,91.65686 88,94.48528 88,97.31371 v 16.97056 c 0,4.24264 0,5.65686 2.82843,8.48529 l 42.42641,42.42639 c 5.65685,5.65686 11.31371,5.65686 16.97056,0 l 16.97056,-16.97056 c 5.65686,-5.65686 5.65686,-11.3137 0,-16.97056 l -42.4264,-42.4264 C 121.94113,86 120.52692,86 116.28428,86 H 99.31371 c -2.82842,0 -5.65685,1.41422 -7.77817,3.53554 z m 4.94975,4.94974 a 8,8 0 0 1 11.3137,0 8,8 0 0 1 0,11.31371 8,8 0 0 1 -11.3137,0 8,8 0 0 1 0,-11.31371 z" />
<path
style="opacity:0.2;fill:#000000;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 88,113.28516 v 1 c 0,4.24264 -3.05e-4,5.65594 2.828125,8.48437 l 42.425785,42.42578 c 5.65685,5.65686 11.31385,5.65686 16.9707,0 l 16.9707,-16.9707 c 2.99512,-2.99511 4.3894,-5.98927 4.21289,-8.98438 -0.15686,2.66175 -1.55114,5.32263 -4.21289,7.98438 l -16.9707,16.9707 c -5.65685,5.65686 -11.31385,5.65686 -16.9707,0 L 90.828125,121.76953 C 87.999695,118.9411 88,117.5278 88,113.28516 Z"
id="path3326" />
<path
style="opacity:0.2;fill:#ffffff;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 100.89062,91.240234 a 8,8 0 0 0 -4.406245,2.244141 8,8 0 0 0 -2.244141,5.40625 8,8 0 0 1 2.244141,-4.40625 8,8 0 0 1 11.314455,0 8,8 0 0 1 2.31445,5.158203 8,8 0 0 0 -2.31445,-6.158203 8,8 0 0 0 -6.90821,-2.244141 z"
id="path3103" />
<path
style="opacity:0.2;fill:#000000;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 110.11328,100.64258 a 8,8 0 0 1 -2.31445,5.15625 8,8 0 0 1 -11.314455,0 8,8 0 0 1 -2.244141,-4.4043 8,8 0 0 0 2.244141,5.4043 8,8 0 0 0 11.314455,0 8,8 0 0 0 2.31445,-6.15625 z"
id="path3389" />
<path
style="opacity:0.2;fill:#ffffff;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 99.314453,86 c -2.82842,0 -5.657977,1.413836 -7.779297,3.535156 C 89.413836,91.656476 88,94.486023 88,97.314453 v 1 c 0,-2.82843 1.413836,-5.657977 3.535156,-7.779297 C 93.656476,88.413836 96.486033,87 99.314453,87 h 16.970707 c 4.24264,0 5.65594,-3.05e-4 8.48437,2.828125 l 42.42578,42.425785 c 2.66175,2.66174 4.05603,5.32458 4.21289,7.98632 0.17651,-2.9951 -1.21777,-5.99121 -4.21289,-8.98632 L 124.76953,88.828125 C 121.9411,85.999695 120.5278,86 116.28516,86 Z"
id="path3346" />
</g>
<g
inkscape:groupmode="layer"
id="layer4"
inkscape:label="Finish"
sodipodi:insensitive="true"
style="display:inline">
<path
id="path3525"
style="fill:url(#linearGradient3588);fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 32,20 A 12,20 0 0 0 20,40 12,20 0 0 0 32,60 v 100 c 0,8 4,12 12,12 27.15145,0 48.786559,-12.88889 74.77539,-21.2832 l 14.47852,14.47851 c 5.65685,5.65686 11.31385,5.65686 16.9707,0 l 16.9707,-16.9707 c 3.49839,-3.49839 4.82865,-6.99576 4,-10.49414 C 171.74927,136.15451 172,134.25875 172,132 V 44 h -0.26758 A 12,20 0 0 0 172,40 12,20 0 0 0 160,20 Z m 125.57812,0.455078 a 12,20 0 0 0 -0.38867,0.128906 12,20 0 0 1 0.38867,-0.128906 z m -2.58984,1.410156 a 12,20 0 0 0 -0.4043,0.333985 12,20 0 0 1 0.4043,-0.333985 z m -2.40039,2.423828 a 12,20 0 0 0 -0.21484,0.314454 12,20 0 0 1 0.21484,-0.314454 z m -1.82031,2.990235 a 12,20 0 0 0 -0.19727,0.373047 12,20 0 0 1 0.19727,-0.373047 z m -1.44531,3.685547 a 12,20 0 0 0 -0.20508,0.697265 12,20 0 0 1 0.20508,-0.697265 z m -0.97266,4.349609 a 12,20 0 0 0 -0.0762,0.650391 12,20 0 0 1 0.0762,-0.650391 z" />
</g>
<metadata
id="metadata1">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:title>Streamd</dc:title>
<dc:creator>
<cc:Agent>
<dc:title>Konstantin Fickel</dc:title>
</cc:Agent>
</dc:creator>
</cc:Work>
</rdf:RDF>
</metadata>
</svg>

Before

Width:  |  Height:  |  Size: 14 KiB

View file

@ -1,6 +1,6 @@
from datetime import date, datetime, time from datetime import date, datetime, time
from streamd.localize.extract_datetime import ( from streamer.localize.extract_datetime import (
extract_date_from_marker, extract_date_from_marker,
extract_datetime_from_file_name, extract_datetime_from_file_name,
extract_datetime_from_marker, extract_datetime_from_marker,

View file

@ -1,6 +1,6 @@
import pytest import pytest
from streamd.localize.repository_configuration import ( from streamer.localize.repository_configuration import (
Dimension, Dimension,
Marker, Marker,
MarkerPlacement, MarkerPlacement,
@ -252,8 +252,8 @@ class TestMergeRepositoryConfiguration:
), ),
}, },
markers={ markers={
"Streamd": Marker( "Streamer": Marker(
display_name="Streamd", display_name="Streamer",
placements=[MarkerPlacement(dimension="project")], placements=[MarkerPlacement(dimension="project")],
) )
}, },
@ -267,8 +267,8 @@ class TestMergeRepositoryConfiguration:
), ),
}, },
markers={ markers={
"Streamd": Marker( "Streamer": Marker(
display_name="Streamd2", display_name="Streamer2",
placements=[ placements=[
MarkerPlacement( MarkerPlacement(
if_with={"Timesheet"}, dimension="timesheet", value="coding" if_with={"Timesheet"}, dimension="timesheet", value="coding"
@ -291,9 +291,9 @@ class TestMergeRepositoryConfiguration:
assert merged.dimensions["moment"].display_name == "Moment" assert merged.dimensions["moment"].display_name == "Moment"
assert merged.dimensions["timesheet"].display_name == "Timesheet" assert merged.dimensions["timesheet"].display_name == "Timesheet"
assert set(merged.markers.keys()) == {"Streamd", "JobHunting"} assert set(merged.markers.keys()) == {"Streamer", "JobHunting"}
assert merged.markers["Streamd"].display_name == "Streamd2" assert merged.markers["Streamer"].display_name == "Streamer2"
assert merged.markers["Streamd"].placements == [ assert merged.markers["Streamer"].placements == [
MarkerPlacement(dimension="project", value=None, if_with=set()), MarkerPlacement(dimension="project", value=None, if_with=set()),
MarkerPlacement( MarkerPlacement(
if_with={"Timesheet"}, dimension="timesheet", value="coding" if_with={"Timesheet"}, dimension="timesheet", value="coding"
@ -359,9 +359,7 @@ class TestMergeRepositoryConfiguration:
], ],
) )
def test_merge_repository_configuration_propagate_preserves_base_when_omitted( def test_merge_repository_configuration_propagate_preserves_base_when_omitted(
base: RepositoryConfiguration, base, second, expected_propagate
second: RepositoryConfiguration,
expected_propagate: bool,
): ):
merged = merge_repository_configuration(base, second) merged = merge_repository_configuration(base, second)
assert merged.dimensions["d"].propagate is expected_propagate assert merged.dimensions["d"].propagate is expected_propagate

View file

@ -1,5 +1,6 @@
from faker import Faker from faker import Faker
from streamd.parse import Shard, StreamFile, parse_markdown_file
from streamer.parse import Shard, StreamFile, parse_markdown_file
fake = Faker() fake = Faker()

View file

@ -2,8 +2,8 @@ from __future__ import annotations
from datetime import datetime from datetime import datetime
from streamd.localize import LocalizedShard from streamer.localize import LocalizedShard
from streamd.query.find import find_shard, find_shard_by_position from streamer.query.find import find_shard, find_shard_by_position
def generate_localized_shard( def generate_localized_shard(

View file

@ -1,14 +1,14 @@
from datetime import datetime from datetime import datetime
from streamd.localize.localize import localize_stream_file from streamer.localize.localize import localize_stream_file
from streamd.localize.localized_shard import LocalizedShard from streamer.localize.localized_shard import LocalizedShard
from streamd.localize.repository_configuration import ( from streamer.localize.repository_configuration import (
Dimension, Dimension,
Marker, Marker,
MarkerPlacement, MarkerPlacement,
RepositoryConfiguration, RepositoryConfiguration,
) )
from streamd.parse.shard import Shard, StreamFile from streamer.parse.shard import Shard, StreamFile
repository_configuration = RepositoryConfiguration( repository_configuration = RepositoryConfiguration(
dimensions={ dimensions={
@ -29,8 +29,8 @@ repository_configuration = RepositoryConfiguration(
), ),
}, },
markers={ markers={
"Streamd": Marker( "Streamer": Marker(
display_name="Streamd", display_name="Streamer",
placements=[ placements=[
MarkerPlacement(dimension="project"), MarkerPlacement(dimension="project"),
MarkerPlacement( MarkerPlacement(
@ -49,39 +49,39 @@ class TestLocalize:
def test_project_simple_stream_file(self): def test_project_simple_stream_file(self):
stream_file = StreamFile( stream_file = StreamFile(
file_name="20250622-121000 Test File.md", file_name="20250622-121000 Test File.md",
shard=Shard(start_line=1, end_line=1, markers=["Streamd"]), shard=Shard(start_line=1, end_line=1, markers=["Streamer"]),
) )
assert localize_stream_file( assert localize_stream_file(
stream_file, repository_configuration stream_file, repository_configuration
) == LocalizedShard( ) == LocalizedShard(
moment=datetime(2025, 6, 22, 12, 10, 0, 0), moment=datetime(2025, 6, 22, 12, 10, 0, 0),
markers=["Streamd"], markers=["Streamer"],
tags=[], tags=[],
start_line=1, start_line=1,
end_line=1, end_line=1,
children=[], children=[],
location={"project": "Streamd", "file": stream_file.file_name}, location={"project": "Streamer", "file": stream_file.file_name},
) )
def test_timesheet_use_case(self): def test_timesheet_use_case(self):
stream_file = StreamFile( stream_file = StreamFile(
file_name="20260131-210000 Test File.md", file_name="20260131-210000 Test File.md",
shard=Shard(start_line=1, end_line=1, markers=["Timesheet", "Streamd"]), shard=Shard(start_line=1, end_line=1, markers=["Timesheet", "Streamer"]),
) )
assert localize_stream_file( assert localize_stream_file(
stream_file, repository_configuration stream_file, repository_configuration
) == LocalizedShard( ) == LocalizedShard(
moment=datetime(2026, 1, 31, 21, 0, 0, 0), moment=datetime(2026, 1, 31, 21, 0, 0, 0),
markers=["Timesheet", "Streamd"], markers=["Timesheet", "Streamer"],
tags=[], tags=[],
start_line=1, start_line=1,
end_line=1, end_line=1,
children=[], children=[],
location={ location={
"file": stream_file.file_name, "file": stream_file.file_name,
"project": "Streamd", "project": "Streamer",
"timesheet": "coding", "timesheet": "coding",
}, },
) )

View file

@ -4,13 +4,13 @@ from datetime import datetime, time
import pytest import pytest
from streamd.localize.localized_shard import LocalizedShard from streamer.localize.localized_shard import LocalizedShard
from streamd.timesheet.configuration import ( from streamer.timesheet.configuration import (
TIMESHEET_DIMENSION_NAME, TIMESHEET_DIMENSION_NAME,
TimesheetPointType, TimesheetPointType,
) )
from streamd.timesheet.extract import extract_timesheets from streamer.timesheet.extract import extract_timesheets
from streamd.timesheet.timecard import SpecialDayType, Timecard, Timesheet from streamer.timesheet.timecard import SpecialDayType, Timecard, Timesheet
def point(at: datetime, type: TimesheetPointType) -> LocalizedShard: def point(at: datetime, type: TimesheetPointType) -> LocalizedShard:
@ -243,7 +243,7 @@ class TestExtractTimesheets:
] ]
with pytest.raises(ValueError, match=r"Last Timecard of .* is not a break"): with pytest.raises(ValueError, match=r"Last Timecard of .* is not a break"):
_ = extract_timesheets(shards) extract_timesheets(shards)
def test_two_special_day_types_same_day_is_invalid(self): def test_two_special_day_types_same_day_is_invalid(self):
""" """
@ -257,7 +257,7 @@ class TestExtractTimesheets:
] ]
with pytest.raises(ValueError, match=r"is both .* and .*"): with pytest.raises(ValueError, match=r"is both .* and .*"):
_ = extract_timesheets(shards) extract_timesheets(shards)
def test_points_with_mixed_dates_inside_one_group_raises(self): def test_points_with_mixed_dates_inside_one_group_raises(self):
""" """
@ -273,7 +273,7 @@ class TestExtractTimesheets:
] ]
with pytest.raises(ValueError, match=r"Last Timecard of .* is not a break"): with pytest.raises(ValueError, match=r"Last Timecard of .* is not a break"):
_ = extract_timesheets(shards) extract_timesheets(shards)
def test_day_with_only_breaks_is_ignored(self): def test_day_with_only_breaks_is_ignored(self):
""" """

148
uv.lock generated
View file

@ -1,6 +1,6 @@
version = 1 version = 1
revision = 3 revision = 3
requires-python = ">=3.13" requires-python = ">=3.12"
[[package]] [[package]]
name = "annotated-doc" name = "annotated-doc"
@ -20,18 +20,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" },
] ]
[[package]]
name = "basedpyright"
version = "1.38.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "nodejs-wheel-binaries" },
]
sdist = { url = "https://files.pythonhosted.org/packages/e4/a3/20aa7c4e83f2f614e0036300f3c352775dede0655c66814da16c37b661a9/basedpyright-1.38.2.tar.gz", hash = "sha256:b433b2b8ba745ed7520cdc79a29a03682f3fb00346d272ece5944e9e5e5daa92", size = 25277019, upload-time = "2026-02-26T11:18:43.594Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ac/12/736cab83626fea3fe65cdafb3ef3d2ee9480c56723f2fd33921537289a5e/basedpyright-1.38.2-py3-none-any.whl", hash = "sha256:153481d37fd19f9e3adedc8629d1d071b10c5f5e49321fb026b74444b7c70e24", size = 12312475, upload-time = "2026-02-26T11:18:40.373Z" },
]
[[package]] [[package]]
name = "click" name = "click"
version = "8.3.1" version = "8.3.1"
@ -55,14 +43,14 @@ wheels = [
[[package]] [[package]]
name = "faker" name = "faker"
version = "40.11.0" version = "40.4.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "tzdata", marker = "sys_platform == 'win32'" }, { name = "tzdata", marker = "sys_platform == 'win32'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/94/dc/b68e5378e5a7db0ab776efcdd53b6fe374b29d703e156fd5bb4c5437069e/faker-40.11.0.tar.gz", hash = "sha256:7c419299103b13126bd02ec14bd2b47b946edb5a5eedf305e66a193b25f9a734", size = 1957570, upload-time = "2026-03-13T14:36:11.844Z" } sdist = { url = "https://files.pythonhosted.org/packages/fc/7e/dccb7013c9f3d66f2e379383600629fec75e4da2698548bdbf2041ea4b51/faker-40.4.0.tar.gz", hash = "sha256:76f8e74a3df28c3e2ec2caafa956e19e37a132fdc7ea067bc41783affcfee364", size = 1952221, upload-time = "2026-02-06T23:30:15.515Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/b1/fa/a86c6ba66f0308c95b9288b1e3eaccd934b545646f63494a86f1ec2f8c8e/faker-40.11.0-py3-none-any.whl", hash = "sha256:0e9816c950528d2a37d74863f3ef389ea9a3a936cbcde0b11b8499942e25bf90", size = 1989457, upload-time = "2026-03-13T14:36:09.792Z" }, { url = "https://files.pythonhosted.org/packages/ac/63/58efa67c10fb27810d34351b7a10f85f109a7f7e2a07dc3773952459c47b/faker-40.4.0-py3-none-any.whl", hash = "sha256:486d43c67ebbb136bc932406418744f9a0bdf2c07f77703ea78b58b77e9aa443", size = 1987060, upload-time = "2026-02-06T23:30:13.44Z" },
] ]
[[package]] [[package]]
@ -105,19 +93,12 @@ wheels = [
] ]
[[package]] [[package]]
name = "nodejs-wheel-binaries" name = "nodeenv"
version = "24.13.1" version = "1.10.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/e5/d0/81d98b8fddc45332f79d6ad5749b1c7409fb18723545eae75d9b7e0048fb/nodejs_wheel_binaries-24.13.1.tar.gz", hash = "sha256:512659a67449a038231e2e972d49e77049d2cf789ae27db39eff4ab1ca52ac57", size = 8056, upload-time = "2026-02-12T17:31:04.368Z" } sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/aa/04/1ffe1838306654fcb50bcf46172567d50c8e27a76f4b9e55a1971fab5c4f/nodejs_wheel_binaries-24.13.1-py2.py3-none-macosx_13_0_arm64.whl", hash = "sha256:360ac9382c651de294c23c4933a02358c4e11331294983f3cf50ca1ac32666b1", size = 54757440, upload-time = "2026-02-12T17:30:35.748Z" }, { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" },
{ url = "https://files.pythonhosted.org/packages/66/f6/81ad81bc3bd919a20b110130c4fd318c7b6a5abb37eb53daa353ad908012/nodejs_wheel_binaries-24.13.1-py2.py3-none-macosx_13_0_x86_64.whl", hash = "sha256:035b718946793986762cdd50deee7f5f1a8f1b0bad0f0cfd57cad5492f5ea018", size = 54932957, upload-time = "2026-02-12T17:30:40.114Z" },
{ url = "https://files.pythonhosted.org/packages/14/be/8e8a2bd50953c4c5b7e0fca07368d287917b84054dc3c93dd26a2940f0f9/nodejs_wheel_binaries-24.13.1-py2.py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:f795e9238438c4225f76fbd01e2b8e1a322116bbd0dc15a7dbd585a3ad97961e", size = 59287257, upload-time = "2026-02-12T17:30:43.781Z" },
{ url = "https://files.pythonhosted.org/packages/58/57/92f6dfa40647702a9fa6d32393ce4595d0fc03c1daa9b245df66cc60e959/nodejs_wheel_binaries-24.13.1-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:978328e3ad522571eb163b042dfbd7518187a13968fe372738f90fdfe8a46afc", size = 59781783, upload-time = "2026-02-12T17:30:47.387Z" },
{ url = "https://files.pythonhosted.org/packages/f7/a5/457b984cf675cf86ace7903204b9c36edf7a2d1b4325ddf71eaf8d1027c7/nodejs_wheel_binaries-24.13.1-py2.py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e1dc893df85299420cd2a5feea0c3f8482a719b5f7f82d5977d58718b8b78b5f", size = 61287166, upload-time = "2026-02-12T17:30:50.646Z" },
{ url = "https://files.pythonhosted.org/packages/3c/99/da515f7bc3bce35cfa6005f0e0c4e3c4042a466782b143112eb393b663be/nodejs_wheel_binaries-24.13.1-py2.py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:0e581ae219a39073dcadd398a2eb648f0707b0f5d68c565586139f919c91cbe9", size = 61870142, upload-time = "2026-02-12T17:30:54.563Z" },
{ url = "https://files.pythonhosted.org/packages/cc/c0/22001d2c96d8200834af7d1de5e72daa3266c7270330275104c3d9ddd143/nodejs_wheel_binaries-24.13.1-py2.py3-none-win_amd64.whl", hash = "sha256:d4c969ea0bcb8c8b20bc6a7b4ad2796146d820278f17d4dc20229b088c833e22", size = 41185473, upload-time = "2026-02-12T17:30:57.524Z" },
{ url = "https://files.pythonhosted.org/packages/ab/c4/7532325f968ecfc078e8a028e69a52e4c3f95fb800906bf6931ac1e89e2b/nodejs_wheel_binaries-24.13.1-py2.py3-none-win_arm64.whl", hash = "sha256:caec398cb9e94c560bacdcba56b3828df22a355749eb291f47431af88cbf26dc", size = 38881194, upload-time = "2026-02-12T17:31:00.214Z" },
] ]
[[package]] [[package]]
@ -162,6 +143,20 @@ dependencies = [
] ]
sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" },
{ url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" },
{ url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" },
{ url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" },
{ url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" },
{ url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" },
{ url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" },
{ url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" },
{ url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" },
{ url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" },
{ url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" },
{ url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" },
{ url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" },
{ url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" },
{ url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" }, { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" },
{ url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" }, { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" },
{ url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" }, { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" },
@ -204,20 +199,24 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" },
{ url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" },
{ url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" },
{ url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" },
{ url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" },
{ url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" },
{ url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" },
] ]
[[package]] [[package]]
name = "pydantic-settings" name = "pydantic-settings"
version = "2.13.1" version = "2.12.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "pydantic" }, { name = "pydantic" },
{ name = "python-dotenv" }, { name = "python-dotenv" },
{ name = "typing-inspection" }, { name = "typing-inspection" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/52/6d/fffca34caecc4a3f97bda81b2098da5e8ab7efc9a66e819074a11955d87e/pydantic_settings-2.13.1.tar.gz", hash = "sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025", size = 223826, upload-time = "2026-02-19T13:45:08.055Z" } sdist = { url = "https://files.pythonhosted.org/packages/43/4b/ac7e0aae12027748076d72a8764ff1c9d82ca75a7a52622e67ed3f765c54/pydantic_settings-2.12.0.tar.gz", hash = "sha256:005538ef951e3c2a68e1c08b292b5f2e71490def8589d4221b95dab00dafcfd0", size = 194184, upload-time = "2025-11-10T14:25:47.013Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/00/4b/ccc026168948fec4f7555b9164c724cf4125eac006e176541483d2c959be/pydantic_settings-2.13.1-py3-none-any.whl", hash = "sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237", size = 58929, upload-time = "2026-02-19T13:45:06.034Z" }, { url = "https://files.pythonhosted.org/packages/c1/60/5d4751ba3f4a40a6891f24eec885f51afd78d208498268c734e256fb13c4/pydantic_settings-2.12.0-py3-none-any.whl", hash = "sha256:fddb9fd99a5b18da837b29710391e945b1e30c135477f484084ee513adb93809", size = 51880, upload-time = "2025-11-10T14:25:45.546Z" },
] ]
[package.optional-dependencies] [package.optional-dependencies]
@ -234,6 +233,19 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
] ]
[[package]]
name = "pyright"
version = "1.1.408"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "nodeenv" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/74/b2/5db700e52554b8f025faa9c3c624c59f1f6c8841ba81ab97641b54322f16/pyright-1.1.408.tar.gz", hash = "sha256:f28f2321f96852fa50b5829ea492f6adb0e6954568d1caa3f3af3a5f555eb684", size = 4400578, upload-time = "2026-01-08T08:07:38.795Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0c/82/a2c93e32800940d9573fb28c346772a14778b84ba7524e691b324620ab89/pyright-1.1.408-py3-none-any.whl", hash = "sha256:090b32865f4fdb1e0e6cd82bf5618480d48eecd2eb2e70f960982a3d9a4c17c1", size = 6399144, upload-time = "2026-01-08T08:07:37.082Z" },
]
[[package]] [[package]]
name = "pytest" name = "pytest"
version = "9.0.2" version = "9.0.2"
@ -265,6 +277,16 @@ version = "6.0.3"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" },
{ url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" },
{ url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" },
{ url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" },
{ url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" },
{ url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" },
{ url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" },
{ url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" },
{ url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" },
{ url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" },
{ url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" },
{ url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" },
{ url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" },
@ -297,40 +319,40 @@ wheels = [
[[package]] [[package]]
name = "rich" name = "rich"
version = "14.3.3" version = "14.3.2"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "markdown-it-py" }, { name = "markdown-it-py" },
{ name = "pygments" }, { name = "pygments" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/b3/c6/f3b320c27991c46f43ee9d856302c70dc2d0fb2dba4842ff739d5f46b393/rich-14.3.3.tar.gz", hash = "sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b", size = 230582, upload-time = "2026-02-19T17:23:12.474Z" } sdist = { url = "https://files.pythonhosted.org/packages/74/99/a4cab2acbb884f80e558b0771e97e21e939c5dfb460f488d19df485e8298/rich-14.3.2.tar.gz", hash = "sha256:e712f11c1a562a11843306f5ed999475f09ac31ffb64281f73ab29ffdda8b3b8", size = 230143, upload-time = "2026-02-01T16:20:47.908Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl", hash = "sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d", size = 310458, upload-time = "2026-02-19T17:23:13.732Z" }, { url = "https://files.pythonhosted.org/packages/ef/45/615f5babd880b4bd7d405cc0dc348234c5ffb6ed1ea33e152ede08b2072d/rich-14.3.2-py3-none-any.whl", hash = "sha256:08e67c3e90884651da3239ea668222d19bea7b589149d8014a21c633420dbb69", size = 309963, upload-time = "2026-02-01T16:20:46.078Z" },
] ]
[[package]] [[package]]
name = "ruff" name = "ruff"
version = "0.15.6" version = "0.15.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/51/df/f8629c19c5318601d3121e230f74cbee7a3732339c52b21daa2b82ef9c7d/ruff-0.15.6.tar.gz", hash = "sha256:8394c7bb153a4e3811a4ecdacd4a8e6a4fa8097028119160dffecdcdf9b56ae4", size = 4597916, upload-time = "2026-03-12T23:05:47.51Z" } sdist = { url = "https://files.pythonhosted.org/packages/c8/39/5cee96809fbca590abea6b46c6d1c586b49663d1d2830a751cc8fc42c666/ruff-0.15.0.tar.gz", hash = "sha256:6bdea47cdbea30d40f8f8d7d69c0854ba7c15420ec75a26f463290949d7f7e9a", size = 4524893, upload-time = "2026-02-03T17:53:35.357Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/9e/2f/4e03a7e5ce99b517e98d3b4951f411de2b0fa8348d39cf446671adcce9a2/ruff-0.15.6-py3-none-linux_armv6l.whl", hash = "sha256:7c98c3b16407b2cf3d0f2b80c80187384bc92c6774d85fefa913ecd941256fff", size = 10508953, upload-time = "2026-03-12T23:05:17.246Z" }, { url = "https://files.pythonhosted.org/packages/bc/88/3fd1b0aa4b6330d6aaa63a285bc96c9f71970351579152d231ed90914586/ruff-0.15.0-py3-none-linux_armv6l.whl", hash = "sha256:aac4ebaa612a82b23d45964586f24ae9bc23ca101919f5590bdb368d74ad5455", size = 10354332, upload-time = "2026-02-03T17:52:54.892Z" },
{ url = "https://files.pythonhosted.org/packages/70/60/55bcdc3e9f80bcf39edf0cd272da6fa511a3d94d5a0dd9e0adf76ceebdb4/ruff-0.15.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ee7dcfaad8b282a284df4aa6ddc2741b3f4a18b0555d626805555a820ea181c3", size = 10942257, upload-time = "2026-03-12T23:05:23.076Z" }, { url = "https://files.pythonhosted.org/packages/72/f6/62e173fbb7eb75cc29fe2576a1e20f0a46f671a2587b5f604bfb0eaf5f6f/ruff-0.15.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:dcd4be7cc75cfbbca24a98d04d0b9b36a270d0833241f776b788d59f4142b14d", size = 10767189, upload-time = "2026-02-03T17:53:19.778Z" },
{ url = "https://files.pythonhosted.org/packages/e7/f9/005c29bd1726c0f492bfa215e95154cf480574140cb5f867c797c18c790b/ruff-0.15.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:3bd9967851a25f038fc8b9ae88a7fbd1b609f30349231dffaa37b6804923c4bb", size = 10322683, upload-time = "2026-03-12T23:05:33.738Z" }, { url = "https://files.pythonhosted.org/packages/99/e4/968ae17b676d1d2ff101d56dc69cf333e3a4c985e1ec23803df84fc7bf9e/ruff-0.15.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d747e3319b2bce179c7c1eaad3d884dc0a199b5f4d5187620530adf9105268ce", size = 10075384, upload-time = "2026-02-03T17:53:29.241Z" },
{ url = "https://files.pythonhosted.org/packages/5f/74/2f861f5fd7cbb2146bddb5501450300ce41562da36d21868c69b7a828169/ruff-0.15.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13f4594b04e42cd24a41da653886b04d2ff87adbf57497ed4f728b0e8a4866f8", size = 10660986, upload-time = "2026-03-12T23:05:53.245Z" }, { url = "https://files.pythonhosted.org/packages/a2/bf/9843c6044ab9e20af879c751487e61333ca79a2c8c3058b15722386b8cae/ruff-0.15.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:650bd9c56ae03102c51a5e4b554d74d825ff3abe4db22b90fd32d816c2e90621", size = 10481363, upload-time = "2026-02-03T17:52:43.332Z" },
{ url = "https://files.pythonhosted.org/packages/c1/a1/309f2364a424eccb763cdafc49df843c282609f47fe53aa83f38272389e0/ruff-0.15.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e2ed8aea2f3fe57886d3f00ea5b8aae5bf68d5e195f487f037a955ff9fbaac9e", size = 10332177, upload-time = "2026-03-12T23:05:56.145Z" }, { url = "https://files.pythonhosted.org/packages/55/d9/4ada5ccf4cd1f532db1c8d44b6f664f2208d3d93acbeec18f82315e15193/ruff-0.15.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a6664b7eac559e3048223a2da77769c2f92b43a6dfd4720cef42654299a599c9", size = 10187736, upload-time = "2026-02-03T17:53:00.522Z" },
{ url = "https://files.pythonhosted.org/packages/30/41/7ebf1d32658b4bab20f8ac80972fb19cd4e2c6b78552be263a680edc55ac/ruff-0.15.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70789d3e7830b848b548aae96766431c0dc01a6c78c13381f423bf7076c66d15", size = 11170783, upload-time = "2026-03-12T23:06:01.742Z" }, { url = "https://files.pythonhosted.org/packages/86/e2/f25eaecd446af7bb132af0a1d5b135a62971a41f5366ff41d06d25e77a91/ruff-0.15.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f811f97b0f092b35320d1556f3353bf238763420ade5d9e62ebd2b73f2ff179", size = 10968415, upload-time = "2026-02-03T17:53:15.705Z" },
{ url = "https://files.pythonhosted.org/packages/76/be/6d488f6adca047df82cd62c304638bcb00821c36bd4881cfca221561fdfc/ruff-0.15.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:542aaf1de3154cea088ced5a819ce872611256ffe2498e750bbae5247a8114e9", size = 12044201, upload-time = "2026-03-12T23:05:28.697Z" }, { url = "https://files.pythonhosted.org/packages/e7/dc/f06a8558d06333bf79b497d29a50c3a673d9251214e0d7ec78f90b30aa79/ruff-0.15.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:761ec0a66680fab6454236635a39abaf14198818c8cdf691e036f4bc0f406b2d", size = 11809643, upload-time = "2026-02-03T17:53:23.031Z" },
{ url = "https://files.pythonhosted.org/packages/71/68/e6f125df4af7e6d0b498f8d373274794bc5156b324e8ab4bf5c1b4fc0ec7/ruff-0.15.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c22e6f02c16cfac3888aa636e9eba857254d15bbacc9906c9689fdecb1953ab", size = 11421561, upload-time = "2026-03-12T23:05:31.236Z" }, { url = "https://files.pythonhosted.org/packages/dd/45/0ece8db2c474ad7df13af3a6d50f76e22a09d078af63078f005057ca59eb/ruff-0.15.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:940f11c2604d317e797b289f4f9f3fa5555ffe4fb574b55ed006c3d9b6f0eb78", size = 11234787, upload-time = "2026-02-03T17:52:46.432Z" },
{ url = "https://files.pythonhosted.org/packages/f1/9f/f85ef5fd01a52e0b472b26dc1b4bd228b8f6f0435975442ffa4741278703/ruff-0.15.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98893c4c0aadc8e448cfa315bd0cc343a5323d740fe5f28ef8a3f9e21b381f7e", size = 11310928, upload-time = "2026-03-12T23:05:45.288Z" }, { url = "https://files.pythonhosted.org/packages/8a/d9/0e3a81467a120fd265658d127db648e4d3acfe3e4f6f5d4ea79fac47e587/ruff-0.15.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bcbca3d40558789126da91d7ef9a7c87772ee107033db7191edefa34e2c7f1b4", size = 11112797, upload-time = "2026-02-03T17:52:49.274Z" },
{ url = "https://files.pythonhosted.org/packages/8c/26/b75f8c421f5654304b89471ed384ae8c7f42b4dff58fa6ce1626d7f2b59a/ruff-0.15.6-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:70d263770d234912374493e8cc1e7385c5d49376e41dfa51c5c3453169dc581c", size = 11235186, upload-time = "2026-03-12T23:05:50.677Z" }, { url = "https://files.pythonhosted.org/packages/b2/cb/8c0b3b0c692683f8ff31351dfb6241047fa873a4481a76df4335a8bff716/ruff-0.15.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:9a121a96db1d75fa3eb39c4539e607f628920dd72ff1f7c5ee4f1b768ac62d6e", size = 11033133, upload-time = "2026-02-03T17:53:33.105Z" },
{ url = "https://files.pythonhosted.org/packages/fc/d4/d5a6d065962ff7a68a86c9b4f5500f7d101a0792078de636526c0edd40da/ruff-0.15.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:55a1ad63c5a6e54b1f21b7514dfadc0c7fb40093fa22e95143cf3f64ebdcd512", size = 10635231, upload-time = "2026-03-12T23:05:37.044Z" }, { url = "https://files.pythonhosted.org/packages/f8/5e/23b87370cf0f9081a8c89a753e69a4e8778805b8802ccfe175cc410e50b9/ruff-0.15.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5298d518e493061f2eabd4abd067c7e4fb89e2f63291c94332e35631c07c3662", size = 10442646, upload-time = "2026-02-03T17:53:06.278Z" },
{ url = "https://files.pythonhosted.org/packages/d6/56/7c3acf3d50910375349016cf33de24be021532042afbed87942858992491/ruff-0.15.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8dc473ba093c5ec238bb1e7429ee676dca24643c471e11fbaa8a857925b061c0", size = 10340357, upload-time = "2026-03-12T23:06:04.748Z" }, { url = "https://files.pythonhosted.org/packages/e1/9a/3c94de5ce642830167e6d00b5c75aacd73e6347b4c7fc6828699b150a5ee/ruff-0.15.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:afb6e603d6375ff0d6b0cee563fa21ab570fd15e65c852cb24922cef25050cf1", size = 10195750, upload-time = "2026-02-03T17:53:26.084Z" },
{ url = "https://files.pythonhosted.org/packages/06/54/6faa39e9c1033ff6a3b6e76b5df536931cd30caf64988e112bbf91ef5ce5/ruff-0.15.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:85b042377c2a5561131767974617006f99f7e13c63c111b998f29fc1e58a4cfb", size = 10860583, upload-time = "2026-03-12T23:05:58.978Z" }, { url = "https://files.pythonhosted.org/packages/30/15/e396325080d600b436acc970848d69df9c13977942fb62bb8722d729bee8/ruff-0.15.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:77e515f6b15f828b94dc17d2b4ace334c9ddb7d9468c54b2f9ed2b9c1593ef16", size = 10676120, upload-time = "2026-02-03T17:53:09.363Z" },
{ url = "https://files.pythonhosted.org/packages/cb/1e/509a201b843b4dfb0b32acdedf68d951d3377988cae43949ba4c4133a96a/ruff-0.15.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:cef49e30bc5a86a6a92098a7fbf6e467a234d90b63305d6f3ec01225a9d092e0", size = 11410976, upload-time = "2026-03-12T23:05:39.955Z" }, { url = "https://files.pythonhosted.org/packages/8d/c9/229a23d52a2983de1ad0fb0ee37d36e0257e6f28bfd6b498ee2c76361874/ruff-0.15.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:6f6e80850a01eb13b3e42ee0ebdf6e4497151b48c35051aab51c101266d187a3", size = 11201636, upload-time = "2026-02-03T17:52:57.281Z" },
{ url = "https://files.pythonhosted.org/packages/6c/25/3fc9114abf979a41673ce877c08016f8e660ad6cf508c3957f537d2e9fa9/ruff-0.15.6-py3-none-win32.whl", hash = "sha256:bbf67d39832404812a2d23020dda68fee7f18ce15654e96fb1d3ad21a5fe436c", size = 10616872, upload-time = "2026-03-12T23:05:42.451Z" }, { url = "https://files.pythonhosted.org/packages/6f/b0/69adf22f4e24f3677208adb715c578266842e6e6a3cc77483f48dd999ede/ruff-0.15.0-py3-none-win32.whl", hash = "sha256:238a717ef803e501b6d51e0bdd0d2c6e8513fe9eec14002445134d3907cd46c3", size = 10465945, upload-time = "2026-02-03T17:53:12.591Z" },
{ url = "https://files.pythonhosted.org/packages/89/7a/09ece68445ceac348df06e08bf75db72d0e8427765b96c9c0ffabc1be1d9/ruff-0.15.6-py3-none-win_amd64.whl", hash = "sha256:aee25bc84c2f1007ecb5037dff75cef00414fdf17c23f07dc13e577883dca406", size = 11787271, upload-time = "2026-03-12T23:05:20.168Z" }, { url = "https://files.pythonhosted.org/packages/51/ad/f813b6e2c97e9b4598be25e94a9147b9af7e60523b0cb5d94d307c15229d/ruff-0.15.0-py3-none-win_amd64.whl", hash = "sha256:dd5e4d3301dc01de614da3cdffc33d4b1b96fb89e45721f1598e5532ccf78b18", size = 11564657, upload-time = "2026-02-03T17:52:51.893Z" },
{ url = "https://files.pythonhosted.org/packages/7f/d0/578c47dd68152ddddddf31cd7fc67dc30b7cdf639a86275fda821b0d9d98/ruff-0.15.6-py3-none-win_arm64.whl", hash = "sha256:c34de3dd0b0ba203be50ae70f5910b17188556630e2178fd7d79fc030eb0d837", size = 11060497, upload-time = "2026-03-12T23:05:25.968Z" }, { url = "https://files.pythonhosted.org/packages/f6/b0/2d823f6e77ebe560f4e397d078487e8d52c1516b331e3521bc75db4272ca/ruff-0.15.0-py3-none-win_arm64.whl", hash = "sha256:c480d632cc0ca3f0727acac8b7d053542d9e114a462a145d0b00e7cd658c515a", size = 10865753, upload-time = "2026-02-03T17:53:03.014Z" },
] ]
[[package]] [[package]]
@ -343,7 +365,7 @@ wheels = [
] ]
[[package]] [[package]]
name = "streamd" name = "streamer"
version = "0.1.0" version = "0.1.0"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
@ -358,8 +380,8 @@ dependencies = [
[package.dev-dependencies] [package.dev-dependencies]
dev = [ dev = [
{ name = "basedpyright" },
{ name = "faker" }, { name = "faker" },
{ name = "pyright" },
{ name = "pytest" }, { name = "pytest" },
{ name = "ruff" }, { name = "ruff" },
] ]
@ -369,23 +391,23 @@ requires-dist = [
{ name = "click", specifier = "==8.3.1" }, { name = "click", specifier = "==8.3.1" },
{ name = "mistletoe", specifier = "==1.5.1" }, { name = "mistletoe", specifier = "==1.5.1" },
{ name = "pydantic", specifier = "==2.12.5" }, { name = "pydantic", specifier = "==2.12.5" },
{ name = "pydantic-settings", extras = ["yaml"], specifier = "==2.13.1" }, { name = "pydantic-settings", extras = ["yaml"], specifier = "==2.12.0" },
{ name = "rich", specifier = "==14.3.3" }, { name = "rich", specifier = "==14.3.2" },
{ name = "typer", specifier = "==0.24.1" }, { name = "typer", specifier = "==0.21.2" },
{ name = "xdg-base-dirs", specifier = "==6.0.2" }, { name = "xdg-base-dirs", specifier = "==6.0.2" },
] ]
[package.metadata.requires-dev] [package.metadata.requires-dev]
dev = [ dev = [
{ name = "basedpyright", specifier = "==1.38.2" }, { name = "faker", specifier = "==40.4.0" },
{ name = "faker", specifier = "==40.11.0" }, { name = "pyright", specifier = "==1.1.408" },
{ name = "pytest", specifier = "==9.0.2" }, { name = "pytest", specifier = "==9.0.2" },
{ name = "ruff", specifier = "==0.15.6" }, { name = "ruff", specifier = "==0.15.0" },
] ]
[[package]] [[package]]
name = "typer" name = "typer"
version = "0.24.1" version = "0.21.2"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "annotated-doc" }, { name = "annotated-doc" },
@ -393,9 +415,9 @@ dependencies = [
{ name = "rich" }, { name = "rich" },
{ name = "shellingham" }, { name = "shellingham" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/f5/24/cb09efec5cc954f7f9b930bf8279447d24618bb6758d4f6adf2574c41780/typer-0.24.1.tar.gz", hash = "sha256:e39b4732d65fbdcde189ae76cf7cd48aeae72919dea1fdfc16593be016256b45", size = 118613, upload-time = "2026-02-21T16:54:40.609Z" } sdist = { url = "https://files.pythonhosted.org/packages/f2/1e/a27cc02a0cd715118c71fa2aef2c687fdefc3c28d90fd0dd789c5118154c/typer-0.21.2.tar.gz", hash = "sha256:1abd95a3b675e17ff61b0838ac637fe9478d446d62ad17fa4bb81ea57cc54028", size = 120426, upload-time = "2026-02-10T19:33:46.182Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/4a/91/48db081e7a63bb37284f9fbcefda7c44c277b18b0e13fbc36ea2335b71e6/typer-0.24.1-py3-none-any.whl", hash = "sha256:112c1f0ce578bfb4cab9ffdabc68f031416ebcc216536611ba21f04e9aa84c9e", size = 56085, upload-time = "2026-02-21T16:54:41.616Z" }, { url = "https://files.pythonhosted.org/packages/b8/cc/d59f893fbdfb5f58770c05febfc4086a46875f1084453621c35605cec946/typer-0.21.2-py3-none-any.whl", hash = "sha256:c3d8de54d00347ef90b82131ca946274f017cffb46683ae3883c360fa958f55c", size = 56728, upload-time = "2026-02-10T19:33:48.01Z" },
] ]
[[package]] [[package]]