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
12 changes: 12 additions & 0 deletions .claude/commands/add-test.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Read the source file at `$ARGUMENTS` and create a comprehensive test file following project conventions:

1. Read the source file to understand all exports and their behavior
2. Create the test file at the corresponding path under `test/` (e.g., `src/core/config.ts` -> `test/core/config.test.ts`)
3. Follow these patterns:
- Import from `vitest` (`describe`, `it`, `expect`, `vi`, `beforeEach`, `afterEach`)
- Use `test/helpers/mock-fs.ts` for temp directories when testing file operations
- Use `vi.mock('@anthropic-ai/sdk')` for anything that calls the Claude API
- Use recorded fixtures from `test/fixtures/api-responses/` where available
- Test happy paths, edge cases, and error conditions
- Group related tests with `describe` blocks
4. Run the tests to verify they pass
10 changes: 10 additions & 0 deletions .claude/commands/coverage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Run the test coverage report and analyze gaps:

```bash
pnpm test:coverage
```

After the report:
1. Identify files with coverage below 80%
2. List the uncovered lines/branches
3. Suggest which tests to write next for maximum coverage improvement
7 changes: 7 additions & 0 deletions .claude/commands/test-file.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Run tests for a specific file:

```bash
pnpm vitest run $ARGUMENTS
```

Report the results. If tests fail, analyze the failures and suggest fixes.
7 changes: 7 additions & 0 deletions .claude/commands/validate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Run the full validation suite and report results:

```bash
pnpm validate
```

If any step fails, diagnose the issue and suggest a fix.
14 changes: 14 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
## Summary
<!-- What and why -->

## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Refactoring
- [ ] Documentation
- [ ] Tests / CI

## Checklist
- [ ] `pnpm validate` passes
- [ ] No real receipts or PII included
- [ ] Docs updated if user-facing behavior changed
78 changes: 78 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
typecheck:
name: Typecheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm typecheck

lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm lint

format:
name: Format
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm format:check

build:
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm build

test:
name: Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm test:coverage
- uses: actions/upload-artifact@v4
if: always()
with:
name: coverage-report
path: coverage/
55 changes: 55 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# receipt2sheet - Claude Code Conventions

## Overview
TypeScript CLI tool (`r2s`) for rental property owners to process receipts into Schedule E expense spreadsheets using Claude AI.

## Stack
- **Runtime:** Node >= 20, ESM
- **Package manager:** pnpm (use `pnpm` for all commands)
- **Language:** TypeScript 5.8, strict mode
- **Testing:** Vitest + @vitest/coverage-v8
- **Linting:** ESLint + typescript-eslint
- **Formatting:** Prettier

## Key Commands
```bash
pnpm validate # typecheck + format + lint + build + test (run before committing)
pnpm test # run tests
pnpm test:watch # run tests in watch mode
pnpm test:coverage # run tests with coverage report
pnpm build # compile TypeScript
pnpm typecheck # type-check without emitting
pnpm lint # lint src/
pnpm format:check # check formatting
```

## Architecture
```
src/
commands/ CLI command handlers (init, process, doctor)
core/ Business logic (config, extract, parse, spreadsheet, ledger, confirm)
schemas/ Zod schemas (config, receipt, expense)
utils/ Pure utilities (currency, dates, files)
index.ts CLI entry point
```

Flow: `commands/` -> `core/` -> `schemas/` -> `utils/`

## Conventions
- ESM with `.js` extensions in imports (even for .ts files)
- Zod for all runtime validation
- No PII or real receipts in git — `inbox/` and `processed/` are gitignored
- `templates/` contains canonical xlsx spreadsheet templates — don't modify without intent
- Amounts are non-negative numbers; dates are `YYYY-MM-DD` strings

## Testing Patterns
- `vi.mock('@anthropic-ai/sdk')` for Claude API calls
- Use `test/helpers/mock-fs.ts` for temp directories (create in `beforeEach`, clean in `afterEach`)
- Use recorded API responses from `test/fixtures/api-responses/`
- Use generated fixture receipts from `test/fixtures/receipts/`
- Spreadsheet tests: copy real templates to temp dirs, verify with ExcelJS
- Extract tests: use actual fixture PDFs (no mocking needed)

## Commit Conventions
- Focus on the "why" not the "what"
- End with: `Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>`
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](./LICENSE)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.8-blue?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
[![Node](https://img.shields.io/badge/Node-20+-green?logo=node.js&logoColor=white)](https://nodejs.org/)
[![CI](https://github.com/TeamNickHart/receipt2sheet/actions/workflows/ci.yml/badge.svg)](https://github.com/TeamNickHart/receipt2sheet/actions/workflows/ci.yml)
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](./CONTRIBUTING.md)

> v0.1.0
Expand Down Expand Up @@ -184,6 +185,17 @@ Claude API usage is minimal:
- ~$0.05–0.15 per receipt (PDF document or image vision)
- Typical month: **$1–3** for 50 receipts

## Development

```bash
pnpm install # install dependencies
pnpm validate # typecheck + format + lint + build + test
pnpm test # run tests
pnpm test:watch # run tests in watch mode
pnpm test:coverage # run tests with coverage report
pnpm fixtures:generate # regenerate test fixture PDFs
```

## Contributing

Contributions are welcome! Please read the [PLAN.md](./PLAN.md) for architecture details and implementation status.
Expand Down
2 changes: 1 addition & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ export default tseslint.config(
eslint.configs.recommended,
...tseslint.configs.recommended,
{
ignores: ['dist/', 'node_modules/'],
ignores: ['dist/', 'node_modules/', 'scripts/'],
},
);
8 changes: 8 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
"start": "node dist/index.js",
"test": "vitest run",
"test:watch": "vitest",
"test:coverage": "vitest run --coverage",
"fixtures:generate": "tsx scripts/generate-fixtures.ts",
"fixtures:record": "tsx scripts/record-api-responses.ts",
"typecheck": "tsc --noEmit",
"lint": "eslint src/",
"format": "prettier --write src/",
Expand Down Expand Up @@ -47,12 +50,17 @@
"@eslint/js": "^10.0.1",
"@types/node": "^22.15.0",
"@types/pdf-parse": "^1.1.4",
"@types/pdfkit": "^0.17.6",
"@vitest/coverage-v8": "^3.2.4",
"eslint": "^10.4.0",
"pdfkit": "^0.18.0",
"prettier": "^3.8.3",
"tsx": "^4.22.2",
"typescript": "^5.8.0",
"typescript-eslint": "^8.59.3",
"vitest": "^3.1.0"
},
"packageManager": "pnpm@10.26.0",
"engines": {
"node": ">=20.0.0"
}
Expand Down
Loading
Loading