Skip to content

Commit 2882977

Browse files
committed
initial
1 parent c6fc5de commit 2882977

117 files changed

Lines changed: 32996 additions & 1 deletion

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../CLAUDE.md

.gitignore

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# dependencies (bun install)
2+
node_modules
3+
4+
# output
5+
out
6+
dist
7+
*.tgz
8+
9+
# code coverage
10+
coverage
11+
*.lcov
12+
13+
# logs
14+
logs
15+
_.log
16+
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
17+
18+
# dotenv environment variable files
19+
.env
20+
.env.development.local
21+
.env.test.local
22+
.env.production.local
23+
.env.local
24+
25+
# caches
26+
.eslintcache
27+
.cache
28+
*.tsbuildinfo
29+
30+
# IntelliJ based IDEs
31+
.idea
32+
33+
# Finder (MacOS) folder config
34+
.DS_Store

.prettierignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Ignore artifacts:
2+
build
3+
coverage

.prettierrc

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"endOfLine": "lf",
3+
"semi": false,
4+
"trailingComma": "none",
5+
"arrowParens": "avoid",
6+
"singleQuote": false,
7+
"tabWidth": 2,
8+
"plugins": ["prettier-plugin-sort-imports"],
9+
"sortingMethod": "alphabetical",
10+
"sortingOrder": "ascending",
11+
"stripNewlines": false,
12+
"importTypeOrder": ["NPMPackages", "localImportsValue", "localImportsType"],
13+
"packageJSONFiles": ["package.json"],
14+
"newlineBetweenTypes": true,
15+
"printWidth": 100,
16+
"proseWrap": "never",
17+
"quoteProps": "consistent",
18+
"experimentalTernaries": true
19+
}

.tool-versions

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
bun 1.3.5

AGENTS.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
---
2+
description: Use Bun instead of Node.js, npm, pnpm, or vite.
3+
globs: "*.ts, *.tsx, *.html, *.css, *.js, *.jsx, package.json"
4+
alwaysApply: false
5+
---
6+
7+
Default to using Bun instead of Node.js.
8+
9+
- Use `bun <file>` instead of `node <file>` or `ts-node <file>`
10+
- Use `bun test` instead of `jest` or `vitest`
11+
- Use `bun build <file.html|file.ts|file.css>` instead of `webpack` or `esbuild`
12+
- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
13+
- Use `bun run <script>` instead of `npm run <script>` or `yarn run <script>` or `pnpm run <script>`
14+
- Use `bunx <package> <command>` instead of `npx <package> <command>`
15+
- Bun automatically loads .env, so don't use dotenv.
16+
17+
## APIs
18+
19+
- `Bun.serve()` supports WebSockets, HTTPS, and routes. Don't use `express`.
20+
- `bun:sqlite` for SQLite. Don't use `better-sqlite3`.
21+
- `Bun.redis` for Redis. Don't use `ioredis`.
22+
- `Bun.sql` for Postgres. Don't use `pg` or `postgres.js`.
23+
- `WebSocket` is built-in. Don't use `ws`.
24+
- Prefer `Bun.file` over `node:fs`'s readFile/writeFile
25+
- Bun.$`ls` instead of execa.
26+
27+
## Testing
28+
29+
Use `bun test` to run tests.
30+
31+
```ts#index.test.ts
32+
import { test, expect } from "bun:test";
33+
34+
test("hello world", () => {
35+
expect(1).toBe(1);
36+
});
37+
```
38+
39+
## Coding Style & Naming Conventions
40+
41+
TypeScript (strict). Runtimes: Bun 1.3+, Node 23. Prettier: 2 spaces, no semicolons, double quotes, width 100. Import order: @ianvs/prettier-plugin-sort-imports + Tailwind plugin. ESLint (flat): any disallowed; unused vars warned (prefix \_ to ignore). Naming: React components PascalCase; files kebab-case (e.g., user-profile.ts); packages @repo/<name>. Comments: avoid writing inline comments everywhere, unless absolutely necessary for a todo or an important thing to take note of. Instead write tsdoc style block level comments at the method/class/function/route level. Focus on comments that provide value in regards to better type inference and clarity of usage. Using things like params/returns/etc..
42+
43+
Always default to useing named paramaters in functions eg myFunction({ ctx, other }) vs myFunction(ctx, other)

ARCHITECTURE.md

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# Architecture
2+
3+
## Why this exists (plain English)
4+
5+
Running multiple local projects at the same time is messy when everything wants the same ports and
6+
"localhost". This CLI gives each repo its own isolated network and stable HTTPS hostnames so you can:
7+
8+
- run many apps concurrently without port juggling
9+
- keep service defaults (Postgres on 5432, Redis on 6379) inside each project
10+
- access every app via predictable `https://<project>.hack`
11+
- get fast local logs plus searchable history
12+
13+
## System overview
14+
15+
`hack` is a Bun CLI that writes per-project Compose files under `.hack/` and manages a machine-wide
16+
proxy + logging stack under `~/.hack/`. Caddy routes `*.hack` based on container labels and the
17+
logging stack (Alloy + Loki + Grafana) captures Docker Compose logs.
18+
19+
```mermaid
20+
flowchart LR
21+
CLI["hack CLI (Bun)"]
22+
Browser["Browser"]
23+
24+
subgraph Host["Developer machine"]
25+
subgraph Global["Global infra (~/.hack)"]
26+
Caddy["Caddy docker-proxy"]
27+
Loki["Loki"]
28+
Grafana["Grafana"]
29+
Alloy["Alloy (Docker logs)"]
30+
end
31+
32+
subgraph Project["Project repo (.hack)"]
33+
Compose["docker-compose.yml"]
34+
Services["Service containers"]
35+
end
36+
end
37+
38+
CLI -->|"hack global install/up"| Global
39+
CLI -->|"hack init/up"| Compose
40+
Compose -->|"docker compose"| Services
41+
42+
Services -->|"Docker labels"| Caddy
43+
Services -->|"container logs"| Alloy
44+
Alloy --> Loki --> Grafana
45+
46+
Caddy -->|"https://*.hack"| Browser
47+
Grafana -->|"https://logs.hack"| Browser
48+
```
49+
50+
## Global vs project scope
51+
52+
- Global scope (`~/.hack`)
53+
- Caddy proxy listens on 80/443 and routes `https://*.hack` via Docker labels
54+
- Logging stack captures all Compose logs (Alloy → Loki → Grafana)
55+
- Networks: `hack-dev` (ingress) and `hack-logging`
56+
57+
- Project scope (`.hack`)
58+
- `docker-compose.yml` defines services and optional Caddy labels
59+
- `hack.config.json` stores project name, dev host, log preferences, OAuth alias
60+
61+
## Lifecycle (init → up → logs)
62+
63+
```mermaid
64+
sequenceDiagram
65+
participant User
66+
participant CLI as hack
67+
participant Docker
68+
participant Caddy
69+
participant Loki
70+
71+
User->>CLI: hack init
72+
CLI->>Docker: create .hack/docker-compose.yml
73+
CLI-->>User: wrote .hack/ files
74+
75+
User->>CLI: hack up
76+
CLI->>Docker: docker compose up
77+
Docker->>Caddy: read labels for routing
78+
Docker->>Loki: logs via Alloy
79+
80+
User->>CLI: hack logs
81+
alt compose backend
82+
CLI->>Docker: docker compose logs
83+
else loki backend
84+
CLI->>Loki: query/tail LogQL
85+
end
86+
```
87+
88+
## Logging pipeline
89+
90+
```mermaid
91+
flowchart LR
92+
Containers["Compose containers"] -->|"stdout/stderr"| Alloy
93+
Alloy -->|"push"| Loki
94+
Loki -->|"query"| CLI["hack logs --loki"]
95+
Loki -->|"Explore"| Grafana
96+
CLI -->|"pretty output"| Terminal
97+
```
98+
99+
## Files and directories
100+
101+
- `~/.hack/`
102+
- `caddy/docker-compose.yml`
103+
- `logging/docker-compose.yml`
104+
- `logging/alloy.alloy`
105+
- `logging/loki.yaml`
106+
- `logging/grafana/...`
107+
- `schemas/hack.config.schema.json`
108+
- `schemas/hack.branches.schema.json`
109+
- `projects.json` (best-effort registry)
110+
111+
- `<repo>/.hack/`
112+
- `docker-compose.yml`
113+
- `hack.config.json`
114+
- `hack.branches.json` (optional)
115+
116+
## Key design choices
117+
118+
- Docker Compose is the execution substrate for predictability and portability.
119+
- Caddy routes by container label so there is no per-repo reverse proxy config.
120+
- Logs default to `docker compose logs` for speed, with Loki for history and filtering.
121+
- Config lives alongside each repo in `.hack/` to keep repos isolated and portable.
122+
123+
## Extension points (future-friendly)
124+
125+
- Log backends could be abstracted (`compose` / `loki` today).
126+
- A structured JSON output mode would enable UI and MCP integration.
127+
- A lightweight daemon could subscribe to Docker events for near-real-time UI updates.

CLAUDE.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
---
2+
description: Use Bun instead of Node.js, npm, pnpm, or vite.
3+
globs: "*.ts, *.tsx, *.html, *.css, *.js, *.jsx, package.json"
4+
alwaysApply: false
5+
---
6+
7+
Default to using Bun instead of Node.js.
8+
9+
- Use `bun <file>` instead of `node <file>` or `ts-node <file>`
10+
- Use `bun test` instead of `jest` or `vitest`
11+
- Use `bun build <file.html|file.ts|file.css>` instead of `webpack` or `esbuild`
12+
- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
13+
- Use `bun run <script>` instead of `npm run <script>` or `yarn run <script>` or `pnpm run <script>`
14+
- Use `bunx <package> <command>` instead of `npx <package> <command>`
15+
- Bun automatically loads .env, so don't use dotenv.
16+
17+
## APIs
18+
19+
- `Bun.serve()` supports WebSockets, HTTPS, and routes. Don't use `express`.
20+
- `bun:sqlite` for SQLite. Don't use `better-sqlite3`.
21+
- `Bun.redis` for Redis. Don't use `ioredis`.
22+
- `Bun.sql` for Postgres. Don't use `pg` or `postgres.js`.
23+
- `WebSocket` is built-in. Don't use `ws`.
24+
- Prefer `Bun.file` over `node:fs`'s readFile/writeFile
25+
- Bun.$`ls` instead of execa.
26+
27+
## Testing
28+
29+
Use `bun test` to run tests.
30+
31+
```ts#index.test.ts
32+
import { test, expect } from "bun:test";
33+
34+
test("hello world", () => {
35+
expect(1).toBe(1);
36+
});
37+
```
38+
39+
## Coding Style & Naming Conventions
40+
41+
TypeScript (strict). Runtimes: Bun 1.3+, Node 23. Prettier: 2 spaces, no semicolons, double quotes, width 100. Import order: @ianvs/prettier-plugin-sort-imports + Tailwind plugin. ESLint (flat): any disallowed; unused vars warned (prefix \_ to ignore). Naming: React components PascalCase; files kebab-case (e.g., user-profile.ts); packages @repo/<name>. Comments: avoid writing inline comments everywhere, unless absolutely necessary for a todo or an important thing to take note of. Instead write tsdoc style block level comments at the method/class/function/route level. Focus on comments that provide value in regards to better type inference and clarity of usage. Using things like params/returns/etc..
42+
43+
Always default to useing named paramaters in functions eg myFunction({ ctx, other }) vs myFunction(ctx, other)

PACKAGING.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
## Packaging plan (non-blocking)
2+
3+
For now, the recommended workflow is **run locally with Bun**:
4+
5+
```bash
6+
bun install
7+
bun dev --help
8+
```
9+
10+
### Local install (dev + binary)
11+
12+
- **Dev (fastest iteration)**:
13+
14+
```bash
15+
bun run install:dev
16+
hack --help
17+
```
18+
19+
- **Binary (release-like)**:
20+
21+
```bash
22+
bun run install:bin
23+
hack --help
24+
```
25+
26+
### Phase 1: compiled Bun executable
27+
28+
We can produce a single executable with:
29+
30+
```bash
31+
bun run build
32+
```
33+
34+
This generates `dist/hack` via `bun build --compile`.
35+
36+
### Bundled assets (gum)
37+
38+
`hack global install` will try to install a bundled `gum` into `~/.hack/bin/gum` if it can find the release tarball(s).
39+
40+
To make that work in packaged distributions, ship the tarballs alongside the binary (or set an assets dir):
41+
42+
- **Preferred layout for a release artifact**:
43+
- `dist/hack`
44+
- `binaries/gum/gum_0.17.0_Darwin_arm64.tar.gz`
45+
- `binaries/gum/gum_0.17.0_Darwin_x86_64.tar.gz`
46+
47+
- **Optional override**:
48+
- Set `HACK_ASSETS_DIR` to a directory that contains either:
49+
- `<HACK_ASSETS_DIR>/binaries/gum/<tarball>`
50+
- `<HACK_ASSETS_DIR>/<tarball>`
51+
52+
If bundled assets aren’t present (or the platform isn’t supported), the CLI will fall back to `gum` on `PATH` (if present) or degrade gracefully.
53+
54+
### Phase 2: distribution
55+
56+
Once the binary + assets story is stable, options include:
57+
58+
- **Homebrew**: a tap formula that installs `dist/hack` (and optionally `binaries/gum/*`) to predictable locations.
59+
- **GitHub Releases**: upload the binary + `binaries/` as a tarball.
60+
- **npm**: publish a thin JS wrapper that downloads the right release artifact (optional; only if we want `npm i -g hack-cli`).

0 commit comments

Comments
 (0)