Compare commits
5
Commits
b40b7ffbc3
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
857b28d351 | ||
|
|
8d656d8e7d | ||
|
|
88a71a026f | ||
|
|
157336d9a8 | ||
|
|
cc7b21b1f3 |
@@ -1,2 +1,3 @@
|
|||||||
# Local Natera config with account IDs, IPs, and internal paths (never commit)
|
# Local Natera config with account IDs, IPs, and internal paths (never commit)
|
||||||
zshrc-natera.private
|
zshrc-natera.private
|
||||||
|
.claude/
|
||||||
|
|||||||
Executable
+152
@@ -0,0 +1,152 @@
|
|||||||
|
#!/bin/zsh
|
||||||
|
#
|
||||||
|
# md2pdf — convert Markdown files to PDF.
|
||||||
|
#
|
||||||
|
# Pipeline: pandoc (md -> self-contained styled HTML) -> Chrome headless
|
||||||
|
# (HTML -> PDF). Uses only pandoc + Google Chrome, both assumed installed.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# md2pdf FILE.md [FILE2.md ...] convert each; PDF written next to source
|
||||||
|
# md2pdf FILE.md -o OUT.pdf explicit output (single input only)
|
||||||
|
# md2pdf FILE.md --css STYLE.css use a custom stylesheet
|
||||||
|
# md2pdf -h | --help
|
||||||
|
#
|
||||||
|
# Output defaults to the same directory and basename as the input
|
||||||
|
# (report.md -> report.pdf). An existing PDF is overwritten.
|
||||||
|
|
||||||
|
emulate -L zsh
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
prog=${0:t}
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<EOF
|
||||||
|
$prog — convert Markdown files to PDF
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
$prog FILE.md [FILE2.md ...] Convert each file; PDF written beside the source
|
||||||
|
$prog FILE.md -o OUT.pdf Write to an explicit path (single input only)
|
||||||
|
$prog FILE.md --css STYLE.css Render with a custom stylesheet
|
||||||
|
$prog -h | --help Show this help
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
- Output defaults to the source directory with a .pdf extension
|
||||||
|
(e.g. notes.md -> notes.pdf) and overwrites any existing PDF.
|
||||||
|
- Requires pandoc and Google Chrome.
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
die() { print -u2 "$prog: $*"; exit 1; }
|
||||||
|
|
||||||
|
# --- locate Google Chrome ---------------------------------------------------
|
||||||
|
find_chrome() {
|
||||||
|
local candidates=(
|
||||||
|
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
|
||||||
|
"$HOME/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
|
||||||
|
"/Applications/Chromium.app/Contents/MacOS/Chromium"
|
||||||
|
)
|
||||||
|
local c
|
||||||
|
for c in $candidates; do
|
||||||
|
[[ -x $c ]] && { print -r -- "$c"; return 0; }
|
||||||
|
done
|
||||||
|
# Fall back to Spotlight lookup by bundle id.
|
||||||
|
local app
|
||||||
|
app=$(mdfind "kMDItemCFBundleIdentifier == 'com.google.Chrome'" 2>/dev/null | head -1)
|
||||||
|
if [[ -n $app && -x "$app/Contents/MacOS/Google Chrome" ]]; then
|
||||||
|
print -r -- "$app/Contents/MacOS/Google Chrome"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- parse arguments --------------------------------------------------------
|
||||||
|
typeset -a inputs
|
||||||
|
local out="" css=""
|
||||||
|
|
||||||
|
while (( $# )); do
|
||||||
|
case $1 in
|
||||||
|
-h|--help) usage; exit 0 ;;
|
||||||
|
-o|--output)
|
||||||
|
[[ -n $2 ]] || die "option $1 requires a path"
|
||||||
|
out=$2; shift 2 ;;
|
||||||
|
--output=*) out=${1#*=}; shift ;;
|
||||||
|
--css)
|
||||||
|
[[ -n $2 ]] || die "option $1 requires a path"
|
||||||
|
css=$2; shift 2 ;;
|
||||||
|
--css=*) css=${1#*=}; shift ;;
|
||||||
|
--) shift; inputs+=("$@"); break ;;
|
||||||
|
-*) die "unknown option: $1 (try --help)" ;;
|
||||||
|
*) inputs+=("$1"); shift ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
(( ${#inputs} )) || { usage; exit 1; }
|
||||||
|
[[ -n $out && ${#inputs} -gt 1 ]] && die "-o/--output cannot be used with multiple input files"
|
||||||
|
|
||||||
|
# --- resolve dependencies once ----------------------------------------------
|
||||||
|
command -v pandoc >/dev/null 2>&1 || die "pandoc not found (install with: brew install pandoc)"
|
||||||
|
|
||||||
|
local chrome
|
||||||
|
chrome=$(find_chrome) || die "Google Chrome not found — install it or use a Chromium build"
|
||||||
|
|
||||||
|
# Default stylesheet lives next to the real script (resolve through symlinks).
|
||||||
|
local default_css="${0:A:h}/md2pdf.css"
|
||||||
|
if [[ -n $css ]]; then
|
||||||
|
[[ -f $css ]] || die "stylesheet not found: $css"
|
||||||
|
elif [[ -f $default_css ]]; then
|
||||||
|
css=$default_css
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- per-run temp workspace, cleaned on exit --------------------------------
|
||||||
|
local workdir
|
||||||
|
workdir=$(mktemp -d "${TMPDIR:-/tmp}/md2pdf.XXXXXX") || die "could not create temp dir"
|
||||||
|
trap 'rm -rf -- "$workdir"' EXIT INT TERM
|
||||||
|
|
||||||
|
# --- convert one file -------------------------------------------------------
|
||||||
|
convert_one() {
|
||||||
|
local in=$1 dest=$2
|
||||||
|
[[ -f $in ]] || { print -u2 "✗ $in: no such file"; return 1; }
|
||||||
|
|
||||||
|
local html="$workdir/${in:t:r}.html"
|
||||||
|
# --standalone gives a full HTML doc (head + linked CSS); an empty title
|
||||||
|
# metadata suppresses pandoc's "please specify a title" warning without
|
||||||
|
# emitting a visible title block above the document's own H1.
|
||||||
|
local -a pandoc_args=(
|
||||||
|
"$in" -f markdown -t html5 --standalone --embed-resources
|
||||||
|
--metadata "title=" -o "$html"
|
||||||
|
)
|
||||||
|
[[ -n $css ]] && pandoc_args+=(--css "$css")
|
||||||
|
|
||||||
|
if ! pandoc $pandoc_args 2>"$workdir/pandoc.err"; then
|
||||||
|
print -u2 "✗ $in: pandoc failed"
|
||||||
|
[[ -s "$workdir/pandoc.err" ]] && print -u2 -- "$(<"$workdir/pandoc.err")"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Chrome's headless PDF mode runs an independent instance and does not
|
||||||
|
# disturb a running Chrome profile, so no --user-data-dir is needed. (An
|
||||||
|
# isolated --user-data-dir actually makes headless Chrome hang on exit.)
|
||||||
|
if ! "$chrome" --headless=new --disable-gpu --no-pdf-header-footer \
|
||||||
|
--print-to-pdf="$dest" "file://$html" >/dev/null 2>"$workdir/chrome.err"; then
|
||||||
|
print -u2 "✗ $in: Chrome failed to render PDF"
|
||||||
|
[[ -s "$workdir/chrome.err" ]] && print -u2 -- "$(<"$workdir/chrome.err")"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
[[ -s $dest ]] || { print -u2 "✗ $in: no PDF produced"; return 1; }
|
||||||
|
print -- "✓ wrote ${dest}"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- drive all inputs -------------------------------------------------------
|
||||||
|
local failures=0 f dest
|
||||||
|
for f in $inputs; do
|
||||||
|
if [[ -n $out ]]; then
|
||||||
|
dest=$out
|
||||||
|
else
|
||||||
|
dest="${f:h}/${f:t:r}.pdf"
|
||||||
|
fi
|
||||||
|
convert_one "$f" "$dest" || (( failures++ ))
|
||||||
|
done
|
||||||
|
|
||||||
|
(( failures == 0 )) || exit 1
|
||||||
Executable
+65
@@ -0,0 +1,65 @@
|
|||||||
|
#!/bin/zsh
|
||||||
|
#
|
||||||
|
# md2pdf-install-quickaction — install (or refresh) the "Convert to PDF"
|
||||||
|
# Finder Quick Action.
|
||||||
|
#
|
||||||
|
# Symlinks the version-controlled workflow bundle from the dotfiles repo into
|
||||||
|
# ~/Library/Services so edits in the repo take effect without reinstalling,
|
||||||
|
# then flushes the Services cache so the menu item appears immediately.
|
||||||
|
#
|
||||||
|
# Usage: md2pdf-install-quickaction [--copy]
|
||||||
|
# --copy Install a copy instead of a symlink (use if you prefer the
|
||||||
|
# installed action to be independent of the repo).
|
||||||
|
|
||||||
|
emulate -L zsh
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
prog=${0:t}
|
||||||
|
die() { print -u2 "$prog: $*"; exit 1; }
|
||||||
|
|
||||||
|
local mode=symlink
|
||||||
|
[[ $1 == --copy ]] && mode=copy
|
||||||
|
[[ -n $1 && $1 != --copy ]] && die "unknown argument: $1 (only --copy is supported)"
|
||||||
|
|
||||||
|
# Source bundle lives next to this script: ../macos/md2pdf.workflow
|
||||||
|
local src="${0:A:h:h}/macos/md2pdf.workflow"
|
||||||
|
[[ -d $src ]] || die "workflow bundle not found at $src"
|
||||||
|
|
||||||
|
local services="$HOME/Library/Services"
|
||||||
|
local dest="$services/md2pdf.workflow"
|
||||||
|
|
||||||
|
mkdir -p "$services" || die "could not create $services"
|
||||||
|
|
||||||
|
# Remove any prior install (symlink or directory) so we start clean.
|
||||||
|
if [[ -L $dest || -e $dest ]]; then
|
||||||
|
rm -rf -- "$dest" || die "could not remove existing $dest"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $mode == symlink ]]; then
|
||||||
|
ln -s "$src" "$dest" || die "could not symlink workflow into $services"
|
||||||
|
print -- "✓ linked $dest -> $src"
|
||||||
|
else
|
||||||
|
cp -R "$src" "$dest" || die "could not copy workflow into $services"
|
||||||
|
print -- "✓ copied workflow to $dest"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Refresh the Services/Quick Actions registry so the item shows up now.
|
||||||
|
local pbs="/System/Library/CoreServices/pbs"
|
||||||
|
if [[ -x $pbs ]]; then
|
||||||
|
"$pbs" -flush 2>/dev/null
|
||||||
|
print -- "✓ flushed Services cache"
|
||||||
|
fi
|
||||||
|
# Nudge the Extensions/Quick Actions registration too (harmless if absent).
|
||||||
|
/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister \
|
||||||
|
-f "$dest" 2>/dev/null
|
||||||
|
|
||||||
|
cat <<EOF
|
||||||
|
|
||||||
|
Installed. To use it:
|
||||||
|
• In Finder, right-click one or more .md files
|
||||||
|
• Choose Quick Actions ▸ Convert to PDF (md2pdf) (may be under a "..." submenu)
|
||||||
|
|
||||||
|
If it doesn't appear immediately, toggle it on in:
|
||||||
|
System Settings ▸ Keyboard ▸ Keyboard Shortcuts… ▸ Services ▸ Files and Folders
|
||||||
|
(or log out and back in to fully refresh the Services menu).
|
||||||
|
EOF
|
||||||
+120
@@ -0,0 +1,120 @@
|
|||||||
|
/* md2pdf default stylesheet — clean, GitHub-flavored, print-friendly. */
|
||||||
|
|
||||||
|
@page {
|
||||||
|
margin: 2cm 1.9cm;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-size: 12pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial,
|
||||||
|
sans-serif;
|
||||||
|
line-height: 1.55;
|
||||||
|
color: #1f2328;
|
||||||
|
max-width: 46em;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 0.5em;
|
||||||
|
-webkit-print-color-adjust: exact;
|
||||||
|
print-color-adjust: exact;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Headings --- */
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
margin-top: 1.4em;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.25;
|
||||||
|
}
|
||||||
|
h1 { font-size: 1.9em; border-bottom: 1px solid #d1d9e0; padding-bottom: 0.3em; }
|
||||||
|
h2 { font-size: 1.45em; border-bottom: 1px solid #d1d9e0; padding-bottom: 0.3em; }
|
||||||
|
h3 { font-size: 1.2em; }
|
||||||
|
h4 { font-size: 1em; }
|
||||||
|
h5 { font-size: 0.9em; }
|
||||||
|
h6 { font-size: 0.85em; color: #59636e; }
|
||||||
|
h1:first-child, h2:first-child, h3:first-child { margin-top: 0; }
|
||||||
|
|
||||||
|
/* Keep a heading with the content that follows it. */
|
||||||
|
h1, h2, h3, h4, h5, h6 { break-after: avoid; }
|
||||||
|
|
||||||
|
/* --- Body elements --- */
|
||||||
|
p, ul, ol, dl, table, pre, blockquote { margin-top: 0; margin-bottom: 1em; }
|
||||||
|
|
||||||
|
a { color: #0969da; text-decoration: none; }
|
||||||
|
a:hover { text-decoration: underline; }
|
||||||
|
|
||||||
|
strong { font-weight: 600; }
|
||||||
|
|
||||||
|
ul, ol { padding-left: 2em; }
|
||||||
|
li + li { margin-top: 0.25em; }
|
||||||
|
li > ul, li > ol { margin-top: 0.25em; margin-bottom: 0; }
|
||||||
|
|
||||||
|
/* --- Code --- */
|
||||||
|
code, pre, kbd, samp {
|
||||||
|
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
|
||||||
|
"Liberation Mono", monospace;
|
||||||
|
font-size: 0.88em;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
background-color: rgba(129, 139, 152, 0.15);
|
||||||
|
padding: 0.2em 0.4em;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
background-color: #f6f8fa;
|
||||||
|
border: 1px solid #d1d9e0;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 1em;
|
||||||
|
overflow: auto;
|
||||||
|
line-height: 1.45;
|
||||||
|
break-inside: avoid;
|
||||||
|
}
|
||||||
|
pre code {
|
||||||
|
background: transparent;
|
||||||
|
padding: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
font-size: 100%;
|
||||||
|
white-space: pre;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Blockquotes --- */
|
||||||
|
blockquote {
|
||||||
|
margin-left: 0;
|
||||||
|
padding: 0 1em;
|
||||||
|
color: #59636e;
|
||||||
|
border-left: 0.25em solid #d1d9e0;
|
||||||
|
}
|
||||||
|
blockquote > :last-child { margin-bottom: 0; }
|
||||||
|
|
||||||
|
/* --- Tables --- */
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
width: 100%;
|
||||||
|
display: block;
|
||||||
|
overflow: auto;
|
||||||
|
break-inside: avoid;
|
||||||
|
}
|
||||||
|
th, td {
|
||||||
|
border: 1px solid #d1d9e0;
|
||||||
|
padding: 0.5em 0.85em;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
th { background-color: #f6f8fa; font-weight: 600; }
|
||||||
|
tr:nth-child(2n) td { background-color: #f6f8fa; }
|
||||||
|
|
||||||
|
/* --- Rules & images --- */
|
||||||
|
hr {
|
||||||
|
height: 1px;
|
||||||
|
border: 0;
|
||||||
|
background-color: #d1d9e0;
|
||||||
|
margin: 1.8em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
img { max-width: 100%; box-sizing: border-box; }
|
||||||
|
|
||||||
|
/* pandoc wraps the --metadata title in a header; keep it unobtrusive. */
|
||||||
|
header#title-block-header h1.title { margin-bottom: 0.8em; }
|
||||||
@@ -1,2 +1,4 @@
|
|||||||
*~
|
*~
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
|
**/.claude/settings.local.json
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>NSServices</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>NSMenuItem</key>
|
||||||
|
<dict>
|
||||||
|
<key>default</key>
|
||||||
|
<string>Convert to PDF (md2pdf)</string>
|
||||||
|
</dict>
|
||||||
|
<key>NSMessage</key>
|
||||||
|
<string>runWorkflowAsService</string>
|
||||||
|
<key>NSRequiredContext</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSApplicationIdentifier</key>
|
||||||
|
<string>com.apple.finder</string>
|
||||||
|
</dict>
|
||||||
|
<key>NSSendFileTypes</key>
|
||||||
|
<array>
|
||||||
|
<string>net.daringfireball.markdown</string>
|
||||||
|
<string>public.plain-text</string>
|
||||||
|
</array>
|
||||||
|
<key>NSSendTypes</key>
|
||||||
|
<array>
|
||||||
|
<string>NSFilenamesPboardType</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,222 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>AMApplicationBuild</key>
|
||||||
|
<string>521</string>
|
||||||
|
<key>AMApplicationVersion</key>
|
||||||
|
<string>2.10</string>
|
||||||
|
<key>AMDocumentVersion</key>
|
||||||
|
<string>2</string>
|
||||||
|
<key>actions</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>action</key>
|
||||||
|
<dict>
|
||||||
|
<key>AMAccepts</key>
|
||||||
|
<dict>
|
||||||
|
<key>Container</key>
|
||||||
|
<string>List</string>
|
||||||
|
<key>Optional</key>
|
||||||
|
<true/>
|
||||||
|
<key>Types</key>
|
||||||
|
<array>
|
||||||
|
<string>com.apple.cocoa.string</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
<key>AMActionVersion</key>
|
||||||
|
<string>2.0.3</string>
|
||||||
|
<key>AMApplication</key>
|
||||||
|
<array>
|
||||||
|
<string>Automator</string>
|
||||||
|
</array>
|
||||||
|
<key>AMParameterProperties</key>
|
||||||
|
<dict>
|
||||||
|
<key>COMMAND_STRING</key>
|
||||||
|
<dict/>
|
||||||
|
<key>CheckedForUserDefaultShell</key>
|
||||||
|
<dict/>
|
||||||
|
<key>inputMethod</key>
|
||||||
|
<dict/>
|
||||||
|
<key>shell</key>
|
||||||
|
<dict/>
|
||||||
|
<key>source</key>
|
||||||
|
<dict/>
|
||||||
|
</dict>
|
||||||
|
<key>AMProvides</key>
|
||||||
|
<dict>
|
||||||
|
<key>Container</key>
|
||||||
|
<string>List</string>
|
||||||
|
<key>Types</key>
|
||||||
|
<array>
|
||||||
|
<string>com.apple.cocoa.string</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
<key>ActionBundlePath</key>
|
||||||
|
<string>/System/Library/Automator/Run Shell Script.action</string>
|
||||||
|
<key>ActionName</key>
|
||||||
|
<string>Run Shell Script</string>
|
||||||
|
<key>ActionParameters</key>
|
||||||
|
<dict>
|
||||||
|
<key>COMMAND_STRING</key>
|
||||||
|
<string># md2pdf Quick Action — convert each selected Markdown file to PDF.
|
||||||
|
# Files arrive as arguments ("$@") because inputMethod is set to 1.
|
||||||
|
tool="$HOME/bin/md2pdf"
|
||||||
|
[ -x "$tool" ] || tool="$HOME/dotfiles/bin/md2pdf"
|
||||||
|
|
||||||
|
ok=0
|
||||||
|
fail=0
|
||||||
|
for f in "$@"; do
|
||||||
|
case "$f" in
|
||||||
|
*.md|*.markdown|*.mdown|*.mkd)
|
||||||
|
if "$tool" "$f"; then
|
||||||
|
ok=$((ok + 1))
|
||||||
|
else
|
||||||
|
fail=$((fail + 1))
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
# Skip anything that isn't Markdown.
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$fail" -gt 0 ]; then
|
||||||
|
osascript -e "display notification \"$ok converted, $fail failed\" with title \"md2pdf\" sound name \"Basso\""
|
||||||
|
elif [ "$ok" -gt 0 ]; then
|
||||||
|
noun="files"
|
||||||
|
if [ "$ok" -eq 1 ]; then noun="file"; fi
|
||||||
|
osascript -e "display notification \"$ok $noun converted to PDF\" with title \"md2pdf\""
|
||||||
|
fi
|
||||||
|
</string>
|
||||||
|
<key>CheckedForUserDefaultShell</key>
|
||||||
|
<true/>
|
||||||
|
<key>inputMethod</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>shell</key>
|
||||||
|
<string>/bin/zsh</string>
|
||||||
|
<key>source</key>
|
||||||
|
<string></string>
|
||||||
|
</dict>
|
||||||
|
<key>BundleIdentifier</key>
|
||||||
|
<string>com.apple.RunShellScript</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>2.0.3</string>
|
||||||
|
<key>CanShowSelectedItemsWhenRun</key>
|
||||||
|
<false/>
|
||||||
|
<key>CanShowWhenRun</key>
|
||||||
|
<true/>
|
||||||
|
<key>Category</key>
|
||||||
|
<array>
|
||||||
|
<string>AMCategoryUtilities</string>
|
||||||
|
</array>
|
||||||
|
<key>Class Name</key>
|
||||||
|
<string>RunShellScriptAction</string>
|
||||||
|
<key>InputUUID</key>
|
||||||
|
<string>2A1B3C4D-5E6F-4A7B-8C9D-0E1F2A3B4C5D</string>
|
||||||
|
<key>Keywords</key>
|
||||||
|
<array>
|
||||||
|
<string>Shell</string>
|
||||||
|
<string>Script</string>
|
||||||
|
<string>Command</string>
|
||||||
|
<string>Run</string>
|
||||||
|
<string>Unix</string>
|
||||||
|
</array>
|
||||||
|
<key>OutputUUID</key>
|
||||||
|
<string>3B2C4D5E-6F7A-4B8C-9D0E-1F2A3B4C5D6E</string>
|
||||||
|
<key>UUID</key>
|
||||||
|
<string>1A2B3C4D-5E6F-4A7B-8C9D-0E1F2A3B4C5E</string>
|
||||||
|
<key>arguments</key>
|
||||||
|
<dict>
|
||||||
|
<key>0</key>
|
||||||
|
<dict>
|
||||||
|
<key>default value</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>name</key>
|
||||||
|
<string>inputMethod</string>
|
||||||
|
<key>required</key>
|
||||||
|
<string>0</string>
|
||||||
|
<key>type</key>
|
||||||
|
<string>0</string>
|
||||||
|
<key>uuid</key>
|
||||||
|
<string>0</string>
|
||||||
|
</dict>
|
||||||
|
<key>1</key>
|
||||||
|
<dict>
|
||||||
|
<key>default value</key>
|
||||||
|
<false/>
|
||||||
|
<key>name</key>
|
||||||
|
<string>CheckedForUserDefaultShell</string>
|
||||||
|
<key>required</key>
|
||||||
|
<string>0</string>
|
||||||
|
<key>type</key>
|
||||||
|
<string>0</string>
|
||||||
|
<key>uuid</key>
|
||||||
|
<string>1</string>
|
||||||
|
</dict>
|
||||||
|
<key>2</key>
|
||||||
|
<dict>
|
||||||
|
<key>default value</key>
|
||||||
|
<string></string>
|
||||||
|
<key>name</key>
|
||||||
|
<string>source</string>
|
||||||
|
<key>required</key>
|
||||||
|
<string>0</string>
|
||||||
|
<key>type</key>
|
||||||
|
<string>0</string>
|
||||||
|
<key>uuid</key>
|
||||||
|
<string>2</string>
|
||||||
|
</dict>
|
||||||
|
<key>3</key>
|
||||||
|
<dict>
|
||||||
|
<key>default value</key>
|
||||||
|
<string>/bin/sh</string>
|
||||||
|
<key>name</key>
|
||||||
|
<string>shell</string>
|
||||||
|
<key>required</key>
|
||||||
|
<string>0</string>
|
||||||
|
<key>type</key>
|
||||||
|
<string>0</string>
|
||||||
|
<key>uuid</key>
|
||||||
|
<string>3</string>
|
||||||
|
</dict>
|
||||||
|
<key>4</key>
|
||||||
|
<dict>
|
||||||
|
<key>default value</key>
|
||||||
|
<string></string>
|
||||||
|
<key>name</key>
|
||||||
|
<string>COMMAND_STRING</string>
|
||||||
|
<key>required</key>
|
||||||
|
<string>0</string>
|
||||||
|
<key>type</key>
|
||||||
|
<string>0</string>
|
||||||
|
<key>uuid</key>
|
||||||
|
<string>4</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<key>isViewVisible</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>location</key>
|
||||||
|
<string>309.000000:253.000000</string>
|
||||||
|
<key>nibPath</key>
|
||||||
|
<string>/System/Library/Automator/Run Shell Script.action/Contents/Resources/Base.lproj/main.nib</string>
|
||||||
|
</dict>
|
||||||
|
<key>isViewVisible</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>connectors</key>
|
||||||
|
<dict/>
|
||||||
|
<key>workflowMetaData</key>
|
||||||
|
<dict>
|
||||||
|
<key>serviceInputTypeIdentifier</key>
|
||||||
|
<string>com.apple.Automator.fileSystemObject</string>
|
||||||
|
<key>serviceOutputTypeIdentifier</key>
|
||||||
|
<string>com.apple.Automator.nothing</string>
|
||||||
|
<key>serviceProcessesInput</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>workflowTypeIdentifier</key>
|
||||||
|
<string>com.apple.Automator.servicesMenu</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -21,5 +21,8 @@ fi
|
|||||||
if command -v pyenv 1>/dev/null 2>&1; then
|
if command -v pyenv 1>/dev/null 2>&1; then
|
||||||
export PYENV_ROOT="$HOME/.pyenv"
|
export PYENV_ROOT="$HOME/.pyenv"
|
||||||
export PATH="$PYENV_ROOT/bin:$PATH"
|
export PATH="$PYENV_ROOT/bin:$PATH"
|
||||||
|
# Clear a stale rehash temp file left behind by a killed pyenv-rehash.
|
||||||
|
# A real rehash completes in well under a second; anything older than a minute is stale.
|
||||||
|
find "$PYENV_ROOT/shims/.pyenv-shim" -mmin +1 -delete 2>/dev/null
|
||||||
eval "$(pyenv init -)"
|
eval "$(pyenv init -)"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -509,7 +509,7 @@ alias topcmd="history | awk '{print \$2}' | sort | uniq -c | sort -rn | head -10
|
|||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
||||||
alias code='cursor .'
|
alias code='cursor .'
|
||||||
alias reload='source ~/.zshrc && echo "✅ Shell configuration reloaded"'
|
alias reload='exec zsh'
|
||||||
alias editrc='cursor ~/dotfiles/zshrc'
|
alias editrc='cursor ~/dotfiles/zshrc'
|
||||||
alias editdev='cursor ~/dotfiles/zshrc-dev'
|
alias editdev='cursor ~/dotfiles/zshrc-dev'
|
||||||
alias c='clear'
|
alias c='clear'
|
||||||
|
|||||||
+60
-9
@@ -7,18 +7,36 @@ export PATH="$HOME/.local/bin:$PATH"
|
|||||||
|
|
||||||
# Claude Code (Bedrock)
|
# Claude Code (Bedrock)
|
||||||
export CLAUDE_CODE_USE_BEDROCK=1
|
export CLAUDE_CODE_USE_BEDROCK=1
|
||||||
export AWS_REGION=us-west-2
|
# export AWS_REGION=us-west-2
|
||||||
export ANTHROPIC_SMALL_FAST_MODEL_AWS_REGION=us-west-2
|
export ANTHROPIC_SMALL_FAST_MODEL_AWS_REGION=us-west-2
|
||||||
export ANTHROPIC_MODEL=global.anthropic.claude-opus-4-6-v1
|
|
||||||
export ANTHROPIC_SMALL_FAST_MODEL=global.anthropic.claude-haiku-4-5-20251001-v1:0
|
export ANTHROPIC_SMALL_FAST_MODEL=global.anthropic.claude-haiku-4-5-20251001-v1:0
|
||||||
export CLAUDE_CODE_ENABLE_TELEMETRY=1
|
export CLAUDE_CODE_ENABLE_TELEMETRY=1
|
||||||
export CLAUDE_CODE_MAX_OUTPUT_TOKENS=32000
|
|
||||||
|
# Primary model (what Claude Code uses by default)
|
||||||
|
export ANTHROPIC_MODEL='us.anthropic.claude-opus-5'
|
||||||
|
# Family pins (used by /model and for subagents)
|
||||||
|
export ANTHROPIC_DEFAULT_OPUS_MODEL='us.anthropic.claude-opus-5[1m]'
|
||||||
|
export ANTHROPIC_DEFAULT_SONNET_MODEL='us.anthropic.claude-sonnet-5[1m]'
|
||||||
|
export ANTHROPIC_DEFAULT_HAIKU_MODEL='us.anthropic.claude-haiku-4-5-20251001-v1:0'
|
||||||
|
|
||||||
|
export CLAUDE_CODE_USE_MANTLE=1
|
||||||
|
export AWS_REGION=us-east-1
|
||||||
|
|
||||||
|
# Raise the default 64000 output cap (helpful on Opus 4.7)
|
||||||
|
export CLAUDE_CODE_MAX_OUTPUT_TOKENS=128000
|
||||||
|
# Request a 1-hour prompt cache TTL instead of the 5-minute default.
|
||||||
|
# Good for long-lived sessions; writes are billed at a higher rate.
|
||||||
|
#export ENABLE_PROMPT_CACHING_1H=1
|
||||||
|
|
||||||
# OpenTelemetry (Natera staging)
|
# OpenTelemetry (Natera staging)
|
||||||
export OTEL_METRICS_EXPORTER=otlp
|
export OTEL_METRICS_EXPORTER=otlp
|
||||||
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
|
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
|
||||||
export OTEL_RESOURCE_ATTRIBUTES="user.email=$USER@natera.com"
|
export OTEL_RESOURCE_ATTRIBUTES="user.email=$USER@natera.com"
|
||||||
|
|
||||||
|
# Jellyfish AI reporting — endpoint + bearer token live in zshrc-natera.private
|
||||||
|
# (sourced last, so its protocol/endpoint overrides take effect). See the
|
||||||
|
# Jellyfish block in zshrc-natera.private.example for the shape.
|
||||||
|
|
||||||
# Project-specific aliases
|
# Project-specific aliases
|
||||||
alias workbench='cd ~/work/rosalis'
|
alias workbench='cd ~/work/rosalis'
|
||||||
alias core='cd ~/work/yalp-core'
|
alias core='cd ~/work/yalp-core'
|
||||||
@@ -37,13 +55,46 @@ alias yalp:api='core && make local_run_api'
|
|||||||
alias yalp:psql='core && docker exec -it yalp-core-postgres psql -U postgres -d yalp_core_db'
|
alias yalp:psql='core && docker exec -it yalp-core-postgres psql -U postgres -d yalp_core_db'
|
||||||
alias yalp:start='core && make local_db_start && make local_run_api'
|
alias yalp:start='core && make local_db_start && make local_run_api'
|
||||||
|
|
||||||
# Natera repos use master as primary; rewrite "git switch main" to "git switch master"
|
# YALP Core perf testing — close all boring tunnels, kill stale API, start perf DB + API (backgrounded)
|
||||||
git() {
|
alias yalp:perf='core && boring list 2>/dev/null | tail -n +2 | awk "{print \$2}" | while read -r t; do boring close "$t" 2>/dev/null; done; lsof -ti :9258 | xargs kill 2>/dev/null; make local_container_db && make local_perf_db_start && make local_perf_run_api &>/dev/null & echo "YALP Core perf API starting on :9258 (pid $!)"'
|
||||||
if [[ "$1" == "switch" && "${2:-}" == "main" ]]; then
|
alias yalp:perf:stop='lsof -ti :9258 | xargs kill 2>/dev/null && echo "YALP Core perf API stopped" || echo "No process on :9258"'
|
||||||
command git switch master "${@:3}"
|
|
||||||
else
|
# Collect knowledgebase docs from all ~/work projects into ~/work/knowledgebases.
|
||||||
command git "$@"
|
# Shareable with teammates — no site-specific dependencies.
|
||||||
|
kb-sync() {
|
||||||
|
local src_base="$HOME/work"
|
||||||
|
local dest_base="$HOME/work/knowledgebases"
|
||||||
|
mkdir -p "$dest_base"
|
||||||
|
for project_dir in "$src_base"/*/; do
|
||||||
|
local project="${project_dir%/}"
|
||||||
|
project="${project##*/}"
|
||||||
|
local kb_dir=""
|
||||||
|
for candidate in "$project_dir"docs/knowledgebase "$project_dir"documentation/knowledgebase; do
|
||||||
|
[[ -d "$candidate" ]] && kb_dir="$candidate" && break
|
||||||
|
done
|
||||||
|
[[ -z "$kb_dir" ]] && continue
|
||||||
|
echo "Syncing $project..."
|
||||||
|
rm -rf "$dest_base/$project"
|
||||||
|
cp -R "$kb_dir" "$dest_base/$project"
|
||||||
|
done
|
||||||
|
echo "Done. Knowledgebases collected in $dest_base"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Build and publish the browsable knowledgebase site to GitLab Pages.
|
||||||
|
# Runs kb-sync first, then updates the site repo.
|
||||||
|
kb-publish() {
|
||||||
|
local dest_base="$HOME/work/knowledgebases"
|
||||||
|
local site_repo="$HOME/work/yalp-knowledgebases"
|
||||||
|
kb-sync
|
||||||
|
if [[ ! -d "$site_repo" ]]; then
|
||||||
|
echo "Site repo not found at $site_repo — clone ewagoner/yalp-knowledgebases first."
|
||||||
|
return 1
|
||||||
fi
|
fi
|
||||||
|
echo "Updating site repo..."
|
||||||
|
rsync -av --delete "$dest_base/" "$site_repo/knowledgebases/"
|
||||||
|
python3 "$site_repo/scripts/build_site.py"
|
||||||
|
(cd "$site_repo" && git add -A && git commit -m "sync: $(date +%Y-%m-%d)" && git push)
|
||||||
|
echo "Site published."
|
||||||
}
|
}
|
||||||
|
|
||||||
# Sensitive values (AWS profile, OTEL endpoint, cert paths, refresh alias) are in zshrc-natera.private
|
# Sensitive values (AWS profile, OTEL endpoint, cert paths, refresh alias) are in zshrc-natera.private
|
||||||
|
|||||||
@@ -8,6 +8,12 @@ export AWS_PROFILE="YOUR_AWS_PROFILE_NAME"
|
|||||||
# OpenTelemetry endpoint (internal)
|
# OpenTelemetry endpoint (internal)
|
||||||
export OTEL_EXPORTER_OTLP_ENDPOINT="http://YOUR_OTEL_ENDPOINT:4318"
|
export OTEL_EXPORTER_OTLP_ENDPOINT="http://YOUR_OTEL_ENDPOINT:4318"
|
||||||
|
|
||||||
|
# Jellyfish AI reporting (contains a live bearer token — keep out of git).
|
||||||
|
# Sourced after zshrc-natera, so these override the protocol/endpoint set there.
|
||||||
|
export OTEL_EXPORTER_OTLP_PROTOCOL=http/json
|
||||||
|
export OTEL_EXPORTER_OTLP_METRICS_ENDPOINT="https://app.jellyfish.co/ingest-webhooks/YOUR_JELLYFISH_PATH"
|
||||||
|
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer YOUR_JELLYFISH_TOKEN"
|
||||||
|
|
||||||
# Netskope TLS cert bundle path (Natera corp)
|
# Netskope TLS cert bundle path (Natera corp)
|
||||||
export AWS_CA_BUNDLE=~/.aws/nskp_config/netskope-cert-bundle.pem
|
export AWS_CA_BUNDLE=~/.aws/nskp_config/netskope-cert-bundle.pem
|
||||||
export REQUESTS_CA_BUNDLE=~/.aws/nskp_config/netskope-cert-bundle.pem
|
export REQUESTS_CA_BUNDLE=~/.aws/nskp_config/netskope-cert-bundle.pem
|
||||||
|
|||||||
Reference in New Issue
Block a user