Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
1 change: 0 additions & 1 deletion .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc

This file was deleted.

10 changes: 2 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,5 @@ jobs:
- name: Install dependencies
run: bun install --frozen-lockfile

- name: Typecheck
run: bun run typecheck

- name: Lint
run: bun run lint

- name: Fast tests
run: bun run test:fast
- name: Check
run: bun run check
8 changes: 0 additions & 8 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,3 @@ report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json

# Finder (MacOS) folder config
.DS_Store

# generated export test outputs
tests/fixtures/exports/*
!tests/fixtures/exports/.gitkeep

# generated watch-folder test outputs
tests/fixtures/watch/*
tests/fixtures/watch_e2e/*
3 changes: 2 additions & 1 deletion .oxfmtrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@
"singleQuote": false,
"trailingComma": "all",
"sortImports": false,
"sortPackageJson": true
"sortPackageJson": true,
"ignorePatterns": [".codex/**", ".cursor/hooks.json", ".entire/**"]
}
26 changes: 11 additions & 15 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ The command accepts PNG, JPEG, and TIFF images or a directory of those images, p

## Prerequisites

- Bun 1.3 or newer.
- Bun 1.3.6.
- TypeScript through `bunx tsc`.
- `ffmpeg` and `ffprobe` on `PATH` are preferred. Bundled fallback binaries are installed through `bun install`.

Expand All @@ -24,8 +24,8 @@ bun install --frozen-lockfile

## Quality Gates

Use `bun run check` before PRs. It runs TypeScript 7 typechecking, type-aware Oxlint, Oxfmt verification, unit tests, and the v1 integration test.
The integration test fixture helpers call system `ffmpeg` and `ffprobe`, so local checks still need both tools on `PATH`.
Use `bun run check` before PRs. It runs TypeScript 7 typechecking, type-aware Oxlint, Oxfmt verification, and the v1 acceptance test.
The acceptance-test oracle calls system `ffmpeg` and `ffprobe`, so local checks still need both tools on `PATH`. A dedicated test removes them from the product process's `PATH` to prove its bundled adapters independently.

## Common Commands

Expand All @@ -38,33 +38,29 @@ bun run prepare-image --help

## Test Layers

- `bun run test:unit`: pure layout and output path tests.
- `bun run test:integration`: the v1 CLI/FFmpeg integration test.
- `bun run test:fast`: unit plus v1 integration tests.
- `bun run test:all`: every Bun test with an explicit 30 second timeout.
- `bun run test`: every acceptance test with an explicit 60 second per-test timeout.
- `bun run test:integration`: the v1 command and Image engine acceptance test.

## Architecture Map

- `src/cli/prepare_image.ts`: command parsing and stdout/stderr behavior.
- `src/domain/prepare_image.ts`: source probing, layout selection, and FFmpeg export.
- `src/domain/prepare_image_layout.ts`: pure target, border, crop, and contain math.
- `src/domain/output_path.ts`: `.jpg` normalization, parent directory creation, and suffixing.
- `src/domain/media_process.ts`: packaged FFmpeg/ffprobe process boundary.
- `src/cli/prepare_image.ts`: argv and stdout/stderr adapter.
- `src/domain/prepare_image.ts`: deep Prepare image workflow module for source discovery, staging, atomic output allocation, commit, and rollback.
- `src/domain/image_engine.ts`: deep concrete Image engine module for FFmpeg/ffprobe adapters, orientation, layout, white-canvas rendering, and JPEG export.

## Branch Knowledge

For feature branches, update the relevant markdown in `docs/plans/` with the problem, decisions, commands run, and lessons learned.
Record durable architecture or product decisions immediately in `docs/plans/decisions-log.md`. Keep executable work in Linear and prefer explicit code plus local comments over new slice Markdown.

## Agent skills

### Issue tracker

Executable work is tracked in Linear Issues/Subissues; durable planning knowledge lives in Linear Documents. See `docs/agents/issue-tracker.md`.
Executable work is tracked in Linear Issues/Subissues; cross-repository and project planning knowledge lives in Linear Documents. See `docs/agents/issue-tracker.md`.

### Triage labels

Use the repo triage mapping with Linear `AFK` for agent-ready work and `HITL` for human-led work. See `docs/agents/triage-labels.md`.

### Domain docs

Single-context repo: read root `CONTEXT.md` and root decision docs before domain-sensitive work. See `docs/agents/domain.md`.
Single-context repo: read `CONTEXT.md` and `docs/plans/decisions-log.md` before domain-sensitive work. See `docs/agents/domain.md`.
20 changes: 12 additions & 8 deletions CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,35 @@

### Ready-to-upload Instagram image

A single highest-quality baseline sRGB JPEG output prepared from one user-provided source image so it can be uploaded manually through the Instagram app without additional resizing or padding. V1 source images are PNG, JPEG, or TIFF. For v1, this excludes scheduler/API upload compatibility, video, batch processing, benchmarking, matrix validation, and experimental research workflows.
A single highest-quality baseline sRGB JPEG output prepared from one user-provided source image so it can be uploaded manually through the Instagram app without additional resizing or padding. V1 source images are PNG, JPEG, or TIFF. For v1, this excludes scheduler/API upload compatibility, video, benchmarking, matrix validation, and experimental research workflows. Directory preparation can produce multiple ready-to-upload Instagram images without changing the contract of each output.

### Metadata preservation

V1 strips source EXIF/XMP metadata from the exported JPEG by default, while still exporting sRGB. Color correctness is part of the image output; GPS/device/time metadata is not.
V1 strips source EXIF/XMP metadata from the exported JPEG. It adds only a generated EXIF ColorSpace tag identifying the output as sRGB; source GPS/device/time/orientation metadata is not copied.

### EXIF orientation

V1 respects source EXIF orientation visually before stripping metadata. The exported JPEG should have correctly oriented pixels without relying on EXIF orientation metadata.

### Prepare image

The single v1 user-facing command/workflow: source image in, upload-ready JPEG out. It accepts one positional input, required `--out` full file path, and optional `--border-px`. Internal analysis and layout are implementation details, not separate v1 product commands.
The single v1 user-facing command/workflow: a supported source file or directory in, ready-to-upload JPEG output or outputs out. It accepts one positional input, required `--out`, and optional `--border-px`. For file input, `--out` is a full file path. For directory input, `--out` is an output directory. Internal source discovery, analysis, layout, staging, and rendering are implementation details, not separate v1 product commands.

### Directory preparation

When Prepare image receives a directory, it processes supported top-level files in sorted filename order and ignores unsupported files and nested directories. It stages every output before publishing any of them. A failed directory preparation leaves no output from that invocation; a successful run prints one final path per source in source order.

### Image engine

V1 uses FFmpeg for source decoding, orientation/color handling, scaling/compositing, and JPEG export.
V1 uses FFmpeg for source decoding, orientation/color handling, scaling/compositing, and JPEG export. Completely and recognizably tagged non-sRGB sources are converted to sRGB characteristics; untagged photo sources are treated as already containing sRGB pixel values.

### Help output

`prepare-image --help` shows a one-line usage string and only the v1 options: `--out` and `--border-px`.

### Output path suffixing

V1 does not overwrite an existing output file by default. If the requested output path already exists after `.jpg` normalization, the command writes to the first available suffixed path: `photo-1.jpg`, `photo-2.jpg`, and so on.
V1 never overwrites an existing output file. It atomically commits to the first available path after `.jpg` normalization: `photo.jpg`, `photo-1.jpg`, `photo-2.jpg`, and so on. This also disambiguates directory inputs whose source files share the same basename.

### Output extension normalization

Expand All @@ -40,7 +44,7 @@ If the parent directory of the requested output file path does not exist, v1 cre

### Command output

The v1 command always prints the actual written output path on success. This may differ from the requested `--out` path when suffixing is applied. Normal behavior, including metadata stripping, should not print warnings.
The v1 command prints every actual written output path on success. A path may differ from the requested or derived output path when suffixing is applied. Normal behavior, including metadata stripping, should not print warnings.

### Command errors

Expand All @@ -60,15 +64,15 @@ By default, v1 chooses the output shape from the input image dimensions after ap

### White canvas

A solid white background used for every v1 output. The source image is attached to this canvas without cropping, creating a polaroid-ish look and a visually consistent Instagram profile grid. This is the canonical term for what a user may call a white border.
A solid white background used for every v1 output. Portrait images are attached without cropping; landscape images may use a centered crop to preserve their equal outer border. The result creates a polaroid-ish look and a visually consistent Instagram profile grid. This is the canonical term for what a user may call a white border.

### Transparency handling

Transparent pixels in the source image are composited onto white in v1 output.

### Minimum white border

The smallest allowed white margin around the fitted source image at full target size, defined as one non-negative integer pixel value. The v1 default is `165px`, converted from `124pt` at `96px / 72pt`. `0px` is valid. If the output must shrink to avoid upscaling, the border scales down by the same factor, for example `165px` becomes about `83px` at `50%` output size. Effective scaled borders are rounded to the nearest integer pixel, with a minimum of `1px` when the configured border is greater than `0`; configured `0px` stays `0px`. Landscape uses the scaled value as the exact equal outer border. Portrait uses it as the minimum border.
The smallest allowed white margin around the fitted source image at full target size, defined as one non-negative integer pixel value. The v1 default is `57px`. `0px` is valid. If the output must shrink to avoid upscaling, the border scales down by the same factor. Effective scaled borders are rounded to the nearest integer pixel, with a minimum of `1px` when the configured border is greater than `0`; configured `0px` stays `0px`. Landscape uses the scaled value as the exact equal outer border. Portrait uses it as the minimum border.

### No upscaling

Expand Down
27 changes: 12 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ The v1 product surface is one command: `prepare-image`. It accepts a single PNG,
- Uses a `57px` default border at full target size.
- Avoids upscaling small source images.
- Applies EXIF orientation before choosing landscape or portrait output.
- Strips EXIF/XMP metadata from the final JPEG.
- Converts completely and recognizably tagged non-sRGB sources, strips source EXIF/XMP, and adds only a generated sRGB color marker.
- Never overwrites existing exports.
- Publishes a directory run only after every source has been prepared successfully.

## Prerequisites

- Bun 1.3 or newer.
- Bun 1.3.6.
- TypeScript through `bunx tsc`.
- `ffmpeg` and `ffprobe` on `PATH` are preferred. Bundled fallback binaries are installed through `bun install`.

Expand Down Expand Up @@ -56,7 +57,8 @@ bun run prepare-image /full/path/input.jpg --out /full/path/exports/photo --bord

- File inputs print one output path.
- Directory inputs process top-level PNG, JPEG, and TIFF files in filename order and print one output path per export.
- Existing outputs are suffixed as `photo-1.jpg`, `photo-2.jpg`, and so on.
- Directory outputs are staged as one batch; a failed run publishes none of its outputs.
- Existing outputs are atomically preserved and new outputs are suffixed as `photo-1.jpg`, `photo-2.jpg`, and so on.
- Landscape inputs use centered cover fitting into an equal-border inner frame.
- Portrait and square inputs use contain fitting so the full source image remains visible.
- Transparent pixels are composited onto white.
Expand All @@ -67,22 +69,17 @@ bun run prepare-image /full/path/input.jpg --out /full/path/exports/photo --bord
bun run check
```

This runs TypeScript 7 typechecking, type-aware Oxlint, Oxfmt verification, unit tests, and the v1 integration test.
The integration test fixture helpers call system `ffmpeg` and `ffprobe`, so local checks still need both tools on `PATH`.
This runs TypeScript 7 typechecking, type-aware Oxlint, Oxfmt verification, and the v1 acceptance test.
The independent test oracle calls system `ffmpeg` and `ffprobe`, so local checks still need both tools on `PATH`; the product's bundled fallback is covered in a separate child-process scenario.

## Project Structure

- `src/cli/prepare_image.ts`: command parsing, file-vs-directory orchestration, and stdout/stderr behavior.
- `src/domain/prepare_image.ts`: source probing, layout selection, and FFmpeg export.
- `src/domain/prepare_image_layout.ts`: pure target, border, crop, and contain math.
- `src/domain/output_path.ts`: `.jpg` normalization, parent directory creation, and suffixing.
- `src/domain/media_process.ts`: FFmpeg/ffprobe process boundary.
- `tests/integration/prepare_image.integration.test.ts`: CLI and FFmpeg integration coverage.
- `src/cli/prepare_image.ts`: argv and stdout/stderr adapter.
- `src/domain/prepare_image.ts`: source discovery, staging, atomic output allocation, commit, and rollback.
- `src/domain/image_engine.ts`: executable selection, orientation, layout, white-canvas rendering, and JPEG export.
- `tests/integration/prepare_image.integration.test.ts`: command and Image engine acceptance coverage.

## Docs

- `docs/v1_split_plan.md`: product contract and split plan.
- `CONTEXT.md`: current product vocabulary and behavior.
- `docs/plans/decisions-log.md`: decision log.
- `docs/plans/v1-prepare-image-layout.md`: layout slice notes.
- `docs/plans/v1-prepare-image-cli.md`: CLI slice notes.
- `docs/plans/prepare-image-directory-input.md`: directory input slice notes.
4 changes: 2 additions & 2 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 2 additions & 5 deletions docs/agents/domain.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,9 @@ This is a single-context repo.
## Before exploring, read these

- **`CONTEXT.md`** at the repo root.
- **`docs/plans/decisions-log.md`** for architectural and planning decisions recorded during branch work.
- **`docs/plans/`** for branch knowledge relevant to the area you're about to work in.
- **`docs/adr/`** if it exists.
- **`docs/plans/decisions-log.md`** for repository architecture and product decisions.

If any of these files don't exist, proceed silently. Don't flag their absence or suggest creating them upfront. Producer skills create them lazily when terms or decisions actually get resolved.
Do not create per-slice plan or ADR Markdown. Keep executable work in Linear, broader project artifacts in Linear Documents, and repository architecture/product decisions in the existing decision log.

## File structure

Expand All @@ -23,7 +21,6 @@ If any of these files don't exist, proceed silently. Don't flag their absence or
├── docs/
│ ├── plans/
│ │ └── decisions-log.md
│ └── adr/
└── src/
```

Expand Down
7 changes: 4 additions & 3 deletions docs/agents/issue-tracker.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Issues and implementation work for this repo live in Linear. Use the Linear tool

- **Initiatives**: Long-running outcomes or business directions.
- **Projects**: The work container for a feature, product area, or themed effort.
- **Documents**: Long-lived knowledge such as PRDs, RFCs, ADRs, research, decision logs, and project planning notes. Attach Documents to the relevant Linear project unless the user asks for a different location.
- **Documents**: Long-lived cross-repository or project knowledge such as PRDs, RFCs, research, and project planning notes. Attach Documents to the relevant Linear project unless the user asks for a different location. Repository-local architecture and product decisions stay in `docs/plans/decisions-log.md`.
- **Issues**: Executable work. Create issues only for work that someone or an agent can actually implement, verify, or decide.
- **Subissues**: Vertical slices or child work under a parent Linear issue. Use `parentId` when breaking an existing issue into slices.
- **Labels**: Use domain labels for product area and the mapped triage labels for workflow state. See `triage-labels.md`.
Expand All @@ -24,7 +24,8 @@ Bracketed prefixes are a naming convention for grouping work by product, effort,

## Common operations

- **Create or update a PRD/RFC/ADR/Research artifact**: create or update a Linear Document on the relevant project.
- **Create or update a PRD/RFC/Research artifact**: create or update a Linear Document on the relevant project.
- **Record a repository architecture or product decision**: append it to `docs/plans/decisions-log.md`.
- **Read source material**: fetch the referenced Linear Document, Project, or Issue, including comments when the source is an issue.
- **Create implementation work**: create Linear Issues in the relevant project.
- **Create vertical slices from an existing issue**: create Linear subissues with `parentId`.
Expand All @@ -33,7 +34,7 @@ Bracketed prefixes are a naming convention for grouping work by product, effort,

## When a skill says "publish to the issue tracker"

For PRDs/RFCs/ADRs/Research, create or update a Linear Document on the relevant project.
For PRDs/RFCs/Research, create or update a Linear Document on the relevant project. Repository-local architecture and product decisions remain in the checked-in decision log.

For executable work, create Linear Issues or Subissues in the relevant project.

Expand Down
23 changes: 0 additions & 23 deletions docs/plans/bundle-ffmpeg-binaries.md

This file was deleted.

Loading
Loading