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
9 changes: 9 additions & 0 deletions .fallowrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"$schema": "https://docs.fallow.tools/schema/fallow.schema.json",
"entry": [
"app/**/__tests__/**/*.{ts,tsx}",
"app/**/*.test.{ts,tsx}",
"lib/**/__tests__/**/*.{ts,tsx}",
"lib/**/*.test.{ts,tsx}"
]
}
14 changes: 14 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
version: 2
updates:
- package-ecosystem: npm
directory: "/"
schedule:
interval: weekly
open-pull-requests-limit: 5
groups:
dev-dependencies:
dependency-type: development
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: weekly
58 changes: 58 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: CI

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

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm run lint

typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm run typecheck

test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm test

# Advisory security scan — surfaces findings without blocking the merge.
semgrep:
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
# setuptools provides pkg_resources, which semgrep's deps need on
# Python 3.12 (where it is no longer bundled).
- run: pip install 'setuptools<81' 'semgrep==1.95.0'
- run: >
semgrep scan
--config=p/typescript --config=p/react --config=p/nextjs --config=p/secrets
--severity=ERROR --metrics=off || true
17 changes: 17 additions & 0 deletions .github/workflows/fallow.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: fallow

on:
pull_request:

jobs:
audit:
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: fallow-rs/fallow@v2
with:
fail-on-issues: false
format: json
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ out/

# Debug
npm-debug.log*
firebase-debug.log*
*.log

# IDE
.idea/
Expand Down
1 change: 1 addition & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
npx lint-staged
1 change: 1 addition & 0 deletions .husky/pre-push
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
npm run typecheck && npm test
3 changes: 3 additions & 0 deletions .lintstagedrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"*.{ts,tsx}": "eslint --fix"
}
129 changes: 129 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# Worklog — Development Guide

AI-powered time logging for consultants. Aggregates your day hour-by-hour from
Google, Slack, Trello, GitHub, Jira, and HubSpot, then uses AI to generate
ready-to-submit time entries mapped to your projects in Milient/Moment.

This file is the canonical guide for both humans and AI agents. `CLAUDE.md`
imports it. For deeper reference, see:

- [docs/ARCHITECTURE.md](./docs/ARCHITECTURE.md) — system design, data flow, auth, caching
- [docs/PATTERNS.md](./docs/PATTERNS.md) — code patterns & conventions
- [docs/DOMAIN-LOGIC.md](./docs/DOMAIN-LOGIC.md) — business rules
- [docs/adr/](./docs/adr/) — architecture decision records

## Tech Stack

- **Framework**: Next.js 14 (App Router)
- **Language**: TypeScript 5 (`strict`)
- **Auth**: NextAuth.js v5 (JWT-only, no database)
- **UI**: shadcn/ui + Tailwind CSS v4 + Lucide icons
- **AI**: Google Gemini 2.5 Flash Lite
- **Time tracking**: Milient / Moment
- **Hosting**: Vercel

## Commands

```bash
npm run dev # Start dev server (http://localhost:3000)
npm run build # Production build
npm run lint # ESLint (flat config)
npm run lint:fix # ESLint with autofix
npm run typecheck # tsc --noEmit
npm test # Vitest (run once)
npm run test:watch # Vitest (watch mode)
```

`npm install` sets up git hooks automatically via husky.

## Quality Gates

Three gates protect `main`. They run locally via git hooks and again in CI:

| Gate | Local | CI |
|------|-------|----|
| Lint (`npm run lint`) | pre-commit (lint-staged, changed files) | `ci.yml` |
| Typecheck (`npm run typecheck`) | pre-push | `ci.yml` |
| Tests (`npm test`) | pre-push | `ci.yml` |

Advisory (non-blocking) checks on PRs: Semgrep security scan (`ci.yml`) and a
`fallow` dead-code/reachability audit (`fallow.yml`). Dependencies are kept
fresh by Dependabot.

Lint blocks only on **errors**. Pre-existing `any` at third-party API
boundaries and `react-hooks/exhaustive-deps` are intentionally **warnings** —
see [Known tech-debt](#known-tech-debt).

## Development Workflow

1. Branch off `main` (`feat/…`, `fix/…`, `chore/…`).
2. Commit after each logical change with a short, descriptive message.
3. **Bump the version** in `package.json` → `"version"` before pushing. This is
the single source of truth; `app/lib/version.ts`, `i18n.tsx`, and the about
page all read from it. Use semver: patch = bug fix, minor = feature,
major = breaking change. **Never push without bumping.**
4. Push and open a PR. CI must be green before merge.

## Testing

- Unit tests live next to the code in `__tests__/` folders and use Vitest.
- Focus on the pure, high-value logic: `app/lib/aggregator.ts`,
`app/lib/ai/parse.ts`, `app/lib/ai/preprocess.ts`.
- The test script pins `TZ=Europe/Oslo`. Anything that reads local time
(`getHours()`) is timezone-sensitive — write fixtures accordingly (pass an
explicit `timezone` arg, or use timestamps without a `Z` suffix so they parse
as local time).

## Coding Conventions

See [docs/PATTERNS.md](./docs/PATTERNS.md) for detail. The essentials:

- **Adapter pattern** for pluggable integrations: `lib/ai/` and `lib/pm/` each
expose an interface + factory + implementation. New provider = one new file +
a factory line.
- **i18n**: all UI strings live in `app/lib/i18n.tsx` as a flat dotted-key
dictionary. Use `const { t, lang } = useTranslation()`.
- **Components**: shadcn/ui base components, Lucide icons, Tailwind only — no
CSS modules or styled-components.
- **API routes** authenticate first, then handle:
```ts
const session = await auth()
if (!session?.accessToken) {
return NextResponse.json({ error: "Not authenticated" }, { status: 401 })
}
```
- **Responsive**: when conditional rendering is needed, branch on
`matchMedia("(min-width: 1024px)")` in JS — never mount two instances of a
stateful component and hide one with CSS.

## Scope Discipline

- Only make changes that are directly requested or clearly necessary.
- Don't add features, refactor, or "improve" beyond what was asked.
- Don't add comments, docstrings, or type annotations to code you didn't change.
- Don't create new files unless necessary — prefer editing existing ones.
- Don't create documentation files unless explicitly requested.

## Git Protocol

- Commit after each logical change, not in large batches.
- Never force-push, amend published commits, or skip hooks.
- Never commit `.env.local` or files containing secrets.

## Human Approval Gates

Always ask before:

- Destructive operations (deleting files/branches, dropping data)
- Actions visible to others (pushing, creating PRs/issues, sending messages)
- Architectural decisions with multiple valid approaches

## Known tech-debt

Tracked as lint warnings to burn down incrementally rather than blocking CI:

- Type the `any` at third-party API boundaries (`google.ts`, `slack.ts`,
`jira.ts`, `hubspot.ts`, `trello.ts`, `milient.ts`).
- Resolve `react-hooks/exhaustive-deps` warnings in `app/page.tsx`.
- Consider promoting Semgrep from advisory to blocking once the baseline is
clean, and adding bundle-size budgets (`size-limit`).
35 changes: 1 addition & 34 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,34 +1 @@
# Agent Rules

See also: [ARCHITECTURE.md](./ARCHITECTURE.md) | [PATTERNS.md](./PATTERNS.md) | [DOMAIN-LOGIC.md](./DOMAIN-LOGIC.md)

## Workflow

After each code change:
1. Commit with a short descriptive message.
2. Push it

**Before pushing** (every `git push`), bump the version in `package.json` → `"version"`. That is the single source of truth — all other places (`i18n.tsx`, about page) read from it automatically via `app/lib/version.ts`.

Use semver: patch for bug fixes, minor for features, major for breaking changes. This is mandatory — never push without checking the version.

## Scope Discipline

- Only make changes that are directly requested or clearly necessary.
- Don't add features, refactor, or "improve" beyond what was asked.
- Don't add comments, docstrings, or type annotations to code you didn't change.
- Don't create new files unless absolutely required — prefer editing existing ones.
- Don't create documentation files unless explicitly requested.

## Git Protocol

- Commit after each logical change, not in large batches.
- Never force-push, amend published commits, or skip hooks.
- Never commit `.env.local` or files containing secrets.

## Human Approval Gates

Always ask before:
- Destructive operations (deleting files/branches, dropping data)
- Actions visible to others (pushing, creating PRs/issues, sending messages)
- Architectural decisions with multiple valid approaches
@AGENTS.md
51 changes: 51 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Contributing to Worklog

Thanks for contributing. This is an internal Frontkom project; the conventions
below keep it pleasant to work in.

## Prerequisites

- Node.js 22 (matches CI)
- npm

## Setup

```bash
npm install # also installs git hooks (husky)
cp .env.example .env.local # fill in credentials — see README.md
npm run dev # http://localhost:3000
```

## Workflow

1. Branch off `main`: `feat/…`, `fix/…`, or `chore/…`.
2. Make focused commits with short, descriptive messages.
3. Run the checks before pushing (the git hooks run these too):
```bash
npm run lint
npm run typecheck
npm test
```
4. **Bump `version` in `package.json`** (semver) before pushing — it is the
single source of truth for the version shown in the app.
5. Open a PR. CI (lint + typecheck + test) must be green to merge.

## Quality gates

- **pre-commit** runs ESLint (with autofix) on staged files.
- **pre-push** runs the typecheck and the test suite.
- **CI** re-runs all three on every PR, plus advisory Semgrep and `fallow`
audits.

Lint blocks on errors only; warnings are tracked tech-debt (see `AGENTS.md`).

## Tests

Unit tests use Vitest and live in `__tests__/` folders next to the code under
test. Prioritise the pure logic in `app/lib/`. The suite pins
`TZ=Europe/Oslo`; keep timezone-sensitive fixtures deterministic.

## Conventions

See [`AGENTS.md`](./AGENTS.md) for the full development guide and
[`docs/`](./docs) for architecture, patterns, and domain rules.
24 changes: 21 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Worklog

[![CI](https://github.com/perandre/worklog/actions/workflows/ci.yml/badge.svg)](https://github.com/perandre/worklog/actions/workflows/ci.yml)

AI-powered time logging for consultants. Aggregates your day hour-by-hour from Google, Slack, Trello, GitHub, Jira, and HubSpot — then uses AI to generate ready-to-submit time entries mapped to your projects in Milient/Moment.

Built with Next.js 14, shadcn/ui, Tailwind CSS, and Google Gemini. No database. Supports dark mode.
Expand All @@ -18,11 +20,25 @@ cp .env.example .env.local # Fill in your credentials
npm run dev # Open http://localhost:3000
```

## Development

```bash
npm run dev # Dev server
npm run lint # ESLint
npm run typecheck # tsc --noEmit
npm test # Vitest
```

Git hooks (installed by `npm install`) lint staged files on commit and run the
typecheck + tests on push. CI runs the same checks on every PR. See
[CONTRIBUTING.md](./CONTRIBUTING.md) for the workflow and [AGENTS.md](./AGENTS.md)
for the full development guide.

## Environment Variables

```
# NextAuth
NEXTAUTH_URL=https://your-app.vercel.app
NEXTAUTH_URL=https://worklog.frontkom.com
NEXTAUTH_SECRET=generate-a-random-secret

# Google OAuth (Calendar, Gmail, Drive Activity)
Expand Down Expand Up @@ -121,9 +137,11 @@ Milient is a time management platform used by Norwegian consulting firms. The ap

1. Push to GitHub
2. Import the project at [vercel.com](https://vercel.com)
3. Add all environment variables (set `NEXTAUTH_URL` to your Vercel domain)
3. Add all environment variables (set `NEXTAUTH_URL` to your production domain)
4. Deploy
5. Update all OAuth redirect URIs to your Vercel domain
5. Add your custom domain in Vercel → Settings → Domains, then point its DNS at
Vercel (see [docs/CALLBACK-URLS.md](./docs/CALLBACK-URLS.md))
6. Update all OAuth redirect URIs to your production domain

## Tech Stack

Expand Down
Loading
Loading