feat: add version display to sw-hello command#358
Conversation
- sw-hello.sh now reads version from package.json at runtime - Main output and --version flag both display "Shipwright vX.Y.Z" - Updated existing tests to match new format - Added new test verifying version matches package.json Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The context efficiency tests were failing because the mock cost/event data used hardcoded timestamps from 2026-02-27, which fell outside the 30-day cutoff window causing sw-cost.sh to return early with "No cost entries" before rendering the CONTEXT EFFICIENCY section. Replace hardcoded timestamps with $(date +%s) so test data always falls within the 30-day window regardless of when tests run. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
📝 WalkthroughWalkthroughChanges introduce version display functionality to the Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
scripts/sw-hello.sh (1)
44-45:⚠️ Potential issue | 🟡 MinorUpdate help examples to match current behavior.
Line 44 still says
Print "hello world", but default execution now printsShipwright vX.Y.Z.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/sw-hello.sh` around lines 44 - 45, Update the help text for the "shipwright hello" command in scripts/sw-hello.sh so the short description reflects current behavior: replace the phrase Print "hello world" with a description like Print current Shipwright version (e.g., "Shipwright vX.Y.Z") so the entry for "shipwright hello" matches what the command actually outputs; keep the "shipwright hello --help" line as-is if it still shows the help text.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.claude/loop-state.md:
- Line 2: Fix the malformed structured data in .claude/loop-state.md: terminate
the unterminated quoted string in the goal field ("goal"), correct the
unbalanced quote in the "[intake]" status line, and repair the invalid JSON
entries on lines 13, 15, 20, 25, 30, 35, and 85 by ensuring all strings are
closed and all JSON property names and string values are properly quoted; after
editing, validate the file with a YAML/JSON linter (or run the repository's
loop-state parser) to confirm the document parses cleanly before committing.
In @.claude/platform-hygiene.json:
- Line 3: Update the hygiene snapshot's repository identifier by replacing the
incorrect value "hello-v3" for the JSON key "repository" with the correct
repository id used by this PR ("sethdford/shipwright"); locate the "repository":
"hello-v3" entry and change its value to "sethdford/shipwright" so hygiene
metadata routes to the correct repo identity.
In `@scripts/sw-hello-test.sh`:
- Line 42: The version-check regex in the conditional against the variable
"output" uses the pattern 'Shipwright\ v[0-9]+\.[0-9]+\.[0-9]+' and needs to be
anchored to the end of the string so suffixes like "-beta" or "-dev" fail;
update both occurrences of the if test that use the pattern (the if [[ "$output"
=~ ... ]] checks) to append '$' to the regex (i.e., make it 'Shipwright\
v[0-9]+\.[0-9]+\.[0-9]+$') so the match requires the version to end the string.
In `@scripts/sw-hello.sh`:
- Around line 14-19: The current assignment unconditionally sets VERSION from
_pkg_version when non-empty, allowing values like "null", "latest", or "v3.2.4";
validate the parsed value against a strict semver regex before overriding the
fallback. After obtaining _pkg_version (from jq/grep), test it with a
POSIX/Bash-compatible regex matching X.Y.Z (e.g. ^[0-9]+\.[0-9]+\.[0-9]+$) and
only assign VERSION="$_pkg_version" when that check passes; otherwise leave the
existing VERSION untouched. Ensure you reference the existing variables and
commands (_pkg_version, VERSION, jq -r '.version') so the change slots into the
same block.
---
Outside diff comments:
In `@scripts/sw-hello.sh`:
- Around line 44-45: Update the help text for the "shipwright hello" command in
scripts/sw-hello.sh so the short description reflects current behavior: replace
the phrase Print "hello world" with a description like Print current Shipwright
version (e.g., "Shipwright vX.Y.Z") so the entry for "shipwright hello" matches
what the command actually outputs; keep the "shipwright hello --help" line as-is
if it still shows the help text.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 1a3a7971-d0eb-4fbf-ae1f-14a209cd4fc3
⛔ Files ignored due to path filters (4)
.claude/loop-logs/iteration-1.logis excluded by!**/*.log.claude/loop-logs/iteration-2.logis excluded by!**/*.log.claude/loop-logs/tests-iter-1.logis excluded by!**/*.log.claude/loop-logs/tests-iter-2.logis excluded by!**/*.log
📒 Files selected for processing (13)
.claude/intelligence-cache.json.claude/loop-state.md.claude/platform-hygiene.json.claude/recovery-state.json.claude/test-holdout/manifest.json.gitignorescripts/sw-cost-test.shscripts/sw-formal-spec-test.shscripts/sw-hello-test.shscripts/sw-hello.shscripts/sw-mutation-executor-test.shscripts/sw-process-reward-test.shscripts/sw-reward-aggregator-test.sh
| @@ -1,119 +1,86 @@ | |||
| --- | |||
| goal: "Add a shipwright ping command that prints pong to stdout and exits 0 | |||
| goal: "Add version display to sw-hello command: read version from package.json, display Shipwright vX.Y.Z, add test | |||
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python - <<'PY'
import json, re
from pathlib import Path
p = Path(".claude/loop-state.md")
text = p.read_text()
# Front matter check
parts = text.split('---')
if len(parts) >= 3:
front = parts[1]
for i, line in enumerate(front.splitlines(), 1):
if line.count('"') % 2 == 1:
print(f"Unbalanced quote in front matter line {i}: {line}")
# JSON-line sanity check for lines beginning with '{'
for idx, line in enumerate(text.splitlines(), 1):
if line.strip().startswith("{"):
try:
json.loads(line)
except Exception as e:
print(f"Invalid JSON at source line {idx}: {e}")
PYRepository: sethdford/shipwright
Length of output: 1052
Fix malformed structured data before commit.
Line 2 has an unterminated quoted string in the goal field. Line 63 has an unbalanced quote in the [intake] status line. Multiple lines (13, 15, 20, 25, 30, 35, 85) contain invalid JSON with unterminated strings or missing property quotes. These malformed entries will break any parser reading loop state.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.claude/loop-state.md at line 2, Fix the malformed structured data in
.claude/loop-state.md: terminate the unterminated quoted string in the goal
field ("goal"), correct the unbalanced quote in the "[intake]" status line, and
repair the invalid JSON entries on lines 13, 15, 20, 25, 30, 35, and 85 by
ensuring all strings are closed and all JSON property names and string values
are properly quoted; after editing, validate the file with a YAML/JSON linter
(or run the repository's loop-state parser) to confirm the document parses
cleanly before committing.
| "timestamp": "2026-03-10T15:34:26Z", | ||
| "repository": "shipwright", | ||
| "timestamp": "2026-04-04T12:42:58Z", | ||
| "repository": "hello-v3", |
There was a problem hiding this comment.
Fix repository identifier mismatch in hygiene snapshot.
On Line 3, "repository": "hello-v3" does not match this PR context (sethdford/shipwright). This can route hygiene metadata to the wrong repo identity and skew downstream reporting/automation.
Suggested fix
- "repository": "hello-v3",
+ "repository": "shipwright",📝 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.
| "repository": "hello-v3", | |
| "repository": "shipwright", |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.claude/platform-hygiene.json at line 3, Update the hygiene snapshot's
repository identifier by replacing the incorrect value "hello-v3" for the JSON
key "repository" with the correct repository id used by this PR
("sethdford/shipwright"); locate the "repository": "hello-v3" entry and change
its value to "sethdford/shipwright" so hygiene metadata routes to the correct
repo identity.
| local output | ||
| output=$("$SCRIPT_DIR/sw-hello.sh") | ||
| assert_equals "hello world" "$output" "hello command outputs 'hello world'" | ||
| if [[ "$output" =~ ^Shipwright\ v[0-9]+\.[0-9]+\.[0-9]+ ]]; then |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Get the file size first
wc -l scripts/sw-hello-test.shRepository: sethdford/shipwright
Length of output: 92
🏁 Script executed:
# Read the file to see lines 42 and 88 with context
sed -n '35,50p' scripts/sw-hello-test.sh | cat -nRepository: sethdford/shipwright
Length of output: 701
🏁 Script executed:
# Also check line 88 area
sed -n '80,95p' scripts/sw-hello-test.sh | cat -nRepository: sethdford/shipwright
Length of output: 802
🏁 Script executed:
# Check if there are other similar regex patterns
rg 'Shipwright.*v\[0-9\]' scripts/sw-hello-test.shRepository: sethdford/shipwright
Length of output: 201
Anchor version regex to the end of string.
Lines 42 and 88 are missing $, so suffixes like "-beta" or "-dev" can still pass the test.
Proposed fix
- if [[ "$output" =~ ^Shipwright\ v[0-9]+\.[0-9]+\.[0-9]+ ]]; then
+ if [[ "$output" =~ ^Shipwright\ v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
@@
- if [[ "$output" =~ ^Shipwright\ v[0-9]+\.[0-9]+\.[0-9]+ ]]; then
+ if [[ "$output" =~ ^Shipwright\ v[0-9]+\.[0-9]+\.[0-9]+$ ]]; thenAlso applies to: 88-88
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/sw-hello-test.sh` at line 42, The version-check regex in the
conditional against the variable "output" uses the pattern 'Shipwright\
v[0-9]+\.[0-9]+\.[0-9]+' and needs to be anchored to the end of the string so
suffixes like "-beta" or "-dev" fail; update both occurrences of the if test
that use the pattern (the if [[ "$output" =~ ... ]] checks) to append '$' to the
regex (i.e., make it 'Shipwright\ v[0-9]+\.[0-9]+\.[0-9]+$') so the match
requires the version to end the string.
| # Read version from package.json (fall back to script VERSION if unavailable) | ||
| _pkg_version=$(jq -r '.version' "$SCRIPT_DIR/../package.json" 2>/dev/null \ | ||
| || grep '"version"' "$SCRIPT_DIR/../package.json" 2>/dev/null \ | ||
| | grep -o '[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*' | head -1 \ | ||
| || true) | ||
| [[ -n "${_pkg_version:-}" ]] && VERSION="$_pkg_version" |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n scripts/sw-hello.sh | sed -n '1,30p'Repository: sethdford/shipwright
Length of output: 2051
🏁 Script executed:
# Check the structure of package.json to understand what jq would parse
head -20 package.jsonRepository: sethdford/shipwright
Length of output: 559
🏁 Script executed:
python3 << 'EOF'
import json
import subprocess
import tempfile
import os
test_cases = [
("valid_semver", {"version": "3.2.4"}),
("null_version", {"version": None}),
("string_version", {"version": "latest"}),
("invalid_semver", {"version": "v3.2.4"}),
("missing_version", {"name": "test"}),
]
for name, data in test_cases:
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
json.dump(data, f)
temp_file = f.name
try:
# Test jq -r '.version'
result = subprocess.run(['jq', '-r', '.version', temp_file],
capture_output=True, text=True, timeout=2)
jq_output = result.stdout.strip()
# Test proposed fix: jq -er '.version // empty'
result2 = subprocess.run(['jq', '-er', '.version // empty', temp_file],
capture_output=True, text=True, timeout=2)
jq_fix_output = result2.stdout.strip()
jq_fix_exit = result2.returncode
# Test grep fallback pattern
if jq_output and jq_output != "null":
grep_result = subprocess.run(
['grep', '-o', r'[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*'],
input=jq_output, capture_output=True, text=True)
grep_match = grep_result.stdout.strip()
else:
grep_match = ""
print(f"{name}:")
print(f" Original jq -r '.version': '{jq_output}'")
print(f" Would pass [[ -n ... ]]: {bool(jq_output and jq_output != '')}")
print(f" Fixed jq -er '.version // empty': '{jq_fix_output}' (exit: {jq_fix_exit})")
print(f" Grep fallback match: '{grep_match}'")
print()
finally:
os.unlink(temp_file)
EOFRepository: sethdford/shipwright
Length of output: 892
🏁 Script executed:
# Verify the proposed regex pattern is correct
python3 << 'EOF'
import re
# Proposed regex from the review
regex = r'^[0-9]+\.[0-9]+\.[0-9]+$'
test_cases = [
"3.2.4", # valid
"0.0.1", # valid (zero versions)
"10.20.30", # valid (multi-digit)
"null", # invalid
"latest", # invalid
"v3.2.4", # invalid (has v prefix)
"3.2", # invalid (missing patch)
"3.2.4.5", # invalid (too many parts)
"3.2.4-beta", # invalid (has suffix)
"3.2.4+build", # invalid (has metadata)
]
for version in test_cases:
matches = bool(re.match(regex, version))
print(f"'{version}' matches regex: {matches}")
EOFRepository: sethdford/shipwright
Length of output: 365
🏁 Script executed:
# Also verify the bash regex syntax works correctly
bash << 'EOF'
# Test the proposed bash regex from the fix
regex='^[0-9]+\.[0-9]+\.[0-9]+$'
test_cases=(
"3.2.4"
"0.0.1"
"10.20.30"
"null"
"latest"
"v3.2.4"
"3.2"
"3.2.4.5"
)
for version in "${test_cases[@]}"; do
if [[ "$version" =~ $regex ]]; then
echo "'$version' matches: YES"
else
echo "'$version' matches: NO"
fi
done
EOFRepository: sethdford/shipwright
Length of output: 230
Validate parsed version before overriding fallback.
Line 19 overwrites VERSION for any non-empty value, including invalid semver text. The jq -r '.version' command returns the literal string "null" when the version field is null or missing, which passes the [[ -n ... ]] check. Similarly, values like "latest" or "v3.2.4" would also be accepted. Add regex validation to gate assignment on semver format.
Proposed hardening
-_pkg_version=$(jq -r '.version' "$SCRIPT_DIR/../package.json" 2>/dev/null \
+_pkg_version=$(jq -er '.version // empty' "$SCRIPT_DIR/../package.json" 2>/dev/null \
|| grep '"version"' "$SCRIPT_DIR/../package.json" 2>/dev/null \
| grep -o '[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*' | head -1 \
|| true)
-[[ -n "${_pkg_version:-}" ]] && VERSION="$_pkg_version"
+[[ "${_pkg_version:-}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] && VERSION="$_pkg_version"📝 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.
| # Read version from package.json (fall back to script VERSION if unavailable) | |
| _pkg_version=$(jq -r '.version' "$SCRIPT_DIR/../package.json" 2>/dev/null \ | |
| || grep '"version"' "$SCRIPT_DIR/../package.json" 2>/dev/null \ | |
| | grep -o '[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*' | head -1 \ | |
| || true) | |
| [[ -n "${_pkg_version:-}" ]] && VERSION="$_pkg_version" | |
| # Read version from package.json (fall back to script VERSION if unavailable) | |
| _pkg_version=$(jq -er '.version // empty' "$SCRIPT_DIR/../package.json" 2>/dev/null \ | |
| || grep '"version"' "$SCRIPT_DIR/../package.json" 2>/dev/null \ | |
| | grep -o '[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*' | head -1 \ | |
| || true) | |
| [[ "${_pkg_version:-}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] && VERSION="$_pkg_version" |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/sw-hello.sh` around lines 14 - 19, The current assignment
unconditionally sets VERSION from _pkg_version when non-empty, allowing values
like "null", "latest", or "v3.2.4"; validate the parsed value against a strict
semver regex before overriding the fallback. After obtaining _pkg_version (from
jq/grep), test it with a POSIX/Bash-compatible regex matching X.Y.Z (e.g.
^[0-9]+\.[0-9]+\.[0-9]+$) and only assign VERSION="$_pkg_version" when that
check passes; otherwise leave the existing VERSION untouched. Ensure you
reference the existing variables and commands (_pkg_version, VERSION, jq -r
'.version') so the change slots into the same block.
Summary
package.jsonat runtime viajqshipwright hello --version(and-v) now outputsShipwright vX.Y.ZVERSIONconstant ifpackage.jsonunavailable--version/-vflags, exit codesTest plan
bash scripts/sw-hello-test.sh→ 7/7 PASSDark Factory Pipeline Proof
🤖 Generated with Claude Code
Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com
Summary by CodeRabbit
New Features
sw-hellocommand to displayShipwright vX.Y.Zinstead of "hello world", with version information automatically sourced from the package configuration.--versionflag now displays the Shipwright version in standardized format.Tests