refactor: switch prompt file naming from commit hashes to dates and update metadata handling
This commit is contained in:
7 files changed
+135
-103
No files matched your search
+30
-12
@@ -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
|
||||
|
||||
(
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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.
|
||||
Reference in new issue
Block a user