{"id":37079071,"url":"https://github.com/dribnif/tt","last_synced_at":"2026-01-14T09:34:28.311Z","repository":{"id":47486592,"uuid":"246403834","full_name":"dribnif/tt","owner":"dribnif","description":"tt time tracker - a small stateful command-line time-tracking application implemented in Python","archived":false,"fork":false,"pushed_at":"2025-08-07T14:29:02.000Z","size":331,"stargazers_count":27,"open_issues_count":1,"forks_count":9,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-08-07T16:27:41.812Z","etag":null,"topics":["command-line","command-line-tool","time","time-tracker","timetracker"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dribnif.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-03-10T20:41:37.000Z","updated_at":"2025-08-07T14:29:07.000Z","dependencies_parsed_at":"2025-08-07T16:15:09.035Z","dependency_job_id":"07541075-c309-42ff-8eb3-1af93b290e85","html_url":"https://github.com/dribnif/tt","commit_stats":{"total_commits":23,"total_committers":4,"mean_commits":5.75,"dds":"0.30434782608695654","last_synced_commit":"3fd585747fa63b068b768fa5262c6ea7d03b01a6"},"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/dribnif/tt","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dribnif%2Ftt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dribnif%2Ftt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dribnif%2Ftt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dribnif%2Ftt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dribnif","download_url":"https://codeload.github.com/dribnif/tt/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dribnif%2Ftt/sbom","scorecard":{"id":356216,"data":{"date":"2025-08-11","repo":{"name":"github.com/dribnif/tt","commit":"af09f596593790b9a16eba81a4ee9ac95c99f1dd"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4,"checks":[{"name":"Maintained","score":0,"reason":"1 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":3,"reason":"Found 6/17 approved changesets -- score normalized to 3","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 28 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-18T09:41:05.472Z","repository_id":47486592,"created_at":"2025-08-18T09:41:05.472Z","updated_at":"2025-08-18T09:41:05.472Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28416114,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T08:38:59.149Z","status":"ssl_error","status_checked_at":"2026-01-14T08:38:43.588Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["command-line","command-line-tool","time","time-tracker","timetracker"],"created_at":"2026-01-14T09:34:27.695Z","updated_at":"2026-01-14T09:34:28.304Z","avatar_url":"https://github.com/dribnif.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"tt - A command-line based time tracker\n======================================\n\n`tt` is a small stateful command line time tracking application\nimplemented in Python. Simple basic usage looks like this:\n\n    $ tt start my-project 14:15\n    $ tt note 'Got the business folk to talk to IT. Went well.'\n    $ tt stop now\n\nAlternatively you can skip the colon when entering times:\n\n    $ tt start my-project 0915\n    $ tt stop 1020\n\n*tl;dr* [Installing using pip](#install-using-pip)\n\nWhat is tt?\n===========\n\n`tt` is a simple command line time tracker. It is based on\n[ti](https://github.com/tbekolay/ti), by Shrikant Sharat and Trevor\nBekolay and is written in Python. `ti` is in turn inspired by both\n[timed](http://adeel.github.com/timed), and the elegantly simple\n[t](http://stevelosh.com/projects/t/).\n\nAs opposed to its predecessor `ti`, `tt` is mostly aimed at cli versed IT\nconsultants or other professionals who need to *precisely* keep track of\ntheir time spent working on multiple projects and issue customer\ninvoices based on their entries.\n\nLogical structure\n-----------------\n\n`tt` is made up of two main logical modules:\n \n* the actual time tracking module, that handles creation and editing of time entries (the C and U in **C**R**U**D).\n* the reporting module, which handles the evaluation of already entered time information (the R in C**R**UD).\n\nTime boxes\n----------\n`tt` enables the user to record their effort in the form of *time segments* or *time boxes*.\n\nA *time box* is defined by:\n* a name\n* a starting point\n* an end point\n* optional notes\n\nExample: \n\n    $ tt start my-project 14:15\n    $ tt note 'Twas an extremely difficult call we had this afternoon'\n    $ tt stop 18:00\n\nStateful time box definition\n----------------------------\n\nAnother, somewhat hidden dimension is the *current date* - or day -\nwhich is *implicitly* **today**, your machine's current `date`, unless\nexplicitly overriden by using the environment variable `TT_CURRENT_DAY`:\n\n    $ export TT_CURRENT_DAY=\"2023-08-30\" \n    # note the ISO formatting\n    # any tt commands issued in between here and the unset command \n    # will refer to the 30th of August 2023\n\n    $ tt start future-task 00:00\n    $ tt note \"Hoverboarding isn't what it used to be\"\n    $ tt note \"Also, I'm probably sleeping right now\"\n    $ tt stop 07:00\n\n    $ unset TT_CURRENT_DAY\n\nStatefulness++ for your convenience\n-----------------------------------\n\nIn addition to the implied *current date*, `tt` saves the outcome of any\nsuccessful command you've issued in a centralised JSON file. This means\nthat there is *only one state* of `tt` for your entire user session. You\ncan start a time box in one terminal window, add notes to it in another\none and finalise the time box in a third terminal.\n\nThe location of the time entry database can be specified via the\nenvironment variable `SHEET_FILE`. The default location is\n`~/.tt-sheet.json`.\n\nTo make your custom DB location persistent, just add the following line\nto your `~/.bashrc` as demonstrated here:\n\n    export SHEET_FILE=/home/johnson/timesheets/time-entries.json\n\nPlease consider backing up this file regularly, so as to avoid any\ndata loss.\n\nTo correct erroneous entries, you can either edit this file\ndirectly or, alternately, use `tt`'s built-in `edit` function.\n\nYou can process the data from the time entry DB with any tool of your choosing.\n`tt` provides you with some basic reporting that might or might\nnot make sense for your purpose.\n\nUsage\n=====\n\nAdding a new time box:\n----------------------\n    $ tt start fav-customer 12:15\n    Started working on fav-customer at 12:15.\n\n    $ tt status\n    You have been working on fav-customer for about 2 hours, since 12:15;\n    It is now 14:12.\n\n    $ tt stop now\n    So you stopped working on fav-customer.\n\n`start` and `stop` can take a time argument of the form `HH:mm`, `HHmm` or the keyword `now` at\nwhich to apply the action. They store the times normalized to GMT in the\ndatabase, taking into account the DST values for your timezone. \n\nTherefore beware when editing the timestamps in the DB file directly - this type of time \narithmetic gets brainfucky really quickly and you *will* probably cock it up!\n\nEnhance your time box with notes and tags:\n------------------------------------------\n\nThe **note** and **tag** commands only work if an active (open) time box\nexists, i.e. if the `stop` command hasn't been invoked yet:\n\n    $ tt note \"implement new user authentication\"\n\nIn order to reduce boilerplate text when adding loads of notes to your timebox,\ntt also allows you to edit the notes of your current timebox only. Do this with the command **ect** or\n**edit-current-timebox**, which will launch the editor you've selected (see next chapter on how to do that):\n\n    $ tt ect\n\nTag your activity for added reportability:\n\n    $ tt tag private\n\nChange entries:\n---------------\n\nBy setting a default text editor (console or gui-based) in the\nenvironment variable `EDITOR`, you can easily correct mistakes. Just add\nthe following line, referencing your favourite text editor to your\n`~/.bashrc` and enable fast time entry correction.\n\n    $ export EDITOR=vim\n    $ tt edit\n\nThis will open your time entry DB using the specified editor in the\nhandy YAML format. Once you save your changes and exit the file, the\nchanges will be persisted back into your SHEET\\_FILE.\n\nSee your entries:\n-----------------\n\n### log\n\nGet a log of the total time spent on each activity:\n\n    $ tt log\n\nGet a log of the total time spent on each activity, filtered by activities started within a given time period:\n\n    $ tt log 2023-11-13 2023-11-15T13:00:00\n\nAccepts datetimes in ISO8601 format. End datetime not required.\n### csv\n\nGet a list of all your individual log entries in CSV format, so that they can be imported\ninto your favourite spreadsheet editor\n\n    $ tt csv\n\n    $ tt csv | grep 2018-01 ### will show all entries you logged in January 2018\n\n    $ tt --no-color csv | grep 2018-01 \u0026gt; /tmp/jan-2018.csv ;\n    libreoffice /tmp/jan-2018.csv\n\nThe last command allows you to break out of the console and takes you\ninto the realm of spreadsheets. The `--no-color` parameter makes sure\nthat the terminal's color markup does not end up in your csv file.\n\n### report\n\nGet a report for your project, grouped and summed up by day:\n\n    $ tt report customeur\n\n    $ tt report customeur | grep 2018-10\n\n    $ tt --no-color report customeur | grep 2018-10 \u003e /tmp/oct-2018.csv ; libreoffice /tmp/oct-2018.csv\n\nSame trick applies here. Beware that the CSV separator is in this case\nthe pipe symbol `|`, since semicolons are used for concatenating all the\ndifferent note entries into one big note field per day.\n\n### calview\n\nDoublecheck your entries per month and gain an overview of your effort\nthroughout the month. No more blindspots.\n\nWhen calling calview with one parameter, the application assumes you\nwant to have the calendar view for the supplied month and the *current\nyear*.\n\n    $ tt calview 7\n\nSpecifying a different year for calview is done like so:\n\n    $ tt calview 1 2054\n\nThis last command will show you the working days of January 2054.\n\nAnd here's some example output:\n![](images/tt_calview.png)\n\nCaveats\n=======\n\nThere is no proper validation of time entries as of now:\n\n-   ~~should your end time be before your starting time, this will be\n    reflected in your reporting.~~ As of version 1.0.3, the end time needs to be after \n    the start time of the timebox.\n-   should the timeboxes defined for various projects overlap, this will\n    be reflected in your reporting.\n\n\u003ca name=\"install-using-pip\"/\u003eInstalling using pip\n====================\n\nExecuting the following command should get you up and running: \n\n    $ python3 -m pip install tt-time-tracker\n    \nBefore you start using it, make sure you have your environment set up - the location of the time entry database and \nthe editor - by adding the appropriate environment variables to your `~/.bashrc` or similar.\n\n    export EDITOR='vim'\n    export SHEET_FILE='/home/johnson/timesheets/time-entries.json'\n\nMake sure you replace the values with ones that make sense to you.\n\nInstalling from source\n======================\n\nAfter having checked out the sources by cloning this repo, change into the new folder and execute the setup script.\n\n    $ git clone git@github.com:dribnif/tt.git\n    $ cd tt/\n    $ python3 setup.py clean install\n    \nAnd you're ready to roll.\n\nNow make sure you add the two export statements to your `~/.bashrc`, in order to have full control over `tt`.\n\n    export SHEET_FILE='/home/johnson/timesheets/time-entries.json' \n    export EDITOR='kate'\n    \nObviously: replace the values of the parameters with ones that make sense for you.\n\nDeveloping\n==========\n\nIf you merely want to test your code changes live, you don't really need to \"install\" the program on your system. \nYou can instead just execute the file tt-dev.py with the arguments of your choosing:\n\n    $ ###assuming you are in the correct folder - the one where tt-dev.py is located\n    $ python3 tt-dev.py start my-awesome-project now\n    \n\nDevelopers\n==========\n\nRemolded into `tt` by [@dribnif](https://github.com/dribnif)\n\nBased on `ti` originally created by Shrikant Sharat\n([@sharat87](https://twitter.com/#!sharat87)), and maintained by\n([@tbekolay](https://github.com/tbekolay)).\n\nFurther Information\n===================\n\nFor more context on tt, feel free to read this blog post [https://metamorphant.de/blog/posts/2020-06-21-tt-ubiquitous-time-tracking-for-command-line-aficionados/](https://metamorphant.de/blog/posts/2020-06-21-tt-ubiquitous-time-tracking-for-command-line-aficionados/)\n\nLicense\n=======\n\nMIT License\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdribnif%2Ftt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdribnif%2Ftt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdribnif%2Ftt/lists"}