refactor: switch prompt file naming from commit hashes to dates and update metadata handling

This commit is contained in:
Janik von Rotz committed 2026-10-05 14:21:41 +02:00
1 parent 129699f479
commit 47da84a181
7 files changed
+135 -103

No files matched your search

+30 -12
View File
@@ -26,8 +26,7 @@ commit-prompt() {
return 1
fi
local commit_hash commit_hash_full
commit_hash="$(git rev-parse --short HEAD)"
local commit_hash_full
commit_hash_full="$(git rev-parse HEAD)"
local title
@@ -47,30 +46,49 @@ commit-prompt() {
local target_path
local commit_msg="feat: add prompt file"
local existing_files=()
for f in "$worktree_path"/${commit_hash}-*.md; do
local existing_file=""
for f in "$worktree_path"/*.md; do
if [[ -f "$f" ]]; then
existing_files+=("$f")
local f_title
f_title="$(grep -m1 '^title:' "$f" | sed 's/^title:[[:space:]]*//; s/^"\(.*\)"$/\1/; s/'"'"'\(.*\)'"'"'$/\1/')"
if [[ "$f_title" == "$title" ]]; then
existing_file="$f"
break
fi
fi
done
if [[ ${#existing_files[@]} -eq 1 ]]; then
target_path="${existing_files[0]}"
if [[ -n "$existing_file" ]]; then
target_path="$existing_file"
filename="$(basename "$target_path")"
commit_msg="feat: update prompt file"
echo "Updating existing prompt file: $filename"
else
filename="${commit_hash}-${slug}.md"
local datestamp
datestamp="$(date +%Y-%m-%d)"
filename="${datestamp}_${slug}.md"
target_path="$worktree_path/$filename"
fi
local state
state="$(grep -m1 '^state:' "$prompt_file" | sed 's/^state:[[:space:]]*//; s/^"\(.*\)"$/\1/; s/'"'"'\(.*\)'"'"'$/\1/')"
cp "$prompt_file" "$target_path"
if grep -q '^commit_ref:' "$target_path"; then
sed -i "s/^commit_ref:.*/commit_ref: $commit_hash_full/" "$target_path"
if [[ "$state" == "completed" ]]; then
if grep -q '^commit_ref:' "$target_path"; then
sed -i "s/^commit_ref:.*/commit_ref: $commit_hash_full/" "$target_path"
else
awk -v hash="$commit_hash_full" 'NR==1 && /^---$/ { print; print "commit_ref: " hash; next } { print }' "$target_path" > "$target_path.tmp"
mv "$target_path.tmp" "$target_path"
fi
else
awk -v hash="$commit_hash_full" 'NR==1 && /^---$/ { print; print "commit_ref: " hash; next } { print }' "$target_path" > "$target_path.tmp"
mv "$target_path.tmp" "$target_path"
if grep -q '^commit_ref:' "$target_path"; then
sed -i 's/^commit_ref:.*/commit_ref:/' "$target_path"
else
awk 'NR==1 && /^---$/ { print; print "commit_ref:"; next } { print }' "$target_path" > "$target_path.tmp"
mv "$target_path.tmp" "$target_path"
fi
fi
(
+7 -5
View File
@@ -50,11 +50,11 @@ convert-prompts-folder-to-prompt-log() {
local filename
filename="$(basename "$file")"
local creation_commit creation_commit_full
creation_commit="$(git log --follow --diff-filter=A --format="%h" -- "$file" | tail -n 1)"
local creation_date creation_commit_full
creation_date="$(git log --follow --diff-filter=A --format="%ad" --date=format:"%Y-%m-%d" -- "$file" | tail -n 1)"
creation_commit_full="$(git log --follow --diff-filter=A --format="%H" -- "$file" | tail -n 1)"
if [[ -z "$creation_commit" ]]; then
if [[ -z "$creation_date" ]]; then
skipped+=("$filename")
continue
fi
@@ -69,12 +69,14 @@ convert-prompts-folder-to-prompt-log() {
local slug
slug=$(echo "$title" | tr '[:upper:]' '[:lower:]' | tr '.' '-' | tr ' ' '-' | tr -cd '[:alnum:]-')
local target_filename="${creation_commit}-${slug}.md"
local target_filename="${creation_date}_${slug}.md"
local target_path="$worktree_path/$target_filename"
cp "$file" "$target_path"
if ! grep -q '^commit_ref:' "$target_path"; then
if grep -q '^commit_ref:' "$target_path"; then
sed -i "s/^commit_ref:.*/commit_ref: $creation_commit_full/" "$target_path"
else
awk -v hash="$creation_commit_full" 'NR==1 && /^---$/ { print; print "commit_ref: " hash; next } { print }' "$target_path" > "$target_path.tmp"
mv "$target_path.tmp" "$target_path"
fi
+4 -5
View File
@@ -40,9 +40,8 @@ delete-prompt() {
if [[ -f "$f" ]]; then
local f_title f_date f_commit
f_title="$(grep -m1 '^title:' "$f" | sed 's/^title:[[:space:]]*//; s/^"\(.*\)"$/\1/; s/'"'"'\(.*\)'"'"'$/\1/')"
f_commit="$(basename "$f")"
f_commit="${f_commit%%-*}"
f_date="$(cd "$repo_path" && git log --format="%ad" --date=short -1 "$f_commit" 2>/dev/null || echo "0000-00-00")"
f_date="$(basename "$f")"
f_date="${f_date%%_*}"
echo "$f_date|$f_title|$(basename "$f")"
fi
done | sort -t'|' -k1,1 -r | while IFS='|' read -r _ title filename; do
@@ -65,10 +64,10 @@ delete-prompt() {
done
if [[ ${#matching_files[@]} -eq 0 ]]; then
echo "Error: No prompt file found starting with commit prefix '$commit'."
echo "Error: No prompt file found starting with date prefix '$commit'."
return 1
elif [[ ${#matching_files[@]} -gt 1 ]]; then
echo "Error: Ambiguous commit prefix '$commit'. Matches:"
echo "Error: Ambiguous date prefix '$commit'. Matches:"
printf '%s\n' "${matching_files[@]}"
return 1
fi
+4 -5
View File
@@ -40,9 +40,8 @@ edit-prompt() {
if [[ -f "$f" ]]; then
local f_title f_date f_commit
f_title="$(grep -m1 '^title:' "$f" | sed 's/^title:[[:space:]]*//; s/^"\(.*\)"$/\1/; s/'"'"'\(.*\)'"'"'$/\1/')"
f_commit="$(basename "$f")"
f_commit="${f_commit%%-*}"
f_date="$(cd "$repo_path" && git log --format="%ad" --date=short -1 "$f_commit" 2>/dev/null || echo "0000-00-00")"
f_date="$(basename "$f")"
f_date="${f_date%%_*}"
echo "$f_date|$f_title|$(basename "$f")"
fi
done | sort -t'|' -k1,1 -r | while IFS='|' read -r _ title filename; do
@@ -65,10 +64,10 @@ edit-prompt() {
done
if [[ ${#matching_files[@]} -eq 0 ]]; then
echo "Error: No prompt file found starting with commit prefix '$commit'."
echo "Error: No prompt file found starting with date prefix '$commit'."
return 1
elif [[ ${#matching_files[@]} -gt 1 ]]; then
echo "Error: Ambiguous commit prefix '$commit'. Matches:"
echo "Error: Ambiguous date prefix '$commit'. Matches:"
printf '%s\n' "${matching_files[@]}"
return 1
fi
+8 -9
View File
@@ -42,16 +42,15 @@ list-prompts() {
local filename
filename="$(basename "$file")"
local commit="${filename%%-*}"
local date="${filename%%_*}"
local date
date="$(cd "$repo_path" && git log --format="%ad" --date=short -1 "$commit" 2>/dev/null || echo "N/A")"
if [[ -z "$date" ]]; then
date="N/A"
local commit_ref
commit_ref="$(grep -m1 '^commit_ref:' "$file" | sed 's/^commit_ref:[[:space:]]*//; s/^"\(.*\)"$/\1/; s/'"'"'\(.*\)'"'"'$/\1/')"
if [[ -n "$commit_ref" ]]; then
commit_ref="${commit_ref:0:7}"
fi
rows+=("$date|$title|$state|$commit")
rows+=("$date|$title|$state|$commit_ref")
if [[ ${#date} -gt max_date_len ]]; then
max_date_len=${#date}
@@ -62,8 +61,8 @@ list-prompts() {
if [[ ${#state} -gt max_state_len ]]; then
max_state_len=${#state}
fi
if [[ ${#commit} -gt max_commit_len ]]; then
max_commit_len=${#commit}
if [[ ${#commit_ref} -gt max_commit_len ]]; then
max_commit_len=${#commit_ref}
fi
done
+4 -5
View File
@@ -40,9 +40,8 @@ show-prompt() {
if [[ -f "$f" ]]; then
local f_title f_date f_commit
f_title="$(grep -m1 '^title:' "$f" | sed 's/^title:[[:space:]]*//; s/^"\(.*\)"$/\1/; s/'"'"'\(.*\)'"'"'$/\1/')"
f_commit="$(basename "$f")"
f_commit="${f_commit%%-*}"
f_date="$(cd "$repo_path" && git log --format="%ad" --date=short -1 "$f_commit" 2>/dev/null || echo "0000-00-00")"
f_date="$(basename "$f")"
f_date="${f_date%%_*}"
echo "$f_date|$f_title|$(basename "$f")"
fi
done | sort -t'|' -k1,1 -r | while IFS='|' read -r _ title filename; do
@@ -65,10 +64,10 @@ show-prompt() {
done
if [[ ${#matching_files[@]} -eq 0 ]]; then
echo "Error: No prompt file found starting with commit prefix '$commit'."
echo "Error: No prompt file found starting with date prefix '$commit'."
return 1
elif [[ ${#matching_files[@]} -gt 1 ]]; then
echo "Error: Ambiguous commit prefix '$commit'. Matches:"
echo "Error: Ambiguous date prefix '$commit'. Matches:"
printf '%s\n' "${matching_files[@]}"
return 1
fi
+78 -62
View File
@@ -1,14 +1,14 @@
---
title: "Implement prompt log commands"
title: "Replace 11ty with Zensical"
author: "Janik von Rotz <login@janikvonrotz.ch>"
state: completed
date_completed: 2026-09-25
model: moonshotai/Kimi-K2.6
input_tokens: "2667000"
output_tokens: "25500"
state: draft
date_completed: YYYY-MM-DD
model:
input_tokens:
output_tokens:
---
# Implement prompt log commands
# Replace 11ty with Zensical
Note: @Clanker refers to the "ai agent" (you) who is working on this prompt file.
@@ -24,89 +24,105 @@ Note: @Clanker refers to the "ai agent" (you) who is working on this prompt file
## Task
Read `bin/create-prompt` and `bin/list-prompts`. The commands help manage prompt instruction. I would like to introduce new commands and a new system to manage prompt files.
I want to replace https://www.11ty.dev/ with https://zensical.org/. First check current 11ty setup:
Instead of storing the prompts in the `prompts` folder I would like to store them on a separated branch called `prompt-log`.
- Navigation: ./_includes/layouts/base.njk
- Assets:
- ./img
- ./js
- Markdown:
- development.md
- examples.md
- library.md
- patterns.md
- README.md
- usage.md
- Node files:
- eleventy.config.js
- package-lock.json
- patterns.md
- pnpm-lock.yaml
- node_modules
- Task commands: `./task help | grep 11ty`
### Design decisions
### Remove Node/11ty
- **Worktree lifecycle:** every command assumes the `prompt-log` git worktree exists and creates it on demand (same logic as `init-prompt-log`) if it doesn't — don't require the user to run `init-prompt-log` first every time.
- **Clipboard copy:** the clipboard-copy step in the current `create-prompt` is dropped entirely, not carried over.
- **`repo-path` argument:** wherever `[repo-path]` appears below, it means "operate on a different git repository" (worktree, branch, and `prompt.md` resolved relative to that path) — applied consistently, not just where spelled out under `create-prompt`.
- **`prompt.md.template`:** remove the `${PROMPT_ID}` placeholder (no more numbering) and stop `create-prompt` from exporting `PROMPT_ID`.
- **Session token/model capture:** out of scope for this task — `model`, `input_tokens`, `output_tokens` stay manually filled in.
Start by removing the Node/11ty files.
### Commands
### Create bin files for 11ty commands
`init-prompt-log`
Move the 11ty functions into scripts in the `bin` folder:
Ensures the current folder has a `.git` folder. Creates the `prompt-log` branch as an orphan branch (no shared history with `main`) via the dedicated git worktree. Idempotent — safe to run again if the branch/worktree already exists.
- `bin/dev-11ty-docs`
- `bin/build-11ty-docs`
- `bin/serve-11ty-docs`
`create-prompt <title> [repo-path]`
Create a new `bin/init-11ty-docs` that installs 11ty with `pnpm`.
Instead of creating an entry in the `prompts` folder this command creates the `prompt.md` file (always overwriting any existing one). The file name no longer matters. If `repo-path` is given, operates against that repository instead of the current directory.
Add a "11ty" section to the `library.md` and add help rows to the `task.template`.
`prompt-with-pi [path]`
### Setup Zensical
Runs the `pi` command and pipes the content of `prompt.md` as query. The harness follows the instructions and updates the `prompt.md` file.
Similar o the 11ty commands create:
By default it uses the `prompt.md` of the current directory. It can also take a path to a prompt file.
- `bin/init-zensical-docs`
- `bin/dev-zensical-docs`
- `bin/build-zensical-docs`
- `bin/serve-zensical-docs`
`commit-prompt [repo-path]`
The `init-zensical-docs` uses `activate-venv`, installs zensical according to https://zensical.org/docs/get-started/#install-with-uv and finally initalizes the project with `zensical new .`. If possible pass option to not create the `.github` workflow.
Reads the current commit hash (`git rev-parse --short HEAD`) of the project and the content of `prompt.md`. Creates a file named `<commit-hash>-<title-with-dashes-and-lowercase>.md` in the `prompt-log` worktree, storing the content. Commits it with message title `feat: add prompt file` and the filename in the message body. Pushes `prompt-log` to the remote, warning (not failing) if the push doesn't succeed.
Add a "Zensical" section to the `library.md` and add help rows to the `task.template`.
`list-prompts [repo-path]`
### Move to docs
Outputs a table with headers:
Move the mentioned markdown files and assets to the `docs` folder.
- date: first-commit date of the file on `prompt-log`
- title: frontmatter attribute `title` in file
- state: frontmatter attribute `state` in file
- commit: short hash of commit from filename
Setup the file navigation and link structure.
Rows are sorted by date.
Use `./task dev-11ty-docs` to check the site.
`edit-prompt [commit] [repo-path]`
Ensure the website is working as before.
If commit is not given, shows an `fzf` dialog of all files in the `prompt-log` branch, listing the frontmatter title. Selecting a file opens it directly in `$EDITOR` within the `tmp/prompt-log` worktree. If commit is given, opens that target file directly in `$EDITOR`. After the editor closes, the change is committed with `feat: update prompt file` and pushed (warning on failure). The file is edited in place; `prompt.md` is not involved.
### Recreate randomFunctionNames.js
`show-prompt [commit] [repo-path]`
There is js script `js/randomFunctionNames.js` that adds name generator to `example.md`. Try to load the js code accoring to https://zensical.org/docs/customization/#additional-javascript and move the function to the appropriate folder.
Similar to `edit-prompt` but outputs the prompt content directly to stdout instead of opening in `$EDITOR`. Accepts an optional commit prefix or shows an `fzf` dialog when no commit is given.
### Create vuepress scripts
`convert-prompts-folder-to-prompt-log [repo-path]`
This is a nice-to-have-task. Many other projects such as `~/Odoo-Build` use Vuepress to build the static site.
Converts an existing `prompts` folder to the new format. For each file, looks up the commit on which it was first added (`git log --follow --diff-filter=A`) and commits it as `<commit-hash>-<title-with-dashes-and-lowercase>.md` in the `prompt-log` branch, using the same commit-message convention as `commit-prompt`. Files with no discoverable creation commit are reported and skipped. On success, deletes the old `prompts/` folder and its files.
```
Odoo-Build]$ task help | grep vuepress
| build-vuepress | | Create vuepress build. |
| dev-vuepress | | Start vuepress development server. |
| install-vuepress | | Install build dependencies. |
| serve-vuepress | | Serve vuepress build. |
```
### Alias
Create bin scripts for these commands as well:
Add an alias to each command:
- `build-vuespress` -> `build-vuepress-docs`
- `dev-vuespress` -> `dev-vuepress-docs`
- `install-vuespress` -> `init-vuepress-docs`
- `serve-vuespress` -> `serve-vuepress-docs`
- `init-prompt-log` → `ipl`
- `create-prompt` → `crp` (existing)
- `prompt-with-pi` → `pwp`
- `commit-prompt` → `cop`
- `list-prompts` → `lp` (existing)
- `edit-prompt` → `ep`
- `show-prompt` → `shp`
- `convert-prompts-folder-to-prompt-log` → `cpfpl`
For the `init-vuepress-docs` additional packages are required. Check the `package.json` for details.
### Docs
When the project is initalized it also has to create the `dev` and `build` scripts in `package.json`
Update `library.md` with the commands in the LLM section. Register the help row in the `task.template` file.
Ignore the copy snippet files instructions of the `Odoo-Build/task`.
Add a "Vuepress" section to the `library.md` and add help rows to the `task.template`.
### Update install script
Finally I want you to update the `./install` script. Find a way to list the `bin` scripts. I do not want update this file manually.
Also create an `uninstall` script. That removes the scripts provided by `taskfile.build`.
## Worklog
- Created new bin scripts: `init-prompt-log`, `prompt-with-pi`, `commit-prompt`, `edit-prompt`, `show-prompt`, `convert-prompts-folder-to-prompt-log`
- Rewrote `bin/create-prompt` to output `prompt.md` directly with `author` from git log
- Rewrote `bin/list-prompts` to read from `tmp/prompt-log` worktree
- Updated `prompt.md.template` with `author` field
- Added aliases and verified no collisions with existing aliases
- Tested full workflow: init → create → store → list → edit → show → convert
- Updated `library.md` and `task.template` with all new commands
- Added `delete-prompt` command (`dp` alias) with fzf selection and commit/push support
- `commit_ref` is injected by `commit-prompt` only, not by `create-prompt`
- `commit-prompt` now errors if worktree is missing instead of auto-creating it
- Added comment documenting the empty-tree hash in `init-prompt-log`
- Sorted fzf dialogs by date (newest first) in `edit-prompt`, `show-prompt`, and `delete-prompt`
@Clanker Add a summary here once the task has been completed.
@Clanker Set frontmatter state to completed and update date and model. If you have access to session info also add token count.