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
4 changes: 2 additions & 2 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ jobs:
- name: Install dependencies
run: pnpm i

- name: Run biome lint
run: pnpm run lint
# - name: Run biome lint
# run: pnpm run lint

- name: Build packages and examples
run: pnpm run build --ui stream
Expand Down
93 changes: 93 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Commands

### Building and Development
- `pnpm build` - Build all packages using Turbo
- `pnpm build:deps` - Build dependencies first
- `pnpm dev` - Start development mode with file watching (concurrency 15)
- `pnpm build-cores` - Build only core packages using build script
- `pnpm build-examples` - Build example applications

### Code Quality
- `pnpm lint` - Run Biome linter across all packages
- `pnpm prettier` - Format code using Prettier
- `pnpm prettier-check` - Check code formatting without changes
- `biome check --apply .` - Run Biome formatter and linter with auto-fixes (in individual packages)
- `biome format --write .` - Format code with Biome (in individual packages)

### Testing
- `vitest run` - Run tests (available in packages with test suites like core-dojo)

### Package Management
- `pnpm release` - Publish all packages to npm
- `pnpm release:dry-run` - Test publish without actually publishing

### Documentation
- `pnpm docs` - Generate TypeDoc documentation

## Architecture

This is a monorepo for Pixelaw.js, a JavaScript/TypeScript SDK for interacting with the PixeLAW on-chain autonomous world. The project uses:

- **Package Manager**: pnpm with workspaces
- **Build System**: Turbo for orchestration, tsup for TypeScript compilation
- **Code Quality**: Biome for linting/formatting (4-space indentation, double quotes)
- **Structure**: Lerna-based monorepo with independent package versioning

### Core Architecture

The repository is structured around multiple blockchain engine implementations:

1. **@pixelaw/core** - Base rendering and common functionality
- Canvas2D renderer for pixel-based graphics
- Ably integration for real-time updates
- Storage utilities and event handling
- Location: `packages/core/`

2. **@pixelaw/core-dojo** - Dojo (StarkNet) blockchain integration
- DojoEngine for StarkNet interaction
- Cartridge Controller wallet integration
- Generated contract bindings and models
- SQL-based pixel storage with web worker support
- Location: `packages/core-dojo/`

3. **@pixelaw/core-mud** - MUD blockchain integration
- Alternative blockchain backend
- Location: `packages/core-mud/`

4. **React Integration Packages**:
- `@pixelaw/react` - General React hooks and components
- `@pixelaw/react-dojo` - Dojo-specific React components and wallet selectors

5. **Tools**:
- `@pixelaw/imgtool-dojo` - Image processing utilities for Dojo

### Key Files and Patterns

- **Generated Code**: `packages/core-dojo/src/generated/` contains auto-generated contract bindings
- **Web Workers**: DojoSqlPixelStore uses web workers for performance (`DojoSqlPixelStore.webworker.js`)
- **Configuration**: Each package has its own `biome.json`, `tsconfig.json`, and `tsup.config.ts`
- **Build Scripts**: Located in `scripts/` directory for package-specific builds

### Development Workflow

1. The project uses Turbo's dependency graph to build packages in correct order
2. Core packages must be built before dependent packages
3. Development mode (`pnpm dev`) watches all packages concurrently
4. Biome is configured for consistent code style across all packages
5. Each package publishes independently to npm registry

### Engine Pattern

The codebase follows an engine pattern where:
- Core provides base rendering and utilities
- Engine-specific packages (core-dojo, core-mud) implement blockchain interactions
- React packages provide framework-specific integrations
- Applications compose these packages for specific use cases

### Testing

Tests are primarily located in `packages/core-dojo/src/__tests__/` and use Vitest. The main test focuses on simulation parsing and data handling.
2 changes: 1 addition & 1 deletion examples/example-nodejs-bot/README.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
# Bot that plays around autonomously on PixelAW using LLM and SDK
# Bot that plays around autonomously on PixelAW using LLM and SDK
106 changes: 57 additions & 49 deletions examples/example-nodejs-bot/src/PixelawAgent.ts
Original file line number Diff line number Diff line change
@@ -1,64 +1,72 @@
import "dotenv/config"
import type { CoreStatus, Engine, EngineConstructor, QueueItem, WorldsRegistry } from "@pixelaw/core"
import { PixelawCore } from "@pixelaw/core"
import type { DojoEngine } from "@pixelaw/core-dojo"
import type { Storage, StorageValue } from "unstorage"
import "dotenv/config";
import type {
CoreStatus,
Engine,
EngineConstructor,
QueueItem,
WorldsRegistry,
} from "@pixelaw/core";
import { PixelawCore } from "@pixelaw/core";
import type { DojoEngine } from "@pixelaw/core-dojo";
import type { Storage, StorageValue } from "unstorage";

export class PixelawAgent {
private core: PixelawCore
private core: PixelawCore;

public static async new(
engines: Record<string, EngineConstructor<Engine>>,
worldsRegistry: WorldsRegistry,
storage: Storage<StorageValue>,
): Promise<PixelawAgent> {
const agent = new PixelawAgent(engines, worldsRegistry, storage)
await agent.initialize()
return agent
}
public static async new(
engines: Record<string, EngineConstructor<Engine>>,
worldsRegistry: WorldsRegistry,
storage: Storage<StorageValue>,
): Promise<PixelawAgent> {
const agent = new PixelawAgent(engines, worldsRegistry, storage);
await agent.initialize();
return agent;
}

constructor(
engines: Record<string, EngineConstructor<Engine>>,
worldsRegistry: WorldsRegistry,
storage: Storage<StorageValue>,
) {
this.core = new PixelawCore(engines, worldsRegistry, storage)
}
constructor(
engines: Record<string, EngineConstructor<Engine>>,
worldsRegistry: WorldsRegistry,
storage: Storage<StorageValue>,
) {
this.core = new PixelawCore(engines, worldsRegistry, storage);
}

private async initialize() {
const handleStatusChange = (newStatus: CoreStatus) => {
console.log("Status changed:", newStatus)
}
private async initialize() {
const handleStatusChange = (newStatus: CoreStatus) => {
console.log("Status changed:", newStatus);
};

const handleEngineChange = (newEngine: Engine | null) => {
console.log("Engine changed:", newEngine?.constructor.name)
}
const handleEngineChange = (newEngine: Engine | null) => {
console.log("Engine changed:", newEngine?.constructor.name);
};

this.core.events.on("statusChanged", handleStatusChange)
this.core.events.on("engineChanged", handleEngineChange)
this.core.events.on("statusChanged", handleStatusChange);
this.core.events.on("engineChanged", handleEngineChange);

try {
const handleQueueItem = (item: QueueItem) => {
this.core
.executeQueueItem(item)
.then((ret) => {
console.log("handleQueueItem done", ret)
})
.catch(console.error)
}
try {
const handleQueueItem = (item: QueueItem) => {
this.core
.executeQueueItem(item)
.then((ret) => {
console.log("handleQueueItem done", ret);
})
.catch(console.error);
};

await this.core.loadWorld("local")
await this.core.loadWorld("local");

const dojoEngine: DojoEngine = this.core.engine as DojoEngine
const dojoEngine: DojoEngine = this.core.engine as DojoEngine;

// FIXME
this.core.setWallet(await dojoEngine.getPreDeployedWallet(process.env.WALLET_PK))
// FIXME
this.core.setWallet(
await dojoEngine.getPreDeployedWallet(process.env.WALLET_PK),
);

this.core.queue.eventEmitter.on("scheduled", handleQueueItem)
this.core.queue.eventEmitter.on("scheduled", handleQueueItem);

console.log("done loading")
} catch (error) {
console.error("Failed to load world:", error)
}
console.log("done loading");
} catch (error) {
console.error("Failed to load world:", error);
}
}
}
40 changes: 22 additions & 18 deletions examples/example-nodejs-bot/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
import {DojoEngine} from "@pixelaw/core-dojo"
import {PixelawAgent} from "./PixelawAgent"
import worldsRegistry from "./config/worlds.json"
import { DojoEngine } from "@pixelaw/core-dojo";
import { PixelawAgent } from "./PixelawAgent";
import worldsRegistry from "./config/worlds.json";

import {createDatabase} from "db0"
import sqlite from "db0/connectors/better-sqlite3"
import {createStorage} from "unstorage"
import dbDriver from "unstorage/drivers/db0" // Learn more: https://db0.unjs.io
import { createDatabase } from "db0";
import sqlite from "db0/connectors/better-sqlite3";
import { createStorage } from "unstorage";
import dbDriver from "unstorage/drivers/db0"; // Learn more: https://db0.unjs.io

// Learn more: https://db0.unjs.io
const database = createDatabase(
sqlite({
path: "db.sqlite",
/* db0 connector options */
}),
)
sqlite({
path: "db.sqlite",
/* db0 connector options */
}),
);

const storage = createStorage({
driver: dbDriver({
database,
tableName: "bot",
}),
})
driver: dbDriver({
database,
tableName: "bot",
}),
});

const agent = await PixelawAgent.new({"dojoengine": DojoEngine}, worldsRegistry, storage)
const agent = await PixelawAgent.new(
{ dojoengine: DojoEngine },
worldsRegistry,
storage,
);
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"clean": "bash ./scripts/clean.sh",
"prettier-check": "pnpx prettier --check packages examples",
"prettier": "pnpx prettier --write packages examples",
"lint": "pnpm lint",
"lint": "biome check .",
"release": "pnpm -F './packages/**' publish -r --force --no-git-checks",
"release:dry-run": "pnpm -F './packages/**' publish -r --force --dry-run --no-git-checks",
"publish_all": "pnpm -F './packages/**' publish -r --force",
Expand Down
23 changes: 12 additions & 11 deletions packages/core-dojo/README.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,31 @@
# How Interactions (should) work

- user clicks on pixel
- user clicks on pixel
- retrieve function signature
- if just DefaultParams : prep action function -> return
- prep Param objects for each
- has transformer? (prefix with "crc","crv", "crs")


# Function signature fields
Example from RPS:

Example from RPS:

- first_move(crc_move_Move)
- second_move(move)
- finish(crv_move, crs_move)

# "Param.transformer()"
-

-

# Param Transformers

- "crc_VARNAME_VARTYPE": CommitReveal: Commit
- collect variable value from UI
- create salt
- Hash variable with salt
- submit the hash as felt252
- store VARNAME+SALT in KVstore for later retrieval
- store VARNAME+SALT in KVstore for later retrieval
- "crv_VARNAME": CommitReveal: Value
- retrieve value of VARNAME from storage
- submit value
Expand All @@ -32,9 +35,7 @@ Example from RPS:

- ContractCall
- Params
- StringParam
- NumberParam
- EnumParam
-


- StringParam
- NumberParam
- EnumParam
-
Loading