Skip to main content

semantic-linefeeds

AI coding agents know where a sentence ends — they just don't wrap lines there.
Line breaks are for meaning, not margins.

A diff-aware prose guardrail for AI coding agents and source repositories. Instructions alone don't reliably prevent it, and a deterministic check at the tool boundary catches it.

The Problem:

When comments and docs are wrapped to a fixed column width, changing one word forces the whole paragraph to reflow, and the diff for that one-word edit touches every line below it.

Telling a coding agent "one sentence per line" in your instructions doesn't fix this either: instructions alone do not reliably prevent non-semantic breaks, however prominently the rule is stated.

What This Kit Does:

semantic-linefeeds enforces semantic line breaks at the moment text is written — in code comments, doc comments, docstrings, and Markdown.

Because each line holds one sentence (or one clause of a long sentence), an edit touches only the line whose meaning changed, and git blame keeps pointing at the sentence that changed.

Adapters wire the same dependency-free Python detector into Claude Code, Codex CLI, opencode, and — through a portable AGENTS.md snippet — any agent that reads instruction files.

Why Semantic Line Breaks

SemBr breaks lines by meaning instead of by column: one sentence per line, and a long sentence splits only at a real clause boundary. The rendered output doesn't change — Markdown still joins the lines back into one paragraph.

The difference shows up in diffs. Add two words near the top of a column-wrapped paragraph (wrapped at 72 characters here), and the rewrap ripples through every line below it:

-The exporter batches metrics in memory, flushes them once per minute,
-and retries failed uploads with exponential backoff until the queue
-drains.
+The exporter batches metrics and traces in memory, flushes them once per
+minute, and retries failed uploads with exponential backoff until the
+queue drains.

The same edit under semantic line breaks touches only the one line whose meaning changed:

-The exporter batches metrics in memory,
+The exporter batches metrics and traces in memory,
 flushes them once per minute,
 and retries failed uploads with exponential backoff until the queue drains.

Reviewers see exactly the word that changed, and blame on any sentence finds the commit that wrote it.

Quickstart

uv tool install semlf        # or: pipx install semlf
semlf install                # detects agents, lists every path it would write, asks y/N
semlf doctor                 # replays the payloads end to end

Claude Code stays on its own plugin marketplace and is never touched by semlf:

claude plugin marketplace add arloliu/semantic-linefeeds
claude plugin install semantic-linefeeds@semantic-linefeeds

A local checkout works the same way — point the first command at its path instead:

claude plugin marketplace add /path/to/semantic-linefeeds
claude plugin install semantic-linefeeds@semantic-linefeeds
Agent Command
Codex CLI semlf install codex
opencode semlf install opencode
Claude Code the marketplace pair above
Anything else (AGENTS.md) semlf install agentsmd PATH

semlf install with no target detects which agents are present and proposes one plan covering all of them, then asks y/N. Naming a target instead — codex, opencode, or agentsmd PATH — applies that one target immediately.

Pick one channel for the semlf command itself. A zipapp left at ~/.local/bin/semlf and a uv tool install/pipx install shim would otherwise shadow each other; semlf install's end-of-run PATH check, and semlf doctor, both name the collision when they see one.

Letting the Agent Set It Up

Once any agent on the machine has been set up, the rest is something you can just ask for. A setup-semlf skill installs alongside the judgment layer, so an agent asked to install, repair, or reconfigure semlf follows a fixed procedure rather than guessing at a package name or hand-editing hooks.json.

In opencode, type the command:

/setup-semlf

In Codex CLI or Claude Code, ask in prose — "set up semlf in this project" is enough to load it.

It installs the CLI when it is missing, walking uv, pipx, pip --user, and the checkout door in that order. It repairs an install left stale by an upgrade, which is the failure that actually recurs. And it offers to write the project's .semlf.ini, though most projects need none.

Three things it will not do, whatever it is asked:

  • Overwrite one of your files. A refusal from semlf install is shown to you with the difference, and --force stays yours to type.
  • Add an exclude line on its own authority. Excludes suppress discovery, so the agent may transcribe one you dictate and never propose one itself.
  • Run anything before showing you the command.

The first install on a fresh machine is still yours to run — an agent cannot fetch a skill it does not yet have. Use the Quickstart above, or the checkout door where there is no package index.

What Gets Installed

Component Location Purpose Who needs it
Checker ${XDG_DATA_HOME:-~/.local/share}/semlf/check_linefeeds.py The enforcement core every installed hook and skill runs Codex CLI, opencode
README published beside the checker, at the same neutral root Resolves the installed skill's suppression-rules link, even on an air-gapped machine Codex CLI, opencode
Codex hook entry $CODEX_HOME/hooks.json (default ~/.codex/hooks.json) Runs the checker after every edit; blocks fused, reports long, and reports wrap on a blocked line or when opted in Codex CLI
Judgment skill ~/.agents/skills/semantic-linefeeds/SKILL.md The judgment layer: clause-boundary calls, suppression syntax, the disagreement rule Codex CLI, opencode
Setup skill ~/.agents/skills/setup-semlf/SKILL.md Lets an agent install, repair, or configure semlf without improvising the commands Codex CLI, opencode
opencode plugin the opencode plugins directory ($XDG_CONFIG_HOME/opencode/plugins, default ~/.config/opencode/plugins) Wires the checker into opencode's edit/write/apply_patch tool output opencode
opencode's checker copy the same opencode plugins directory, beside the plugin The plugin resolves its checker next to itself, not at the neutral root opencode
opencode setup command $XDG_CONFIG_HOME/opencode/commands/setup-semlf.md (default ~/.config/...) Makes /setup-semlf typeable: opencode offers skills to the model, commands to you opencode
AGENTS.md snippet the file you name with semlf install agentsmd PATH The judgment layer for any agent with no native skill mechanism Any AGENTS.md-reading agent

The skill is published once, to ~/.agents/skills, which Codex CLI and opencode both read. opencode 1.18.18 or newer is required, and OPENCODE_DISABLE_EXTERNAL_SKILLS disables that scan — with it set, opencode sees no skill regardless of where it is installed.

On the package channel, the semlf package is the installer and cannot be skipped — but its check commands (semlf check, semlf --staged, and so on) stay optional after that. A guardrail-only machine that wants no CLI at all is the checkout door's offer instead: install.sh --codex, with no --cli, writes the hook, the skill, and the neutral-root payloads, and stops there (see Air-gapped and mirror installs).

Why Telling an Agent Isn't Enough

Writing the rule into AGENTS.md, CLAUDE.md, or any other instruction file doesn't hold up at generation time: agents keep producing non-semantic line breaks with the rule in plain sight.

So this kit backs the rule with three layers instead of trusting the model to remember it:

  1. Hook — a post-edit hook runs a deterministic detector over the text just written to any supported file. Violations come back as feedback the moment they happen, which is the one channel that survives a long session. Only fused stops the edit; a long line comes back as advice, because leaving it long is often the right answer. wrap is not reported to the model at all, because a labeled corpus measured its false positives and the number was not small enough to act on. Set SEMLF_EXPERIMENTAL_WRAP=1 to see it anyway, as advice that never blocks, or set experimental-wrap = true in .semlf.ini to opt a whole project in.
  2. Skillsemantic-linefeeds carries the judgment calls the detector can't make on its own: what counts as a clause boundary, the compound-object and test, the never-break list, and the bounded disagreement rule — judge a finding before rewriting, and a believed false positive or a finding that survives one repair goes to the user instead of another rewrite. Claude Code ships it as a plugin skill; semlf install codex and semlf install opencode publish the same native copy to a root both agents read; an agent with no native skill mechanism falls back to the AGENTS.md snippet. Hook feedback names the skill only when a usable copy is present at a location Codex resolves skills from.
  3. Detectorscripts/check_linefeeds.py, dependency-free Python 3. Three precision-tuned heuristics: fused (two sentences on one line), wrap (a line severed mid-clause), and long (over a configurable limit, default 120 chars, reported whether or not a clause boundary is found). --file audits report all three, because an audit is read by the person who asked for it. The hook reads a stable snapshot of the edited file for context, so it can report a real line number and skip findings an edit didn't touch, and it falls back to checking only the text just written when that mapping fails. Either way, legacy column-wrapped files are never flagged into a noisy rewrap.

Suppressing a finding

Two directives, each scoped to exactly one line:

  • semlf-ignore — withholds every finding anchored on the line carrying it.
  • semlf-ignore-next — withholds every finding anchored on the next line.

The directive rides in a comment, so it is written the way the file's own language writes comments.

Language Standalone Trailing
Markdown <!-- semlf-ignore-next --> … judged text. <!-- semlf-ignore long -->
Go, Rust, C-family, JavaScript // semlf-ignore-next // … judged text. // semlf-ignore long
Python, Ruby, shell # semlf-ignore-next # … judged text. # semlf-ignore long
Lua, SQL -- semlf-ignore-next -- … judged text. -- semlf-ignore long

Standalone, on a line of its own:

<!-- semlf-ignore-next -->
A line the checker will leave alone.
// semlf-ignore-next
// A line the checker will leave alone.

Trailing, after the line it judges:

A long judged line that runs on past the limit. <!-- semlf-ignore long -->
# A long judged line that runs on past the limit.  # semlf-ignore long

A trailing carrier is the rightmost comment marker to the end of the line, so the judged text is whatever stands to its left.

A two-line wrap finding anchors on its upper line, so a trailing carrier suppressing it must sit on that first line, not the second.

Each directive takes zero or more kind arguments (fused, wrap, long); no arguments suppresses every kind on that line. A recognized directive name with any unrecognized argument is malformed and inert — it suppresses nothing, and the findings it would have hidden stay visible.

Suppression is user-directed. An agent never adds a semlf-ignore or semlf-ignore-next directive on its own authority: when it judges a finding to be a false positive, it leaves the text as written and raises the disagreement with you instead.

Suggested replacements

A blocking fused report on !, ?, or . carries an exact two-line replacement when the split is unambiguous (ADR-0007; periods since ADR-0028):

Suggested replacement for line 12:
    Stop now!
    Go on.

Period boundaries earned the suggestion in v0.9: a sealed holdout round scored the class at 34 of 35 acceptable against a preregistered 0.80 floor, and only then was it admitted (ADR-0028). It is never applied for you — apply it as your next edit, after judging the finding like any other.

Configuration

.semlf.ini at the repository root holds every project setting the checker reads, one [semlf] section, three optional keys:

[semlf]
long-limit = 100
experimental-wrap = true
exclude =
    docs/legacy/
    *.gen.md
  • long-limit — the long-line advisory threshold, in characters (0 disables the advisory). Precedence is the --long-limit flag, then $SEMLF_LONG_LINE, then this key, then the built-in default of 120. Fused and wrap findings are never affected — only the advisory threshold moves.
  • experimental-wrap — opts wrap findings back into hook feedback, arriving as advice that never blocks an edit (ADR-0017). $SEMLF_EXPERIMENTAL_WRAP decides outright whenever it is set to a non-empty value — 0, false, no, or off reads as off, anything else as on — so it can force the kind on for a repo that never opted in, or off for one that did.
  • exclude — one glob pattern per line, matched per path segment; a trailing / names a folder, excluded at any depth unless an inner / anchors a chain at the config root (see "Excluding paths" below).

The flag and the environment variable always win over the file (ADR-0012). The core discovers .semlf.ini by walking upward from the checked file and stops at the first directory holding either the file or a .git entry, so a config never leaks across a repository boundary. A missing, malformed, or unparseable file is inert — the checker falls back to the next precedence leg rather than failing the run, and an invalid value in one key never silences another.

Hook mode skips paths under the platform temp directory and any tmp/ component, so agent scratch files are never flagged. --file mode always checks exactly the paths you name.

Excluding paths

exclude takes one pattern per line; the three shapes below are worth seeing side by side:

[semlf]
exclude =
    vendor/
    docs/generated/
    *.generated.md

A trailing / names a folder — a bare name like vendor/ excludes it at any depth, while an inner / before the trailing one anchors a chain at the config root — today the repository root, where .semlf.ini lives (ADR-0012) — so docs/generated/ excludes only that path, not plugins/docs/generated/. A pattern without a trailing / is a glob: with a / it must match the whole relative path, segment by segment; without one, like *.generated.md, it matches any single path component at any depth. Matching is case-sensitive everywhere.

Excludes filter discovery only — hook mode and the three git modes below. A path you name explicitly on --file or check is always checked, exclude or no exclude, because naming a path is the judgment call excludes exist to encode. An agent never adds an exclude line on its own authority (ADR-0010's principle); it raises the disagreement with you instead.

Checking git snapshots

Three more modes check a git snapshot instead of files you name:

  • semlf --staged checks the index — what git commit would record — reading each staged blob by its own object id.
  • semlf --diff checks the worktree copy of every unstaged change against the index.
  • semlf --changed checks the worktree copy of everything different from HEAD, staged and unstaged together.

All three accept --json and --long-limit N, the same as --file.

A fourth verb answers a different question about the same snapshots. semlf reflow [REF] verifies that the worktree differs from REF (default HEAD) only in where its prose breaks: no word changed, no code or markup changed, no paragraph appeared or disappeared. A reflow-only diff looks like a content change in review — lines vanish and reappear — and this is the one-line proof that it is not, so a commit that only moves breaks can say so and be believed. Any other change — a word, a code line, a deleted file, a file outside prose scope — fails it with the first difference named.

Only tracked changes are enumerated; an untracked file needs git add before any mode can see it. Symlinks are never checked, in any mode.

Two more selection modes exist for CI, where a clean checkout has no local delta: semlf --base REF checks the files changed since merge-base(REF, HEAD) and reports only the diagnostics the changed lines own, because a CI run annotates someone's pull request and a finding that predates the branch is not that author's to answer; semlf --all checks every tracked file under the configured excludes. semlf render sarif|github turns a captured --json documents list into a SARIF report or GitHub workflow annotations, re-analyzing nothing.

Exit codes

Every mode answers with the same small vocabulary, and from 1.0 the meaning of each code is a compatibility promise:

Mode 0 1 2 64
check / --file, --staged, --diff, --changed, --base, --all clean, or advisory long only a fused/wrap violation, or an unreadable file or git source bad usage
--hook claude, --hook codex advisory-only output, a payload that is not the hook's to judge, or a malformed payload a blocking finding, reported on stderr bad usage
render rendered, however many findings the documents hold unreadable or invalid input bad usage
reflow pure reflow, or nothing differs not a pure reflow, or a git source error bad usage
doctor every probe passed a probe failed arguments given
install, status, uninstall applied or reported refused or failed bad usage
--version, --help printed

Two of these rows are choices worth naming. A hook exits 0 on a malformed payload — fail-open is deliberate, because a hook must not block editing on its own malfunction. And render exits 0 regardless of findings: what a finding means for a build is the gate's decision, not the renderer's.

Compatibility

From 1.0, semlf follows semantic versioning over its public surfaces: the CLI modes and flags, the .semlf.ini schema, the --json document schema, the suppression directives, the exit codes above, the GitHub Action's inputs and outputs, and the names you install by — the semlf package and command, the pre-commit hook id, the Claude plugin name — all on Python 3.9 or newer. A change that would break any of them costs a major version; new capability arrives in minor versions. Two things are explicitly not promised: which lines draw findings (precision keeps improving between majors), and the wording of messages — the text is for people, the structure is for tools.

The GitHub Action

The Action installs the checker it shipped with — pinning the Action pins everything — checks the pull request against its base, annotates the changed lines, and fails only on the kinds you ask for.

input default meaning
fail-on fused kinds that fail the build: a comma-separated list of fused and wrap in either order, at least one; whitespace is trimmed, empty segments and duplicates are ignored; long is refused, it never fails a build
mode base base checks changed files against the PR base; all checks the whole tree
base the PR's base the ref for mode: base; required on non-PR events
sarif-file none write a SARIF report here and expose it as the sarif-file output; never uploaded

The default, annotations only:

on: pull_request
jobs:
  prose:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0   # mode "base" needs history reaching the merge base
      - uses: arloliu/semantic-linefeeds@main

With a SARIF report, whose upload — and its permission — belongs to your workflow:

on: pull_request
permissions:
  security-events: write
jobs:
  prose:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: arloliu/semantic-linefeeds@main
        id: semlf
        with:
          sarif-file: semlf.sarif
      - uses: github/codeql-action/upload-sarif@v3
        if: always() && steps.semlf.outputs.sarif-file != ''
        with:
          sarif_file: ${{ steps.semlf.outputs.sarif-file }}

Policy — .semlf.ini, including exclude — is always read from the working tree, even for --staged: its content is the index, but its policy is the checkout it runs in. A config that is staged but not yet on disk does not yet govern --staged (ADR-0013).

Pre-commit

repos:
  - repo: https://github.com/arloliu/semantic-linefeeds
    rev: <tag>
    hooks:
      - id: semlf

language: python lets pre-commit build this repository into its own environment, so semlf does not need to be installed or on PATH beforehand.

Supported Languages

Languages Extensions
C, C++, Objective-C .c .h .cc .cpp .hpp .hh .m .mm
Java, Kotlin, Scala, Groovy .java .kt .kts .scala .groovy .gradle
JS/TS .js .jsx .ts .tsx .mjs .cjs
C#, VB.NET .cs .vb
Go, Rust, Zig .go .rs .zig
Python .py .pyi
Swift, Dart, PHP .swift .dart .php
shell, PowerShell .sh .bash .ps1 .psm1 .psd1
Ruby, Perl, Lua .rb .rake .pl .pm .lua
SQL, R, Haskell, Elixir .sql .r .R .hs .ex .exs
Markdown .md .markdown .mdx

What is not analyzed

The checker reads English prose. Its two sentence-reading kinds each depend on an ASCII signal — a lowercase word before the stop, and a lowercase opener on the following line — so CJK text is passed over rather than checked. A Chinese, Japanese, or Korean paragraph therefore draws no fused and no wrap, however it is broken. The long advisory is the exception: it counts characters rather than reading the sentence, so a CJK line past the limit draws one like any other line. Its message says the checker found no boundary it recognizes, which is the honest answer for CJK text and leaves the split to your judgment. A full-width terminator is recognized as ending a line, so a Chinese sentence above an English one is not accused at the boundary. A CJK line that ends without punctuation can still draw a wrap when the line under it opens on a lowercase English word, because that judgment is read off the English line. In none of these cases is the CJK text itself read as prose: what fires is a rule about the English line beside it, or about width.

Recommended Instructions Companion

The one habit that survives inline generation belongs in your agent's global instruction file — AGENTS.md for most agents, CLAUDE.md for Claude Code. Everything else lives in the skill and the hook:

## Comment and doc formatting

One sentence per line in all comments and Markdown (semantic linefeeds).
After writing any text block, act on whatever the linefeeds hook reports.
A blocked edit must be fixed; an advisory is a judgment call, and leaving the line alone can be right.

Appendix

Air-gapped and mirror installs

The package channel's git+URL form still works when a machine can't reach PyPI:

pipx install git+https://github.com/arloliu/semantic-linefeeds
# or
uv tool install git+https://github.com/arloliu/semantic-linefeeds

For a fully offline or mirrored network, the checkout door covers every artifact this kit installs. One command clones (or updates) a checkout under ${XDG_DATA_HOME:-~/.local/share}/semantic-linefeeds and hands the remaining arguments to scripts/install.py, the same shared lifecycle engine behind semlf install:

curl -fsSL https://raw.githubusercontent.com/arloliu/semantic-linefeeds/main/install.sh | sh -s -- --codex --cli

Pass the flag for your agent to the one-liner above, or to python3 scripts/install.py inside a checkout. Passing no flag prints a status report of what's installed where.

Agent Installer flag
Codex CLI --codex
opencode --opencode
Anything else --agentsmd PATH

Add --cli to build the semlf zipapp and install it as ~/.local/bin/semlf — the one channel that needs no package index at all, and the one the package door deliberately has no equivalent for: pipx, uv, and the zipapp all want the same ~/.local/bin/semlf shim, so building and removing the zipapp stays exclusive to the checkout door.

install.sh reads its clone source from --repo or the SEMLF_REPO env var, and the checker itself never touches the network. Mirror this repo to your internal git host, curl the script from the mirror's raw endpoint, and point SEMLF_REPO back at the mirror:

# GitLab raw path shown; Gitea/Forgejo use /raw/branch/main/ instead of /-/raw/main/
curl -fsSL https://git.internal/you/semantic-linefeeds/-/raw/main/install.sh |
  SEMLF_REPO=git@git.internal:you/semantic-linefeeds.git sh -s -- --codex

Export SEMLF_REPO once in your shell profile, and every later re-run installs and updates from the mirror without repeating it. --ref/SEMLF_REF pins a tag or branch; --home/SEMLF_HOME moves the checkout.

For Claude Code, claude plugin marketplace add git@git.internal:you/semantic-linefeeds.git covers the same case.

The Vercel skills CLI can fetch the judgment-layer skill on its own, as a supplement rather than an install path. It copies SKILL.md and nothing else, so it cannot install the hook — this kit's load-bearing layer, the one that surfaces findings at generation time — and an agent that only has the skill this way still needs semlf install or the checkout door for the hook.

Lifecycle

semlf install [TARGET...]     # detect agents and propose a plan, or apply one named target
semlf status [agentsmd PATH]  # report every discoverable or recorded artifact's state
semlf uninstall TARGET...     # preflight-then-apply removal of a target's artifacts
semlf doctor                  # replay a payload end to end, report evidence

Upgrading is a two-command pair:

uv tool upgrade semlf && semlf install
# or: pipx upgrade semlf && semlf install

The first command updates the semlf command itself, its embedded payloads included; the second re-applies the current payload set over whatever is already installed. Upgrades are provenance-aware: re-running an installer replaces an untouched older release silently. A release newer than the one already published is a downgrade and refuses by default; --force states the intent and replaces it, no backup, since a managed file is never the only copy of anything. An edited or unrecorded file always refuses first; --force there takes an exclusive backup to <name>.bak before replacing it, and an occupied backup slot refuses either way.

Uninstalling one integration never deletes the neutral root's published checker or README: their independence from any single integration is the point of the neutral root, so they are left in place once the last integration that used them is gone, and semlf status lists the specific leftover file paths in one line for manual removal.

A zipapp left over from before this redesign is a migration case: semlf install runs a PATH check at the end of every run and warns when semlf on PATH is not the artifact that just ran; semlf status and semlf doctor repeat the warning, with the checkout-door removal pointer (install.py --uninstall --cli).

Testing

python3 -m pytest tests/ -q                        # full suite: CLI, detector, extractor, install/doctor
python3 scripts/check_linefeeds.py --file <files>   # audit files by hand
bun test adapters/opencode/                         # opencode plugin's own unit tests (needs bun)

Detector fixtures live under tests/fixtures/<language>/; inline {fused}, {wrap}, or {long} markers mark the lines that should be flagged. good_* fixtures must carry zero markers, and a dedicated test enforces it.

The extractor has golden tests under tests/extractor/. After changing extraction logic, regenerate with python3 -m pytest tests/test_extractor.py --update-golden and diff-review the result.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

semlf-1.0.0.tar.gz (303.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

semlf-1.0.0-py3-none-any.whl (197.0 kB view details)

Uploaded Python 3

File details

Details for the file semlf-1.0.0.tar.gz.

File metadata

  • Download URL: semlf-1.0.0.tar.gz
  • Upload date:
  • Size: 303.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.12.4 {"installer":{"name":"uv","version":"0.12.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Linux Mint","version":"22.3","id":"zena","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for semlf-1.0.0.tar.gz
Algorithm Hash digest
SHA256 1252e2ff12942804101b73a80d9298c7556aa42cd6285671d90b659bf7621f18
MD5 42fc340431c2fdcb902cd85b88f2e973
BLAKE2b-256 bd719533ff5867af2af1102275339de3a7cbe2c7088a3bb1c7982415a477b1e6

See more details on using hashes here.

File details

Details for the file semlf-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: semlf-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 197.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.12.4 {"installer":{"name":"uv","version":"0.12.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Linux Mint","version":"22.3","id":"zena","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for semlf-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 dd7f2ee730c94b0a721a0f5d435ce21c6c6a29b409c53775f88a9d0e23917968
MD5 3aa4c7eb682b8215ca6af04337fefdec
BLAKE2b-256 8d5ba36c162dc9b0ae61e4ea52f52cd40d90830c4f992c663ed68f0ed8f48cb3

See more details on using hashes here.

Release history Release notifications | RSS feed

1.0.1

2 files

This release

1.0.0 This release

2 files

0.9.0

2 files

0.8.1

2 files

0.8.0

2 files

0.7.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page