ProSonata Tools for Visual Studio CodeThis VS Code extension creates ProSonata time entries straight from VS Code, bound to commits and branches, through the ProSonata API. Instead of starting a timer in ProSonata's web interface and typing the description by hand, you start the timer in VS Code, and the measured time becomes one time entry per branch — or, switchable, per branch and day or per commit — with a description taken from the commit. The idea: a time entry represents a piece of work the client can be billed for. If you bill by deliverable, it doesn't matter whether the work took one day or several; if you bill by time, the two other modes are for you. This is not an official ProSonata product. LanguageThe interface is English, with a German translation. The extension follows VS Code's display language. The CLI and the git hook take the language from What goes into ProSonata stays German: an open entry is marked InstallationSearch for ProSonata Tools under Extensions in VS Code, or in a terminal:
The extension is in the Visual Studio Marketplace and on Open VSX — where Cursor, VSCodium and Windsurf find it too. Updates arrive by themselves. Ready-made packageWithout access to the Marketplace: download the
Installed this way, there are no automatic updates. From sourceYou need a Node environment. Building, packaging and installing happen in one
step — the result is the same
To update, do the same again after ConfigurationOpen ProSonata in the Activity Bar and click ‹ProSonata: Set Up Account›. It asks for the ProSonata base URL and a user API key and writes both to Use a personal user key, not an app integration, so the time entries are linked to your user. Run the same command again later to change the key or the base URL — when the key expires, for instance. The base URL is already filled in, and leaving the key field empty keeps the current one. All other settings in Settings per repositoryThe order is fixed, and one step in it is a gate:
Click ProSonata: Choose Project for This Repository — it links the repository to a project and installs the Right after that the extension asks for the time category. ProSonata requires one on every time entry; as long as none is chosen, nothing is written. You can change it at any time through the Category row in the panel, and the change also applies to the entries that are still open. The project can be corrected later as well. Since a change almost always fixes a mistake, all unfinished time entries of this repository move along — including those already in ProSonata, and the running measurement. Only what is closed, and whose closing has already reached ProSonata, stays behind; if such an entry is already invoiced there, a follow-up entry is created in the new project instead of a change. You can also choose the rounding grid and the mode: one entry per branch, per branch and day, or per commit (see How it works). The rounding grid applies to all open time entries the next time they are written. Switching to “one entry per commit”, on the other hand, closes the open branch entry and asks for its final text first. The rounding grid applies only to the time entry that goes to ProSonata — never to the segments. Measuring and the log are exact to the second; rounding happens only when writing, and once per time entry — not for each segment. Rounding goes up to the next step: with a 15-minute grid, a measured 2:05 h is booked as 2:15 h. Because rounding happens per entry, the rounding grows with the number of entries: on If a repository chooses no grid of its own, the default from Every command is also available on the command line. How it worksWhile you work in VS Code, times are stored locally on your computer as segments. A time entry is what is stored in ProSonata. A time entry is made of any number of segments. Writing to ProSonata does not wait for the end: ten minutes after the timer starts, the tool creates the time entry — still without text, under a placeholder — and then updates it with the grown total on every further write. A branch entry is therefore in ProSonata from the start and grows there. Closing is just the last update.
The idea behind it: a branch is the piece of work a client pays for as a unit. Commits are intermediate steps the client doesn't need to see. Work on the main branch, however, is maintenance, and each commit is worth its own time entry. The mode can be switched per branch (“ProSonata: Choose Time Entry Mode”). What a commit does depends on the mode — the segment is cut at the moment of the commit in every case:
The text of the time entry comes from the commit, from a trailer starting with
On the main branch, where each commit closes its own entry, the subject line
applies when there is no trailer. On a branch only the trailer counts; the one
written last in a commit wins, plain subject lines are ignored. An entry without
text appears in ProSonata as What the tool needs to know about a time entry is kept in ProSonata's
The The Start and End fields hold the span of the working day — the start of the first and the end of the latest segment. If an entry spans several days, both stay empty: a span only means something for a single day. On several computersThe ID of a branch comes from the first commit of the repository and the branch name — the same in every clone. The second computer therefore finds the time entry again and adds to it instead of creating a second one. This assumes the two don't measure at the same time. Each computer remembers the foreign share and writes
The value written depends only on the computer's own state, never on what it just read — so a repeated write never doubles anything. Two people, on the other hand, don't get in each other's way: times belong to users in ProSonata, and each keeps their own entry per branch. If a timer is currently running on the same branch on another computer, you are told when you start — with the day it was started. If that was before yesterday, it no longer says “running since” but “stopping was probably forgotten there”. The extension can't do more than tell you: a timer on a closed laptop can't be stopped from here. If one computer closes the entry while time has accumulated on the other, the other one asks: add to the closed entry or create a new one. Nothing is written until you answer, and the closed entry is never touched without asking. When a timer is forgottenIf a segment runs for hours on end, the extension asks how much of it counts — all of it, a duration of your own, or nothing. It doesn't guess: a timer that ran overnight measured wall-clock time, and only you know how much of it was work. If you answer “two hours”, those are the first two — the work was at the start, the stopping was forgotten. If the computer slept in between, the extension knows exactly: timers don't fire while a machine is suspended, and a beat that comes back hours later proves that nothing ran on this machine. The panel then shows a row with the measured duration — deduct or keep. After deducting, the timer continues from the moment of waking up.
Without waiting for that question, a running segment can be discarded
entirely at any time — the case “committed, forgot to stop, didn't work
afterwards”. The option sits at the top of the adjustment dialog and names the
amount. Nothing is booked, the timer stops, and the log keeps the discarded
duration as You can turn the time forward and back with the pencil icon on the timer row or with “ProSonata: Adjust Time”. Type either a duration or a clock time:
The German Clock times need a running timer. They change the running segment; when
the timer is stopped, there is none they could refer to. Then add time as a
duration — Every option shows beforehand what it does: Closing the last VS Code window pauses the timer. Turn this off with
The status bar shows what the branch has collected in total — the figure
that goes on the invoice. The running segment is in the tooltip, together with
the branch and the sending status. The panel shows both figures side by side,
the running segment first: Follow-up work after a commitOn For this the sidebar shows Not booked as soon as such time is there. It is always attached to the last closed entry of this branch, as a plain total — the text and date there stay as they are. You confirm first, with the figures:
The second line is the reason for asking: the rounding grid rounds up, so a quarter-hour grid turns five minutes into a quarter of an hour. An entry that is already invoiced is refused — that can only be done in ProSonata itself. If the open entry is already in ProSonata, it disappears when attached: its time has moved. Only if another computer measured into it does it stay, and attaching is refused. Taking interrupted work to a branch of its ownYou started something on Run If the new branch already has an open entry, nothing is taken — merge the two when browsing the time entries instead. Merging a branchThe order at the end of a package, after a squash merge:
A merge with Browsing time entriesThe list icon in the title bar of the ProSonata panel fetches the time entries of the current repository from ProSonata into a list where several can be selected; in the Command Palette it is called “ProSonata: Browse Time Entries”. It offers three actions:
Invoiced entries stay in all three actions: they belong to the invoice. The segment log is not adjusted; it remains the archive of the measurement, while the billing is what gets corrected. The logThe clock icon next to it opens all measured segments as a rendered preview, grouped by day, with a branch picker in front. It can't be edited: the log is an archive, and what is billed is in the time entry. Corrections therefore go through time adjustment, not the text. When a time entry is closed, it gets a row of its own — without times, with its total and the note Time entry. Everything above belongs to it; that way you see which segments went into which invoice line without the text being repeated on every row. When you show a single branch, the branch column is left out. The branch list comes from the log in What is recorded is what was measured on this computer. What happened on another one is in ProSonata, but not here. CLI commands
A command that takes an argument asks for it when it is left out — with the
argument it runs without asking, which also makes it fit for scripts. What the extension doesn't do
Data and privacy
PlatformsThe extension is in daily use on macOS. The tests also run on Linux. Windows is untested: the hook is a shell script and should work with Git for Windows, but that is not confirmed. Please report back as an issue. DevelopmentTrying out a changeNothing gets installed for this. F5 opens a second VS Code window in which the extension runs straight from this working directory. There are two launch configurations:
The sandbox has to be created first — the command rebuilds it every time:
The two dashes before Checking and building
Visible texts go through The full reasoning, including the rejected alternatives and why, is in KONZEPT.md (German). The API behaviour the tool relies on was measured against a live account; the requests are in bruno/. LicenseMIT — see LICENSE. |



