Skip to content

Commit f686fc1

Browse files
committed
[refactor] Update prompt template filenames and enhance project title parsing in tests
1 parent 3a6395c commit f686fc1

File tree

6 files changed

+495
-40
lines changed

6 files changed

+495
-40
lines changed

docs/prompt-templates.md

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ By convention, store your templates in the `.giv/templates/` folder in your proj
3333
```
3434
.giv/
3535
├── templates/
36-
│ ├── summary.md
37-
│ ├── announcement.md
38-
│ └── message.md
36+
│ ├── summary_prompt.md
37+
│ ├── announcement_prompt.md
38+
│ └── message_prompt.md
3939
├── src/
4040
```
4141

@@ -51,17 +51,16 @@ Follow these steps to create or customize a prompt template:
5151
2. **Copy or Create File:** In `templates/`, create a new file named `<subcommand>_prompt.md`, or copy an existing template from the `templates/` directory of this repository.
5252
3. **Insert Tokens:** Open the file and place tokens where dynamic content should appear. Use headers, paragraphs, or lists for structure.
5353
4. **Save & Reference:** Save the file. When running `giv`, use the `--prompt-file` option to point to your template:
54-
5554
```sh
5655
giv summary --prompt-file=templates/summary_custom.md
57-
```
56+
```
5857
5. **Test & Iterate:** Review the generated output, tweak your template, and retest until satisfied.
5958

6059
---
6160

6261
## 5. Example: Custom Summary Template
6362

64-
Below is a sample `templates/summary.md` for the `giv summary` subcommand. Copy and adapt it as needed:
63+
Below is a sample `templates/summary_summary.md` for the `giv summary` subcommand. Copy and adapt it as needed:
6564

6665
```markdown
6766
# Project: [PROJECT_TITLE]

src/giv.sh

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -462,15 +462,14 @@ EOF
462462
# # -------------------------------------------------------------------
463463

464464
cmd_message() {
465-
commit_id="$1"
465+
commit_id="${1:-}"
466466
if [ -z "${commit_id}" ]; then
467467
commit_id="--current"
468468
fi
469469

470470
print_debug "Generating commit message for ${commit_id}"
471471

472-
# Handle both --current and --cached, as argument parsing may set either value.
473-
# This duplication ensures correct behavior regardless of which is set.
472+
# Handle both --current and --cached (see argument parsing section for details).
474473
if [ "$commit_id" = "--current" ] || [ "$commit_id" = "--cached" ]; then
475474
hist=$(portable_mktemp "commit_history_XXXXXX.md")
476475
build_history "$hist" "$commit_id" "$todo_pattern" "$PATHSPEC"
@@ -479,10 +478,14 @@ cmd_message() {
479478
printf '%s' "$(build_prompt "${PROMPT_DIR}/message_prompt.md" "$hist")" >"$pr"
480479
print_debug "Generated prompt file $pr"
481480
res=$(generate_response "$pr" "$model_mode")
482-
printf '%s\n' "$res"
481+
res=$(generate_response "$pr" "$model_mode")
482+
if [ $? -ne 0 ]; then
483+
printf 'Error: Failed to generate AI response.\n' >&2
484+
exit 1
485+
fi
486+
printf '%s\n' "${res}"
483487
return
484488
fi
485-
486489
# Detect exactly two- or three-dot ranges (A..B or A...B)
487490
if echo "$commit_id" | grep -qE '\.\.\.?'; then
488491
print_debug "Detected commit range syntax: $commit_id"
@@ -503,6 +506,7 @@ cmd_message() {
503506
print_debug "Processing two-dot range: $commit_id"
504507
git --no-pager log --reverse --pretty=%B "$commit_id" | sed '${/^$/d;}'
505508
;;
509+
*) ;;
506510
esac
507511
return
508512
fi
@@ -512,7 +516,7 @@ cmd_message() {
512516
printf 'Error: Invalid commit ID: %s\n' "$commit_id" >&2
513517
exit 1
514518
fi
515-
git --no-pager log -1 --pretty=%B "$commit_id" | sed '${/^$/d;}'
519+
git --no-pager log -1 --pretty=%B "${commit_id}" | sed '${/^$/d;}'
516520
return
517521
}
518522

@@ -525,10 +529,11 @@ cmd_summary() {
525529
summaries_file=$(portable_mktemp "summary_summaries_XXXXXX.md")
526530

527531
summarize_target "${sum_revision}" "${summaries_file}" "${sum_model_mode}"
528-
cat "${summaries_file}" >&2
532+
print_debug "$(cat "${summaries_file}" || true)"
533+
529534
# Early exit if no summaries were produced
530535
if [ ! -f "${summaries_file}" ]; then
531-
printf 'Error: No summaries generated for %s.\n' "$sum_revision" >&2
536+
printf 'Error: No summaries generated for %s.\n' "${sum_revision}" >&2
532537
exit 1
533538
fi
534539

src/helpers.sh

Lines changed: 139 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
#!/bin/sh
22

3+
GIV_TOKEN_PROJECT_TITLE=""
4+
GIV_TOKEN_VERSION=""
5+
GIV_TOKEN_EXAMPLE=""
6+
GIV_TOKEN_RULES=""
7+
GIV_TOKEN_SUMMARY=""
8+
39
is_valid_git_range() {
410
git rev-list "$1" >/dev/null 2>&1
511
}
@@ -26,7 +32,7 @@ portable_mktemp_dir() {
2632

2733
# Ensure only one subfolder under $TMPDIR/giv exists per execution of the script
2834
# If GIV_TMPDIR is not set, create a new temporary directory
29-
if [ -z "$GIV_TMPDIR" ]; then
35+
if [ -z "${GIV_TMPDIR}" ]; then
3036

3137
if command -v mktemp >/dev/null 2>&1; then
3238
GIV_TMPDIR="$(mktemp -d -p "${base_path}")"
@@ -57,7 +63,7 @@ print_debug() {
5763
fi
5864
}
5965
print_info() {
60-
printf '%s\n' "$*" >&2
66+
printf 'INFO: %s\n' "$*" >&2
6167
}
6268
print_warn() {
6369
printf 'WARNING: %s\n' "$*" >&2
@@ -104,10 +110,10 @@ replace_tokens() {
104110
# Exports GIV_TOKEN_SUMMARY (and any of PROJECT_TITLE, VERSION, EXAMPLE, RULES
105111
# if passed) then runs replace_tokens on the template.
106112
build_prompt() {
107-
project_title=""
108-
version=""
109-
example=""
110-
rules=""
113+
project_title=$(parse_project_title)
114+
version="${GIV_TOKEN_VERSION:-}"
115+
example="${GIV_TOKEN_EXAMPLE:-}"
116+
rules="${GIV_TOKEN_RULES:-}"
111117
# parse flags
112118
while [ $# -gt 2 ]; do
113119
case "$1" in
@@ -138,18 +144,18 @@ build_prompt() {
138144
diff_file=$2
139145

140146
# validate files
141-
if [ ! -f "$prompt_template" ]; then
142-
printf 'template file not found: %s\n' "$prompt_template" >&2
147+
if [ ! -f "${prompt_template}" ]; then
148+
printf 'template file not found: %s\n' "${prompt_template}" >&2
143149
return 1
144150
fi
145-
if [ ! -f "$diff_file" ]; then
146-
printf 'diff file not found: %s\n' "$diff_file" >&2
151+
if [ ! -f "${diff_file}" ]; then
152+
printf 'diff file not found: %s\n' "${diff_file}" >&2
147153
return 1
148154
fi
149-
155+
# export our tokens
150156
# export our tokens
151157
export GIV_TOKEN_SUMMARY
152-
GIV_TOKEN_SUMMARY=$(cat "$diff_file")
158+
GIV_TOKEN_SUMMARY=$(cat "${diff_file}")
153159
[ -n "$project_title" ] && export GIV_TOKEN_PROJECT_TITLE="$project_title"
154160
[ -n "$version" ] && export GIV_TOKEN_VERSION="$version"
155161
[ -n "$example" ] && export GIV_TOKEN_EXAMPLE="$example"
@@ -159,6 +165,54 @@ build_prompt() {
159165
replace_tokens <"$prompt_template"
160166
}
161167

168+
# Locate the project from the codebase. Looks for common project files
169+
# like package.json, pyproject.toml, setup.py, Cargo.toml, composer.json
170+
# build.gradle, pom.xml, etc. and extracts the project name.
171+
# If no project file is found, returns an empty string.
172+
# If a project name is found, it is printed to stdout.
173+
parse_project_title() {
174+
# Look for common project files
175+
for file in package.json pyproject.toml setup.py Cargo.toml composer.json build.gradle pom.xml; do
176+
if [ -f "${file}" ]; then
177+
# Extract project name based on file type
178+
case "${file}" in
179+
package.json)
180+
awk -F'"' '/"name"[[:space:]]*:/ {print $4; exit}' "${file}"
181+
;;
182+
pyproject.toml)
183+
awk -F' = ' '/^name/ {gsub(/"/, "", $2); print $2; exit}' "${file}"
184+
;;
185+
setup.py)
186+
# Double quotes
187+
grep -E '^[[:space:]]*name[[:space:]]*=[[:space:]]*"[^"]+"' "${file}" | sed -E 's/^[[:space:]]*name[[:space:]]*=[[:space:]]*"([^"]+)".*/\1/' | head -n1 &&
188+
# Single quotes
189+
grep -E "^[[:space:]]*name[[:space:]]*=[[:space:]]*'[^']+'" "${file}" | sed -E "s/^[[:space:]]*name[[:space:]]*=[[:space:]]*'([^']+)'.*/\1/" | head -n1
190+
;;
191+
Cargo.toml)
192+
awk -F' = ' '/^name/ {gsub(/"/, "", $2); print $2; exit}' "${file}"
193+
;;
194+
composer.json)
195+
awk -F'"' '/"name"[[:space:]]*:/ {print $4; exit}' "${file}"
196+
;;
197+
build.gradle)
198+
# Double quotes
199+
grep -E '^[[:space:]]*rootProject\.name[[:space:]]*=[[:space:]]*"[^"]+"' "${file}" | sed -E 's/^[[:space:]]*rootProject\.name[[:space:]]*=[[:space:]]*"([^"]+)".*/\1/' | head -n1 &&
200+
# Single quotes
201+
grep -E "^[[:space:]]*rootProject\.name[[:space:]]*=[[:space:]]*'[^']+'" "${file}" | sed -E "s/^[[:space:]]*rootProject\.name[[:space:]]*=[[:space:]]*'([^']+)'.*/\1/" | head -n1
202+
;;
203+
pom.xml)
204+
awk -F'[<>]' '/<name>/ {print $3; exit}' "${file}"
205+
;;
206+
*)
207+
echo "Unknown project file type: ${file}" >&2
208+
return 1
209+
;;
210+
esac
211+
return
212+
fi
213+
done
214+
}
215+
162216
# Extract version string from a line (preserving v if present)
163217
parse_version() {
164218
#printf 'Parsing version from: %s\n' "$1" >&2
@@ -259,11 +313,11 @@ generate_remote() {
259313

260314
# Escape for JSON (replace backslash, double quote, and control characters)
261315
# Use json_escape to safely encode the prompt as a JSON string
262-
escaped_content=$(printf "%s" "${content}" | json_escape)
263-
316+
escaped_content=$(printf "%s" "${content}" | json_escape)
317+
264318
# shellcheck disable=SC2154
265319
body=$(printf '{"model":"%s","messages":[{"role":"user","content":%s}],"max_completion_tokens":8192}' \
266-
"${api_model}" "${escaped_content}")
320+
"${api_model}" "${escaped_content}")
267321

268322
# shellcheck disable=SC2154
269323
response=$(curl -s -X POST "${api_url}" \
@@ -385,6 +439,11 @@ get_message_header() {
385439
esac
386440
}
387441

442+
# Finds and returns the path to a version file in the current directory.
443+
# The function checks if the variable 'version_file' is set and points to an existing file.
444+
# If not, it searches for common version files (package.json, pyproject.toml, setup.py, Cargo.toml, composer.json, build.gradle, pom.xml).
445+
# If none are found, it attempts to locate a 'giv.sh' script using git.
446+
# If no suitable file is found, it returns an empty string.
388447
# helper: finds the version file path
389448
find_version_file() {
390449
print_debug "Finding version file..."
@@ -409,7 +468,37 @@ find_version_file() {
409468

410469
}
411470

412-
# helper: extract version text from a file or git index/commit
471+
# get_version_info
472+
#
473+
# Extracts version information from a specified file or from a file as it exists
474+
# in a given git commit or index state.
475+
#
476+
# Usage:
477+
# get_version_info <commit> <file_path>
478+
#
479+
# Parameters:
480+
# commit - Specifies the git commit or index state to extract the version from.
481+
# Accepts:
482+
# --current or "" : Use the current working directory file.
483+
# --cached : Use the staged (index) version of the file.
484+
# <commit_hash> : Use the file as it exists in the specified commit.
485+
# file_path - Path to the file containing the version information.
486+
#
487+
# Behavior:
488+
# - Searches for a version string matching the pattern 'versionX.Y' or 'version X.Y.Z'
489+
# (case-insensitive) in the specified file or git object.
490+
# - Returns the first matching version string found, parsed by parse_version.
491+
# - Returns an empty string if the file or version string is not found.
492+
#
493+
# Dependencies:
494+
# - Requires 'git' command-line tool for accessing git objects.
495+
# - Relies on a 'parse_version' function to process the raw version string.
496+
# - Uses 'print_debug' for optional debug output.
497+
#
498+
# Example:
499+
# get_version_info --current ./package.json
500+
# get_version_info --cached ./setup.py
501+
# get_version_info abc123 ./src/version.txt
413502
get_version_info() {
414503
commit="$1"
415504
vf="$2"
@@ -624,21 +713,45 @@ summarize_target() {
624713
exit 1
625714
}
626715

716+
# summarize_commit - Generates a summary for a given commit.
717+
#
718+
# Arguments:
719+
# $1 - The commit hash or identifier to summarize.
720+
# $2 - (Optional) The generation mode to use; defaults to the value of $model_mode.
721+
#
722+
# Description:
723+
# This function creates temporary files to store commit history, prompt, and summary results.
724+
# It builds the commit history, retrieves version information, and constructs a summary prompt.
725+
# The prompt is then used to generate a summary response, which is saved to a file and printed.
726+
#
727+
# Outputs:
728+
# Prints the generated summary to stdout.
729+
#
730+
# Dependencies:
731+
# - portable_mktemp
732+
# - print_debug
733+
# - build_history
734+
# - find_version_file
735+
# - get_version_info
736+
# - build_prompt
737+
# - generate_response
627738
summarize_commit() {
628739
commit="$1"
629-
gen_mode="${2:-$model_mode}"
740+
gen_mode="${2:-${model_mode}}"
630741
hist=$(portable_mktemp "hist.${commit}.XXXXXX.md")
631742
pr=$(portable_mktemp "prompt.${commit}.XXXXXX.md")
632743
res_file=$(portable_mktemp "summary.${commit}.XXXXXX.md")
633-
print_debug "summarize_commit $commit $hist $pr"
634-
build_history "$hist" "$commit" "$todo_pattern" "$PATHSPEC"
635-
summary_template=$(build_prompt "${PROMPT_DIR}/summary_prompt.md" "$hist")
636-
print_debug "Using summary prompt: $summary_template"
637-
printf '%s\n' "$summary_template" >"$pr"
638-
res=$(generate_response "$pr" "${gen_mode}")
639-
echo "${res}" >"$res_file"
640-
641-
printf '%s\n' "$res"
744+
print_debug "summarize_commit ${commit} ${hist} ${pr}"
745+
build_history "${hist}" "${commit}" "${todo_pattern}" "$PATHSPEC"
746+
sc_version_file=$(find_version_file)
747+
sc_version=$(get_version_info "${commit}" "${sc_version_file}")
748+
summary_template=$(build_prompt --version "${sc_version}" "${PROMPT_DIR}/summary_prompt.md" "${hist}")
749+
print_debug "Using summary prompt: ${summary_template}"
750+
printf '%s\n' "${summary_template}" >"${pr}"
751+
res=$(generate_response "${pr}" "${gen_mode}")
752+
echo "${res}" >"${res_file}"
753+
754+
printf '%s\n' "${res}"
642755
}
643756

644757
# extract_section <section_name> <markdown_file> [<header_id>]

tests/.tmp/tmp.4ifJ9Hiotv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Subproject commit c932ca3d5b714623b97e2b172e5bcf2ccfcdfc2d

0 commit comments

Comments
 (0)