Skip to content
Open
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
17 changes: 17 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: CI

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

jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- run: bun install
- run: bun tsc --noEmit
- run: bun test
14 changes: 14 additions & 0 deletions .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name: Release Please

on:
push:
branches: [main]

jobs:
release-please:
runs-on: ubuntu-latest
steps:
- uses: googleapis/release-please-action@v4
with:
config-file: release-please-config.json
manifest-file: .release-please-manifest.json
3 changes: 3 additions & 0 deletions .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
".": "0.1.0"
}
8 changes: 7 additions & 1 deletion SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -347,10 +347,16 @@ mcp__aibtc__stacks_sign_message(message: "Bitcoin will be the currency of AIs")
```

Register:

**Important:** `stacks_sign_message` returns the signature with a `0x` prefix. The `/api/register` endpoint rejects it — strip the prefix before sending: `stx_sig="${stx_sig#0x}"`

```bash
# Strip 0x prefix from Stacks signature (AIBTC endpoint rejects it)
stx_sig="${stx_sig#0x}"

RESPONSE=$(curl -s -w "\n%{http_code}" -X POST https://aibtc.com/api/register \
-H "Content-Type: application/json" \
-d '{"bitcoinSignature":"<btc_sig>","stacksSignature":"<stx_sig>"}')
-d "{\"bitcoinSignature\":\"$btc_sig\",\"stacksSignature\":\"$stx_sig\",\"btcAddress\":\"$btc_address\"}")
HTTP_CODE=$(echo "$RESPONSE" | tail -1)
BODY=$(echo "$RESPONSE" | head -1)
if [ "$HTTP_CODE" != "200" ] && [ "$HTTP_CODE" != "201" ]; then
Expand Down
67 changes: 67 additions & 0 deletions daemon/loop.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { describe, it, expect } from "bun:test";
import { existsSync, readFileSync } from "fs";
import { join } from "path";

const ROOT = join(import.meta.dir, "..");

describe("loop-starter-kit scaffold", () => {
it("has SKILL.md", () => {
expect(existsSync(join(ROOT, "SKILL.md"))).toBe(true);
});

it("has CLAUDE.md template", () => {
expect(existsSync(join(ROOT, "CLAUDE.md"))).toBe(true);
});

it("has SOUL.md template", () => {
expect(existsSync(join(ROOT, "SOUL.md"))).toBe(true);
});

it("has daemon directory with required files", () => {
const required = ["loop.md", "STATE.md", "health.json", "queue.json", "processed.json", "outbox.json"];
for (const file of required) {
expect(existsSync(join(ROOT, "daemon", file))).toBe(true);
}
});

it("has memory directory with required files", () => {
const required = ["journal.md", "contacts.md", "learnings.md"];
for (const file of required) {
expect(existsSync(join(ROOT, "memory", file))).toBe(true);
}
});

it("health.json is valid JSON with expected fields", () => {
const health = JSON.parse(readFileSync(join(ROOT, "daemon", "health.json"), "utf-8"));
expect(health).toHaveProperty("cycle");
expect(health).toHaveProperty("status");
expect(health).toHaveProperty("phases");
});

it("SKILL.md documents 0x prefix stripping for registration", () => {
const skill = readFileSync(join(ROOT, "SKILL.md"), "utf-8");
expect(skill).toContain("0x prefix");
expect(skill).toContain("stx_sig=\"${stx_sig#0x}\"");
});

it("has wrangler.jsonc with worker-logs service binding", () => {
const wrangler = readFileSync(join(ROOT, "wrangler.jsonc"), "utf-8");
expect(wrangler).toContain("worker-logs");
expect(wrangler).toContain("WORKER_LOGS");
});

it("wrangler.jsonc has staging and production environments", () => {
const wrangler = readFileSync(join(ROOT, "wrangler.jsonc"), "utf-8");
expect(wrangler).toContain("staging");
expect(wrangler).toContain("production");
});

it("has CI workflow", () => {
expect(existsSync(join(ROOT, ".github", "workflows", "ci.yml"))).toBe(true);
});

it("has release-please config", () => {
expect(existsSync(join(ROOT, "release-please-config.json"))).toBe(true);
expect(existsSync(join(ROOT, ".release-please-manifest.json"))).toBe(true);
});
});
15 changes: 15 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "loop-starter-kit",
"version": "0.1.0",
"private": true,
"description": "Autonomous agent loop starter kit for AIBTC",
"scripts": {
"typecheck": "bun tsc --noEmit",
"test": "bun test",
"check": "bun tsc --noEmit && bun test"
},
"devDependencies": {
"typescript": "^5.7.0",
"@types/bun": "latest"
}
}
7 changes: 7 additions & 0 deletions release-please-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"packages": {
".": {
"release-type": "node"
}
}
}
19 changes: 19 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"compilerOptions": {
"strict": true,
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "bundler",
"skipLibCheck": true,
"outDir": "./dist",
"rootDir": ".",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
},
"include": ["daemon/**/*.ts", "scripts/**/*.ts"],
"exclude": ["node_modules", "dist"]
}
31 changes: 31 additions & 0 deletions wrangler.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
// Loop Starter Kit — Cloudflare Workers configuration
// See: https://developers.cloudflare.com/workers/wrangler/configuration/
"name": "loop-starter-kit",
"main": "daemon/index.ts",
"compatibility_date": "2025-12-01",

// Service bindings — connect to other workers in the AIBTC ecosystem
"services": [
{
// Central logging at logs.aibtc.com (issue #13)
"binding": "WORKER_LOGS",
"service": "worker-logs"
}
],

// Environment: staging (issue #14)
"env": {
"staging": {
"name": "loop-starter-kit-staging",
"vars": { "ENVIRONMENT": "staging" }
},
"production": {
"name": "loop-starter-kit",
"vars": { "ENVIRONMENT": "production" },
"routes": [
{ "pattern": "loop.drx4.xyz", "custom_domain": true }
]
}
}
}