Skip to content
Open
Show file tree
Hide file tree
Changes from 97 commits
Commits
Show all changes
104 commits
Select commit Hold shift + click to select a range
b878c90
more stable wait_for
KommuSoft Aug 16, 2025
4051ab8
better white-black front colors for whatday
KommuSoft Aug 17, 2025
c32e36f
validate json data
KommuSoft Aug 17, 2025
bedb464
update checkout script
KommuSoft Aug 17, 2025
1bd181f
add pass-extension-otp
KommuSoft Aug 17, 2025
10dbca9
otp
KommuSoft Aug 17, 2025
fdfa17e
timeprompt
KommuSoft Aug 17, 2025
e588ca9
allow immediate ending in the waitfor command
KommuSoft Aug 17, 2025
f68b13a
allow cancelling waitfors
KommuSoft Aug 17, 2025
710e372
rabbitdoc
KommuSoft Aug 26, 2025
3ab6688
install GitHub cli
KommuSoft Aug 26, 2025
59f2909
gitcpr
KommuSoft Aug 26, 2025
4bd5f8f
exit code waitfor
KommuSoft Aug 27, 2025
811966b
mark early exits
KommuSoft Aug 27, 2025
7948269
open browser to issue
KommuSoft Aug 29, 2025
9894062
gitpr
KommuSoft Aug 29, 2025
7e1fbcd
gitpr
KommuSoft Aug 29, 2025
c84bc54
gitpr
KommuSoft Aug 29, 2025
7fde93b
fix gitcpr and redirect to new gitcpr
KommuSoft Aug 29, 2025
84adfeb
gitci
KommuSoft Aug 29, 2025
0e55404
fix gitci
KommuSoft Aug 29, 2025
4c91c9f
blackcp
KommuSoft Aug 30, 2025
86f9582
blackcp
KommuSoft Aug 30, 2025
823d704
blackcp
KommuSoft Aug 30, 2025
528c4de
blackcp
KommuSoft Aug 30, 2025
017f03e
blackcp
KommuSoft Aug 30, 2025
0b550f2
blackcp
KommuSoft Aug 30, 2025
c144b48
blackcp
KommuSoft Aug 30, 2025
321f2eb
blackcp
KommuSoft Aug 30, 2025
23448b5
isortcp
KommuSoft Aug 30, 2025
d3ba6d0
gitclonec
KommuSoft Aug 31, 2025
ad27325
make taskflush more robust
KommuSoft Sep 8, 2025
69bb6e5
add_docstrings
KommuSoft Sep 8, 2025
1feddca
add docstrings to add docstrings
KommuSoft Sep 8, 2025
148e53e
pydoc
KommuSoft Sep 8, 2025
8b77455
pydoc
KommuSoft Sep 9, 2025
d19c681
📝 Add docstrings to `feature/doc-update-6`
coderabbitai[bot] Sep 9, 2025
75b01d4
install.sh template
KommuSoft Sep 13, 2025
2a68d15
-y
KommuSoft Sep 13, 2025
5d8c128
include copy of chmod
KommuSoft Sep 13, 2025
1efd3cf
install pyright
KommuSoft Sep 13, 2025
c5a52ef
Merge pull request #12 from hapytex/coderabbitai/docstrings/8b77455
KommuSoft Sep 13, 2025
f33d04b
add shabang
KommuSoft Sep 13, 2025
aacd810
typehintcheck
KommuSoft Sep 13, 2025
ccb916a
exit code for check_typehints
KommuSoft Sep 13, 2025
f90b899
better exit code
KommuSoft Sep 13, 2025
4e864bb
fix out of bounds waitfor
KommuSoft Sep 14, 2025
382e6fc
waitfor with progress bar
KommuSoft Sep 14, 2025
85b24a2
docker group
KommuSoft Sep 20, 2025
e636771
season
KommuSoft Sep 20, 2025
0474076
four polarities
KommuSoft Sep 20, 2025
10f5827
four polarities
KommuSoft Sep 20, 2025
aaeb456
vimgit
KommuSoft Sep 21, 2025
ebd609b
season colors
KommuSoft Sep 21, 2025
cd3f541
mkdir --
KommuSoft Sep 21, 2025
e8f85b5
seasons
KommuSoft Sep 21, 2025
e500314
waitfor pct clock
KommuSoft Sep 29, 2025
e396037
better circle
KommuSoft Sep 29, 2025
162cbb7
calligraphic ℬ𝓁𝒶𝒸𝓀
KommuSoft Oct 5, 2025
fea9cdf
list branches if no argument for gitb
KommuSoft Oct 5, 2025
5c861d2
gitcp do not push if commit fails
KommuSoft Oct 10, 2025
4d1de78
hour colors
KommuSoft Oct 18, 2025
385ac32
hour color
KommuSoft Oct 18, 2025
092da21
more parity
KommuSoft Oct 18, 2025
469bf52
add pre-commit
KommuSoft Oct 18, 2025
4e8b686
black reformatting
KommuSoft Oct 18, 2025
f400314
end pomodoro if ending the script prematurely
KommuSoft Oct 18, 2025
71cb689
end pomodoro if ending the script prematurely
KommuSoft Oct 18, 2025
8397183
fix polarity
KommuSoft Oct 19, 2025
1804786
log biblespeak
KommuSoft Oct 19, 2025
b0dffad
log biblespeak
KommuSoft Oct 19, 2025
cec3fb0
start working on communication tooling
KommuSoft Oct 25, 2025
ee76127
mkdircdgit
KommuSoft Oct 25, 2025
c53e4d1
copy gitignores
KommuSoft Oct 25, 2025
903375d
ignore gitignores
KommuSoft Oct 25, 2025
9e85908
add gitignore to git
KommuSoft Oct 25, 2025
386de31
multilang gitignore
KommuSoft Oct 25, 2025
f075309
note
KommuSoft Oct 28, 2025
6b06c88
noteloop
KommuSoft Oct 28, 2025
2a29266
visual marking for notebooks
KommuSoft Oct 28, 2025
da6c659
more visual hints for notebook
KommuSoft Oct 28, 2025
49bcd99
fix visual clues
KommuSoft Oct 28, 2025
9b35a92
document notes
KommuSoft Oct 28, 2025
73dcf48
rename noteloop to notes
KommuSoft Oct 28, 2025
75fe57d
rename noteloop to notes
KommuSoft Oct 28, 2025
7012d30
empty notes not saved
KommuSoft Oct 28, 2025
cad71ff
better note taking
KommuSoft Oct 29, 2025
19e51a3
airplane
KommuSoft Oct 29, 2025
b8a0c7f
mynote
KommuSoft Oct 30, 2025
c087c80
variables.json
KommuSoft Nov 2, 2025
12ad4e2
change variables.json to allow default values
KommuSoft Nov 2, 2025
a29b992
faster setvar
KommuSoft Nov 2, 2025
b9e6b84
facialmask
KommuSoft Nov 5, 2025
26b9ab6
heal colors
KommuSoft Nov 5, 2025
86b7c2a
damn dockersave
KommuSoft Nov 6, 2025
9ce9b97
visidata
KommuSoft Nov 8, 2025
ecdd0cb
move to sqlite based db for measurements
KommuSoft Nov 9, 2025
4e87d9a
migrate old data
KommuSoft Nov 9, 2025
cb94d5c
undo migrate script
KommuSoft Nov 9, 2025
fc4a5e8
dockerscpload
KommuSoft Nov 9, 2025
c30781f
coffee colors
KommuSoft Nov 11, 2025
c21d80f
keycolor
KommuSoft Nov 11, 2025
bb83b6e
compress stream when docker transferring
KommuSoft Nov 11, 2025
d7bbf11
parameterize language
KommuSoft Nov 11, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,17 @@ jobs:
black:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- uses: psf/black@stable
with:
options: "--check"
jsonlint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: validate JSON
env:
REPORT_ONLY: true
SHOW_ERRORS: true
run: bash <(curl -s https://raw.githubusercontent.com/CICDToolbox/json-lint/master/pipeline.sh)
Comment on lines +16 to +20
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid executing unpinned remote scripts in CI; replace with a local validator or pin to a commit.

curl-ing and executing a remote script from a moving branch (master) is a supply-chain risk and makes builds non-deterministic. Prefer a local validator (no network), or at minimum fetch a specific commit and verify integrity.

Preferred fix: replace the remote script with a Python-based JSON validation that fails the job on invalid JSON.

-      - name: validate JSON
-        env:
-          REPORT_ONLY: true
-          SHOW_ERRORS: true
-        run: bash <(curl -s https://raw.githubusercontent.com/CICDToolbox/json-lint/master/pipeline.sh)
+      - name: Validate JSON (fail on invalid)
+        run: |
+          set -euo pipefail
+          # Validate all *.json files (tracked and untracked). Fails on first invalid file.
+          found=false
+          while IFS= read -r -d '' f; do
+            found=true
+            python3 -m json.tool "$f" >/dev/null
+          done < <(find . -type f -name "*.json" -print0)
+          # If no JSON files exist, succeed without doing anything.
+          if [ "$found" = false ]; then
+            echo "No JSON files found."
+          fi

Alternative (if you must keep the remote script):

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: validate JSON
env:
REPORT_ONLY: true
SHOW_ERRORS: true
run: bash <(curl -s https://raw.githubusercontent.com/CICDToolbox/json-lint/master/pipeline.sh)
- name: Validate JSON (fail on invalid)
run: |
set -euo pipefail
# Validate all *.json files (tracked and untracked). Fails on first invalid file.
found=false
while IFS= read -r -d '' f; do
found=true
python3 -m json.tool "$f" >/dev/null
done < <(find . -type f -name "*.json" -print0)
# If no JSON files exist, succeed without doing anything.
if [ "$found" = false ]; then
echo "No JSON files found."
fi
🤖 Prompt for AI Agents
.github/workflows/build.yml around lines 16-20: the workflow currently curl|bash
executes an unpinned remote script (supply-chain risk and non-deterministic);
replace it with a local JSON validator that runs without network (or, if you
must keep remote, fetch a pinned commit and verify its SHA). Specifically:
remove the curl... pipeline.sh step and add a step that runs a local validation
command (e.g., iterate repo JSON files and run python -m json.tool on each,
failing the job on any parse error), ensure the validator is committed in the
repo or uses built-in python so no network is required; alternatively, change
the URL to the raw file at a specific commit and add a SHA256 verification
before executing.


27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ A set of functions for the **F**riendly **I**nteractive **Sh**ell (fish). This i
- **`battery`**: print the percentage of the battery;
- **`between`**: check if the given values are ordered, usually used for three items to check membership of a range;
- **`biblespeak`**: speak the next verse of the *Bible*;
- **`blackcp`**: run the *Black* formatter, and if Black made changes, commit and push these;
- **`blooddonation`**: register a blood donation together with blood metrics;
- **`bloodresults`**: store blood result measurements;
- **`bloodpressure`**: register a blood pressure measurement;
Expand Down Expand Up @@ -52,6 +53,7 @@ A set of functions for the **F**riendly **I**nteractive **Sh**ell (fish). This i

### F

- **`facialmask`**: register the use of a facial mask;
- **`fill`**: fill the entire screen with a certain color;
- **`fish_home`**: get the directory where the fish functions are stored;
- **`frmclip`**: copy from the clipboard to the stdout;
Expand All @@ -69,14 +71,19 @@ A set of functions for the **F**riendly **I**nteractive **Sh**ell (fish). This i
- **`gitacp`**: add items to the repository, commit, and push;
- **`gitb`**: make a new git branch;
- **`gitc`**: make a git commit (short for `git commit -am`);
- **`gitci`**: list the last CI/CD runs on *GitHub*;
- **`gitclonec`**: clone a repository and chance the directory to that of the cloned repository;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Typo: “chance” → “change”.

User-facing docs.

-  - **`gitclonec`**: clone a repository and chance the directory to that of the cloned repository;
+  - **`gitclonec`**: clone a repository and change to the cloned repository’s directory;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- **`gitclonec`**: clone a repository and chance the directory to that of the cloned repository;
- **`gitclonec`**: clone a repository and change to the cloned repository’s directory;
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

74-74: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)

🤖 Prompt for AI Agents
In README.md around line 74, the word "chance" is a typo and should be "change";
update the bullet to read "**`gitclonec`**: clone a repository and change the
directory to that of the cloned repository;" so the documentation uses the
correct verb and remains user-facing friendly.

- **`gitcp`**: commit with the given parameters, and push the changes;
- **`gitcpr`**: commit with the given parameters, push the changes and open a pull request on *GitHub*;
- **`gitd`**: calculate the git difference and work with a pager to read the full response;
- **`gitf`**: alias for `git fetch`;
- **`gith`**: checkout a given branch;
- **`gitm`**: move a given file in a git repository;
- **`gitmaster`**: checkout the master branch (and stash work in progress if any);
- **`gitmastertag`**: checkout the master branch, pull from remote, tag the commit and push the tag to the repository;
- **`gitp`**: pushes the changes to the remote repository (short for `git push`);
- **`gitpr`**: open a pull request on *GitHub*;
- **`gitprv`**: show comments of the active pull request;
- **`gitremote`**: add the remote as origin with the given `GIT_PREFIX` and the name of the directory as git project;
- **`gits`**: prints the status of the current Git repository (short for `git status`);
- **`gitt`**: make a git tag and push the tag;
Expand All @@ -90,13 +97,15 @@ A set of functions for the **F**riendly **I**nteractive **Sh**ell (fish). This i
- **`here_is_the_news`**: beeps a few times to mark a certain event;
- **`hlint`**: run Haskell lint and use a pager for the results;
- **`homebattery`**: determine how long it will take to load the battery full;
- **`hourcolor`**: change the keyboard to the hour color, can be scheduled as cronjob;
- **`household`**: wait until a household device has finished with info;
- **`hydrate`**: keep track of the amount of water we drink;

### I

- **`igrep`**: run grep in a case insensitive way;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add hyphen to compound adjective: "case-insensitive".

When compound adjectives precede a noun, they should be hyphenated for proper grammar.

Apply this diff:

-  - **`igrep`**: run grep in a case insensitive way;
+  - **`igrep`**: run grep in a case-insensitive way;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- **`igrep`**: run grep in a case insensitive way;
- **`igrep`**: run grep in a case-insensitive way;
🧰 Tools
🪛 LanguageTool

[grammar] ~105-~105: Use a hyphen to join words.
Context: ...# I - igrep: run grep in a case insensitive way; - import: initi...

(QB_NEW_EN_HYPHEN)

🪛 markdownlint-cli2 (0.18.1)

105-105: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)

🤖 Prompt for AI Agents
In README.md around line 105, the phrase "run grep in a case insensitive way"
should use a hyphenated compound adjective; change it to "run grep in a
case-insensitive way" to correct grammar.

- **`import`**: initializes the python shell with `import …`, so `import datetime` for example can be written in the shell as first Python command;
- **`isortcp`**: run the isort formatter, and if isort made changes, commit and push these;

### J

Expand All @@ -122,15 +131,23 @@ A set of functions for the **F**riendly **I**nteractive **Sh**ell (fish). This i
- **`measureall`**: start asking for many measures in bulk, used as helper function;
- **`measurelog`**: add a given measurement at a given time;
- **`mkdircd`**: make a directory, if it does not yet exist, and move the cd to that directory;
- **`mkdircdgit`**: make a directory, if it does not yet exist, cd into that directory, and start a git repository;
- **`mkvim`**: make the directories needed to edit a file;
- **`mkvimgit`**: make the directories needed to edit a file and add the file to git;
- **`mynote`**: add a notebook entry to a specific topic;

### N

- **`natrix`**: the Python interpreter of the fish environment;
- **`natrix-env`**: run a Python program in the natrix environment;
- **`note`**: add a notebook entry;
- **`notes`**: notebook in a loop to log entries;
- **`numpy`**: start a Python shell with `numpy` imported;

### O

- **`otp`**: generate/configure One Time Passwords (OTPs) with pass-extension-otp;
Comment on lines +148 to +150
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix indentation for new section O.

The otp bullet has 1 space instead of 2, making it inconsistent with all other bullets in the document. Align it with the established format.

 ### O

- - **`otp`**: generate/configure One Time Passwords (OTPs) with pass-extension-otp;
+  - **`otp`**: generate/configure One Time Passwords (OTPs) with pass-extension-otp;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
### O
- **`otp`**: generate/configure One Time Passwords (OTPs) with pass-extension-otp;
### O
- **`otp`**: generate/configure One Time Passwords (OTPs) with pass-extension-otp;
Suggested change
### O
- **`otp`**: generate/configure One Time Passwords (OTPs) with pass-extension-otp;
### O
- **`otp`**: generate/configure One Time Passwords (OTPs) with pass-extension-otp;
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

144-144: Unordered list indentation
Expected: 0; Actual: 1

(MD007, ul-indent)

🤖 Prompt for AI Agents
In README.md around lines 142 to 144, the new "O" section's bullet for `otp` is
indented with a single leading space instead of the two spaces used for other
bullets; fix this by changing the indentation to two spaces so the line reads
with the same leading spacing as other list items (aligning the dash and content
to match the established bullet formatting).


### P

- **`pandas`**: start a Python shell with `numpy` and `pandas` imported;
Expand All @@ -143,6 +160,7 @@ A set of functions for the **F**riendly **I**nteractive **Sh**ell (fish). This i
- **`proofreadedit`**: let vim open the file along with the proofread file;
- **`proofreadmd`**: proofread Markdown files with Groq to find spelling and grammatical errors;
- **`pwd`**: list the absolute path for the given files listed or the `pwd` if no arguments were provided;
- **`pydoc`**: add doc strings to all elements of a Python file;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Terminology: "doc strings" → "docstrings".

Use the standard Python term "docstrings" (one word) instead of "doc strings".

-  - **`pydoc`**: add doc strings to all elements of a Python file;
+  - **`pydoc`**: add docstrings to all elements of a Python file;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- **`pydoc`**: add doc strings to all elements of a Python file;
- **`pydoc`**: add docstrings to all elements of a Python file;
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

163-163: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)

🤖 Prompt for AI Agents
In README.md around line 163, the phrase "doc strings" is used; change it to the
standard Python term "docstrings" (one word) so the line reads "**`pydoc`**: add
docstrings to all elements of a Python file;" ensuring consistency with Python
terminology.

- **`python3`**: overrides normal Python prompt with an IPython shell without banner, no confirmations and matplotlib;

### Q
Expand All @@ -151,6 +169,7 @@ A set of functions for the **F**riendly **I**nteractive **Sh**ell (fish). This i

### R

- **`rabbitdoc`**: ask [*CodeRabbit*](https://github.com/coderabbitai) to generate docstrings for the latest pull request;
- **`redo`**: redo a command until the exit code is 0;
- **`remake`**: a loop to remake certain products with a Makefile;
- **`rmake`**: walk up the filetree until it finds a Makefile;
Expand All @@ -159,6 +178,7 @@ A set of functions for the **F**riendly **I**nteractive **Sh**ell (fish). This i

### S

- **`season`**: determine the season for a given day;
- **`semver`**: convert a given requirements file to its sem-ver equivalent;
- **`sensors`**: show the temperature measured by sensors updated every second;
- **`setvar`**: check if a variable with the name exists; if not, query for a value;
Expand All @@ -185,9 +205,11 @@ A set of functions for the **F**riendly **I**nteractive **Sh**ell (fish). This i
- **`taskd`**: set the task with the given id as done;
- **`taskflush`**: set all expired tasks to done;
- **`teeth`**: help cleaning teeth;
- **`timeprompt`**: wait for a given amount of time, or until the person hits a key exits with 1 if the user interrupted;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Clarify the description of timeprompt.

The current phrasing is awkward: "wait for a given amount of time, or until the person hits a key exits with 1 if the user interrupted." Consider revising for clarity:

-  - **`timeprompt`**: wait for a given amount of time, or until the person hits a key exits with 1 if the user interrupted;
+  - **`timeprompt`**: wait for a given amount of time or until a key is pressed; exits with 1 if the user interrupted;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- **`timeprompt`**: wait for a given amount of time, or until the person hits a key exits with 1 if the user interrupted;
- **`timeprompt`**: wait for a given amount of time or until a key is pressed; exits with 1 if the user interrupted;
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

203-203: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)

🤖 Prompt for AI Agents
In README.md around line 203, the `timeprompt` description is awkward and
unclear; replace the current fragment with a single clear sentence such as:
"timeprompt: waits for a specified duration or until the user presses any key;
exits with status 1 if the user interrupts." Ensure punctuation and grammar are
correct and that the behavior (wait duration, key press, exit code on interrupt)
is unambiguous.

- **`timestamp`**: get a string that specifies date and time, used for filenames mainly;
- **`todo`**: add or inspect to a todo list;
- **`truthful`**: check if at least one of the given Python literals or JSON values has truthiness `true`;
- **`typehintcheck`**: check if all defined functions have type hints;

### U

Expand All @@ -198,6 +220,7 @@ A set of functions for the **F**riendly **I**nteractive **Sh**ell (fish). This i

- **`videos`**: see random videos;
- **`vimc`**: create a directory if the directory does not yet exist before running `vim`;
- **`vimgit`**: edit the given files in the parameters, and add these to git;
- **`vimo`**: open two or more files in vim next to each other;
- **`vimp`**: use vim with different tabs, a shortcut for `vim -p`;
- **`vimpsrc`**: open all the Haskell files in the `src` directory with `vim`;
Expand Down Expand Up @@ -251,6 +274,10 @@ The following emoji aliases can be used as commands:
- **`GROQ_API_TOKEN`**: the API token for *Groq* to make chatbot requests, can be found [here](https://console.groq.com/keys); and
- **`USER_SLUG`**: the slug used to prepend to file names when adding an author to it, for example `Elon_Musk`.

## Measurements

One of the core features is that one can measure all sorts of parameters, like health, activities, notes, etc. These are stored in `assets/Measurements.json`, a JSON file that allows cascading measurements.

## Installation

One can install this by downloading (or cloning) and put the files in the `~/.config/fish/functions/`
Expand Down
99 changes: 99 additions & 0 deletions add_docstrings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#!/usr/bin/python3

import ast
import os
import os.path
import sys

import astor
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify dependency: astor present if targeting <3.9.

If you intend to support Python <3.9, ensure astor is declared in your packaging (pyproject/requirements). Otherwise, the fallback will fail at runtime.

Run to check packaging references:


🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo "Searching packaging for 'astor'…"
fd -t f -a 'pyproject.toml' 'setup.cfg' 'setup.py' 'requirements*.txt' 2>/dev/null | while read -r f; do
  echo "==> $f"
  rg -n --fixed-strings "astor" "$f" || true
done

Length of output: 32


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Listing root directory files:"
ls -1

echo
echo "Finding packaging files:"
find . -maxdepth 2 -type f \( -name 'pyproject.toml' -o -name 'setup.py' -o -name 'setup.cfg' -o -name 'requirements.txt' -o -name 'requirements*.txt' \)

PACK_FILES=$(find . -maxdepth 2 -type f \( -name 'pyproject.toml' -o -name 'setup.py' -o -name 'setup.cfg' -o -name 'requirements.txt' -o -name 'requirements*.txt' \))
if [[ -n "$PACK_FILES" ]]; then
  echo
  echo "Searching for 'astor' in packaging files:"
  echo "$PACK_FILES" | while read -r f; do
    echo "==> $f"
    rg -n --fixed-strings "astor" "$f" || true
  done
fi

Length of output: 2739


Add missing astor dependency
add_docstrings.py unconditionally imports astor, but neither requirements.txt nor templates/requirements.txt declare it. Add astor (e.g. astor>=0.8.1) to your requirements files to prevent runtime failures on Python <3.9.

🤖 Prompt for AI Agents
In add_docstrings.py around line 8 the script imports astor but the project does
not declare astor as a dependency; add astor (for example astor>=0.8.1) to
requirements.txt and to templates/requirements.txt so environments (and
generated projects) install astor and prevent runtime failures on Python <3.9;
update both files with the new pinned entry, run your dependency tooling (e.g.,
pip install -r requirements.txt or regenerate lock files) and verify imports
succeed in CI.



class AddDocString(ast.NodeTransformer):
def __init__(self, *args, docstring: str, **kwargs):
"""
Initialize the AddDocString transformer with the docstring text to insert.

The provided `docstring` value is stored on the instance as `self.docstring` and will be inserted into any Module, ClassDef, FunctionDef, or AsyncFunctionDef that lacks a docstring.
Any additional positional and keyword arguments are forwarded to the superclass initializer.
"""
super().__init__(*args, **kwargs)
self.docstring = docstring

def add_docstring(self, node):
"""
Insert the configured docstring into the given AST node if it has no existing docstring.

If the node (Module, ClassDef, FunctionDef, or AsyncFunctionDef) has no docstring, this function inserts the value of self.docstring as the first statement of node.body. If a docstring already exists, the node is left unchanged. The node is then traversed with generic_visit and returned.

Parameters:
node (ast.AST): An AST node that has a `body` sequence (module, class, or function node).

Returns:
ast.AST: The (possibly modified) node after applying generic visitation.
"""
if ast.get_docstring(node) is None:
node.body.insert(0, ast.Expr(value=ast.Constant(self.docstring)))
return self.generic_visit(node)

def visit_FunctionDef(self, node):
"""
Ensure an ast.FunctionDef node has a docstring, inserting the transformer's configured docstring if one is missing, then continue generic traversal.

Delegates to add_docstring(node) and returns the possibly modified ast.FunctionDef node.
"""
return self.add_docstring(node)

def visit_AsyncFunctionDef(self, node):
"""
Visit an async function definition node and ensure it has a docstring.

If the async function node has no existing docstring, inserts self.docstring
as the first statement in the node's body. Continues generic AST traversal
and returns the (possibly modified) node.

Parameters:
node (ast.AsyncFunctionDef): The async function AST node to visit.

Returns:
ast.AST: The visited (and possibly modified) node.
"""
return self.add_docstring(node)

def visit_ClassDef(self, node):
"""
Visit a class definition node and ensure it has a docstring.

If the class has no docstring, insert the transformer's configured docstring as the first statement. Returns the (possibly modified) AST ClassDef node for further traversal.

Parameters:
node (ast.ClassDef): The class definition AST node to visit and potentially modify.

Returns:
ast.AST: The transformed ClassDef node.
"""
return self.add_docstring(node)

def visit_Module(self, node):
"""
Ensure the given module AST node has a docstring. If the module lacks a docstring, insert the transformer's configured docstring as the first statement, then continue a generic visit of the node's children.

Parameters:
node (ast.Module): The module AST node to process.

Returns:
ast.AST: The module node after potential modification and traversal.
"""
return self.add_docstring(node)


if __name__ == "__main__":
converter = AddDocString(
docstring=os.environ.get("PY_DOCSTRING", "@coderabbitai generate docstrings")
)
for file in sys.argv[1:]:
with open(file, "rb") as f:
tree = ast.parse(f.read())
result = astor.to_source(converter.visit(tree))
file, ext = os.path.splitext(file)
with open(f"{file}_modified{ext}", "w") as f:
f.write(result)
66 changes: 20 additions & 46 deletions add_measure.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,66 +6,47 @@
from ast import literal_eval
from datetime import datetime
from shutil import move
import sqlite3
from peewee import DateTimeField, Model, SqliteDatabase, TextField

from color_pprint import cprint
from dateparser import parse
from filelock import FileLock

DURATION = re.compile(r"\d{1,2}([:]\d{2})+")
ASSET_PATH = "assets/measurements.json"
ASSET_PATH = "assets/measurements.db"

db = SqliteDatabase(ASSET_PATH)

def filter_dict(data, dt):
if isinstance(data, dict):
result = {}
for k, v in data.items():
v = filter_dict(v, dt)
if v:
try:
dt2 = datetime.fromisoformat(k)
except ValueError:
result[k] = v
else:
if dt2 >= dt:
result[k] = v
return result
return data
class Measurement(Model):
key = TextField()
value = TextField()
created_date = DateTimeField(default=datetime.now)

class Meta:
database = db

if __name__ == "__main__":
n = len(sys.argv)
assert (
n > 1
), "You should provide the name of the measurement and the corresponding value"
with FileLock(f"{ASSET_PATH}.lock"):
try:
with open(ASSET_PATH, "r") as f:
data = json.load(f)
except IOError:
data = {}
timestamp = datetime.now().replace(microsecond=0)
db.connect()
db.create_tables([Measurement])
key_dt = timestamp = datetime.now()
dt = timestamp.isoformat()
to_sort = {}
_def = {}
for i in range(1, n, 2):
key = sys.argv[i]
try:
key, dt2 = key.rsplit("@", 1)
timefilter = dt2 = parse(dt2)
timefilter = dt2 = parse(dt2).replace(tzinfo=None)
if dt2 is not None:
key_dt = dt2.replace(microsecond=0, tzinfo=None).isoformat()
key_dt = dt2.replace(tzinfo=None)
except ValueError:
# key is already fine
timefilter = datetime.min
key_dt = dt
datum = data
for ky in key.split("."):
_datum = datum.setdefault(ky, _def)
if _datum is _def:
_def = {} # new one
# need to resort datum, new entry
to_sort[id(datum)] = datum
datum = _datum
if n > i + 1:
val = sys.argv[i + 1].strip()
if DURATION.fullmatch(val.strip()):
Expand All @@ -83,16 +64,9 @@ def filter_dict(data, dt):
except ValueError:
pass # keep it a string
if val: # None, False, etc. are all omitted
datum[key_dt] = val
if key_dt is not dt:
# we add a key from the past
to_sort[id(datum)] = datum
Measurement.create(created_date=key_dt, key=key, value=json.dumps(val))
else:
cprint(filter_dict(datum, timefilter))
for datum in to_sort.values():
datum_sort = {k: v for k, v in sorted(datum.items())}
datum.clear()
datum.update(datum_sort)
with open(f"{ASSET_PATH}.tmp", "w") as f:
json.dump(data, f, indent=4)
move(f"{ASSET_PATH}.tmp", ASSET_PATH)
datum = {}
for entry in Measurement.select().where(Measurement.key == key, Measurement.created_date >= timefilter.isoformat()):
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix type mismatch in database query.

The comparison Measurement.created_date >= timefilter.isoformat() compares a DateTimeField to a string. Peewee expects datetime objects for datetime comparisons, not ISO-formatted strings. This will likely cause incorrect query results or errors.

Apply this diff to compare datetime objects directly:

-                for entry in Measurement.select().where(Measurement.key == key, Measurement.created_date >= timefilter.isoformat()):
+                for entry in Measurement.select().where(Measurement.key == key, Measurement.created_date >= timefilter):
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for entry in Measurement.select().where(Measurement.key == key, Measurement.created_date >= timefilter.isoformat()):
for entry in Measurement.select().where(Measurement.key == key, Measurement.created_date >= timefilter):
🤖 Prompt for AI Agents
In add_measure.py around line 70, the query currently compares
Measurement.created_date (a DateTimeField) to timefilter.isoformat() (a string);
change the comparison to use a datetime object instead—either pass timefilter
directly if it is already a datetime, or parse the ISO string back to a datetime
(e.g., datetime.fromisoformat / dateutil.parser.parse) and use that datetime in
the WHERE clause, making sure to preserve timezone awareness/UTC handling
consistent with Measurement.created_date.

datum[parse(entry.created_date).replace(microsecond=0)] = json.loads(entry.value)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Remove unnecessary datetime parsing.

entry.created_date is already a datetime object returned by Peewee's DateTimeField, so calling parse(entry.created_date) is unnecessary and adds overhead.

Apply this diff to use the datetime object directly:

-                    datum[parse(entry.created_date).replace(microsecond=0)] = json.loads(entry.value)
+                    datum[entry.created_date.replace(microsecond=0)] = json.loads(entry.value)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
datum[parse(entry.created_date).replace(microsecond=0)] = json.loads(entry.value)
datum[entry.created_date.replace(microsecond=0)] = json.loads(entry.value)
🤖 Prompt for AI Agents
In add_measure.py around line 71, the code unnecessarily calls parse() on
entry.created_date even though Peewee already returns a datetime; replace
parse(entry.created_date).replace(microsecond=0) with
entry.created_date.replace(microsecond=0) so you use the existing datetime
object directly and avoid the extra parsing overhead, then assign
datum[entry.created_date.replace(microsecond=0)] = json.loads(entry.value).

cprint(datum)
6 changes: 6 additions & 0 deletions assets/.gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
measurements*.json
*_measurements.json
*_measurements.json.gz
measurements*.sqlite3
*_measurements.sqlite3
*_measurements.slite3.gz
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix typo in file extension.

Line 6 contains "slite3" instead of "sqlite3".

Apply this diff to fix the typo:

-*_measurements.slite3.gz
+*_measurements.sqlite3.gz
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
*_measurements.slite3.gz
*_measurements.sqlite3.gz
🤖 Prompt for AI Agents
In assets/.gitignore around line 6, the filename pattern contains a typo
("slite3") instead of the correct SQLite extension; update the pattern to
"*_measurements.sqlite3.gz" to match actual files, save the file, and commit the
change.

measurements*.db
*_measurements.db
*_measurements.db.gz
list.json
*_list.json
*.lock
Expand Down
Loading
Loading