Skip to content

Development Workflow

Sprawl uses tsx for TypeScript execution, Vitest for testing, and Just for task orchestration. All TS apps run directly from source — no build step. Optic is the exception (Rust, compiled with cargo).

FileRole
JustfileTask runner (primary interface)
pnpm-workspace.yamlWorkspace config
apps/*/package.jsonApp dependencies
packages/*/package.jsonPackage dependencies
CommandDescription
just devConstruct dev mode (file watching)
just start <instance>Start named Construct instance (reads .env.<instance>)
just cli [instance] [args]Construct CLI
just cortex-devCortex dev mode
just cortex-startCortex production
just cortex-backfill [days]Backfill historical data
just synapse-devSynapse dev mode
just synapse-startSynapse production
just synapse-statusPortfolio summary
just deck-dev <instance>Deck dev mode
just optic [db] [synapse]Optic TUI
just optic-buildBuild Optic release binary
just testRun all tests (pnpm -r run test)
just test-constructConstruct tests only
just test-cairnCairn tests only
just test-synapseSynapse tests only
just test-aiAI integration tests
just typecheckTypecheck all packages
just db-migrate [inst]Run Construct DB migrations

Each app reads its env from .env.construct, .env.cortex, .env.synapse, .env.loom, etc. Named instances (e.g., just start myinstance) read from .env.myinstance.

{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"noEmit": true,
"noUnusedLocals": true,
"noUnusedParameters": true
}
}

Key points:

  • No compilation: noEmit: true — tsx handles runtime transpilation
  • Path alias: @/* maps to ./src/* (used in vitest config)
  • Strict mode: Full TypeScript strict checks enabled
  • Bundler module resolution: Modern resolution compatible with tsx

The project uses tsx (via --import=tsx) as a TypeScript loader. This means:

  • No build step required
  • Source files are transpiled on-the-fly
  • File watching uses Node.js native --watch-path flag
  • Extension tool files use jiti instead of tsx for dynamic loading
export default defineConfig({
resolve: {
alias: { "@": new URL("./src", import.meta.url).pathname },
},
test: {
globals: true,
environment: "node",
},
});
  • Global test functions: describe, it, expect, etc. are available without imports
  • Node environment: Tests run in Node.js (not jsdom)
  • Path alias: @/ resolves to src/ in test files

Tests are colocated with their source in __tests__/ directories:

src/tools/core/__tests__/
memory.test.ts
schedule.test.ts
src/tools/self/__tests__/
deploy.test.ts
exec.test.ts
extension-scope.test.ts
self.test.ts
src/tools/web/__tests__/
web.test.ts
src/tools/__tests__/
packs.test.ts
src/extensions/__tests__/
dynamic-tools.test.ts
loader.test.ts
secrets.test.ts
skills.test.ts
Terminal window
just test # Run all tests (pnpm -r run test)
just test-construct # Construct tests only
just test-cairn # Cairn tests only
npx vitest run -t memory # Filter by test name

The self_run_tests tool also runs npx vitest run --reporter=verbose with a 60-second timeout.

The logging system uses @logtape/logtape with these loggers:

LoggerCategory
log['construct']
agentLog['construct', 'agent']
toolLog['construct', 'tool']
telegramLog['construct', 'telegram']
schedulerLog['construct', 'scheduler']
dbLog['construct', 'db']
  • Console: Always active, uses a custom formatter
  • File: Active when LOG_FILE is set. Uses a swappable WriteStream to support runtime log rotation.
2026-02-24T15:30:00.000Z [info] construct.agent: Processing message from telegram
  • Automatic: On startup, if the log file exceeds 5 MB, it is rotated
  • Manual: The shell tool can trigger rotation (e.g., truncate -c -s 0 logfile)
  • Rotation keeps up to 3 archived files: construct.log.1, construct.log.2, construct.log.3

Deployment is manual. After the agent edits code via edit and shell tools:

Docker:

Terminal window
docker compose -f deploy/docker-compose.yml up -d --build

Systemd:

Terminal window
sudo systemctl restart construct

Recommended: run just check before deploying. Create a git tag for rollback:

Terminal window
git tag pre-deploy-$(date -u +%Y%m%d-%H%M%S)

See Deployment Guide for full details.

When NODE_ENV=development:

  • File watching is active (--watch-path)
  • Deployment is manual (no self-deploy tool)
  • Context preamble includes [DEV MODE] and a development warning
  • EXTENSIONS_DIR defaults to ./data instead of XDG path
PackageVersionPurpose
@mariozechner/pi-agent-core^0.54.2Agent framework
@mariozechner/pi-ai^0.54.2LLM model access
@sinclair/typebox^0.34.48JSON Schema / TypeBox for tool parameters
grammy^1.40.0Telegram Bot API
kysely^0.28.11Type-safe SQL query builder
croner^10.0.1Cron job scheduling
citty^0.2.1CLI framework
jiti^2.6.1Dynamic TypeScript loading (extensions)
@logtape/logtape^2.0.2Structured logging
nanoid^5.1.6ID generation
yaml^2.8.2YAML parsing (skill frontmatter)
zod^4.3.6Environment validation
date-fns^4.1.0Date utilities
chalk^5.6.2Terminal coloring
consola^3.4.2Console utilities
PackageVersionPurpose
typescript^5.9.3Type checking
tsx^4.21.0TypeScript execution
vitest^4.0.18Testing framework
@types/node^25.3.0Node.js type definitions