From 0d2c8f897ec37a39a6c8de4da3f57978aa78b7a1 Mon Sep 17 00:00:00 2001 From: Janik von Rotz Date: Mon, 5 Oct 2026 11:28:25 +0200 Subject: [PATCH] feat: add delete-prompt command and enhance prompt-log metadata tracking --- bin/convert-prompts-folder-to-prompt-log | 5 ++ bin/delete-prompt | 99 ++++++++++++++++++++++++ bin/edit-prompt | 9 ++- bin/init-prompt-log | 1 + bin/log-prompt | 26 +++---- bin/show-prompt | 9 ++- install | 7 ++ library.md | 6 ++ prompt.md | 5 ++ task.template | 1 + 10 files changed, 148 insertions(+), 20 deletions(-) create mode 100644 bin/delete-prompt diff --git a/bin/convert-prompts-folder-to-prompt-log b/bin/convert-prompts-folder-to-prompt-log index 3597f6d..21ada1d 100755 --- a/bin/convert-prompts-folder-to-prompt-log +++ b/bin/convert-prompts-folder-to-prompt-log @@ -73,6 +73,11 @@ convert-prompts-folder-to-prompt-log() { cp "$file" "$target_path" + if ! grep -q '^commit_ref:' "$target_path"; then + awk -v hash="$creation_commit" 'NR==1 && /^---$/ { print; print "commit_ref: " hash; next } { print }' "$target_path" > "$target_path.tmp" + mv "$target_path.tmp" "$target_path" + fi + ( cd "$worktree_path" || exit 1 git add "$target_filename" diff --git a/bin/delete-prompt b/bin/delete-prompt new file mode 100644 index 0000000..8a38553 --- /dev/null +++ b/bin/delete-prompt @@ -0,0 +1,99 @@ +#!/usr/bin/env bash + +delete-prompt() { + local commit="" + local repo_path="${2:-.}" + + if [[ -n "$1" ]]; then + if [[ -n "$2" ]]; then + commit="$1" + elif [[ "$1" == */* ]] || [[ "$1" == "." ]]; then + repo_path="$1" + else + commit="$1" + fi + fi + + if [[ ! -d "$repo_path/.git" ]]; then + echo "Error: No .git directory found in $repo_path." + return 1 + fi + + local worktree_path="$repo_path/tmp/prompt-log" + + if [[ ! -d "$worktree_path" ]]; then + echo "No prompt-log worktree found. Run init-prompt-log first." + return 1 + fi + + local target_file + + if [[ -z "$commit" ]]; then + if ! command -v fzf &> /dev/null; then + echo "Error: fzf is required for interactive selection." + return 1 + fi + + local selected + selected="$( + for f in "$worktree_path"/*.md; do + 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")" + echo "$f_date|$f_title|$(basename "$f")" + fi + done | sort -t'|' -k1,1 -r | while IFS='|' read -r _ title filename; do + echo "$filename: $title" + done | fzf | cut -d: -f1 + )" + + if [[ -z "$selected" ]]; then + echo "No prompt selected." + return 0 + fi + + target_file="$worktree_path/$selected" + else + local matching_files=() + for f in "$worktree_path"/${commit}*.md; do + if [[ -f "$f" ]]; then + matching_files+=("$f") + fi + done + + if [[ ${#matching_files[@]} -eq 0 ]]; then + echo "Error: No prompt file found starting with commit prefix '$commit'." + return 1 + elif [[ ${#matching_files[@]} -gt 1 ]]; then + echo "Error: Ambiguous commit prefix '$commit'. Matches:" + printf '%s\n' "${matching_files[@]}" + return 1 + fi + + target_file="${matching_files[0]}" + fi + + local filename + filename="$(basename "$target_file")" + + read -r -p "Are you sure you want to delete '$filename'? (y/n) " confirm + if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then + echo "Deletion cancelled." + return 0 + fi + + rm "$target_file" + + ( + cd "$worktree_path" || exit 1 + git add "$filename" + git commit -m "feat: delete prompt file" -m "$filename" + git push 2>/dev/null || echo "Warning: push failed." + ) || echo "Warning: commit/push step failed." +} +dp() { + delete-prompt "$@" +} diff --git a/bin/edit-prompt b/bin/edit-prompt index 364f8a6..8969198 100755 --- a/bin/edit-prompt +++ b/bin/edit-prompt @@ -38,10 +38,15 @@ edit-prompt() { selected="$( for f in "$worktree_path"/*.md; do if [[ -f "$f" ]]; then - local f_title + local f_title f_date f_commit f_title="$(grep -m1 '^title:' "$f" | sed 's/^title:[[:space:]]*//; s/^"\(.*\)"$/\1/; s/'"'"'\(.*\)'"'"'$/\1/')" - echo "$(basename "$f"): $f_title" + 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")" + echo "$f_date|$f_title|$(basename "$f")" fi + done | sort -t'|' -k1,1 -r | while IFS='|' read -r _ title filename; do + echo "$filename: $title" done | fzf | cut -d: -f1 )" diff --git a/bin/init-prompt-log b/bin/init-prompt-log index 957d505..af3df83 100755 --- a/bin/init-prompt-log +++ b/bin/init-prompt-log @@ -27,6 +27,7 @@ init-prompt-log() { fi if ! git show-ref --verify --quiet refs/heads/prompt-log; then + # 4b825dc... is the SHA-1 of Git's empty tree object (constant across all repos) local empty_tree="4b825dc642cb6eb9a060e54bf8d69288fbee4904" local orphan_commit orphan_commit="$(echo "init: prompt-log branch" | git commit-tree "$empty_tree")" diff --git a/bin/log-prompt b/bin/log-prompt index 0b5d00d..a874798 100755 --- a/bin/log-prompt +++ b/bin/log-prompt @@ -21,22 +21,9 @@ log-prompt() { fi if [[ ! -d "tmp/prompt-log" ]] || ! git worktree list | grep -qF "$(pwd)/tmp/prompt-log"; then - echo "Creating prompt-log worktree." - if ! git show-ref --verify --quiet refs/heads/prompt-log; then - local current_branch - current_branch="$(git rev-parse --abbrev-ref HEAD)" - if [[ "$current_branch" == "prompt-log" ]]; then - echo "Error: Cannot create prompt-log worktree while on prompt-log branch." - cd "$original_dir" || true - return 1 - fi - local empty_tree="4b825dc642cb6eb9a060e54bf8d69288fbee4904" - local orphan_commit - orphan_commit="$(echo "init: prompt-log branch" | git commit-tree "$empty_tree")" - git branch prompt-log "$orphan_commit" - fi - mkdir -p tmp - git worktree add "tmp/prompt-log" prompt-log + echo "Error: prompt-log worktree not found. Run init-prompt-log first." + cd "$original_dir" || true + return 1 fi local commit_hash @@ -60,6 +47,13 @@ log-prompt() { cp "$prompt_file" "$target_path" + if grep -q '^commit_ref:' "$target_path"; then + sed -i "s/^commit_ref:.*/commit_ref: $commit_hash/" "$target_path" + else + awk -v hash="$commit_hash" 'NR==1 && /^---$/ { print; print "commit_ref: " hash; next } { print }' "$target_path" > "$target_path.tmp" + mv "$target_path.tmp" "$target_path" + fi + ( cd "$worktree_path" || exit 1 git add "$filename" diff --git a/bin/show-prompt b/bin/show-prompt index 850235c..bb4f1ab 100755 --- a/bin/show-prompt +++ b/bin/show-prompt @@ -38,10 +38,15 @@ show-prompt() { selected="$( for f in "$worktree_path"/*.md; do if [[ -f "$f" ]]; then - local f_title + local f_title f_date f_commit f_title="$(grep -m1 '^title:' "$f" | sed 's/^title:[[:space:]]*//; s/^"\(.*\)"$/\1/; s/'"'"'\(.*\)'"'"'$/\1/')" - echo "$(basename "$f"): $f_title" + 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")" + echo "$f_date|$f_title|$(basename "$f")" fi + done | sort -t'|' -k1,1 -r | while IFS='|' read -r _ title filename; do + echo "$filename: $title" done | fzf | cut -d: -f1 )" diff --git a/install b/install index 780089e..db6caf9 100755 --- a/install +++ b/install @@ -17,20 +17,24 @@ scripts=( clone-taskfile commit-git-folder commit-with-llm + convert-prompts-folder-to-prompt-log copy-dotenv copy-to-clipboard create-dotenv create-prompt create-symlink create-symlink-files + delete-prompt download-git-folder edit-dotenv + edit-prompt ensure-directory find-git-folder generate-ssh-key get-pass-from-keepass init-dotenv-dir init-project + init-prompt-log list-dotenv list-git-folder list-prompts @@ -42,12 +46,14 @@ scripts=( load-kubeconfig-from-pass log-error login-docker + log-prompt log-success log-warning ls-git-folder merge-git-remote open-url open-with-os + prompt-with-pi pull-all-repos pull-git-folder push-git-folder @@ -68,6 +74,7 @@ scripts=( setup-completion show-dotenv show-dotenv-setup + show-prompt stage-git-folder start-jupyter-lab status-all-repos diff --git a/library.md b/library.md index 7b1c47c..176ed5f 100644 --- a/library.md +++ b/library.md @@ -332,6 +332,12 @@ rows+=("edit-prompt|[commit][repo-path]|Edit prompt directly in prompt-log workt rows+=("show-prompt|[commit][repo-path]|Show prompt content from prompt-log.") ``` +### delete-prompt + +```bash +rows+=("delete-prompt|[commit][repo-path]|Delete prompt from prompt-log.") +``` + ### convert-prompts-folder-to-prompt-log ```bash diff --git a/prompt.md b/prompt.md index f7eb4e8..a0e8641 100644 --- a/prompt.md +++ b/prompt.md @@ -105,3 +105,8 @@ Update `library.md` with the commands in the LLM section. Register the help row - 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 `log-prompt` only, not by `create-prompt` +- `log-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` diff --git a/task.template b/task.template index 0847299..7d824f7 100755 --- a/task.template +++ b/task.template @@ -49,6 +49,7 @@ help-table() { rows+=("list-prompts|[repo-path]|List prompts from prompt-log branch.") rows+=("edit-prompt|[commit][repo-path]|Edit prompt directly in prompt-log worktree.") rows+=("show-prompt|[commit][repo-path]|Show prompt content from prompt-log.") + rows+=("delete-prompt|[commit][repo-path]|Delete prompt from prompt-log.") rows+=("convert-prompts-folder-to-prompt-log|[repo-path]|Convert prompts folder to prompt-log branch.") rows+=("===Logging===| | ") rows+=("log-error||Colored output for errors.")