@citadel-labs/citadel 0.1.0
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 +145 -0
- package/dist/__tests__/append-bead.test.d.ts +2 -0
- package/dist/__tests__/append-bead.test.d.ts.map +1 -0
- package/dist/__tests__/append-bead.test.js +88 -0
- package/dist/__tests__/append-bead.test.js.map +1 -0
- package/dist/__tests__/blocked-outposts.test.d.ts +2 -0
- package/dist/__tests__/blocked-outposts.test.d.ts.map +1 -0
- package/dist/__tests__/blocked-outposts.test.js +142 -0
- package/dist/__tests__/blocked-outposts.test.js.map +1 -0
- package/dist/__tests__/bugfixes.test.d.ts +2 -0
- package/dist/__tests__/bugfixes.test.d.ts.map +1 -0
- package/dist/__tests__/bugfixes.test.js +503 -0
- package/dist/__tests__/bugfixes.test.js.map +1 -0
- package/dist/__tests__/citizen-tribute.test.d.ts +2 -0
- package/dist/__tests__/citizen-tribute.test.d.ts.map +1 -0
- package/dist/__tests__/citizen-tribute.test.js +106 -0
- package/dist/__tests__/citizen-tribute.test.js.map +1 -0
- package/dist/__tests__/cli-e2e/dispatch-note-resolve.test.d.ts +2 -0
- package/dist/__tests__/cli-e2e/dispatch-note-resolve.test.d.ts.map +1 -0
- package/dist/__tests__/cli-e2e/dispatch-note-resolve.test.js +65 -0
- package/dist/__tests__/cli-e2e/dispatch-note-resolve.test.js.map +1 -0
- package/dist/__tests__/cli-e2e/full-lifecycle.test.d.ts +2 -0
- package/dist/__tests__/cli-e2e/full-lifecycle.test.d.ts.map +1 -0
- package/dist/__tests__/cli-e2e/full-lifecycle.test.js +157 -0
- package/dist/__tests__/cli-e2e/full-lifecycle.test.js.map +1 -0
- package/dist/__tests__/cli-e2e/helpers.d.ts +28 -0
- package/dist/__tests__/cli-e2e/helpers.d.ts.map +1 -0
- package/dist/__tests__/cli-e2e/helpers.js +76 -0
- package/dist/__tests__/cli-e2e/helpers.js.map +1 -0
- package/dist/__tests__/cli-e2e/init-outpost-status.test.d.ts +2 -0
- package/dist/__tests__/cli-e2e/init-outpost-status.test.d.ts.map +1 -0
- package/dist/__tests__/cli-e2e/init-outpost-status.test.js +79 -0
- package/dist/__tests__/cli-e2e/init-outpost-status.test.js.map +1 -0
- package/dist/__tests__/cli-e2e/reset-stop-tribute-trace.test.d.ts +2 -0
- package/dist/__tests__/cli-e2e/reset-stop-tribute-trace.test.d.ts.map +1 -0
- package/dist/__tests__/cli-e2e/reset-stop-tribute-trace.test.js +158 -0
- package/dist/__tests__/cli-e2e/reset-stop-tribute-trace.test.js.map +1 -0
- package/dist/__tests__/courier.test.d.ts +2 -0
- package/dist/__tests__/courier.test.d.ts.map +1 -0
- package/dist/__tests__/courier.test.js +97 -0
- package/dist/__tests__/courier.test.js.map +1 -0
- package/dist/__tests__/e2e-smoke.test.d.ts +2 -0
- package/dist/__tests__/e2e-smoke.test.d.ts.map +1 -0
- package/dist/__tests__/e2e-smoke.test.js +137 -0
- package/dist/__tests__/e2e-smoke.test.js.map +1 -0
- package/dist/__tests__/fo-broadcast.test.d.ts +2 -0
- package/dist/__tests__/fo-broadcast.test.d.ts.map +1 -0
- package/dist/__tests__/fo-broadcast.test.js +134 -0
- package/dist/__tests__/fo-broadcast.test.js.map +1 -0
- package/dist/__tests__/fo-command-processor.test.d.ts +2 -0
- package/dist/__tests__/fo-command-processor.test.d.ts.map +1 -0
- package/dist/__tests__/fo-command-processor.test.js +86 -0
- package/dist/__tests__/fo-command-processor.test.js.map +1 -0
- package/dist/__tests__/fo-escalation.test.d.ts +2 -0
- package/dist/__tests__/fo-escalation.test.d.ts.map +1 -0
- package/dist/__tests__/fo-escalation.test.js +126 -0
- package/dist/__tests__/fo-escalation.test.js.map +1 -0
- package/dist/__tests__/fo-nudge-watcher.test.d.ts +2 -0
- package/dist/__tests__/fo-nudge-watcher.test.d.ts.map +1 -0
- package/dist/__tests__/fo-nudge-watcher.test.js +154 -0
- package/dist/__tests__/fo-nudge-watcher.test.js.map +1 -0
- package/dist/__tests__/fo-nudge-wiring.test.d.ts +2 -0
- package/dist/__tests__/fo-nudge-wiring.test.d.ts.map +1 -0
- package/dist/__tests__/fo-nudge-wiring.test.js +31 -0
- package/dist/__tests__/fo-nudge-wiring.test.js.map +1 -0
- package/dist/__tests__/fo-relay.test.d.ts +2 -0
- package/dist/__tests__/fo-relay.test.d.ts.map +1 -0
- package/dist/__tests__/fo-relay.test.js +90 -0
- package/dist/__tests__/fo-relay.test.js.map +1 -0
- package/dist/__tests__/fo-task-report.test.d.ts +2 -0
- package/dist/__tests__/fo-task-report.test.d.ts.map +1 -0
- package/dist/__tests__/fo-task-report.test.js +81 -0
- package/dist/__tests__/fo-task-report.test.js.map +1 -0
- package/dist/__tests__/fo-webhook.test.d.ts +2 -0
- package/dist/__tests__/fo-webhook.test.d.ts.map +1 -0
- package/dist/__tests__/fo-webhook.test.js +70 -0
- package/dist/__tests__/fo-webhook.test.js.map +1 -0
- package/dist/__tests__/integration.test.d.ts +2 -0
- package/dist/__tests__/integration.test.d.ts.map +1 -0
- package/dist/__tests__/integration.test.js +763 -0
- package/dist/__tests__/integration.test.js.map +1 -0
- package/dist/__tests__/multi-outpost-dep.test.d.ts +2 -0
- package/dist/__tests__/multi-outpost-dep.test.d.ts.map +1 -0
- package/dist/__tests__/multi-outpost-dep.test.js +173 -0
- package/dist/__tests__/multi-outpost-dep.test.js.map +1 -0
- package/dist/__tests__/nudge.test.d.ts +2 -0
- package/dist/__tests__/nudge.test.d.ts.map +1 -0
- package/dist/__tests__/nudge.test.js +103 -0
- package/dist/__tests__/nudge.test.js.map +1 -0
- package/dist/__tests__/outpost-registry.test.d.ts +2 -0
- package/dist/__tests__/outpost-registry.test.d.ts.map +1 -0
- package/dist/__tests__/outpost-registry.test.js +72 -0
- package/dist/__tests__/outpost-registry.test.js.map +1 -0
- package/dist/__tests__/process-registry.test.d.ts +2 -0
- package/dist/__tests__/process-registry.test.d.ts.map +1 -0
- package/dist/__tests__/process-registry.test.js +108 -0
- package/dist/__tests__/process-registry.test.js.map +1 -0
- package/dist/__tests__/session-log.test.d.ts +2 -0
- package/dist/__tests__/session-log.test.d.ts.map +1 -0
- package/dist/__tests__/session-log.test.js +60 -0
- package/dist/__tests__/session-log.test.js.map +1 -0
- package/dist/__tests__/spawn-citizen.test.d.ts +2 -0
- package/dist/__tests__/spawn-citizen.test.d.ts.map +1 -0
- package/dist/__tests__/spawn-citizen.test.js +48 -0
- package/dist/__tests__/spawn-citizen.test.js.map +1 -0
- package/dist/__tests__/timeout-watchdog.test.d.ts +2 -0
- package/dist/__tests__/timeout-watchdog.test.d.ts.map +1 -0
- package/dist/__tests__/timeout-watchdog.test.js +81 -0
- package/dist/__tests__/timeout-watchdog.test.js.map +1 -0
- package/dist/__tests__/worktree-manager.test.d.ts +2 -0
- package/dist/__tests__/worktree-manager.test.d.ts.map +1 -0
- package/dist/__tests__/worktree-manager.test.js +98 -0
- package/dist/__tests__/worktree-manager.test.js.map +1 -0
- package/dist/cli/aliases.d.ts +10 -0
- package/dist/cli/aliases.d.ts.map +1 -0
- package/dist/cli/aliases.js +56 -0
- package/dist/cli/aliases.js.map +1 -0
- package/dist/cli/command.d.ts +3 -0
- package/dist/cli/command.d.ts.map +1 -0
- package/dist/cli/command.js +63 -0
- package/dist/cli/command.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +29 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.d.ts +3 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +57 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/outpost.d.ts +3 -0
- package/dist/cli/outpost.d.ts.map +1 -0
- package/dist/cli/outpost.js +65 -0
- package/dist/cli/outpost.js.map +1 -0
- package/dist/cli/reset.d.ts +3 -0
- package/dist/cli/reset.d.ts.map +1 -0
- package/dist/cli/reset.js +67 -0
- package/dist/cli/reset.js.map +1 -0
- package/dist/cli/session.d.ts +3 -0
- package/dist/cli/session.d.ts.map +1 -0
- package/dist/cli/session.js +112 -0
- package/dist/cli/session.js.map +1 -0
- package/dist/cli/start.d.ts +3 -0
- package/dist/cli/start.d.ts.map +1 -0
- package/dist/cli/start.js +105 -0
- package/dist/cli/start.js.map +1 -0
- package/dist/cli/status.d.ts +3 -0
- package/dist/cli/status.d.ts.map +1 -0
- package/dist/cli/status.js +128 -0
- package/dist/cli/status.js.map +1 -0
- package/dist/cli/tribute.d.ts +3 -0
- package/dist/cli/tribute.d.ts.map +1 -0
- package/dist/cli/tribute.js +160 -0
- package/dist/cli/tribute.js.map +1 -0
- package/dist/cli/worktree.d.ts +3 -0
- package/dist/cli/worktree.d.ts.map +1 -0
- package/dist/cli/worktree.js +64 -0
- package/dist/cli/worktree.js.map +1 -0
- package/dist/core/append-bead.d.ts +13 -0
- package/dist/core/append-bead.d.ts.map +1 -0
- package/dist/core/append-bead.js +68 -0
- package/dist/core/append-bead.js.map +1 -0
- package/dist/core/blocked-outposts.d.ts +15 -0
- package/dist/core/blocked-outposts.d.ts.map +1 -0
- package/dist/core/blocked-outposts.js +77 -0
- package/dist/core/blocked-outposts.js.map +1 -0
- package/dist/core/citizen-bootstrap.d.ts +17 -0
- package/dist/core/citizen-bootstrap.d.ts.map +1 -0
- package/dist/core/citizen-bootstrap.js +118 -0
- package/dist/core/citizen-bootstrap.js.map +1 -0
- package/dist/core/citizen-tribute.d.ts +17 -0
- package/dist/core/citizen-tribute.d.ts.map +1 -0
- package/dist/core/citizen-tribute.js +55 -0
- package/dist/core/citizen-tribute.js.map +1 -0
- package/dist/core/command-bead.d.ts +6 -0
- package/dist/core/command-bead.d.ts.map +1 -0
- package/dist/core/command-bead.js +20 -0
- package/dist/core/command-bead.js.map +1 -0
- package/dist/core/courier.d.ts +52 -0
- package/dist/core/courier.d.ts.map +1 -0
- package/dist/core/courier.js +167 -0
- package/dist/core/courier.js.map +1 -0
- package/dist/core/fo-broadcast.d.ts +15 -0
- package/dist/core/fo-broadcast.d.ts.map +1 -0
- package/dist/core/fo-broadcast.js +57 -0
- package/dist/core/fo-broadcast.js.map +1 -0
- package/dist/core/fo-command-processor.d.ts +7 -0
- package/dist/core/fo-command-processor.d.ts.map +1 -0
- package/dist/core/fo-command-processor.js +123 -0
- package/dist/core/fo-command-processor.js.map +1 -0
- package/dist/core/fo-dispatch.d.ts +24 -0
- package/dist/core/fo-dispatch.d.ts.map +1 -0
- package/dist/core/fo-dispatch.js +76 -0
- package/dist/core/fo-dispatch.js.map +1 -0
- package/dist/core/fo-escalation.d.ts +20 -0
- package/dist/core/fo-escalation.d.ts.map +1 -0
- package/dist/core/fo-escalation.js +83 -0
- package/dist/core/fo-escalation.js.map +1 -0
- package/dist/core/fo-handlers/ceiling-hit.d.ts +8 -0
- package/dist/core/fo-handlers/ceiling-hit.d.ts.map +1 -0
- package/dist/core/fo-handlers/ceiling-hit.js +10 -0
- package/dist/core/fo-handlers/ceiling-hit.js.map +1 -0
- package/dist/core/fo-handlers/slot-open.d.ts +13 -0
- package/dist/core/fo-handlers/slot-open.d.ts.map +1 -0
- package/dist/core/fo-handlers/slot-open.js +63 -0
- package/dist/core/fo-handlers/slot-open.js.map +1 -0
- package/dist/core/fo-nudge-watcher.d.ts +19 -0
- package/dist/core/fo-nudge-watcher.d.ts.map +1 -0
- package/dist/core/fo-nudge-watcher.js +71 -0
- package/dist/core/fo-nudge-watcher.js.map +1 -0
- package/dist/core/fo-nudge-wiring.d.ts +13 -0
- package/dist/core/fo-nudge-wiring.d.ts.map +1 -0
- package/dist/core/fo-nudge-wiring.js +120 -0
- package/dist/core/fo-nudge-wiring.js.map +1 -0
- package/dist/core/fo-relay.d.ts +7 -0
- package/dist/core/fo-relay.d.ts.map +1 -0
- package/dist/core/fo-relay.js +47 -0
- package/dist/core/fo-relay.js.map +1 -0
- package/dist/core/fo-retry-policy.d.ts +17 -0
- package/dist/core/fo-retry-policy.d.ts.map +1 -0
- package/dist/core/fo-retry-policy.js +89 -0
- package/dist/core/fo-retry-policy.js.map +1 -0
- package/dist/core/fo-state.d.ts +25 -0
- package/dist/core/fo-state.d.ts.map +1 -0
- package/dist/core/fo-state.js +99 -0
- package/dist/core/fo-state.js.map +1 -0
- package/dist/core/fo-task-report.d.ts +16 -0
- package/dist/core/fo-task-report.d.ts.map +1 -0
- package/dist/core/fo-task-report.js +63 -0
- package/dist/core/fo-task-report.js.map +1 -0
- package/dist/core/fo-webhook.d.ts +22 -0
- package/dist/core/fo-webhook.d.ts.map +1 -0
- package/dist/core/fo-webhook.js +43 -0
- package/dist/core/fo-webhook.js.map +1 -0
- package/dist/core/grand-archives.d.ts +14 -0
- package/dist/core/grand-archives.d.ts.map +1 -0
- package/dist/core/grand-archives.js +79 -0
- package/dist/core/grand-archives.js.map +1 -0
- package/dist/core/nudge.d.ts +6 -0
- package/dist/core/nudge.d.ts.map +1 -0
- package/dist/core/nudge.js +19 -0
- package/dist/core/nudge.js.map +1 -0
- package/dist/core/outpost-registry.d.ts +16 -0
- package/dist/core/outpost-registry.d.ts.map +1 -0
- package/dist/core/outpost-registry.js +27 -0
- package/dist/core/outpost-registry.js.map +1 -0
- package/dist/core/pre-spawn-check.d.ts +16 -0
- package/dist/core/pre-spawn-check.d.ts.map +1 -0
- package/dist/core/pre-spawn-check.js +50 -0
- package/dist/core/pre-spawn-check.js.map +1 -0
- package/dist/core/process-registry.d.ts +17 -0
- package/dist/core/process-registry.d.ts.map +1 -0
- package/dist/core/process-registry.js +71 -0
- package/dist/core/process-registry.js.map +1 -0
- package/dist/core/spawn-citizen.d.ts +23 -0
- package/dist/core/spawn-citizen.d.ts.map +1 -0
- package/dist/core/spawn-citizen.js +99 -0
- package/dist/core/spawn-citizen.js.map +1 -0
- package/dist/core/timeout-watchdog.d.ts +14 -0
- package/dist/core/timeout-watchdog.d.ts.map +1 -0
- package/dist/core/timeout-watchdog.js +68 -0
- package/dist/core/timeout-watchdog.js.map +1 -0
- package/dist/core/validate-tribute.d.ts +11 -0
- package/dist/core/validate-tribute.d.ts.map +1 -0
- package/dist/core/validate-tribute.js +44 -0
- package/dist/core/validate-tribute.js.map +1 -0
- package/dist/core/worktree-manager.d.ts +20 -0
- package/dist/core/worktree-manager.d.ts.map +1 -0
- package/dist/core/worktree-manager.js +123 -0
- package/dist/core/worktree-manager.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/types/agent.d.ts +47 -0
- package/dist/types/agent.d.ts.map +1 -0
- package/dist/types/agent.js +4 -0
- package/dist/types/agent.js.map +1 -0
- package/dist/types/beads.d.ts +175 -0
- package/dist/types/beads.d.ts.map +1 -0
- package/dist/types/beads.js +4 -0
- package/dist/types/beads.js.map +1 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +4 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/registry.d.ts +110 -0
- package/dist/types/registry.d.ts.map +1 -0
- package/dist/types/registry.js +3 -0
- package/dist/types/registry.js.map +1 -0
- package/docs/README.md +7 -0
- package/docs/architecture.md +106 -0
- package/docs/cli-reference.md +81 -0
- package/docs/configuration.md +143 -0
- package/docs/contributing.md +65 -0
- package/docs/getting-started.md +88 -0
- package/grand-archives/archetypes/architect/archetype.json +18 -0
- package/grand-archives/archetypes/qa-engineer/archetype.json +18 -0
- package/grand-archives/archetypes/software-engineer/archetype.json +18 -0
- package/grand-archives/archetypes/software-engineer/system-prompt.md +39 -0
- package/grand-archives/archetypes/software-engineer/toolset.json +15 -0
- package/grand-archives/first-officer/config.json +21 -0
- package/grand-archives/first-officer/system-prompt.md +47 -0
- package/grand-archives/first-officer/toolset.json +19 -0
- package/grand-archives/registry.json +8 -0
- package/grand-archives/shared/base-prompt-preamble.md +25 -0
- package/grand-archives/shared/base-tools.json +10 -0
- package/package.json +73 -0
package/README.md
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# CITADEL
|
|
2
|
+
|
|
3
|
+
Cold-start, hub-and-spoke orchestration for autonomous LLM agents.
|
|
4
|
+
|
|
5
|
+
CITADEL coordinates multiple LLM "Citizens" across independent code repositories ("Outposts"). Agents don't idle -- they are summoned, work in isolated git worktrees, deliver Tribute, and go dark. Everything persists in append-only JSONL records called Beads. Sessions are ephemeral; state survives.
|
|
6
|
+
|
|
7
|
+
## How It Works
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
Commander (you)
|
|
11
|
+
| issues orders via CLI
|
|
12
|
+
v
|
|
13
|
+
First Officer (persistent LLM agent, agent-agnostic)
|
|
14
|
+
| selects archetype, mints citizen_id, writes Mail
|
|
15
|
+
v
|
|
16
|
+
Courier (daemon)
|
|
17
|
+
| watches filesystem, manages worktrees, spawns Citizens
|
|
18
|
+
v
|
|
19
|
+
Citizens (cold-start LLM agents in isolated git worktrees)
|
|
20
|
+
| read Mail, do work, write Tribute
|
|
21
|
+
v
|
|
22
|
+
First Officer receives Tribute, validates, broadcasts, retries or escalates
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install -g @citadel-labs/citadel
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Requires Node.js v20+. Works on macOS, Linux, and Windows.
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
citadel init my-project
|
|
37
|
+
cd my-project
|
|
38
|
+
|
|
39
|
+
citadel outpost register --slug api-gateway --path ../api-gateway
|
|
40
|
+
citadel start
|
|
41
|
+
citadel dispatch --outpost api-gateway --task "Implement GET /health endpoint"
|
|
42
|
+
citadel status
|
|
43
|
+
citadel tribute --task <task-id>
|
|
44
|
+
citadel trace --task <task-id>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
See [Getting Started](docs/getting-started.md) for a full walkthrough.
|
|
48
|
+
|
|
49
|
+
## CLI Reference
|
|
50
|
+
|
|
51
|
+
| Command | Description |
|
|
52
|
+
|---|---|
|
|
53
|
+
| `citadel init [dir]` | Scaffold workspace |
|
|
54
|
+
| `citadel start [--agent <runtime>]` | Boot the First Officer |
|
|
55
|
+
| `citadel stop` | Graceful shutdown |
|
|
56
|
+
| `citadel status` | Session info and outpost overview |
|
|
57
|
+
| `citadel outpost register --slug <name> --path <path>` | Register a repo |
|
|
58
|
+
| `citadel dispatch --outpost <slug> --task <text>` | Assign a task |
|
|
59
|
+
| `citadel note --outpost <slug> --body <text>` | Send guidance |
|
|
60
|
+
| `citadel resolve --task-id <id> --action <action>` | Resolve escalation |
|
|
61
|
+
| `citadel command retry --task <id>` | Force retry |
|
|
62
|
+
| `citadel tribute --task <id>` | View tribute attempts |
|
|
63
|
+
| `citadel trace --task <id>` | Full event timeline |
|
|
64
|
+
| `citadel reset [--hard]` | Clear state (hard removes `.citadel/`) |
|
|
65
|
+
| `citadel worktree list/prune` | Manage worktrees |
|
|
66
|
+
|
|
67
|
+
Full details: [CLI Reference](docs/cli-reference.md)
|
|
68
|
+
|
|
69
|
+
## Architecture
|
|
70
|
+
|
|
71
|
+
| Role | What It Does | Writes |
|
|
72
|
+
|---|---|---|
|
|
73
|
+
| **Commander** | Issues orders, reviews Broadcasts | Command Beads |
|
|
74
|
+
| **First Officer** | Orchestrates -- dispatch, retry, escalate | Mail Beads (downward) |
|
|
75
|
+
| **Courier** | Watches filesystem, spawns Citizens | Nudge Beads only |
|
|
76
|
+
| **Citizens** | Execute tasks in isolated worktrees | Tribute + Mail (upward) |
|
|
77
|
+
|
|
78
|
+
All state lives in append-only JSONL Beads, written through `appendBead()` with lockfile serialization. Mail is the sole wake trigger. Nudges are disposable signals. Full details: [Architecture](docs/architecture.md)
|
|
79
|
+
|
|
80
|
+
## Workspace Layout
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
my-project/
|
|
84
|
+
courier.config.yaml
|
|
85
|
+
.citadel/
|
|
86
|
+
identity.jsonl
|
|
87
|
+
outposts.json
|
|
88
|
+
nudges.jsonl
|
|
89
|
+
commands/
|
|
90
|
+
logs/
|
|
91
|
+
grand-archives/
|
|
92
|
+
first-officer/
|
|
93
|
+
archetypes/
|
|
94
|
+
shared/
|
|
95
|
+
outposts/
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Configuration
|
|
99
|
+
|
|
100
|
+
`courier.config.yaml` at the workspace root:
|
|
101
|
+
|
|
102
|
+
```yaml
|
|
103
|
+
citadel_root: .
|
|
104
|
+
poll_interval_ms: 500
|
|
105
|
+
debounce_ms: 200
|
|
106
|
+
default_max_citizens: 3
|
|
107
|
+
citizen_timeout_seconds: 1800
|
|
108
|
+
log_level: INFO
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Full details: [Configuration](docs/configuration.md)
|
|
112
|
+
|
|
113
|
+
## Design Principles
|
|
114
|
+
|
|
115
|
+
- **Mail is the universal envelope.** Every activation begins with a Mail Bead.
|
|
116
|
+
- **Beads are state. Nudges are signals.** Full state from Mail + Tribute only.
|
|
117
|
+
- **Write authority is strict.** FO writes down. Citizens write up. Courier writes Nudge only.
|
|
118
|
+
- **Writes are serialized via lockfiles.** Cross-platform safe.
|
|
119
|
+
- **Identity persists. Sessions are cattle.** Kill and restart freely.
|
|
120
|
+
- **Everything communicates via files.** No PIDs, no pipes, no tight coupling.
|
|
121
|
+
|
|
122
|
+
## Development
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
git clone <repo>
|
|
126
|
+
cd citadel
|
|
127
|
+
npm install
|
|
128
|
+
npm run build
|
|
129
|
+
npm test
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
See [Contributing](docs/contributing.md) for full guidelines.
|
|
133
|
+
|
|
134
|
+
## Current Status
|
|
135
|
+
|
|
136
|
+
Milestones 0-3 implemented.
|
|
137
|
+
|
|
138
|
+
- **M0** -- Scaffold: Types, Grand Archives, CLI entrypoint
|
|
139
|
+
- **M1** -- Foundation: Bead I/O, Courier, WorktreeManager, ProcessRegistry, CLI
|
|
140
|
+
- **M2** -- Labor & Tribute: First Officer, Citizen lifecycle, retry, escalation
|
|
141
|
+
- **M3** -- Broadcast & Routing: Dependencies, relay, task reports, webhook
|
|
142
|
+
|
|
143
|
+
## License
|
|
144
|
+
|
|
145
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"append-bead.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/append-bead.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { appendBead, readBeads, mintBeadId } from '../core/append-bead.js';
|
|
6
|
+
import { SCHEMA_VERSION } from '../types/index.js';
|
|
7
|
+
function makeBead(overrides = {}) {
|
|
8
|
+
return {
|
|
9
|
+
bead_id: mintBeadId(),
|
|
10
|
+
bead_type: 'NUDGE',
|
|
11
|
+
schema_version: SCHEMA_VERSION,
|
|
12
|
+
created_at: new Date().toISOString(),
|
|
13
|
+
...overrides,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
describe('appendBead', () => {
|
|
17
|
+
let tmpDir;
|
|
18
|
+
beforeEach(async () => {
|
|
19
|
+
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'citadel-test-'));
|
|
20
|
+
});
|
|
21
|
+
afterEach(async () => {
|
|
22
|
+
await fs.rm(tmpDir, { recursive: true, force: true });
|
|
23
|
+
});
|
|
24
|
+
it('appends a bead and reads it back with matching fields', async () => {
|
|
25
|
+
const filePath = path.join(tmpDir, 'beads.jsonl');
|
|
26
|
+
const bead = makeBead();
|
|
27
|
+
const returned = await appendBead(filePath, bead);
|
|
28
|
+
expect(returned).toEqual(bead);
|
|
29
|
+
const beads = await readBeads(filePath);
|
|
30
|
+
expect(beads).toHaveLength(1);
|
|
31
|
+
expect(beads[0].bead_id).toBe(bead.bead_id);
|
|
32
|
+
expect(beads[0].bead_type).toBe(bead.bead_type);
|
|
33
|
+
expect(beads[0].schema_version).toBe(bead.schema_version);
|
|
34
|
+
expect(beads[0].created_at).toBe(bead.created_at);
|
|
35
|
+
});
|
|
36
|
+
it('handles 10 rapid sequential appends without data loss', async () => {
|
|
37
|
+
const filePath = path.join(tmpDir, 'concurrent.jsonl');
|
|
38
|
+
const beads = Array.from({ length: 10 }, () => makeBead());
|
|
39
|
+
// Append all beads sequentially in rapid succession
|
|
40
|
+
for (const b of beads) {
|
|
41
|
+
await appendBead(filePath, b);
|
|
42
|
+
}
|
|
43
|
+
const written = await readBeads(filePath);
|
|
44
|
+
expect(written).toHaveLength(10);
|
|
45
|
+
// Verify each bead is on its own line (no partial/merged lines)
|
|
46
|
+
const raw = await fs.readFile(filePath, 'utf-8');
|
|
47
|
+
const lines = raw.split('\n').filter((l) => l.trim() !== '');
|
|
48
|
+
expect(lines).toHaveLength(10);
|
|
49
|
+
// Each line must parse as valid JSON
|
|
50
|
+
for (const line of lines) {
|
|
51
|
+
expect(() => JSON.parse(line)).not.toThrow();
|
|
52
|
+
}
|
|
53
|
+
// All original bead_ids are present
|
|
54
|
+
const writtenIds = new Set(written.map((b) => b.bead_id));
|
|
55
|
+
for (const bead of beads) {
|
|
56
|
+
expect(writtenIds.has(bead.bead_id)).toBe(true);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
describe('readBeads', () => {
|
|
61
|
+
let tmpDir;
|
|
62
|
+
beforeEach(async () => {
|
|
63
|
+
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'citadel-test-'));
|
|
64
|
+
});
|
|
65
|
+
afterEach(async () => {
|
|
66
|
+
await fs.rm(tmpDir, { recursive: true, force: true });
|
|
67
|
+
});
|
|
68
|
+
it('returns empty array for an empty file', async () => {
|
|
69
|
+
const filePath = path.join(tmpDir, 'empty.jsonl');
|
|
70
|
+
await fs.writeFile(filePath, '');
|
|
71
|
+
const beads = await readBeads(filePath);
|
|
72
|
+
expect(beads).toEqual([]);
|
|
73
|
+
});
|
|
74
|
+
it('returns empty array for a missing file', async () => {
|
|
75
|
+
const filePath = path.join(tmpDir, 'nonexistent.jsonl');
|
|
76
|
+
const beads = await readBeads(filePath);
|
|
77
|
+
expect(beads).toEqual([]);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
describe('mintBeadId', () => {
|
|
81
|
+
it('returns a string starting with "bead-" of reasonable length', () => {
|
|
82
|
+
const id = mintBeadId();
|
|
83
|
+
expect(id).toMatch(/^bead-.+$/);
|
|
84
|
+
expect(id.length).toBeGreaterThanOrEqual(10);
|
|
85
|
+
expect(id.length).toBeLessThanOrEqual(50);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
//# sourceMappingURL=append-bead.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"append-bead.test.js","sourceRoot":"","sources":["../../src/__tests__/append-bead.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blocked-outposts.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/blocked-outposts.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import fs from 'node:fs/promises';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import { getBlockedOutposts } from '../core/blocked-outposts.js';
|
|
6
|
+
describe('getBlockedOutposts', () => {
|
|
7
|
+
let tmpDir;
|
|
8
|
+
let alphaPath;
|
|
9
|
+
let betaPath;
|
|
10
|
+
beforeEach(async () => {
|
|
11
|
+
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'citadel-blocked-'));
|
|
12
|
+
alphaPath = path.join(tmpDir, 'outposts', 'alpha');
|
|
13
|
+
betaPath = path.join(tmpDir, 'outposts', 'beta');
|
|
14
|
+
// Create dirs
|
|
15
|
+
await fs.mkdir(path.join(tmpDir, '.citadel'), { recursive: true });
|
|
16
|
+
await fs.mkdir(path.join(alphaPath, '.beads', 'mail'), { recursive: true });
|
|
17
|
+
await fs.mkdir(path.join(alphaPath, '.beads', 'tribute'), { recursive: true });
|
|
18
|
+
await fs.mkdir(path.join(betaPath, '.beads', 'mail'), { recursive: true });
|
|
19
|
+
await fs.mkdir(path.join(betaPath, '.beads', 'tribute'), { recursive: true });
|
|
20
|
+
// Write registry
|
|
21
|
+
const registry = {
|
|
22
|
+
schema_version: '1.0',
|
|
23
|
+
outposts: [
|
|
24
|
+
{ slug: 'alpha', path: alphaPath, status: 'ACTIVE', registered_at: '2026-01-01T00:00:00Z' },
|
|
25
|
+
{ slug: 'beta', path: betaPath, status: 'ACTIVE', registered_at: '2026-01-02T00:00:00Z' },
|
|
26
|
+
],
|
|
27
|
+
};
|
|
28
|
+
await fs.writeFile(path.join(tmpDir, '.citadel', 'outposts.json'), JSON.stringify(registry));
|
|
29
|
+
});
|
|
30
|
+
afterEach(async () => {
|
|
31
|
+
await fs.rm(tmpDir, { recursive: true, force: true });
|
|
32
|
+
});
|
|
33
|
+
const makeMail = (taskId, to, deps) => ({
|
|
34
|
+
bead_id: `bead-${taskId}`,
|
|
35
|
+
bead_type: 'MAIL',
|
|
36
|
+
schema_version: '9.0',
|
|
37
|
+
created_at: '2026-01-01T00:00:00Z',
|
|
38
|
+
task_id: taskId,
|
|
39
|
+
mail_type: 'TASK_ASSIGNMENT',
|
|
40
|
+
from: 'citadel',
|
|
41
|
+
to,
|
|
42
|
+
subject: `Task ${taskId}`,
|
|
43
|
+
read: false,
|
|
44
|
+
retry_of: null,
|
|
45
|
+
payload: {
|
|
46
|
+
archetype: 'software-engineer',
|
|
47
|
+
citizen_id: `citizen-${taskId}`,
|
|
48
|
+
priority: 'MEDIUM',
|
|
49
|
+
title: `Task ${taskId}`,
|
|
50
|
+
description: 'test',
|
|
51
|
+
acceptance_criteria: [],
|
|
52
|
+
context_refs: [],
|
|
53
|
+
dependencies: deps,
|
|
54
|
+
timeout_seconds: 1800,
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
const makeTribute = (taskId, outpost, status) => ({
|
|
58
|
+
bead_id: `tribute-${taskId}`,
|
|
59
|
+
bead_type: 'TRIBUTE',
|
|
60
|
+
schema_version: '9.0',
|
|
61
|
+
created_at: '2026-01-01T01:00:00Z',
|
|
62
|
+
citizen_id: `citizen-${taskId}`,
|
|
63
|
+
task_id: taskId,
|
|
64
|
+
originating_mail_ref: `bead-${taskId}`,
|
|
65
|
+
archetype: 'software-engineer',
|
|
66
|
+
outpost,
|
|
67
|
+
worktree: `worktrees/citizen/${taskId}`,
|
|
68
|
+
attempt: 1,
|
|
69
|
+
status,
|
|
70
|
+
summary: 'done',
|
|
71
|
+
artifacts: [],
|
|
72
|
+
acceptance_met: [],
|
|
73
|
+
elapsed_seconds: 60,
|
|
74
|
+
});
|
|
75
|
+
const writeMail = async (outpostPath, taskId, mail) => {
|
|
76
|
+
await fs.writeFile(path.join(outpostPath, '.beads', 'mail', `${taskId}.jsonl`), JSON.stringify(mail) + '\n');
|
|
77
|
+
};
|
|
78
|
+
const writeTribute = async (outpostPath, taskId, tribute) => {
|
|
79
|
+
await fs.writeFile(path.join(outpostPath, '.beads', 'tribute', `${taskId}.jsonl`), JSON.stringify(tribute) + '\n');
|
|
80
|
+
};
|
|
81
|
+
it('finds tasks blocked on the resolved task_id', async () => {
|
|
82
|
+
// alpha has task-A depending on task-X
|
|
83
|
+
await writeMail(alphaPath, 'task-A', makeMail('task-A', 'alpha', ['task-X']));
|
|
84
|
+
// beta has task-B depending on task-X
|
|
85
|
+
await writeMail(betaPath, 'task-B', makeMail('task-B', 'beta', ['task-X']));
|
|
86
|
+
const blocked = await getBlockedOutposts(tmpDir, 'task-X');
|
|
87
|
+
expect(blocked).toHaveLength(2);
|
|
88
|
+
expect(blocked.map(b => b.outpost).sort()).toEqual(['alpha', 'beta']);
|
|
89
|
+
expect(blocked.map(b => b.taskId).sort()).toEqual(['task-A', 'task-B']);
|
|
90
|
+
});
|
|
91
|
+
it('excludes tasks that already have a SUCCESS tribute', async () => {
|
|
92
|
+
await writeMail(alphaPath, 'task-A', makeMail('task-A', 'alpha', ['task-X']));
|
|
93
|
+
await writeTribute(alphaPath, 'task-A', makeTribute('task-A', 'alpha', 'SUCCESS'));
|
|
94
|
+
await writeMail(betaPath, 'task-B', makeMail('task-B', 'beta', ['task-X']));
|
|
95
|
+
const blocked = await getBlockedOutposts(tmpDir, 'task-X');
|
|
96
|
+
expect(blocked).toHaveLength(1);
|
|
97
|
+
expect(blocked[0].outpost).toBe('beta');
|
|
98
|
+
expect(blocked[0].taskId).toBe('task-B');
|
|
99
|
+
});
|
|
100
|
+
it('includes tasks with PARTIAL tribute (not yet succeeded)', async () => {
|
|
101
|
+
await writeMail(alphaPath, 'task-A', makeMail('task-A', 'alpha', ['task-X']));
|
|
102
|
+
await writeTribute(alphaPath, 'task-A', makeTribute('task-A', 'alpha', 'PARTIAL'));
|
|
103
|
+
const blocked = await getBlockedOutposts(tmpDir, 'task-X');
|
|
104
|
+
expect(blocked).toHaveLength(1);
|
|
105
|
+
expect(blocked[0].taskId).toBe('task-A');
|
|
106
|
+
});
|
|
107
|
+
it('returns empty when no tasks depend on the resolved task', async () => {
|
|
108
|
+
await writeMail(alphaPath, 'task-A', makeMail('task-A', 'alpha', ['task-Y']));
|
|
109
|
+
const blocked = await getBlockedOutposts(tmpDir, 'task-X');
|
|
110
|
+
expect(blocked).toHaveLength(0);
|
|
111
|
+
});
|
|
112
|
+
it('ignores non-TASK_ASSIGNMENT mail', async () => {
|
|
113
|
+
// Write an OPERATOR_NOTE mail (no dependencies field used for matching)
|
|
114
|
+
const note = {
|
|
115
|
+
bead_id: 'bead-note-1',
|
|
116
|
+
bead_type: 'MAIL',
|
|
117
|
+
schema_version: '9.0',
|
|
118
|
+
created_at: '2026-01-01T00:00:00Z',
|
|
119
|
+
task_id: 'task-A',
|
|
120
|
+
mail_type: 'OPERATOR_NOTE',
|
|
121
|
+
from: 'citadel',
|
|
122
|
+
to: 'alpha',
|
|
123
|
+
subject: 'Note',
|
|
124
|
+
read: false,
|
|
125
|
+
retry_of: null,
|
|
126
|
+
payload: { body: 'hello' },
|
|
127
|
+
};
|
|
128
|
+
await fs.writeFile(path.join(alphaPath, '.beads', 'mail', 'note-1.jsonl'), JSON.stringify(note) + '\n');
|
|
129
|
+
const blocked = await getBlockedOutposts(tmpDir, 'task-X');
|
|
130
|
+
expect(blocked).toHaveLength(0);
|
|
131
|
+
});
|
|
132
|
+
it('handles missing mail/tribute directories gracefully', async () => {
|
|
133
|
+
// Remove alpha's mail dir
|
|
134
|
+
await fs.rm(path.join(alphaPath, '.beads', 'mail'), { recursive: true });
|
|
135
|
+
await fs.rm(path.join(alphaPath, '.beads', 'tribute'), { recursive: true });
|
|
136
|
+
await writeMail(betaPath, 'task-B', makeMail('task-B', 'beta', ['task-X']));
|
|
137
|
+
const blocked = await getBlockedOutposts(tmpDir, 'task-X');
|
|
138
|
+
expect(blocked).toHaveLength(1);
|
|
139
|
+
expect(blocked[0].outpost).toBe('beta');
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
//# sourceMappingURL=blocked-outposts.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blocked-outposts.test.js","sourceRoot":"","sources":["../../src/__tests__/blocked-outposts.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACrE,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAGjE,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IAClC,IAAI,MAAc,CAAC;IACnB,IAAI,SAAiB,CAAC;IACtB,IAAI,QAAgB,CAAC;IAErB,UAAU,CAAC,KAAK,IAAI,EAAE;QACpB,MAAM,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,kBAAkB,CAAC,CAAC,CAAC;QACtE,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;QACnD,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC;QAEjD,cAAc;QACd,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACnE,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5E,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,EAAE,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/E,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3E,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAE9E,iBAAiB;QACjB,MAAM,QAAQ,GAAoB;YAChC,cAAc,EAAE,KAAK;YACrB,QAAQ,EAAE;gBACR,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,sBAAsB,EAAE;gBAC3F,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,sBAAsB,EAAE;aAC1F;SACF,CAAC;QACF,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,UAAU,EAAE,eAAe,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC/F,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,KAAK,IAAI,EAAE;QACnB,MAAM,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,CAAC,MAAc,EAAE,EAAU,EAAE,IAAc,EAAY,EAAE,CAAC,CAAC;QAC1E,OAAO,EAAE,QAAQ,MAAM,EAAE;QACzB,SAAS,EAAE,MAAM;QACjB,cAAc,EAAE,KAAK;QACrB,UAAU,EAAE,sBAAsB;QAClC,OAAO,EAAE,MAAM;QACf,SAAS,EAAE,iBAAiB;QAC5B,IAAI,EAAE,SAAS;QACf,EAAE;QACF,OAAO,EAAE,QAAQ,MAAM,EAAE;QACzB,IAAI,EAAE,KAAK;QACX,QAAQ,EAAE,IAAI;QACd,OAAO,EAAE;YACP,SAAS,EAAE,mBAAmB;YAC9B,UAAU,EAAE,WAAW,MAAM,EAAE;YAC/B,QAAQ,EAAE,QAAiB;YAC3B,KAAK,EAAE,QAAQ,MAAM,EAAE;YACvB,WAAW,EAAE,MAAM;YACnB,mBAAmB,EAAE,EAAE;YACvB,YAAY,EAAE,EAAE;YAChB,YAAY,EAAE,IAAI;YAClB,eAAe,EAAE,IAAI;SACtB;KACF,CAAC,CAAC;IAEH,MAAM,WAAW,GAAG,CAAC,MAAc,EAAE,OAAe,EAAE,MAAwC,EAAe,EAAE,CAAC,CAAC;QAC/G,OAAO,EAAE,WAAW,MAAM,EAAE;QAC5B,SAAS,EAAE,SAAS;QACpB,cAAc,EAAE,KAAK;QACrB,UAAU,EAAE,sBAAsB;QAClC,UAAU,EAAE,WAAW,MAAM,EAAE;QAC/B,OAAO,EAAE,MAAM;QACf,oBAAoB,EAAE,QAAQ,MAAM,EAAE;QACtC,SAAS,EAAE,mBAAmB;QAC9B,OAAO;QACP,QAAQ,EAAE,qBAAqB,MAAM,EAAE;QACvC,OAAO,EAAE,CAAC;QACV,MAAM;QACN,OAAO,EAAE,MAAM;QACf,SAAS,EAAE,EAAE;QACb,cAAc,EAAE,EAAE;QAClB,eAAe,EAAE,EAAE;KACpB,CAAC,CAAC;IAEH,MAAM,SAAS,GAAG,KAAK,EAAE,WAAmB,EAAE,MAAc,EAAE,IAAc,EAAE,EAAE;QAC9E,MAAM,EAAE,CAAC,SAAS,CAChB,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,QAAQ,CAAC,EAC3D,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,IAAI,CAC5B,CAAC;IACJ,CAAC,CAAC;IAEF,MAAM,YAAY,GAAG,KAAK,EAAE,WAAmB,EAAE,MAAc,EAAE,OAAoB,EAAE,EAAE;QACvF,MAAM,EAAE,CAAC,SAAS,CAChB,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,MAAM,QAAQ,CAAC,EAC9D,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,IAAI,CAC/B,CAAC;IACJ,CAAC,CAAC;IAEF,EAAE,CAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;QAC3D,uCAAuC;QACvC,MAAM,SAAS,CAAC,SAAS,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAC9E,sCAAsC;QACtC,MAAM,SAAS,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAE5E,MAAM,OAAO,GAAG,MAAM,kBAAkB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC3D,MAAM,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAChC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;QACtE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;QAClE,MAAM,SAAS,CAAC,SAAS,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAC9E,MAAM,YAAY,CAAC,SAAS,EAAE,QAAQ,EAAE,WAAW,CAAC,QAAQ,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC;QAEnF,MAAM,SAAS,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAE5E,MAAM,OAAO,GAAG,MAAM,kBAAkB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC3D,MAAM,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAChC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACxC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yDAAyD,EAAE,KAAK,IAAI,EAAE;QACvE,MAAM,SAAS,CAAC,SAAS,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAC9E,MAAM,YAAY,CAAC,SAAS,EAAE,QAAQ,EAAE,WAAW,CAAC,QAAQ,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC;QAEnF,MAAM,OAAO,GAAG,MAAM,kBAAkB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC3D,MAAM,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAChC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yDAAyD,EAAE,KAAK,IAAI,EAAE;QACvE,MAAM,SAAS,CAAC,SAAS,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAE9E,MAAM,OAAO,GAAG,MAAM,kBAAkB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC3D,MAAM,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kCAAkC,EAAE,KAAK,IAAI,EAAE;QAChD,wEAAwE;QACxE,MAAM,IAAI,GAAa;YACrB,OAAO,EAAE,aAAa;YACtB,SAAS,EAAE,MAAM;YACjB,cAAc,EAAE,KAAK;YACrB,UAAU,EAAE,sBAAsB;YAClC,OAAO,EAAE,QAAQ;YACjB,SAAS,EAAE,eAAe;YAC1B,IAAI,EAAE,SAAS;YACf,EAAE,EAAE,OAAO;YACX,OAAO,EAAE,MAAM;YACf,IAAI,EAAE,KAAK;YACX,QAAQ,EAAE,IAAI;YACd,OAAO,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE;SAC3B,CAAC;QACF,MAAM,EAAE,CAAC,SAAS,CAChB,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,cAAc,CAAC,EACtD,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,IAAI,CAC5B,CAAC;QAEF,MAAM,OAAO,GAAG,MAAM,kBAAkB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC3D,MAAM,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;QACnE,0BAA0B;QAC1B,MAAM,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACzE,MAAM,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,EAAE,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAE5E,MAAM,SAAS,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAE5E,MAAM,OAAO,GAAG,MAAM,kBAAkB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC3D,MAAM,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAChC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bugfixes.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/bugfixes.test.ts"],"names":[],"mappings":""}
|