Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
79 changes: 51 additions & 28 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,19 @@
# Native Git hook - enable with: git config core.hooksPath .githooks
set -euo pipefail

git diff --cached --check
ignored_staged_paths=(
":(exclude).agents/**"
":(exclude).claude/**"
)

git diff --cached --check -- . "${ignored_staged_paths[@]}"

has_staged_changes() {
if [[ "$#" -eq 0 ]]; then
git diff --cached --quiet -- . "${ignored_staged_paths[@]}" || return 0
return 1
fi

git diff --cached --quiet -- "$@" || return 0
return 1
}
Expand All @@ -13,6 +23,10 @@ if [[ ! -f package.json ]]; then
exit 0
fi

if ! has_staged_changes; then
exit 0
fi

# `npm pkg fix` normalizes package.json (e.g., sorts keys, trims invalid
# fields). Only run it when package.json is already staged, so the hook never
# pulls unrelated working-tree edits into a commit.
Expand All @@ -21,52 +35,61 @@ if has_staged_changes package.json && command -v npm >/dev/null 2>&1; then
git add package.json 2>/dev/null || true
fi

run_script() {
package_script_exists() {
local script_name="$1"
# Check if the script exists in package.json (node - reads from stdin, arg becomes argv[2])
if node - "$script_name" <<'EOF'
node - "$script_name" <<'EOF'
const fs = require("fs");
const pkg = JSON.parse(fs.readFileSync("package.json", "utf8"));
const scripts = pkg.scripts || {};
process.exit(scripts[process.argv[2]] ? 0 : 1);
EOF
then
if command -v pnpm >/dev/null 2>&1; then
pnpm -s run "$script_name"
elif command -v npm >/dev/null 2>&1; then
npm run --silent "$script_name"
else
echo "No package manager available to run script: $script_name" >&2
exit 1
fi
}

run_package_script() {
local script_name="$1"
if command -v pnpm >/dev/null 2>&1; then
pnpm run "$script_name"
elif command -v npm >/dev/null 2>&1; then
npm run "$script_name"
else
# return, not exit: callers redirect this function's output into a log
# they only dump on failure — exit here would abort before the dump,
# leaving the commit to die with no visible error.
echo "No package manager available to run script: $script_name" >&2
return 127
fi
}

script_log=$(mktemp)
trap 'rm -f "$script_log"' EXIT

# Silent on success; on failure, dump captured output and exit non-zero.
run_script() {
local script_name="$1"
if ! package_script_exists "$script_name"; then
return 0
fi
echo "==> $script_name" >&2
if run_package_script "$script_name" > "$script_log" 2>&1; then
return 0
fi
cat "$script_log" >&2
exit 1
}

if [[ -f pnpm-lock.yaml ]] && command -v pnpm >/dev/null 2>&1; then
if has_staged_changes package.json pnpm-lock.yaml pnpm-workspace.yaml; then
# Order matters: dedupe is the cheaper read-only check, so fail fast on
# lockfile bloat before the heavier lockfile-vs-package.json sync check.
pnpm dedupe --check
pnpm dedupe --check --config.confirm-modules-purge=false
pnpm install --frozen-lockfile --ignore-scripts --config.confirm-modules-purge=false
fi
fi

if has_staged_changes; then
run_script "format:check"
fi

run_script "format:check"
run_script "knip"
run_script "typecheck"
run_script "lint"
run_script "fta"

# Run tests quietly - only show output on failure
test_log=$(mktemp)
trap 'rm -f "$test_log"' EXIT

if run_script "test" > "$test_log" 2>&1; then
: # Success, stay silent
else
cat "$test_log" >&2
exit 1
fi
run_script "test"
17 changes: 7 additions & 10 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,8 @@ on:

jobs:
quality-checks:
# Private dev dependencies require NPM_TOKEN, which fork PRs cannot access.
if: ${{ github.event_name != 'pull_request' || !github.event.pull_request.head.repo.fork }}
runs-on: ubuntu-latest
Comment thread
Jercik marked this conversation as resolved.
timeout-minutes: 10
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
steps:
- name: Checkout code
uses: actions/checkout@v6
Expand All @@ -28,9 +24,10 @@ jobs:
node-version-file: "package.json"
cache: "pnpm"

- name: Configure npm.j4k.dev auth
if: ${{ env.NPM_TOKEN != '' }}
run: printf "registry=https://npm.j4k.dev/\n//npm.j4k.dev/:_authToken=%s\n" "${NPM_TOKEN}" > .npmrc
- name: Configure registry auth
run: echo "//npm.j4k.dev/:_authToken=${NODE_AUTH_TOKEN}" > ~/.npmrc
Comment thread
Jercik marked this conversation as resolved.
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

- name: Install dependencies
Comment thread
Jercik marked this conversation as resolved.
run: pnpm install --frozen-lockfile
Comment thread
Jercik marked this conversation as resolved.
Comment thread
Jercik marked this conversation as resolved.
Comment thread
Jercik marked this conversation as resolved.
Comment thread
Jercik marked this conversation as resolved.
Comment thread
Jercik marked this conversation as resolved.
Comment thread
Jercik marked this conversation as resolved.
Comment thread
Jercik marked this conversation as resolved.
Expand All @@ -50,8 +47,8 @@ jobs:
- name: Run fta
run: pnpm fta

- name: Run tests
run: pnpm run test

- name: Run build
run: pnpm build

- name: Run tests
run: pnpm run test
11 changes: 4 additions & 7 deletions .github/workflows/dedupe-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,8 @@ on:

jobs:
dedupe-check:
# Private dev dependencies require NPM_TOKEN, which fork PRs cannot access.
if: ${{ github.event_name != 'pull_request' || !github.event.pull_request.head.repo.fork }}
runs-on: ubuntu-latest
Comment thread
Jercik marked this conversation as resolved.
timeout-minutes: 5
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
steps:
- name: Checkout code
uses: actions/checkout@v6
Expand All @@ -32,9 +28,10 @@ jobs:
node-version-file: "package.json"
cache: "pnpm"

- name: Configure npm.j4k.dev auth
if: ${{ env.NPM_TOKEN != '' }}
run: printf "registry=https://npm.j4k.dev/\n//npm.j4k.dev/:_authToken=%s\n" "${NPM_TOKEN}" > .npmrc
- name: Configure registry auth
run: echo "//npm.j4k.dev/:_authToken=${NODE_AUTH_TOKEN}" > ~/.npmrc
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

- name: Install dependencies
run: pnpm install --frozen-lockfile
Comment thread
Jercik marked this conversation as resolved.
Comment thread
Jercik marked this conversation as resolved.
Expand Down
15 changes: 6 additions & 9 deletions .github/workflows/release-npm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,19 @@ jobs:
uses: actions/setup-node@v6
with:
node-version-file: "package.json"
registry-url: https://registry.npmjs.org
cache: "pnpm"

- name: Configure npm.j4k.dev auth
- name: Configure registry auth (install)
run: echo "//npm.j4k.dev/:_authToken=${NODE_AUTH_TOKEN}" > ~/.npmrc
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
if [ -z "${NPM_TOKEN}" ]; then
echo "NPM_TOKEN is required to install private dependencies." >&2
exit 1
fi
printf "registry=https://npm.j4k.dev/\n//npm.j4k.dev/:_authToken=%s\n" "${NPM_TOKEN}" > .npmrc
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

- name: Install dependencies
run: pnpm install --frozen-lockfile
Comment thread
Jercik marked this conversation as resolved.
Comment thread
Jercik marked this conversation as resolved.

- name: Configure public npm registry (publish)
Comment thread
Jercik marked this conversation as resolved.
run: echo "registry=https://registry.npmjs.org/" >> ~/.npmrc
Comment thread
Jercik marked this conversation as resolved.
Comment thread
Jercik marked this conversation as resolved.

- name: Release
id: release
env:
Expand Down
3 changes: 1 addition & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
{
"js/ts.experimental.useTsgo": true,
"typescript.native-preview.tsdk": "./node_modules/@typescript/native-preview"
"js/ts.experimental.useTsgo": true
}
36 changes: 0 additions & 36 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,42 +117,6 @@ email.bulkSend(generateExpiryEmails(getExpiredUsers(db.getUsers(), new Date())))

Test the functional core, not the shell. Core tests are fast, deterministic, and need no mocks; the shell becomes thin orchestration where bugs are easy to spot through review. If shell tests are explicitly requested, prefer integration tests over unit tests with mocks.

# Rule: Inline Obvious Code

Keep simple, self-explanatory code inline rather than extracting it into functions. Every abstraction carries cognitive cost—readers must jump to another location, parse a signature, and track context. For obvious logic, this overhead exceeds any benefit.

Extracting code into a function is not inherently virtuous. A function should exist because it encapsulates meaningful complexity, not because code appears twice.

```ts
// GOOD: Inline obvious logic
if (removedFrom.length === 0) {
return { ok: true, message: "No credentials found" };
}
return { ok: true, message: `Removed from ${removedFrom.join(" and ")}` };

// BAD: Extraction hides obvious logic behind indirection
return formatRemovalResult(removedFrom);
```

## When to extract

Extract when duplication causes real maintenance risk, not merely because code appears twice:

- A name clarifies complex intent
- Multiple call sites must stay in lockstep and silent divergence would be a bug
- The function encapsulates a coherent standalone concept
- Testing it in isolation provides value

Don't extract for hypothetical reuse:

- For a single caller
- Because "we might need this elsewhere"
- When the name describes implementation rather than purpose

## The wrong abstraction

Abstractions decay when requirements diverge: programmer A extracts duplication into a shared function, programmer B adds a parameter for different behavior, and this repeats until the "abstraction" is a mess of conditionals. When an abstraction proves wrong, re-introduce duplication and let the code show you what's actually shared. Duplication is far cheaper than the wrong abstraction.

# Rule: No Logic in Tests

Write test assertions as concrete input/output examples, not computed values. Avoid operators, string concatenation, loops, and conditionals in test bodies—these obscure bugs and make tests harder to verify at a glance.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,5 +81,5 @@
"engines": {
"node": ">=24.0.0"
},
"packageManager": "pnpm@10.33.0"
"packageManager": "pnpm@11.5.1"
}
2 changes: 2 additions & 0 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
allowBuilds:
esbuild: true
enableGlobalVirtualStore: true
registries:
default: https://npm.j4k.dev/