Skip to content
Draft
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
80 changes: 80 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,83 @@ cache/
/infra/amp/providers/*.toml
/infra/amp/anvil.json
!/infra/amp/providers/anvil.toml

# Generated by Rulesync
**/AGENTS.md
**/.agents/
**/.agent/rules/
**/.agent/skills/
**/.agent/workflows/
**/.augmentignore
**/.augment/rules/
**/.augment-guidelines
**/CLAUDE.md
**/CLAUDE.local.md
**/.claude/CLAUDE.md
**/.claude/CLAUDE.local.md
**/.claude/memories/
**/.claude/rules/
**/.claude/commands/
**/.claude/agents/
**/.claude/skills/
**/.claude/settings.local.json
**/.mcp.json
**/.clinerules/
**/.clinerules/workflows/
**/.clineignore
**/.cline/mcp.json
**/.codexignore
**/.codex/memories/
**/.codex/skills/
**/.codex/subagents/
**/.cursor/
**/.cursorignore
**/.factorydroid/AGENTS.md
**/.factorydroid/memories/
**/.factorydroid/commands/
**/.factorydroid/droids/
**/.factorydroid/skills/
**/.factorydroid/mcp.json
**/GEMINI.md
**/.gemini/memories/
**/.gemini/commands/
**/.gemini/subagents/
**/.gemini/skills/
**/.geminiignore
**/.github/copilot-instructions.md
**/.github/instructions/
**/.github/prompts/
**/.github/agents/
**/.github/skills/
**/.vscode/mcp.json
**/.junie/guidelines.md
**/.junie/mcp.json
**/.kilocode/rules/
**/.kilocode/skills/
**/.kilocode/workflows/
**/.kilocode/mcp.json
**/.kilocodeignore
**/.kiro/steering/
**/.kiro/prompts/
**/.kiro/skills/
**/.kiro/agents/
**/.kiro/settings/mcp.json
**/.aiignore
**/.opencode/memories/
**/.opencode/command/
**/.opencode/agent/
**/.opencode/skill/
**/QWEN.md
**/.qwen/memories/
**/replit.md
**/.roo/rules/
**/.roo/skills/
**/.rooignore
**/.roo/mcp.json
**/.roo/subagents/
**/.warp/
**/WARP.md
**/modular-mcp.json
.rulesync/rules/*.local.md
rulesync.local.jsonc
!.rulesync/.aiignore
1 change: 1 addition & 0 deletions .rulesync/.aiignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
credentials/
17 changes: 17 additions & 0 deletions .rulesync/commands/review-pr.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
description: 'Review a pull request'
targets: ["*"]
---

target_pr = $ARGUMENTS

If target_pr is not provided, use the PR of the current branch.

Execute the following in parallel:

1. Check code quality and style consistency
2. Review test coverage
3. Verify documentation updates
4. Check for potential bugs or security issues

Then provide a summary of findings and suggestions for improvement.
134 changes: 134 additions & 0 deletions .rulesync/rules/effect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
---
root: false
targets: ["*"]
description: "When you write any code using Effect, you must follow these standards."
globs: ["**/*.ts"]
---

# Effect Standards

This codebase uses the [Effect](https://effect.website) library throughout. All new code must follow these patterns.

## Imports

Always import Effect modules as namespaces from subpaths:

```typescript
// Good
import * as Effect from "effect/Effect"
import * as Schema from "effect/Schema"
import * as Stream from "effect/Stream"

// Bad — never import from the barrel
import { Effect, Schema, Stream } from "effect"
```

## Services

Define services with `Context.Tag`:

```typescript
class MyService extends Context.Tag("Amp/MyService")<MyService, {
readonly doSomething: (input: string) => Effect.Effect<Result, MyError>
}>() {}
```

Access services in generators with `yield*`:

```typescript
Effect.gen(function*() {
const svc = yield* MyService
return yield* svc.doSomething("input")
})
```

## Layers

### Naming: exported layers must start with `layer`

```typescript
// Good
export const layer: Layer.Layer<MyService> = Layer.effect(MyService, make)
export const layerWithState = (initial: StateSnapshot): Layer.Layer<StateStore> => ...
export const layerTest = (options: TestOptions): Layer.Layer<StateStore> => ...

// Bad
export const live: Layer.Layer<MyService> = ...
export const testLayer: Layer.Layer<StateStore> = ...
```

### Compose layers ahead of time — never chain `Effect.provide`

Multiple `Effect.provide` calls each wrap the effect in another layer of indirection. Compose into a single layer and provide once:

```typescript
// Bad — repeated provide calls
Effect.runPromise(program.pipe(
Effect.provide(TransactionalStream.layer),
Effect.provide(InMemoryStateStore.layer),
Effect.provide(ProtocolStream.layer),
Effect.provide(ArrowFlight.layer),
Effect.provide(Transport.layer)
))

// Good — compose first, provide once
const AppLayer = TransactionalStream.layer.pipe(
Layer.provide(InMemoryStateStore.layer),
Layer.provide(ProtocolStream.layer),
Layer.provide(ArrowFlight.layer),
Layer.provide(Transport.layer)
)

Effect.runPromise(program.pipe(Effect.provide(AppLayer)))
```

## Use `Effect.fn` instead of arrow functions returning `Effect.gen`

Wrapping `Effect.gen` in an arrow function allocates a new generator on every call. `Effect.fn` avoids this overhead and automatically adds a tracing span.

```typescript
// Bad — allocates a generator per invocation
const query = (sql: string) =>
Effect.gen(function*() {
const svc = yield* ArrowFlight
return yield* svc.query(sql)
})

// Good
const query = Effect.fn("query")(function*(sql: string) {
const svc = yield* ArrowFlight
return yield* svc.query(sql)
})

// With explicit return type annotation
const query = Effect.fn("query")(function*(sql: string): Effect.fn.Return<
QueryResult,
ArrowFlightError
> {
const svc = yield* ArrowFlight
return yield* svc.query(sql)
})
```

## Schemas and Branded Types

Use `Schema.brand` for domain primitives:

```typescript
export const Network = Schema.Lowercase.pipe(
Schema.brand("Amp/Models/Network")
).annotations({ identifier: "Network" })
export type Network = typeof Network.Type
```

## Tagged Errors

Use `Schema.TaggedError` for typed, serializable errors:

```typescript
export class MyError extends Schema.TaggedError<MyError>(
"Amp/MyError"
)("MyError", {
cause: Schema.Defect
}) {}
```
63 changes: 63 additions & 0 deletions .rulesync/rules/overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
---
root: true
targets: ["*"]
description: "Project overview, architecture, and development guidelines"
globs: ["**/*"]
---

# Amp TypeScript SDK

## What Is This

A pnpm workspace monorepo for the **Amp TypeScript SDK** — a toolkit for building and managing blockchain datasets, built on the [Effect](https://effect.website) library.

| Package | Path | Description |
| ---------------------- | --------------------- | ------------------------------------------------- |
| `@edgeandnode/amp` | `packages/amp/` | Core SDK |
| `@edgeandnode/amp-cli` | `packages/cli/` | CLI tool (`amp` command) via `@effect/cli` |
| `@amp/oxc` | `packages/tools/oxc/` | Custom oxlint rules for Effect import conventions |

## Core SDK Module Map (`packages/amp/src/`)

```
src/
├── index.ts # Package entrypoint (namespace re-exports)
├── core.ts → core/ # Domain models: branded types, schemas (BlockRange, Network, AuthInfo, Dataset*, etc.)
├── arrow-flight.ts → arrow-flight/ # Arrow Flight SQL client (Transport, ArrowFlight service, errors)
├── protocol-stream.ts → protocol-stream/ # Stateless reorg detection on top of ArrowFlight streams
├── transactional-stream.ts → transactional-stream/ # Exactly-once semantics, crash recovery, commit control
├── auth/ # OAuth2 auth (device flow, token refresh, caching)
├── admin/ # Admin API (datasets, jobs, workers, providers)
├── registry/ # Registry API (dataset discovery)
├── protobuf/ # Generated protobuf (Flight, FlightSql)
└── internal/ # Private: Arrow IPC parsing, PKCE
```

Each public module has a **root-level barrel `.ts` file** in `src/` that re-exports from its subdirectory. No `index.ts` barrel files in subdirectories. `src/internal/` is blocked from external import.

## Commands

```bash
pnpm check # Type check root project
pnpm check:recursive # Type check all packages
pnpm lint # Check with oxlint + dprint
pnpm lint:fix # Auto-fix with oxlint + dprint
pnpm test # Run all tests (vitest)
pnpm vitest run <file> # Single test file
pnpm build # Full build (tsc + babel)
```

## Code Style

- **Formatter**: dprint — no semicolons (ASI), double quotes, no trailing commas, 120 char line width
- **Linter**: oxlint with custom `@amp/oxc` plugin
- **Array syntax**: `Array<T>` and `ReadonlyArray<T>`, never `T[]` (enforced by `typescript/array-type`)
- **Effect imports**: Always namespace imports from subpaths — `import * as Effect from "effect/Effect"`, never `from "effect"`
- **Type imports**: Inline — `import { type Foo, bar } from "..."` (enforced by `typescript/consistent-type-imports`)
- **TypeScript strictness**: `strict`, `exactOptionalPropertyTypes`, `noUnusedLocals`, `verbatimModuleSyntax`
- **File extensions**: `.ts` in all imports (rewritten to `.js` at build time)
- **Unused variables**: Prefix with `_`

## Agent Rules

All coding agent rules are managed via [rulesync](https://github.com/dyoshikawa/rulesync) in `.rulesync/rules/`. Edit rules there, then run `pnpm exec rulesync generate` to regenerate agent-specific config files. Do not edit generated files (e.g. `.claude/rules/`, `.opencode/rules/`) directly.
9 changes: 9 additions & 0 deletions .rulesync/skills/project-context/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
name: project-context
description: "Summarize the project context and key constraints"
targets: ["*"]
---

Summarize the project goals, core constraints, and relevant dependencies.
Call out any architecture decisions, shared conventions, and validation steps.
Keep the summary concise and ready to reuse in future tasks.
16 changes: 16 additions & 0 deletions .rulesync/subagents/planner.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
name: planner
targets: ["*"]
description: >-
This is the general-purpose planner. The user asks the agent to plan to
suggest a specification, implement a new feature, refactor the codebase, or
fix a bug. This agent can be called by the user explicitly only.
claudecode:
model: inherit
---

You are the planner for any tasks.

Based on the user's instruction, create a plan while analyzing the related files. Then, report the plan in detail. You can output files to @tmp/ if needed.

Attention, again, you are just the planner, so though you can read any files and run any commands for analysis, please don't write any code.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"globals": "^17.2.0",
"madge": "^8.0.0",
"oxlint": "^1.42.0",
"rulesync": "^6.7.0",
"ts-patch": "^3.3.0",
"typescript": "^5.9.3",
"vite-tsconfig-paths": "^6.0.5",
Expand Down
2 changes: 1 addition & 1 deletion packages/amp/src/admin/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import * as HttpApiError from "@effect/platform/HttpApiError"
import * as HttpApiGroup from "@effect/platform/HttpApiGroup"
import * as HttpApiSchema from "@effect/platform/HttpApiSchema"
import * as Schema from "effect/Schema"
import * as Models from "../models.ts"
import * as Models from "../core/domain.ts"
import * as Domain from "./domain.ts"
import * as Error from "./error.ts"

Expand Down
2 changes: 1 addition & 1 deletion packages/amp/src/admin/domain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* and responses.
*/
import * as Schema from "effect/Schema"
import * as Models from "../models.ts"
import * as Models from "../core/domain.ts"

// =============================================================================
// Dataset Request/Response Schemas
Expand Down
Loading
Loading