Skip to content

feat: add version display to sw-hello command#358

Open
sethdford wants to merge 6 commits intomainfrom
feat/add-version-display-to-sw-hello-command
Open

feat: add version display to sw-hello command#358
sethdford wants to merge 6 commits intomainfrom
feat/add-version-display-to-sw-hello-command

Conversation

@sethdford
Copy link
Copy Markdown
Owner

@sethdford sethdford commented Apr 4, 2026

Summary

  • Reads version from package.json at runtime via jq
  • shipwright hello --version (and -v) now outputs Shipwright vX.Y.Z
  • Falls back to hardcoded VERSION constant if package.json unavailable
  • Adds 7 tests covering: version output format, package.json match, --version/-v flags, exit codes

Test plan

  • bash scripts/sw-hello-test.sh → 7/7 PASS
  • Constitutional check: 0 violations
  • Dark factory dark proof: spec.json generated, test holdout partitioned, process rewards logged

Dark Factory Pipeline Proof

  • Intake → spec generated with acceptance criteria
  • Test holdout: 70/30 split created
  • Build: 2 iterations with process reward scoring
  • Constitutional: 0 principle violations
  • All 7 tests pass

🤖 Generated with Claude Code

Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com

Summary by CodeRabbit

  • New Features

    • Updated the sw-hello command to display Shipwright vX.Y.Z instead of "hello world", with version information automatically sourced from the package configuration.
    • The --version flag now displays the Shipwright version in standardized format.
  • Tests

    • Enhanced test coverage to validate version display behavior sourced from package configuration.

Seth Ford and others added 6 commits April 4, 2026 08:34
- 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>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 4, 2026

📝 Walkthrough

Walkthrough

Changes introduce version display functionality to the sw-hello command by reading from package.json, update associated test validations to expect Shipwright vX.Y.Z format, refresh internal cache and state metadata, establish a test holdout configuration manifest, and update synthetic test data to use current timestamps.

Changes

Cohort / File(s) Summary
Version Display Implementation
scripts/sw-hello.sh, scripts/sw-hello-test.sh
Added logic to sw-hello.sh to derive VERSION from package.json (with grep fallback) and output Shipwright v${VERSION} as default/--version behavior. Updated test cases in sw-hello-test.sh to validate the new pattern and added a new test that reads package.json directly to verify version consistency.
Synthetic Test Data
scripts/sw-cost-test.sh
Updated mock events.jsonl and costs.json generation to use current UTC timestamp/epoch instead of hardcoded historical dates, ensuring synthetic data falls within dashboard's 30-day window.
Internal Metadata & State
.claude/intelligence-cache.json, .claude/loop-state.md, .claude/platform-hygiene.json, .claude/recovery-state.json
Updated cache entry timestamp, replaced loop-state tracking from prior ping implementation to new version-display specification, refreshed platform hygiene counts and findings sample, and added new recovery state initialization file.
Test Holdout Configuration
.claude/test-holdout/manifest.json, .gitignore
Added new test holdout manifest file recording metadata and test entries; added .gitignore rule to exclude sealed test artifacts from version control.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 Thump thump! Sweet version dance,
From package.json's hidden glance,
Shipwright sings its numbered way,
While tests ensure all works today,
Caches fresh and holdouts sealed,
Version glory now revealed! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: add version display to sw-hello command' directly and clearly describes the main change—adding version display functionality to the sw-hello command, which is the primary focus of the changeset.
Description check ✅ Passed The description provides a clear summary, lists key changes, documents test results (7/7 PASS), and includes pipeline proof. However, it lacks the structured sections (Test Plan checkbox format, Shipwright Standards Checklist) specified in the repository template, and several standard checklist items are not explicitly addressed.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/add-version-display-to-sw-hello-command

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 | 🟡 Minor

Update help examples to match current behavior.

Line 44 still says Print "hello world", but default execution now prints Shipwright 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

📥 Commits

Reviewing files that changed from the base of the PR and between bc9d7bf and bda7375.

⛔ Files ignored due to path filters (4)
  • .claude/loop-logs/iteration-1.log is excluded by !**/*.log
  • .claude/loop-logs/iteration-2.log is excluded by !**/*.log
  • .claude/loop-logs/tests-iter-1.log is excluded by !**/*.log
  • .claude/loop-logs/tests-iter-2.log is 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
  • .gitignore
  • scripts/sw-cost-test.sh
  • scripts/sw-formal-spec-test.sh
  • scripts/sw-hello-test.sh
  • scripts/sw-hello.sh
  • scripts/sw-mutation-executor-test.sh
  • scripts/sw-process-reward-test.sh
  • scripts/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
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 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}")
PY

Repository: 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",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Suggested change
"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
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Get the file size first
wc -l scripts/sw-hello-test.sh

Repository: 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 -n

Repository: sethdford/shipwright

Length of output: 701


🏁 Script executed:

# Also check line 88 area
sed -n '80,95p' scripts/sw-hello-test.sh | cat -n

Repository: 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.sh

Repository: 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]+$ ]]; then

Also 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.

Comment on lines +14 to +19
# 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"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 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.json

Repository: 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)
EOF

Repository: 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}")
EOF

Repository: 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
EOF

Repository: 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.

Suggested change
# 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant