@jamesaphoenix/tx-test-utils 0.4.2 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +480 -0
- package/dist/adapters/better-sqlite3-adapter.d.ts +36 -0
- package/dist/adapters/better-sqlite3-adapter.d.ts.map +1 -0
- package/dist/adapters/better-sqlite3-adapter.js +78 -0
- package/dist/adapters/better-sqlite3-adapter.js.map +1 -0
- package/dist/chaos/chaos-utilities.d.ts +465 -0
- package/dist/chaos/chaos-utilities.d.ts.map +1 -0
- package/dist/chaos/chaos-utilities.js +793 -0
- package/dist/chaos/chaos-utilities.js.map +1 -0
- package/dist/chaos/chaos.test.d.ts +9 -0
- package/dist/chaos/chaos.test.d.ts.map +1 -0
- package/dist/chaos/chaos.test.js +498 -0
- package/dist/chaos/chaos.test.js.map +1 -0
- package/dist/chaos/index.d.ts +20 -0
- package/dist/chaos/index.d.ts.map +1 -0
- package/dist/chaos/index.js +39 -0
- package/dist/chaos/index.js.map +1 -0
- package/dist/database/index.d.ts +1 -1
- package/dist/database/index.d.ts.map +1 -1
- package/dist/database/index.js +1 -1
- package/dist/database/index.js.map +1 -1
- package/dist/database/test-database.d.ts +25 -5
- package/dist/database/test-database.d.ts.map +1 -1
- package/dist/database/test-database.js +142 -11
- package/dist/database/test-database.js.map +1 -1
- package/dist/factories/index.d.ts +0 -3
- package/dist/factories/index.d.ts.map +1 -1
- package/dist/factories/index.js +0 -6
- package/dist/factories/index.js.map +1 -1
- package/dist/factories/learning.factory.js +1 -1
- package/dist/factories/learning.factory.js.map +1 -1
- package/dist/helpers/index.d.ts +2 -0
- package/dist/helpers/index.d.ts.map +1 -1
- package/dist/helpers/index.js +4 -0
- package/dist/helpers/index.js.map +1 -1
- package/dist/helpers/shared-test-layer.d.ts +74 -0
- package/dist/helpers/shared-test-layer.d.ts.map +1 -0
- package/dist/helpers/shared-test-layer.js +104 -0
- package/dist/helpers/shared-test-layer.js.map +1 -0
- package/dist/helpers/sqlite-factory.d.ts +49 -0
- package/dist/helpers/sqlite-factory.d.ts.map +1 -0
- package/dist/helpers/sqlite-factory.js +74 -0
- package/dist/helpers/sqlite-factory.js.map +1 -0
- package/dist/index.d.ts +6 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +30 -8
- package/dist/index.js.map +1 -1
- package/dist/singleton.d.ts +49 -0
- package/dist/singleton.d.ts.map +1 -0
- package/dist/singleton.js +66 -0
- package/dist/singleton.js.map +1 -0
- package/package.json +11 -10
- package/dist/factories/factories.test.d.ts +0 -8
- package/dist/factories/factories.test.d.ts.map +0 -1
- package/dist/factories/factories.test.js +0 -419
- package/dist/factories/factories.test.js.map +0 -1
package/README.md
ADDED
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
# tx
|
|
2
|
+
|
|
3
|
+
**TanStack for AI agents.** Primitives, not frameworks.
|
|
4
|
+
|
|
5
|
+
Headless infrastructure for memory, tasks, and orchestration.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @jamesaphoenix/tx
|
|
9
|
+
tx init
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## The Problem
|
|
15
|
+
|
|
16
|
+
Your agents lose context between sessions. Tasks collide when multiple agents work in parallel. Learnings vanish into conversation history. You're rebuilding the same infrastructure every project.
|
|
17
|
+
|
|
18
|
+
## The Solution
|
|
19
|
+
|
|
20
|
+
Composable primitives that handle the hard parts. You keep control of the orchestration.
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
┌─────────────────────────────────────────────────────────┐
|
|
24
|
+
│ Your Orchestration (your code, your rules) │
|
|
25
|
+
├─────────────────────────────────────────────────────────┤
|
|
26
|
+
│ tx primitives │
|
|
27
|
+
│ │
|
|
28
|
+
│ tx ready tx done tx context tx learn │
|
|
29
|
+
│ tx claim tx block tx handoff tx sync │
|
|
30
|
+
│ │
|
|
31
|
+
└─────────────────────────────────────────────────────────┘
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Primitives
|
|
37
|
+
|
|
38
|
+
### Memory
|
|
39
|
+
|
|
40
|
+
Learnings that persist and surface when relevant.
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# Store knowledge (with optional file path)
|
|
44
|
+
tx learning:add "Use bcrypt for passwords, not SHA256" --file src/auth/hash.ts
|
|
45
|
+
tx learning:add "Redis cache invalidation has race conditions"
|
|
46
|
+
|
|
47
|
+
# Retrieve via search or task context
|
|
48
|
+
tx learning:search "authentication"
|
|
49
|
+
tx context tx-abc123 # Get relevant learnings for a task
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Learnings can be tagged with file paths for organization. Hybrid search (BM25 + vector) finds relevant knowledge.
|
|
53
|
+
|
|
54
|
+
### Tasks
|
|
55
|
+
|
|
56
|
+
Dependency-aware task management. Agents only see work they can actually do.
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# Create with dependencies
|
|
60
|
+
tx add "Implement auth service" --score 800
|
|
61
|
+
tx add "Design auth schema" --score 900
|
|
62
|
+
tx block tx-impl tx-schema # impl waits for schema
|
|
63
|
+
|
|
64
|
+
# Work on what's ready
|
|
65
|
+
tx ready # Only unblocked tasks
|
|
66
|
+
tx done tx-schema # Completes → unblocks dependents
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Full hierarchy support. Epics contain milestones contain tasks contain subtasks.
|
|
70
|
+
|
|
71
|
+
### Coordination
|
|
72
|
+
|
|
73
|
+
Primitives for multi-agent workflows without prescribing the pattern.
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
tx claim tx-abc123 # Prevent collisions
|
|
77
|
+
tx checkpoint tx-abc123 \
|
|
78
|
+
--note "API done, UI next" # Save progress
|
|
79
|
+
tx handoff tx-abc123 \
|
|
80
|
+
--to reviewer # Transfer with context
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Your Loop, Your Rules
|
|
86
|
+
|
|
87
|
+
We ship **example loops**, not **the loop**:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# Simple: one agent, one task
|
|
91
|
+
while task=$(tx ready --limit 1 --json | jq -r '.[0].id'); do
|
|
92
|
+
claude "Work on task $task, then run: tx done $task"
|
|
93
|
+
done
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
# Parallel: N agents pulling from queue
|
|
98
|
+
for i in {1..5}; do
|
|
99
|
+
(while task=$(tx claim --next); do
|
|
100
|
+
claude "Complete $task" && tx done $task
|
|
101
|
+
done) &
|
|
102
|
+
done
|
|
103
|
+
wait
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
# Human-in-loop: agent proposes, human approves
|
|
108
|
+
task=$(tx ready --limit 1)
|
|
109
|
+
claude "Plan implementation for $task" > plan.md
|
|
110
|
+
read -p "Approve? [y/n] " && claude "Execute plan.md"
|
|
111
|
+
tx done $task
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**The flow is yours.** Serial, parallel, swarm, human-in-loop. Your call.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Why tx?
|
|
119
|
+
|
|
120
|
+
| | Native Tasks | CLAUDE.md | tx |
|
|
121
|
+
|---|---|---|---|
|
|
122
|
+
| **Persistence** | Session-scoped | File grows forever | Git-native, branch-aware |
|
|
123
|
+
| **Multi-agent** | Collisions | Manual coordination | Claim, block, handoff |
|
|
124
|
+
| **Knowledge** | Lost each session | Static dump | Graph RAG, contextual retrieval |
|
|
125
|
+
| **Orchestration** | None | None | Primitives for any pattern |
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Design Principles
|
|
130
|
+
|
|
131
|
+
- **No opinions on orchestration.** Serial, parallel, swarm, human-in-loop. Your call.
|
|
132
|
+
- **Powerful defaults.** `tx ready` just works. So does dependency resolution.
|
|
133
|
+
- **Escape hatches everywhere.** Raw SQL access, JSONL export, custom scoring.
|
|
134
|
+
- **Framework agnostic.** CLI, MCP, REST API, TypeScript SDK. Use what fits.
|
|
135
|
+
- **Local-first.** SQLite + git. No server required. Works offline.
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Non-Goals
|
|
140
|
+
|
|
141
|
+
- **Not an agent framework.** You bring your own orchestration.
|
|
142
|
+
- **Not a hosted memory product.** Local-first, your data stays yours.
|
|
143
|
+
- **Not a prompt library.** Primitives, not templates.
|
|
144
|
+
- **Not a replacement for your issue tracker.** (Unless you want it to be.)
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Three Systems
|
|
149
|
+
|
|
150
|
+
### 1. Knowledge System
|
|
151
|
+
|
|
152
|
+
**Working today:**
|
|
153
|
+
- Learnings stored with file path tags
|
|
154
|
+
- Basic hybrid search (BM25 + vector)
|
|
155
|
+
- Retrieval by task ID via `tx context`
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
tx learning:add "Use bcrypt for passwords" --file src/auth/hash.ts
|
|
159
|
+
tx learning:search "authentication"
|
|
160
|
+
tx context tx-abc123 # Get learnings relevant to a task
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**Research in progress:**
|
|
164
|
+
- Symbol anchoring (AST-based code references, not just file paths)
|
|
165
|
+
- Knowledge graph expansion (automatic relationship discovery)
|
|
166
|
+
- Auto-invalidation when code changes
|
|
167
|
+
|
|
168
|
+
### 2. Task System
|
|
169
|
+
|
|
170
|
+
**Working today:**
|
|
171
|
+
- N-level hierarchy (epics → tasks → subtasks)
|
|
172
|
+
- Explicit dependencies with cycle detection
|
|
173
|
+
- Priority scoring
|
|
174
|
+
- Claim/release with lease expiry
|
|
175
|
+
|
|
176
|
+
```
|
|
177
|
+
Epic: "User Authentication"
|
|
178
|
+
├── Task: "Design schema" ✓ done
|
|
179
|
+
├── Task: "Implement service" ● ready (unblocked)
|
|
180
|
+
│ └── blocks: "Write tests", "Add endpoints"
|
|
181
|
+
└── Task: "Write tests" ○ blocked
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
**Research in progress:**
|
|
185
|
+
- LLM-based reprioritization
|
|
186
|
+
- Automatic task decomposition
|
|
187
|
+
|
|
188
|
+
### 3. Worker System
|
|
189
|
+
|
|
190
|
+
**Working today:**
|
|
191
|
+
- `runWorker()` with execute/captureIO hooks
|
|
192
|
+
- Lease-based claims (prevents collisions)
|
|
193
|
+
- Automatic lease renewal
|
|
194
|
+
- Coordinator reconciliation (dead worker recovery)
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
runWorker({
|
|
198
|
+
execute: async (task, ctx) => {
|
|
199
|
+
await ctx.renewLease() // For long tasks
|
|
200
|
+
return { success: true }
|
|
201
|
+
}
|
|
202
|
+
})
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
**Research in progress:**
|
|
206
|
+
- Daemon watching `~/.claude/projects/**/*.jsonl`
|
|
207
|
+
- Automatic learning extraction from sessions
|
|
208
|
+
- Confidence scoring for auto-promotion
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## Worker Orchestration (TypeScript SDK)
|
|
213
|
+
|
|
214
|
+
For programmatic control, the TypeScript SDK provides `runWorker()` — a headless worker that executes tasks using your hooks.
|
|
215
|
+
|
|
216
|
+
### Two Hooks. That's It.
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
import { runWorker } from "@jamesaphoenix/tx-core"
|
|
220
|
+
|
|
221
|
+
runWorker({
|
|
222
|
+
name: "my-worker",
|
|
223
|
+
execute: async (task, ctx) => {
|
|
224
|
+
// YOUR LOGIC HERE
|
|
225
|
+
console.log(`Working on: ${task.title}`)
|
|
226
|
+
|
|
227
|
+
// Use ctx.renewLease() for long tasks
|
|
228
|
+
await ctx.renewLease()
|
|
229
|
+
|
|
230
|
+
// Return success or failure
|
|
231
|
+
return { success: true, output: "Done!" }
|
|
232
|
+
},
|
|
233
|
+
captureIO: (runId, task) => ({
|
|
234
|
+
transcriptPath: `.tx/runs/${runId}.jsonl`,
|
|
235
|
+
stderrPath: `.tx/runs/${runId}.stderr`
|
|
236
|
+
})
|
|
237
|
+
})
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
| Hook | Required | Purpose |
|
|
241
|
+
|------|----------|---------|
|
|
242
|
+
| `execute` | Yes | Your task execution logic |
|
|
243
|
+
| `captureIO` | No | Paths for transcript/stderr/stdout capture |
|
|
244
|
+
|
|
245
|
+
### WorkerContext
|
|
246
|
+
|
|
247
|
+
The `ctx` object provides tx primitives:
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
interface WorkerContext {
|
|
251
|
+
workerId: string // This worker's ID
|
|
252
|
+
runId: string // Unique ID for this execution
|
|
253
|
+
renewLease: () => Promise<void> // Extend lease for long tasks
|
|
254
|
+
log: (message: string) => void // Log with worker prefix
|
|
255
|
+
state: Record<string, unknown> // Mutable state within task
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Custom Context
|
|
260
|
+
|
|
261
|
+
Pass your own primitives via generics:
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
interface MyContext {
|
|
265
|
+
llm: AnthropicClient
|
|
266
|
+
db: Database
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
runWorker<MyContext>({
|
|
270
|
+
context: {
|
|
271
|
+
llm: new Anthropic(),
|
|
272
|
+
db: myDatabase
|
|
273
|
+
},
|
|
274
|
+
execute: async (task, ctx) => {
|
|
275
|
+
// ctx.llm and ctx.db available here
|
|
276
|
+
const response = await ctx.llm.messages.create(...)
|
|
277
|
+
return { success: true }
|
|
278
|
+
}
|
|
279
|
+
})
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Claims and Leases
|
|
283
|
+
|
|
284
|
+
Workers use a lease-based system to prevent collisions:
|
|
285
|
+
|
|
286
|
+
```
|
|
287
|
+
Worker A claims task → Lease expires in 30 min → Renew or lose it
|
|
288
|
+
Worker B tries to claim same task → Rejected (already claimed)
|
|
289
|
+
Worker A dies → Lease expires → Coordinator reclaims task
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
Key points:
|
|
293
|
+
- **Claims are atomic** — only one worker can claim a task
|
|
294
|
+
- **Leases expire** — prevents stuck tasks from dead workers
|
|
295
|
+
- **Auto-renewal** — `runWorker()` renews automatically; use `ctx.renewLease()` for extra-long tasks
|
|
296
|
+
- **Coordinator reconciles** — dead workers detected, orphaned tasks recovered
|
|
297
|
+
|
|
298
|
+
### Example Worker Loops
|
|
299
|
+
|
|
300
|
+
#### Basic: One Worker
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
import { Effect, Layer } from "effect"
|
|
304
|
+
import { runWorker, makeMinimalLayer, SqliteClientLive } from "@jamesaphoenix/tx-core"
|
|
305
|
+
|
|
306
|
+
const layer = makeMinimalLayer.pipe(
|
|
307
|
+
Layer.provide(SqliteClientLive(".tx/tasks.db"))
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
Effect.runPromise(
|
|
311
|
+
runWorker({
|
|
312
|
+
execute: async (task, ctx) => {
|
|
313
|
+
ctx.log(`Processing: ${task.title}`)
|
|
314
|
+
// ... your logic
|
|
315
|
+
return { success: true }
|
|
316
|
+
}
|
|
317
|
+
}).pipe(Effect.provide(layer))
|
|
318
|
+
)
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
#### With Claude Code
|
|
322
|
+
|
|
323
|
+
```typescript
|
|
324
|
+
import { spawn } from "child_process"
|
|
325
|
+
|
|
326
|
+
runWorker({
|
|
327
|
+
execute: async (task, ctx) => {
|
|
328
|
+
return new Promise((resolve) => {
|
|
329
|
+
const proc = spawn("claude", [
|
|
330
|
+
"--print",
|
|
331
|
+
`Work on task ${task.id}: ${task.title}`
|
|
332
|
+
])
|
|
333
|
+
|
|
334
|
+
proc.on("close", (code) => {
|
|
335
|
+
resolve({
|
|
336
|
+
success: code === 0,
|
|
337
|
+
error: code !== 0 ? `Exit code ${code}` : undefined
|
|
338
|
+
})
|
|
339
|
+
})
|
|
340
|
+
})
|
|
341
|
+
}
|
|
342
|
+
})
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
#### Parallel Workers
|
|
346
|
+
|
|
347
|
+
```typescript
|
|
348
|
+
// Start N workers (each in its own process or fiber)
|
|
349
|
+
for (let i = 0; i < 5; i++) {
|
|
350
|
+
Effect.fork(
|
|
351
|
+
runWorker({
|
|
352
|
+
name: `worker-${i}`,
|
|
353
|
+
execute: async (task, ctx) => {
|
|
354
|
+
// Workers automatically coordinate via claims
|
|
355
|
+
return { success: true }
|
|
356
|
+
}
|
|
357
|
+
})
|
|
358
|
+
)
|
|
359
|
+
}
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
#### Long-Running Tasks
|
|
363
|
+
|
|
364
|
+
```typescript
|
|
365
|
+
runWorker({
|
|
366
|
+
execute: async (task, ctx) => {
|
|
367
|
+
for (let step = 0; step < 100; step++) {
|
|
368
|
+
// Periodic lease renewal for tasks > 30 min
|
|
369
|
+
if (step % 10 === 0) {
|
|
370
|
+
await ctx.renewLease()
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Track progress in mutable state
|
|
374
|
+
ctx.state.progress = step
|
|
375
|
+
|
|
376
|
+
await doExpensiveWork(step)
|
|
377
|
+
}
|
|
378
|
+
return { success: true }
|
|
379
|
+
}
|
|
380
|
+
})
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
---
|
|
384
|
+
|
|
385
|
+
## Interfaces
|
|
386
|
+
|
|
387
|
+
| Interface | Use Case |
|
|
388
|
+
|-----------|----------|
|
|
389
|
+
| **CLI** | Scripts, terminal workflows, RALPH loops |
|
|
390
|
+
| **MCP Server** | Claude Code integration (16 tools) |
|
|
391
|
+
| **REST API** | Custom dashboards, external integrations |
|
|
392
|
+
| **TypeScript SDK** | Programmatic access from your agents |
|
|
393
|
+
| **Dashboard** | Visual monitoring and management |
|
|
394
|
+
|
|
395
|
+
---
|
|
396
|
+
|
|
397
|
+
## Quick Reference
|
|
398
|
+
|
|
399
|
+
```bash
|
|
400
|
+
# Tasks
|
|
401
|
+
tx add <title> # Create
|
|
402
|
+
tx ready # List unblocked
|
|
403
|
+
tx done <id> # Complete
|
|
404
|
+
tx block <id> <blocker> # Add dependency
|
|
405
|
+
tx tree <id> # Show hierarchy
|
|
406
|
+
|
|
407
|
+
# Memory
|
|
408
|
+
tx learning:add <content> # Store
|
|
409
|
+
tx learning:search <query> # Find
|
|
410
|
+
tx context <task-id> # Contextual retrieval
|
|
411
|
+
|
|
412
|
+
# Coordination
|
|
413
|
+
tx claim <id> # Prevent collisions
|
|
414
|
+
tx handoff <id> --to <agent>
|
|
415
|
+
tx checkpoint <id> --note "..."
|
|
416
|
+
|
|
417
|
+
# Sync
|
|
418
|
+
tx sync export # SQLite → JSONL (git-friendly)
|
|
419
|
+
tx sync import # JSONL → SQLite
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
---
|
|
423
|
+
|
|
424
|
+
## Storage
|
|
425
|
+
|
|
426
|
+
```
|
|
427
|
+
.tx/
|
|
428
|
+
├── tasks.db # SQLite (gitignored)
|
|
429
|
+
├── tasks.jsonl # Git-tracked
|
|
430
|
+
├── learnings.jsonl # Git-tracked
|
|
431
|
+
└── runs.jsonl # Git-tracked
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
Local SQLite for speed. JSONL for git sync. Branch your knowledge with your code.
|
|
435
|
+
|
|
436
|
+
---
|
|
437
|
+
|
|
438
|
+
## Status
|
|
439
|
+
|
|
440
|
+
**Shipping now (concrete, tested):**
|
|
441
|
+
- Core task primitives: add, ready, done, block, claim, handoff
|
|
442
|
+
- Dependency management with cycle detection
|
|
443
|
+
- Worker orchestration via `runWorker()` with claims/leases
|
|
444
|
+
- Learnings with file path tagging
|
|
445
|
+
- Hybrid search (BM25 + vector)
|
|
446
|
+
- CLI (20+ commands), MCP server (16 tools)
|
|
447
|
+
- 389+ tests
|
|
448
|
+
|
|
449
|
+
**Research in progress (not yet stable):**
|
|
450
|
+
- Symbol anchoring (AST-based code references)
|
|
451
|
+
- Knowledge graph expansion
|
|
452
|
+
- Auto-invalidation when code changes
|
|
453
|
+
- Daemon-based learning extraction
|
|
454
|
+
- LLM reprioritization
|
|
455
|
+
|
|
456
|
+
---
|
|
457
|
+
|
|
458
|
+
## Documentation
|
|
459
|
+
|
|
460
|
+
- **[CLAUDE.md](CLAUDE.md)**: Doctrine and quick reference
|
|
461
|
+
- **[docs/](docs/)**: Full documentation (17 PRDs, 17 Design Docs)
|
|
462
|
+
|
|
463
|
+
### Docs Site
|
|
464
|
+
|
|
465
|
+
Run the documentation site locally:
|
|
466
|
+
|
|
467
|
+
```bash
|
|
468
|
+
cd apps/docs
|
|
469
|
+
npm run dev # Development server at http://localhost:3000
|
|
470
|
+
npm run build # Production build
|
|
471
|
+
npm run start # Serve production build
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
The docs site is built with [Fumadocs](https://fumadocs.vercel.app/) and Next.js, featuring full-text search, syntax highlighting, and automatic navigation from the markdown files in `docs/`.
|
|
475
|
+
|
|
476
|
+
---
|
|
477
|
+
|
|
478
|
+
## License
|
|
479
|
+
|
|
480
|
+
MIT
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* better-sqlite3 adapter for Vitest compatibility
|
|
3
|
+
*
|
|
4
|
+
* Wraps better-sqlite3 to match the SqliteDatabase interface used by @tx/core.
|
|
5
|
+
* This allows tests to use Vitest (with proper parallelization) while production
|
|
6
|
+
* uses bun:sqlite.
|
|
7
|
+
*
|
|
8
|
+
* The SqliteDatabase interface abstracts away the implementation, so this adapter
|
|
9
|
+
* makes better-sqlite3 compatible with the existing code.
|
|
10
|
+
*/
|
|
11
|
+
import type { SqliteDatabase } from "@jamesaphoenix/tx-core";
|
|
12
|
+
type BetterSqlite3Database = {
|
|
13
|
+
prepare(sql: string): {
|
|
14
|
+
run(...params: unknown[]): {
|
|
15
|
+
lastInsertRowid: number | bigint;
|
|
16
|
+
changes: number;
|
|
17
|
+
};
|
|
18
|
+
get(...params: unknown[]): unknown;
|
|
19
|
+
all(...params: unknown[]): unknown[];
|
|
20
|
+
};
|
|
21
|
+
exec(sql: string): void;
|
|
22
|
+
close(): void;
|
|
23
|
+
pragma(key: string, value?: unknown): unknown;
|
|
24
|
+
transaction<T>(fn: () => T): () => T;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Wrap a better-sqlite3 Database to match SqliteDatabase interface
|
|
28
|
+
*/
|
|
29
|
+
export declare function wrapBetterSqlite3(db: BetterSqlite3Database): SqliteDatabase;
|
|
30
|
+
/**
|
|
31
|
+
* Create an in-memory better-sqlite3 database wrapped as SqliteDatabase
|
|
32
|
+
* with migrations applied.
|
|
33
|
+
*/
|
|
34
|
+
export declare function createBetterSqlite3Database(): Promise<SqliteDatabase>;
|
|
35
|
+
export {};
|
|
36
|
+
//# sourceMappingURL=better-sqlite3-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"better-sqlite3-adapter.d.ts","sourceRoot":"","sources":["../../src/adapters/better-sqlite3-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAoC,MAAM,wBAAwB,CAAA;AAG9F,KAAK,qBAAqB,GAAG;IAC3B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG;QACpB,GAAG,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG;YAAE,eAAe,EAAE,MAAM,GAAG,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAA;QAChF,GAAG,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAA;QAClC,GAAG,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAAA;KACrC,CAAA;IACD,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,KAAK,IAAI,IAAI,CAAA;IACb,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAA;IAC7C,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,CAAA;CACrC,CAAA;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,qBAAqB,GAAG,cAAc,CA8C3E;AAED;;;GAGG;AACH,wBAAsB,2BAA2B,IAAI,OAAO,CAAC,cAAc,CAAC,CAe3E"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* better-sqlite3 adapter for Vitest compatibility
|
|
3
|
+
*
|
|
4
|
+
* Wraps better-sqlite3 to match the SqliteDatabase interface used by @tx/core.
|
|
5
|
+
* This allows tests to use Vitest (with proper parallelization) while production
|
|
6
|
+
* uses bun:sqlite.
|
|
7
|
+
*
|
|
8
|
+
* The SqliteDatabase interface abstracts away the implementation, so this adapter
|
|
9
|
+
* makes better-sqlite3 compatible with the existing code.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Wrap a better-sqlite3 Database to match SqliteDatabase interface
|
|
13
|
+
*/
|
|
14
|
+
export function wrapBetterSqlite3(db) {
|
|
15
|
+
return {
|
|
16
|
+
prepare(sql) {
|
|
17
|
+
const stmt = db.prepare(sql);
|
|
18
|
+
return {
|
|
19
|
+
run(...params) {
|
|
20
|
+
const result = stmt.run(...params);
|
|
21
|
+
return {
|
|
22
|
+
lastInsertRowid: result.lastInsertRowid ?? 0,
|
|
23
|
+
changes: result.changes ?? 0
|
|
24
|
+
};
|
|
25
|
+
},
|
|
26
|
+
get(...params) {
|
|
27
|
+
return stmt.get(...params) ?? null;
|
|
28
|
+
},
|
|
29
|
+
all(...params) {
|
|
30
|
+
return stmt.all(...params);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
},
|
|
34
|
+
run(sql, ...params) {
|
|
35
|
+
// Handle PRAGMA statements — better-sqlite3 uses .pragma("key = value") as a single string
|
|
36
|
+
const trimmed = sql.trim();
|
|
37
|
+
if (trimmed.toUpperCase().startsWith("PRAGMA")) {
|
|
38
|
+
// Strip the "PRAGMA " prefix and pass the rest directly to .pragma()
|
|
39
|
+
const pragmaArg = trimmed.slice(6).trim();
|
|
40
|
+
db.pragma(pragmaArg);
|
|
41
|
+
return { lastInsertRowid: 0, changes: 0 };
|
|
42
|
+
}
|
|
43
|
+
// Regular SQL statements
|
|
44
|
+
const result = db.prepare(sql).run(...params);
|
|
45
|
+
return {
|
|
46
|
+
lastInsertRowid: result.lastInsertRowid ?? 0,
|
|
47
|
+
changes: result.changes ?? 0
|
|
48
|
+
};
|
|
49
|
+
},
|
|
50
|
+
exec(sql) {
|
|
51
|
+
db.exec(sql);
|
|
52
|
+
},
|
|
53
|
+
close() {
|
|
54
|
+
db.close();
|
|
55
|
+
},
|
|
56
|
+
transaction(fn) {
|
|
57
|
+
return db.transaction(fn);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Create an in-memory better-sqlite3 database wrapped as SqliteDatabase
|
|
63
|
+
* with migrations applied.
|
|
64
|
+
*/
|
|
65
|
+
export async function createBetterSqlite3Database() {
|
|
66
|
+
// Dynamic import to avoid requiring better-sqlite3 at module load time
|
|
67
|
+
// (only needed when running tests with Vitest)
|
|
68
|
+
const Database = (await import("better-sqlite3")).default;
|
|
69
|
+
const { applyMigrations } = await import("@jamesaphoenix/tx-core");
|
|
70
|
+
const db = new Database(":memory:");
|
|
71
|
+
db.pragma("journal_mode = WAL");
|
|
72
|
+
db.pragma("foreign_keys = ON");
|
|
73
|
+
db.pragma("busy_timeout = 5000");
|
|
74
|
+
const wrapped = wrapBetterSqlite3(db);
|
|
75
|
+
applyMigrations(wrapped);
|
|
76
|
+
return wrapped;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=better-sqlite3-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"better-sqlite3-adapter.js","sourceRoot":"","sources":["../../src/adapters/better-sqlite3-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAiBH;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,EAAyB;IACzD,OAAO;QACL,OAAO,CAAc,GAAW;YAC9B,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;YAC5B,OAAO;gBACL,GAAG,CAAC,GAAG,MAAiB;oBACtB,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAA0D,CAAA;oBAC3F,OAAO;wBACL,eAAe,EAAE,MAAM,CAAC,eAAe,IAAI,CAAC;wBAC5C,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,CAAC;qBAC7B,CAAA;gBACH,CAAC;gBACD,GAAG,CAAC,GAAG,MAAiB;oBACtB,OAAQ,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAO,IAAI,IAAI,CAAA;gBAC3C,CAAC;gBACD,GAAG,CAAC,GAAG,MAAiB;oBACtB,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAQ,CAAA;gBACnC,CAAC;aACF,CAAA;QACH,CAAC;QACD,GAAG,CAAC,GAAW,EAAE,GAAG,MAAiB;YACnC,2FAA2F;YAC3F,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAA;YAC1B,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC/C,qEAAqE;gBACrE,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;gBACzC,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;gBACpB,OAAO,EAAE,eAAe,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAA;YAC3C,CAAC;YACD,yBAAyB;YACzB,MAAM,MAAM,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,MAAM,CAA0D,CAAA;YACtG,OAAO;gBACL,eAAe,EAAE,MAAM,CAAC,eAAe,IAAI,CAAC;gBAC5C,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,CAAC;aAC7B,CAAA;QACH,CAAC;QACD,IAAI,CAAC,GAAW;YACd,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QACd,CAAC;QACD,KAAK;YACH,EAAE,CAAC,KAAK,EAAE,CAAA;QACZ,CAAC;QACD,WAAW,CAAI,EAAW;YACxB,OAAO,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC,CAAA;QAC3B,CAAC;KACF,CAAA;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,2BAA2B;IAC/C,uEAAuE;IACvE,+CAA+C;IAC/C,MAAM,QAAQ,GAAG,CAAC,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC,OAAO,CAAA;IACzD,MAAM,EAAE,eAAe,EAAE,GAAG,MAAM,MAAM,CAAC,wBAAwB,CAAC,CAAA;IAElE,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,UAAU,CAAC,CAAA;IACnC,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAA;IAC/B,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAA;IAC9B,EAAE,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAA;IAEhC,MAAM,OAAO,GAAG,iBAAiB,CAAC,EAAE,CAAC,CAAA;IACrC,eAAe,CAAC,OAAO,CAAC,CAAA;IAExB,OAAO,OAAO,CAAA;AAChB,CAAC"}
|