Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
65df04a
Copy refresh.sh to .erb/scripts/ (preserve root for workflow compat) …
rolfheij-sil Mar 9, 2026
65a695e
Add papi-live.fixture.ts for runtime command verification (#2114)
rolfheij-sil Mar 24, 2026
13d7782
Add Chromatic CI + fix Storybook production build (#2168)
rolfheij-sil Apr 15, 2026
48f2f99
Add secret prevention: gitleaks pre-commit hook and .gitignore expans…
martijnbar Apr 20, 2026
3f46632
docs(readme): Add gitleaks to Developer Install prerequisites (#2217)
rolfheij-sil Apr 22, 2026
237a911
workflow: Fix ScrTextCollection pollution from empty-path DummyScrTex…
rolfheij-sil Apr 22, 2026
6fb0259
ci(hooks): Typecheck only affected workspaces in ai-branch pre-commit…
rolfheij-sil Apr 22, 2026
567cb8f
fix: Register JsonStringEnumConverter in SerializationOptions (#2215)
rolfheij-sil Apr 22, 2026
45e9b88
ci(chromatic): Fix Chromatic workflow — CLI v16 flag conflict + proje…
rolfheij-sil Apr 24, 2026
7cb212b
workflow: Allow multi-line .chromatic-story-filter (#2237)
rolfheij-sil May 1, 2026
9d6a2c8
[P3][backend+ui] markers-checklist: Backend + UI implementation (C# d…
rolfheij-sil May 6, 2026
5f23353
[P3][backend] manage-books: Port Manage Books from PT9 (C# data provi…
rolfheij-sil May 7, 2026
af5a3ea
workflow: e2e cdp.fixture enforces 1920x1080 viewport + screenshot di…
rolfheij-sil May 11, 2026
2e7b5ae
chore(pbr): exclude design-principles.mdx from remark --output (#2270)
tombogle May 15, 2026
077ddf3
chore(manage-books): Remove orphan GreekEstherTemplatePicker web view…
rolfheij-sil May 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"isRoot": true,
"tools": {
"csharpier": {
"version": "0.27.3",
"version": "0.29.2",
"commands": ["dotnet-csharpier"]
}
}
Expand Down
54 changes: 54 additions & 0 deletions .erb/scripts/refresh.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/bin/bash
# Quick app refresh - stops, rebuilds, and restarts Platform.Bible with CDP enabled
# This is a FAST operation (~30s). Agents should run this freely without optimization concerns.
set -e
cd "$(dirname "$0")/../.."

echo "Stopping app..."
npm stop 2>/dev/null || true

echo "Building..."
npm run build


# Safety net: Claude Code / VS Code set this, which makes Electron act as plain Node.js
unset ELECTRON_RUN_AS_NODE

# Start with CDP enabled. On Linux, use xvfb for headless operation.
# On macOS (and other platforms without xvfb), show the GUI window.
if command -v xvfb-run >/dev/null 2>&1; then
echo "Starting with CDP enabled (headless via xvfb)..."
xvfb-run --auto-servernum --server-args="-screen 0 1920x1080x24" \
env MAIN_ARGS="--remote-debugging-port=9223 --maximize" npm start &
else
echo "Starting with CDP enabled (visible window — xvfb not available)..."
env MAIN_ARGS="--remote-debugging-port=9223 --maximize" npm start &
fi
APP_PID=$!

# Kill the background process on failure/exit
cleanup() {
if kill -0 "$APP_PID" 2>/dev/null; then
echo "Cleaning up background process $APP_PID..."
kill "$APP_PID" 2>/dev/null || true
fi
}
trap cleanup EXIT

# Wait for all ports (max 3 minutes)
echo "Waiting for app to be ready..."
for i in {1..36}; do
RENDERER=$(curl -s -m 2 http://localhost:1212 > /dev/null 2>&1 && echo "UP" || echo "DOWN")
WS=$(curl -s -m 2 http://localhost:8876 > /dev/null 2>&1 && echo "UP" || echo "DOWN")
CDP=$(curl -s -m 2 http://localhost:9223/json > /dev/null 2>&1 && echo "UP" || echo "DOWN")
if [ "$RENDERER" = "UP" ] && [ "$WS" = "UP" ] && [ "$CDP" = "UP" ]; then
echo "✓ App ready (Renderer: $RENDERER, WebSocket: $WS, CDP: $CDP)"
# Disable the trap — app should keep running after successful startup
trap - EXIT
exit 0
fi
echo " Waiting... (Renderer: $RENDERER, WebSocket: $WS, CDP: $CDP)"
sleep 5
done
echo "✗ Timeout waiting for app"
exit 1
83 changes: 83 additions & 0 deletions .github/workflows/chromatic.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
name: Chromatic

on:
pull_request:
types: [labeled, synchronize]
branches: [ai/main]

jobs:
chromatic:
name: Publish to Chromatic
# Only run when the 'storybook-review' label is present
if: contains(github.event.pull_request.labels.*.name, 'storybook-review')
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Read package.json
id: package_json
uses: zoexx/github-action-json-file-properties@1.0.6
with:
file_path: 'package.json'

- name: Checkout scripture-editors
uses: actions/checkout@v4
with:
repository: eten-tech-foundation/scripture-editors
ref: platform-yalc
path: dev-packages/scripture-editors

- name: Install Volta and toolchain
uses: volta-cli/action@v4

- name: Install scripture-editors dependencies
env:
VOLTA_FEATURE_PNPM: '1'
run: |
cd dev-packages/scripture-editors
pnpm install

- name: Install Node.js and NPM
uses: actions/setup-node@v4
with:
node-version: ${{ fromJson(steps.package_json.outputs.volta).node }}
cache: npm

- name: Install packages and build
run: |
npm ci
npm run build

- name: Read story filter
id: story_filter
run: |
# .chromatic-story-filter may contain one glob per line for
# readability; join lines with spaces so the value stays a valid
# single-line GITHUB_OUTPUT entry. Chromatic's --only-story-files
# is variadic and accepts whitespace-separated filespecs, so the
# joined string passes through chromaui/action correctly.
if [ -f .chromatic-story-filter ]; then
glob=$(tr '\n' ' ' < .chromatic-story-filter | sed -e 's/ */ /g' -e 's/^ //' -e 's/ $//')
else
glob="extensions/src/**/*.stories.tsx"
fi
echo "glob=$glob" >> "$GITHUB_OUTPUT"

- name: Run Chromatic
uses: chromaui/action@v16
with:
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN_10_POWER }}
buildScriptName: storybook:build
# onlyStoryFiles and onlyChanged are mutually exclusive in Chromatic CLI v16.
# The story filter (from .chromatic-story-filter or the default) is what scopes
# the review to the relevant feature; onlyChanged was redundant and caused the
# CLI to reject the flags combination.
onlyStoryFiles: ${{ steps.story_filter.outputs.glob }}
exitZeroOnChanges: true
env:
CHROMATIC_BRANCH: ${{ github.event.pull_request.head.ref || github.ref_name }}
CHROMATIC_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
CHROMATIC_SLUG: ${{ github.repository }}
19 changes: 19 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,22 @@ CLAUDE.md
CLAUDE.md.backup

.review

# Secrets & credentials
.env
.env.*
.env.local
.env.*.local
secrets/
*.pem
*.key
*.pfx
*.p12
credentials.json
service-account.json

# Brainstorming session artifacts (visual companion)
.superpowers/

# Phase 3 UI evidence proofs (canonical location is ai-prompts/.context/features/{feature}/proofs/)
/proofs/
72 changes: 70 additions & 2 deletions .husky/lib/ai-hooks.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,78 @@ validate_branch_name() {
fi
}

# Determine which npm workspaces need typechecking based on staged TS files.
#
# Rules (B1 hybrid — see .claude/rules/* or commit history for rationale):
# 1. Staged files in lib/*, extensions/, extensions/src/* → include their workspace
# 2. If ANY lib/* workspace is included → also include ALL extensions/* workspaces
# (extensions depend on lib, so lib changes can break extension consumers)
# 3. Staged TS files outside any npm workspace (e.g., e2e-tests/, scripts/) are
# covered by `npm run typecheck:core`; they add no workspace to the list.
#
# Prints space-separated list of `--workspace=<path>` flags, or empty if nothing applies.
compute_affected_workspaces() {
local files changed_workspaces lib_changed=0
files=$(get_staged_files | grep -E '\.(ts|tsx)$' || true)
if [ -z "$files" ]; then
return 0
fi

# Collect workspaces that contain staged TS files.
# NF guards ensure the path has at least one segment under the parent
# directory (e.g. lib/foo/bar.ts → "lib/foo", but lib/foo.ts at the
# top level — which isn't a valid workspace — falls through to the
# `extensions` fallback or is skipped).
changed_workspaces=$(
echo "$files" | awk -F/ '
/^lib\// && NF >= 3 { print "lib/" $2; next }
/^extensions\/src\// && NF >= 4 { print "extensions/src/" $3; next }
/^extensions\// && NF >= 2 { print "extensions"; next }
' | sort -u
)

if echo "$changed_workspaces" | grep -q '^lib/'; then
lib_changed=1
fi

# If any lib/* workspace changed, expand to include all extensions/* workspaces
# (extensions consume lib via workspace symlinks — consumer types can break).
if [ "$lib_changed" = "1" ]; then
local all_extensions
all_extensions=$(ls -d extensions/src/*/ 2>/dev/null | sed 's|/$||')
changed_workspaces=$(printf '%s\n%s\n' "$changed_workspaces" "$all_extensions" | sort -u)
fi

# Emit as --workspace=<path> flags.
echo "$changed_workspaces" | while IFS= read -r ws; do
[ -n "$ws" ] && printf -- '--workspace=%s ' "$ws"
done
}

run_typecheck() {
echo "Running TypeScript type checking..."
if ! npm run typecheck 2>&1; then
error_msg "AI-001" "TypeScript type checking failed" "Run 'npm run typecheck' to see errors"

# Always run root typecheck (covers src/main, src/renderer, src/extension-host,
# src/shared, and any non-workspace TS like e2e-tests/).
if ! npm run typecheck:core 2>&1; then
error_msg "AI-001" "TypeScript type checking failed (root)" "Run 'npm run typecheck:core' to see errors"
return $AI_EXIT_TYPECHECK
fi

# Scope workspace typecheck to workspaces affected by staged files (B1 hybrid).
local ws_flags
ws_flags=$(compute_affected_workspaces)

if [ -z "$ws_flags" ]; then
echo "No workspace TS files staged, skipping workspace typecheck"
echo "TypeScript type checking passed"
return 0
fi

echo "Typechecking affected workspaces: $ws_flags"
# shellcheck disable=SC2086 # Intentional word-splitting on ws_flags
if ! npm run typecheck $ws_flags --if-present 2>&1; then
error_msg "AI-001" "TypeScript type checking failed (workspace)" "Run 'npm run typecheck' to see errors"
return $AI_EXIT_TYPECHECK
fi
echo "TypeScript type checking passed"
Expand Down
33 changes: 33 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,5 +1,38 @@
#!/usr/bin/env sh

# ============================================
# Secret scanning (ALL branches)
# ============================================
if ! command -v gitleaks >/dev/null 2>&1; then
echo ""
echo "=========================================="
echo "ERROR: gitleaks is not installed"
echo "=========================================="
echo "gitleaks is required to prevent committing secrets to this public repository."
echo ""
echo "Install it:"
echo " macOS: brew install gitleaks"
echo " Windows: winget install Gitleaks.Gitleaks"
echo " Linux: https://github.com/gitleaks/gitleaks/releases"
echo ""
echo "Then retry your commit."
echo "=========================================="
exit 1
fi

echo "Scanning for secrets..."
if ! gitleaks git --pre-commit --staged --no-banner; then
echo ""
echo "=========================================="
echo "SECRET DETECTED — commit blocked"
echo "=========================================="
echo "Remove the secret from your staged files before committing."
echo "See above for the file and line number."
echo "=========================================="
exit 1
fi
echo "No secrets found"

# ============================================
# Standard hooks (ALL branches)
# ============================================
Expand Down
16 changes: 14 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,21 @@ Set up pre-requisites, build, and run:
# 8.0.412 (or similar 8.* version)
```

3. Prerequisites for macOS or Linux (below).
3. Install [`gitleaks`](https://github.com/gitleaks/gitleaks). The pre-commit hook runs `gitleaks` on staged files to block accidental secret commits — without it, every `git commit` fails.

4. Clone, install, build, and run (below).
- **macOS**: `brew install gitleaks`
- **Windows**: `winget install Gitleaks.Gitleaks`
- **Linux**: download a prebuilt binary from the [releases page](https://github.com/gitleaks/gitleaks/releases) and place it on your `PATH` (e.g., `~/.local/bin/gitleaks`), or `sudo apt install gitleaks` on Ubuntu 24.04+.

To verify:

```bash
gitleaks version
```

4. Prerequisites for macOS or Linux (below).

5. Clone, install, build, and run (below).

### Linux Development Pre-requisites

Expand Down
Loading
Loading