@loompad/cli 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/ARCHITECTURE.md +211 -0
- package/CHANGELOG.md +470 -0
- package/LICENSE +21 -0
- package/README.md +630 -0
- package/dist/adapters/base.d.ts +71 -0
- package/dist/adapters/base.js +187 -0
- package/dist/adapters/base.js.map +1 -0
- package/dist/adapters/bridges/antigravity.d.ts +53 -0
- package/dist/adapters/bridges/antigravity.js +106 -0
- package/dist/adapters/bridges/antigravity.js.map +1 -0
- package/dist/adapters/bridges/cdp.d.ts +82 -0
- package/dist/adapters/bridges/cdp.js +222 -0
- package/dist/adapters/bridges/cdp.js.map +1 -0
- package/dist/adapters/bridges/gui-chat.d.ts +113 -0
- package/dist/adapters/bridges/gui-chat.js +498 -0
- package/dist/adapters/bridges/gui-chat.js.map +1 -0
- package/dist/adapters/bridges/kiro.d.ts +19 -0
- package/dist/adapters/bridges/kiro.js +22 -0
- package/dist/adapters/bridges/kiro.js.map +1 -0
- package/dist/adapters/bridges/profiles.d.ts +82 -0
- package/dist/adapters/bridges/profiles.js +125 -0
- package/dist/adapters/bridges/profiles.js.map +1 -0
- package/dist/adapters/claude-code.d.ts +26 -0
- package/dist/adapters/claude-code.js +209 -0
- package/dist/adapters/claude-code.js.map +1 -0
- package/dist/adapters/codex.d.ts +51 -0
- package/dist/adapters/codex.js +291 -0
- package/dist/adapters/codex.js.map +1 -0
- package/dist/adapters/echo.d.ts +20 -0
- package/dist/adapters/echo.js +79 -0
- package/dist/adapters/echo.js.map +1 -0
- package/dist/adapters/grok.d.ts +61 -0
- package/dist/adapters/grok.js +217 -0
- package/dist/adapters/grok.js.map +1 -0
- package/dist/adapters/index.d.ts +9 -0
- package/dist/adapters/index.js +33 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/opencode.d.ts +64 -0
- package/dist/adapters/opencode.js +426 -0
- package/dist/adapters/opencode.js.map +1 -0
- package/dist/cli/common.d.ts +11 -0
- package/dist/cli/common.js +40 -0
- package/dist/cli/common.js.map +1 -0
- package/dist/cli/doctor.d.ts +12 -0
- package/dist/cli/doctor.js +181 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/index.d.ts +8 -0
- package/dist/cli/index.js +641 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/tui-model.d.ts +73 -0
- package/dist/cli/tui-model.js +312 -0
- package/dist/cli/tui-model.js.map +1 -0
- package/dist/cli/tui.d.ts +15 -0
- package/dist/cli/tui.js +451 -0
- package/dist/cli/tui.js.map +1 -0
- package/dist/cli/ui.d.ts +10 -0
- package/dist/cli/ui.js +135 -0
- package/dist/cli/ui.js.map +1 -0
- package/dist/core/ades.d.ts +79 -0
- package/dist/core/ades.js +130 -0
- package/dist/core/ades.js.map +1 -0
- package/dist/core/baton.d.ts +37 -0
- package/dist/core/baton.js +92 -0
- package/dist/core/baton.js.map +1 -0
- package/dist/core/brain-extract.d.ts +111 -0
- package/dist/core/brain-extract.js +248 -0
- package/dist/core/brain-extract.js.map +1 -0
- package/dist/core/brain-index.d.ts +128 -0
- package/dist/core/brain-index.js +338 -0
- package/dist/core/brain-index.js.map +1 -0
- package/dist/core/brain.d.ts +214 -0
- package/dist/core/brain.js +425 -0
- package/dist/core/brain.js.map +1 -0
- package/dist/core/claude-cli.d.ts +12 -0
- package/dist/core/claude-cli.js +47 -0
- package/dist/core/claude-cli.js.map +1 -0
- package/dist/core/distill.d.ts +19 -0
- package/dist/core/distill.js +88 -0
- package/dist/core/distill.js.map +1 -0
- package/dist/core/eventlog.d.ts +43 -0
- package/dist/core/eventlog.js +211 -0
- package/dist/core/eventlog.js.map +1 -0
- package/dist/core/git.d.ts +203 -0
- package/dist/core/git.js +460 -0
- package/dist/core/git.js.map +1 -0
- package/dist/core/logbook.d.ts +59 -0
- package/dist/core/logbook.js +118 -0
- package/dist/core/logbook.js.map +1 -0
- package/dist/core/memory.d.ts +34 -0
- package/dist/core/memory.js +124 -0
- package/dist/core/memory.js.map +1 -0
- package/dist/core/notify.d.ts +11 -0
- package/dist/core/notify.js +30 -0
- package/dist/core/notify.js.map +1 -0
- package/dist/core/projection.d.ts +20 -0
- package/dist/core/projection.js +96 -0
- package/dist/core/projection.js.map +1 -0
- package/dist/core/registry.d.ts +76 -0
- package/dist/core/registry.js +147 -0
- package/dist/core/registry.js.map +1 -0
- package/dist/core/router.d.ts +42 -0
- package/dist/core/router.js +99 -0
- package/dist/core/router.js.map +1 -0
- package/dist/core/routes.d.ts +93 -0
- package/dist/core/routes.js +399 -0
- package/dist/core/routes.js.map +1 -0
- package/dist/core/search.d.ts +63 -0
- package/dist/core/search.js +0 -0
- package/dist/core/search.js.map +1 -0
- package/dist/core/setup.d.ts +70 -0
- package/dist/core/setup.js +250 -0
- package/dist/core/setup.js.map +1 -0
- package/dist/core/suggestions.d.ts +15 -0
- package/dist/core/suggestions.js +38 -0
- package/dist/core/suggestions.js.map +1 -0
- package/dist/core/worktree.d.ts +32 -0
- package/dist/core/worktree.js +83 -0
- package/dist/core/worktree.js.map +1 -0
- package/dist/daemon/app-page.d.ts +32 -0
- package/dist/daemon/app-page.js +6363 -0
- package/dist/daemon/app-page.js.map +1 -0
- package/dist/daemon/auth.d.ts +51 -0
- package/dist/daemon/auth.js +135 -0
- package/dist/daemon/auth.js.map +1 -0
- package/dist/daemon/board.d.ts +117 -0
- package/dist/daemon/board.js +202 -0
- package/dist/daemon/board.js.map +1 -0
- package/dist/daemon/brand-icons.d.ts +29 -0
- package/dist/daemon/brand-icons.js +44 -0
- package/dist/daemon/brand-icons.js.map +1 -0
- package/dist/daemon/client.d.ts +127 -0
- package/dist/daemon/client.js +237 -0
- package/dist/daemon/client.js.map +1 -0
- package/dist/daemon/geist-font.d.ts +10 -0
- package/dist/daemon/geist-font.js +783 -0
- package/dist/daemon/geist-font.js.map +1 -0
- package/dist/daemon/guards.d.ts +18 -0
- package/dist/daemon/guards.js +40 -0
- package/dist/daemon/guards.js.map +1 -0
- package/dist/daemon/linear.d.ts +59 -0
- package/dist/daemon/linear.js +117 -0
- package/dist/daemon/linear.js.map +1 -0
- package/dist/daemon/push.d.ts +22 -0
- package/dist/daemon/push.js +58 -0
- package/dist/daemon/push.js.map +1 -0
- package/dist/daemon/runtime.d.ts +299 -0
- package/dist/daemon/runtime.js +1016 -0
- package/dist/daemon/runtime.js.map +1 -0
- package/dist/daemon/server.d.ts +125 -0
- package/dist/daemon/server.js +1728 -0
- package/dist/daemon/server.js.map +1 -0
- package/dist/daemon/tasks.d.ts +153 -0
- package/dist/daemon/tasks.js +264 -0
- package/dist/daemon/tasks.js.map +1 -0
- package/dist/daemon/terminals.d.ts +82 -0
- package/dist/daemon/terminals.js +372 -0
- package/dist/daemon/terminals.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/sdk.d.ts +17 -0
- package/dist/sdk.js +17 -0
- package/dist/sdk.js.map +1 -0
- package/dist/types.d.ts +236 -0
- package/dist/types.js +18 -0
- package/dist/types.js.map +1 -0
- package/docs/SETUP.md +265 -0
- package/docs/adapters.md +132 -0
- package/docs/design-system.md +245 -0
- package/docs/img/board-linear.png +0 -0
- package/docs/img/board-projects.png +0 -0
- package/docs/img/brain.png +0 -0
- package/docs/img/settings.png +0 -0
- package/docs/img/workspace.png +0 -0
- package/docs/integration-notes.md +76 -0
- package/docs/proposals/brain.md +262 -0
- package/package.json +84 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logbook.js","sourceRoot":"","sources":["../../src/core/logbook.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified memory — "multiple memory in one".
|
|
3
|
+
*
|
|
4
|
+
* Every AI dev environment (ADE) keeps its own memory: Claude Code has
|
|
5
|
+
* CLAUDE.md, OpenCode has AGENTS.md, others have their own files. Loom's job
|
|
6
|
+
* is to make those ONE brain: it imports each connected ADE's native memory
|
|
7
|
+
* into the shared event log, merges it with the project's decisions and
|
|
8
|
+
* recent context, and projects the union back out on every handoff.
|
|
9
|
+
*
|
|
10
|
+
* Connect a new ADE → its existing knowledge joins the shared brain, and
|
|
11
|
+
* everything the other agents learned flows into it. That's the seam an
|
|
12
|
+
* isolation-first tool (separate worktrees) structurally can't own.
|
|
13
|
+
*/
|
|
14
|
+
import type { AgentConfig, LoomEvent, ProjectConfig, UnifiedMemory } from "../types.js";
|
|
15
|
+
export declare function nativeMemoryFiles(agent: AgentConfig): string[];
|
|
16
|
+
export interface ImportedBlock {
|
|
17
|
+
agentId: string;
|
|
18
|
+
kind: string;
|
|
19
|
+
file: string;
|
|
20
|
+
content: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Read every configured ADE's native memory from disk. Deduplicates by file
|
|
24
|
+
* path (agents commonly share AGENTS.md) — the first agent to claim a file
|
|
25
|
+
* owns it in the brain.
|
|
26
|
+
*/
|
|
27
|
+
export declare function readNativeMemory(projectDir: string, config: ProjectConfig): ImportedBlock[];
|
|
28
|
+
export declare function hashContent(content: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* Build the unified brain document from the log + freshly-read native memory.
|
|
31
|
+
* This is what "multiple memory in one" produces, and what enriches every
|
|
32
|
+
* handoff projection.
|
|
33
|
+
*/
|
|
34
|
+
export declare function buildUnifiedMemory(projectName: string, events: LoomEvent[], imported: ImportedBlock[]): UnifiedMemory;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified memory — "multiple memory in one".
|
|
3
|
+
*
|
|
4
|
+
* Every AI dev environment (ADE) keeps its own memory: Claude Code has
|
|
5
|
+
* CLAUDE.md, OpenCode has AGENTS.md, others have their own files. Loom's job
|
|
6
|
+
* is to make those ONE brain: it imports each connected ADE's native memory
|
|
7
|
+
* into the shared event log, merges it with the project's decisions and
|
|
8
|
+
* recent context, and projects the union back out on every handoff.
|
|
9
|
+
*
|
|
10
|
+
* Connect a new ADE → its existing knowledge joins the shared brain, and
|
|
11
|
+
* everything the other agents learned flows into it. That's the seam an
|
|
12
|
+
* isolation-first tool (separate worktrees) structurally can't own.
|
|
13
|
+
*/
|
|
14
|
+
import crypto from "node:crypto";
|
|
15
|
+
import fs from "node:fs";
|
|
16
|
+
import path from "node:path";
|
|
17
|
+
const MAX_IMPORT_CHARS = 8_000; // per source, keeps the brain bounded
|
|
18
|
+
const RECENT_DECISIONS = 40;
|
|
19
|
+
/**
|
|
20
|
+
* Where each ADE keeps its native, project-local memory. Project-scoped only
|
|
21
|
+
* by default — global files (e.g. ~/.claude/CLAUDE.md) are intentionally NOT
|
|
22
|
+
* imported so the shared brain stays about THIS project. Override per agent
|
|
23
|
+
* with `memoryFiles` in config.
|
|
24
|
+
*/
|
|
25
|
+
const NATIVE_MEMORY = {
|
|
26
|
+
"claude-code": ["CLAUDE.md", ".claude/CLAUDE.md", "AGENTS.md"],
|
|
27
|
+
// Codex reads AGENTS.md — that convention is OpenAI's, and it's the one most
|
|
28
|
+
// widely shared. `.codex/` is its own directory when a repo keeps them apart.
|
|
29
|
+
codex: ["AGENTS.md", ".codex/AGENTS.md", "codex.md"],
|
|
30
|
+
opencode: ["AGENTS.md", ".opencode/AGENTS.md", "opencode.md"],
|
|
31
|
+
"grok-code": ["AGENTS.md", ".grok/AGENTS.md", "GROK.md"],
|
|
32
|
+
antigravity: [".antigravity/memory.md", "AGENTS.md", ".windsurfrules"],
|
|
33
|
+
// Kiro keeps steering documents rather than one memory file; they're the
|
|
34
|
+
// closest thing it has to "what this agent knows about this repo".
|
|
35
|
+
kiro: [".kiro/steering/product.md", ".kiro/steering/tech.md", ".kiro/steering/structure.md", "AGENTS.md"],
|
|
36
|
+
echo: [],
|
|
37
|
+
};
|
|
38
|
+
export function nativeMemoryFiles(agent) {
|
|
39
|
+
if (agent.memoryFiles)
|
|
40
|
+
return agent.memoryFiles;
|
|
41
|
+
return NATIVE_MEMORY[agent.kind] ?? ["AGENTS.md"];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Read every configured ADE's native memory from disk. Deduplicates by file
|
|
45
|
+
* path (agents commonly share AGENTS.md) — the first agent to claim a file
|
|
46
|
+
* owns it in the brain.
|
|
47
|
+
*/
|
|
48
|
+
export function readNativeMemory(projectDir, config) {
|
|
49
|
+
const blocks = [];
|
|
50
|
+
const seenFiles = new Set();
|
|
51
|
+
for (const agent of config.agents) {
|
|
52
|
+
for (const rel of nativeMemoryFiles(agent)) {
|
|
53
|
+
const norm = rel.replace(/^\.\//, "");
|
|
54
|
+
if (seenFiles.has(norm))
|
|
55
|
+
continue;
|
|
56
|
+
const abs = path.join(projectDir, norm);
|
|
57
|
+
if (!fs.existsSync(abs))
|
|
58
|
+
continue;
|
|
59
|
+
try {
|
|
60
|
+
const raw = fs.readFileSync(abs, "utf8").trim();
|
|
61
|
+
if (!raw)
|
|
62
|
+
continue;
|
|
63
|
+
seenFiles.add(norm);
|
|
64
|
+
blocks.push({
|
|
65
|
+
agentId: agent.id,
|
|
66
|
+
kind: agent.kind,
|
|
67
|
+
file: norm,
|
|
68
|
+
content: raw.length > MAX_IMPORT_CHARS ? raw.slice(0, MAX_IMPORT_CHARS) + "\n… (truncated)" : raw,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// Unreadable file — skip, never fatal.
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return blocks;
|
|
77
|
+
}
|
|
78
|
+
export function hashContent(content) {
|
|
79
|
+
return crypto.createHash("sha1").update(content).digest("hex").slice(0, 12);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Build the unified brain document from the log + freshly-read native memory.
|
|
83
|
+
* This is what "multiple memory in one" produces, and what enriches every
|
|
84
|
+
* handoff projection.
|
|
85
|
+
*/
|
|
86
|
+
export function buildUnifiedMemory(projectName, events, imported) {
|
|
87
|
+
const decisions = events
|
|
88
|
+
.filter((e) => e.kind === "decision")
|
|
89
|
+
.map((e) => String(e.payload.text ?? ""))
|
|
90
|
+
.filter(Boolean)
|
|
91
|
+
.slice(-RECENT_DECISIONS);
|
|
92
|
+
const sources = imported.map((b) => ({
|
|
93
|
+
agentId: b.agentId,
|
|
94
|
+
kind: b.kind,
|
|
95
|
+
file: b.file,
|
|
96
|
+
chars: b.content.length,
|
|
97
|
+
}));
|
|
98
|
+
const lines = [];
|
|
99
|
+
lines.push(`# Loom unified memory — ${projectName}`);
|
|
100
|
+
lines.push("");
|
|
101
|
+
lines.push("> One brain across every connected agent. Merged from each ADE's native memory,", "> the project's decisions, and the shared thread. Regenerated on demand.");
|
|
102
|
+
lines.push("");
|
|
103
|
+
if (decisions.length) {
|
|
104
|
+
lines.push("## Decisions (shared across all agents)");
|
|
105
|
+
for (const d of decisions)
|
|
106
|
+
lines.push(`- ${d}`);
|
|
107
|
+
lines.push("");
|
|
108
|
+
}
|
|
109
|
+
if (imported.length) {
|
|
110
|
+
lines.push("## Imported from connected ADEs");
|
|
111
|
+
for (const b of imported) {
|
|
112
|
+
lines.push(`### ${b.agentId} · \`${b.file}\` (${b.kind})`);
|
|
113
|
+
lines.push("");
|
|
114
|
+
lines.push(b.content);
|
|
115
|
+
lines.push("");
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (!decisions.length && !imported.length) {
|
|
119
|
+
lines.push("_No decisions recorded yet and no ADE memory files found._");
|
|
120
|
+
lines.push("");
|
|
121
|
+
}
|
|
122
|
+
return { projectName, decisions, sources, document: lines.join("\n") };
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=memory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/core/memory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fire-and-notify — when a background agent needs input or finishes, tell
|
|
3
|
+
* the human. This is the local half: a desktop notification (macOS osascript /
|
|
4
|
+
* linux notify-send). The phone half rides the same event feed through Expo
|
|
5
|
+
* push — see daemon/push.ts.
|
|
6
|
+
*/
|
|
7
|
+
export interface Notification {
|
|
8
|
+
title: string;
|
|
9
|
+
body: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function notify(n: Notification): void;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fire-and-notify — when a background agent needs input or finishes, tell
|
|
3
|
+
* the human. This is the local half: a desktop notification (macOS osascript /
|
|
4
|
+
* linux notify-send). The phone half rides the same event feed through Expo
|
|
5
|
+
* push — see daemon/push.ts.
|
|
6
|
+
*/
|
|
7
|
+
import { spawn } from "node:child_process";
|
|
8
|
+
function run(cmd, args) {
|
|
9
|
+
try {
|
|
10
|
+
const child = spawn(cmd, args, { stdio: "ignore", detached: true });
|
|
11
|
+
child.unref();
|
|
12
|
+
child.on("error", () => { });
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
// Notifications are best-effort by design.
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function notify(n) {
|
|
19
|
+
if (process.env.LOOM_NO_NOTIFY)
|
|
20
|
+
return;
|
|
21
|
+
const body = n.body.replace(/"/g, "'").slice(0, 200);
|
|
22
|
+
const title = n.title.replace(/"/g, "'").slice(0, 60);
|
|
23
|
+
if (process.platform === "darwin") {
|
|
24
|
+
run("osascript", ["-e", `display notification "${body}" with title "${title}"`]);
|
|
25
|
+
}
|
|
26
|
+
else if (process.platform === "linux") {
|
|
27
|
+
run("notify-send", [title, body]);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=notify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notify.js","sourceRoot":"","sources":["../../src/core/notify.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAO3C,SAAS,GAAG,CAAC,GAAW,EAAE,IAAc;IACtC,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QACpE,KAAK,CAAC,KAAK,EAAE,CAAC;QACd,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,2CAA2C;IAC7C,CAAC;AACH,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,CAAe;IACpC,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc;QAAE,OAAO;IACvC,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACrD,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACtD,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAClC,GAAG,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,yBAAyB,IAAI,iBAAiB,KAAK,GAAG,CAAC,CAAC,CAAC;IACnF,CAAC;SAAM,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACxC,GAAG,CAAC,aAAa,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;IACpC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Projections — rendering the event log into (a) a persistent, namespaced
|
|
3
|
+
* memory document for an agent, and (b) a short one-shot handoff briefing.
|
|
4
|
+
*
|
|
5
|
+
* v1 is deterministic (no LLM in the loop): a distillation template over the
|
|
6
|
+
* recent log. An LLM-synthesized variant can slot in behind the same
|
|
7
|
+
* signatures later.
|
|
8
|
+
*/
|
|
9
|
+
import type { LoomEvent, ProjectConfig } from "../types.js";
|
|
10
|
+
export interface ProjectionInput {
|
|
11
|
+
projectName: string;
|
|
12
|
+
config: ProjectConfig;
|
|
13
|
+
events: LoomEvent[];
|
|
14
|
+
targetAgentId: string;
|
|
15
|
+
fromAgentId?: string | null;
|
|
16
|
+
}
|
|
17
|
+
/** Persistent memory document written to .loom/memory/<agent>.md. */
|
|
18
|
+
export declare function buildProjection(input: ProjectionInput): string;
|
|
19
|
+
/** Short one-shot briefing injected with the first turn after a handoff. */
|
|
20
|
+
export declare function buildBriefing(input: ProjectionInput): string;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Projections — rendering the event log into (a) a persistent, namespaced
|
|
3
|
+
* memory document for an agent, and (b) a short one-shot handoff briefing.
|
|
4
|
+
*
|
|
5
|
+
* v1 is deterministic (no LLM in the loop): a distillation template over the
|
|
6
|
+
* recent log. An LLM-synthesized variant can slot in behind the same
|
|
7
|
+
* signatures later.
|
|
8
|
+
*/
|
|
9
|
+
const MAX_MSG_CHARS = 300;
|
|
10
|
+
const RECENT_MESSAGES = 30;
|
|
11
|
+
const RECENT_FILES = 20;
|
|
12
|
+
function truncate(text, max = MAX_MSG_CHARS) {
|
|
13
|
+
const oneLine = text.replace(/\s+/g, " ").trim();
|
|
14
|
+
return oneLine.length > max ? oneLine.slice(0, max - 1) + "…" : oneLine;
|
|
15
|
+
}
|
|
16
|
+
function author(e) {
|
|
17
|
+
return e.agentId ?? e.payload.author ?? "user";
|
|
18
|
+
}
|
|
19
|
+
/** Persistent memory document written to .loom/memory/<agent>.md. */
|
|
20
|
+
export function buildProjection(input) {
|
|
21
|
+
const { projectName, config, events, targetAgentId } = input;
|
|
22
|
+
const messages = events.filter((e) => e.kind === "message").slice(-RECENT_MESSAGES);
|
|
23
|
+
const decisions = events.filter((e) => e.kind === "decision");
|
|
24
|
+
const handoffs = events.filter((e) => e.kind === "handoff").slice(-5);
|
|
25
|
+
const files = [
|
|
26
|
+
...new Set(events
|
|
27
|
+
.filter((e) => e.kind === "file_edit")
|
|
28
|
+
.map((e) => String(e.payload.path ?? ""))
|
|
29
|
+
.filter(Boolean)),
|
|
30
|
+
].slice(-RECENT_FILES);
|
|
31
|
+
const lines = [];
|
|
32
|
+
lines.push(`# Loom shared context — ${projectName}`);
|
|
33
|
+
lines.push("");
|
|
34
|
+
lines.push("> Managed by Loom. Do not edit — regenerated on every baton handoff.", `> You are \`${targetAgentId}\`. This is the shared state of the project across all agents.`);
|
|
35
|
+
lines.push("");
|
|
36
|
+
lines.push("## Agents & roles");
|
|
37
|
+
for (const a of config.agents) {
|
|
38
|
+
const marker = a.id === targetAgentId ? " ← you" : "";
|
|
39
|
+
lines.push(`- \`${a.id}\` (${a.kind}) — ${a.role}${marker}`);
|
|
40
|
+
}
|
|
41
|
+
lines.push("");
|
|
42
|
+
if (handoffs.length) {
|
|
43
|
+
lines.push("## Recent baton handoffs");
|
|
44
|
+
for (const h of handoffs) {
|
|
45
|
+
const from = h.payload.from ?? "—";
|
|
46
|
+
const to = h.payload.to ?? "—";
|
|
47
|
+
lines.push(`- ${new Date(h.ts).toISOString()}: ${from} → ${to}`);
|
|
48
|
+
}
|
|
49
|
+
lines.push("");
|
|
50
|
+
}
|
|
51
|
+
if (decisions.length) {
|
|
52
|
+
lines.push("## Decisions");
|
|
53
|
+
for (const d of decisions) {
|
|
54
|
+
lines.push(`- ${truncate(String(d.payload.text ?? ""))}`);
|
|
55
|
+
}
|
|
56
|
+
lines.push("");
|
|
57
|
+
}
|
|
58
|
+
if (messages.length) {
|
|
59
|
+
lines.push("## Recent conversation (condensed)");
|
|
60
|
+
for (const m of messages) {
|
|
61
|
+
lines.push(`- **${author(m)}**: ${truncate(String(m.payload.text ?? ""))}`);
|
|
62
|
+
}
|
|
63
|
+
lines.push("");
|
|
64
|
+
}
|
|
65
|
+
if (files.length) {
|
|
66
|
+
lines.push("## Files recently touched");
|
|
67
|
+
for (const f of files)
|
|
68
|
+
lines.push(`- \`${f}\``);
|
|
69
|
+
lines.push("");
|
|
70
|
+
}
|
|
71
|
+
return lines.join("\n");
|
|
72
|
+
}
|
|
73
|
+
/** Short one-shot briefing injected with the first turn after a handoff. */
|
|
74
|
+
export function buildBriefing(input) {
|
|
75
|
+
const { projectName, events, targetAgentId, fromAgentId } = input;
|
|
76
|
+
const messages = events.filter((e) => e.kind === "message").slice(-8);
|
|
77
|
+
const decisions = events.filter((e) => e.kind === "decision").slice(-6);
|
|
78
|
+
const lines = [];
|
|
79
|
+
lines.push(`[Loom handoff] You are "${targetAgentId}" picking up the baton for project "${projectName}"` +
|
|
80
|
+
(fromAgentId ? ` from "${fromAgentId}".` : "."));
|
|
81
|
+
lines.push(`A fuller shared-context document is at .loom/memory/${targetAgentId}.md — read it if you need more history.`);
|
|
82
|
+
if (decisions.length) {
|
|
83
|
+
lines.push("Key decisions so far:");
|
|
84
|
+
for (const d of decisions)
|
|
85
|
+
lines.push(`- ${truncate(String(d.payload.text ?? ""), 200)}`);
|
|
86
|
+
}
|
|
87
|
+
if (messages.length) {
|
|
88
|
+
lines.push("Most recent exchanges:");
|
|
89
|
+
for (const m of messages) {
|
|
90
|
+
lines.push(`- ${author(m)}: ${truncate(String(m.payload.text ?? ""), 200)}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
lines.push("Continue the work seamlessly; do not re-ask for context the log already answers.");
|
|
94
|
+
return lines.join("\n");
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=projection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projection.js","sourceRoot":"","sources":["../../src/core/projection.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,MAAM,aAAa,GAAG,GAAG,CAAC;AAC1B,MAAM,eAAe,GAAG,EAAE,CAAC;AAC3B,MAAM,YAAY,GAAG,EAAE,CAAC;AAExB,SAAS,QAAQ,CAAC,IAAY,EAAE,GAAG,GAAG,aAAa;IACjD,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IACjD,OAAO,OAAO,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC;AAC1E,CAAC;AAED,SAAS,MAAM,CAAC,CAAY;IAC1B,OAAO,CAAC,CAAC,OAAO,IAAK,CAAC,CAAC,OAAO,CAAC,MAA6B,IAAI,MAAM,CAAC;AACzE,CAAC;AAUD,qEAAqE;AACrE,MAAM,UAAU,eAAe,CAAC,KAAsB;IACpD,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,GAAG,KAAK,CAAC;IAE7D,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC,eAAe,CAAC,CAAC;IACpF,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC;IAC9D,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACtE,MAAM,KAAK,GAAG;QACZ,GAAG,IAAI,GAAG,CACR,MAAM;aACH,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC;aACrC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;aACxC,MAAM,CAAC,OAAO,CAAC,CACnB;KACF,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC;IAEvB,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,CAAC,IAAI,CAAC,2BAA2B,WAAW,EAAE,CAAC,CAAC;IACrD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CACR,sEAAsE,EACtE,eAAe,aAAa,gEAAgE,CAC7F,CAAC;IACF,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IAChC,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAC9B,MAAM,MAAM,GAAG,CAAC,CAAC,EAAE,KAAK,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;QACtD,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,IAAI,GAAG,MAAM,EAAE,CAAC,CAAC;IAC/D,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;QACpB,KAAK,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;QACvC,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;YACzB,MAAM,IAAI,GAAI,CAAC,CAAC,OAAO,CAAC,IAAsB,IAAI,GAAG,CAAC;YACtD,MAAM,EAAE,GAAI,CAAC,CAAC,OAAO,CAAC,EAAoB,IAAI,GAAG,CAAC;YAClD,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,KAAK,IAAI,MAAM,EAAE,EAAE,CAAC,CAAC;QACnE,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,IAAI,SAAS,CAAC,MAAM,EAAE,CAAC;QACrB,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC3B,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;YAC1B,KAAK,CAAC,IAAI,CAAC,KAAK,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;QAC5D,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;QACpB,KAAK,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;QACjD,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;YACzB,KAAK,CAAC,IAAI,CAAC,OAAO,MAAM,CAAC,CAAC,CAAC,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;QAC9E,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QACjB,KAAK,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;QACxC,KAAK,MAAM,CAAC,IAAI,KAAK;YAAE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAChD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,aAAa,CAAC,KAAsB;IAClD,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,WAAW,EAAE,GAAG,KAAK,CAAC;IAClE,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACtE,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAExE,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,CAAC,IAAI,CACR,2BAA2B,aAAa,uCAAuC,WAAW,GAAG;QAC3F,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,WAAW,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAClD,CAAC;IACF,KAAK,CAAC,IAAI,CACR,uDAAuD,aAAa,yCAAyC,CAC9G,CAAC;IACF,IAAI,SAAS,CAAC,MAAM,EAAE,CAAC;QACrB,KAAK,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;QACpC,KAAK,MAAM,CAAC,IAAI,SAAS;YAAE,KAAK,CAAC,IAAI,CAAC,KAAK,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IAC5F,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;QACpB,KAAK,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;QACrC,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;YACzB,KAAK,CAAC,IAAI,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QAC/E,CAAC;IACH,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,kFAAkF,CAAC,CAAC;IAC/F,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loom home (~/.loom) — project registry, daemon config, and per-project
|
|
3
|
+
* .loom/ config/state files.
|
|
4
|
+
*
|
|
5
|
+
* LOOM_HOME env var overrides the home directory (used heavily by tests).
|
|
6
|
+
*/
|
|
7
|
+
import type { ChatInfo, ProjectConfig, ProjectInfo, RouteState } from "../types.js";
|
|
8
|
+
export declare function loomHome(): string;
|
|
9
|
+
export declare function ensureLoomHome(): string;
|
|
10
|
+
export declare function newId(bytes?: number): string;
|
|
11
|
+
export declare function listProjects(): ProjectInfo[];
|
|
12
|
+
export declare function findProject(idOrDirOrName: string): ProjectInfo | undefined;
|
|
13
|
+
export declare function registerProject(dir: string, name: string): ProjectInfo;
|
|
14
|
+
export declare function unregisterProject(id: string): void;
|
|
15
|
+
export declare function projectLoomDir(projectDir: string): string;
|
|
16
|
+
export declare function readProjectConfig(projectDir: string): ProjectConfig | null;
|
|
17
|
+
export declare function writeProjectConfig(projectDir: string, cfg: ProjectConfig): void;
|
|
18
|
+
/** Mutable per-project state: baton holder + adapter session state. */
|
|
19
|
+
export interface ProjectState {
|
|
20
|
+
holder: string | null;
|
|
21
|
+
holderSince?: number;
|
|
22
|
+
agents: Record<string, Record<string, unknown>>;
|
|
23
|
+
/** Active (or last) multi-hop route. */
|
|
24
|
+
route?: RouteState;
|
|
25
|
+
/**
|
|
26
|
+
* Named conversations. The main chat is implicit — every project has it and
|
|
27
|
+
* it holds everything written before chats existed, so it isn't stored here.
|
|
28
|
+
* Kept in state rather than derived from the log so a chat you just made,
|
|
29
|
+
* and haven't said anything in yet, still exists.
|
|
30
|
+
*/
|
|
31
|
+
chats?: ChatInfo[];
|
|
32
|
+
/** Task cards you wrote yourself. See BoardTask. */
|
|
33
|
+
tasks?: BoardTask[];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* A card you made, as opposed to the ones the board derives from live agents
|
|
37
|
+
* and pull requests. Yours, so its column IS its state — dragging one really
|
|
38
|
+
* moves it, unlike a PR card whose truth belongs to GitHub.
|
|
39
|
+
*/
|
|
40
|
+
export interface BoardTask {
|
|
41
|
+
id: string;
|
|
42
|
+
title: string;
|
|
43
|
+
/** working | needs-you | in-review | ready — the column it lives in. */
|
|
44
|
+
column: string;
|
|
45
|
+
/** Optional agent this is meant for. */
|
|
46
|
+
agent?: string;
|
|
47
|
+
createdAt: number;
|
|
48
|
+
}
|
|
49
|
+
export declare function readProjectState(projectDir: string): ProjectState;
|
|
50
|
+
export declare function writeProjectState(projectDir: string, state: ProjectState): void;
|
|
51
|
+
/** Namespaced memory file Loom manages for an agent. Never a user file. */
|
|
52
|
+
export declare function memoryFile(projectDir: string, agentId: string): string;
|
|
53
|
+
export declare function writeMemoryFile(projectDir: string, agentId: string, content: string): string;
|
|
54
|
+
export interface PairedClient {
|
|
55
|
+
id: string;
|
|
56
|
+
name: string;
|
|
57
|
+
token: string;
|
|
58
|
+
createdAt: number;
|
|
59
|
+
/** Expo push token, when the device registered for notifications. */
|
|
60
|
+
pushToken?: string;
|
|
61
|
+
platform?: string;
|
|
62
|
+
}
|
|
63
|
+
export interface DaemonConfig {
|
|
64
|
+
host: string;
|
|
65
|
+
port: number;
|
|
66
|
+
adminToken: string;
|
|
67
|
+
clients: PairedClient[];
|
|
68
|
+
pid?: number;
|
|
69
|
+
}
|
|
70
|
+
export declare function daemonConfigFile(): string;
|
|
71
|
+
export declare function readDaemonConfig(): DaemonConfig | null;
|
|
72
|
+
export declare function writeDaemonConfig(cfg: DaemonConfig): void;
|
|
73
|
+
export declare function ensureDaemonConfig(defaults: {
|
|
74
|
+
host: string;
|
|
75
|
+
port: number;
|
|
76
|
+
}): DaemonConfig;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loom home (~/.loom) — project registry, daemon config, and per-project
|
|
3
|
+
* .loom/ config/state files.
|
|
4
|
+
*
|
|
5
|
+
* LOOM_HOME env var overrides the home directory (used heavily by tests).
|
|
6
|
+
*/
|
|
7
|
+
import crypto from "node:crypto";
|
|
8
|
+
import fs from "node:fs";
|
|
9
|
+
import os from "node:os";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
import { logbook } from "./logbook.js";
|
|
12
|
+
export function loomHome() {
|
|
13
|
+
return process.env.LOOM_HOME ?? path.join(os.homedir(), ".loom");
|
|
14
|
+
}
|
|
15
|
+
export function ensureLoomHome() {
|
|
16
|
+
const home = loomHome();
|
|
17
|
+
fs.mkdirSync(home, { recursive: true });
|
|
18
|
+
return home;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Read a JSON file, or fall back.
|
|
22
|
+
*
|
|
23
|
+
* The fallback is right for a file that isn't there — a project with no
|
|
24
|
+
* state.json has no state, which is a fact rather than a fault. It is very
|
|
25
|
+
* wrong for a file that IS there and is corrupt: `.loom/config.json` with a
|
|
26
|
+
* trailing comma silently became an empty roster, which looks exactly like
|
|
27
|
+
* "my agents disappeared" and gives you nothing to search for.
|
|
28
|
+
*
|
|
29
|
+
* So: missing is quiet, unreadable is loud. Both still return the fallback —
|
|
30
|
+
* refusing to start because one file is damaged helps nobody — but the second
|
|
31
|
+
* one says so in the Console.
|
|
32
|
+
*/
|
|
33
|
+
function readJson(file, fallback) {
|
|
34
|
+
let raw;
|
|
35
|
+
try {
|
|
36
|
+
raw = fs.readFileSync(file, "utf8");
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return fallback; // not there: nothing to report
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
return JSON.parse(raw);
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
logbook.warn("config", `${path.basename(file)} is unreadable — using defaults, so this will look like data went missing`, `${file}\n${err instanceof Error ? err.message : String(err)}`);
|
|
46
|
+
return fallback;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function writeJson(file, value, mode) {
|
|
50
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
51
|
+
fs.writeFileSync(file, JSON.stringify(value, null, 2) + "\n", mode ? { mode } : {});
|
|
52
|
+
}
|
|
53
|
+
export function newId(bytes = 6) {
|
|
54
|
+
return crypto.randomBytes(bytes).toString("hex");
|
|
55
|
+
}
|
|
56
|
+
function registryFile() {
|
|
57
|
+
return path.join(loomHome(), "registry.json");
|
|
58
|
+
}
|
|
59
|
+
export function listProjects() {
|
|
60
|
+
return readJson(registryFile(), { projects: [] }).projects;
|
|
61
|
+
}
|
|
62
|
+
export function findProject(idOrDirOrName) {
|
|
63
|
+
const projects = listProjects();
|
|
64
|
+
const dir = path.resolve(idOrDirOrName);
|
|
65
|
+
return projects.find((p) => p.id === idOrDirOrName || p.name === idOrDirOrName || path.resolve(p.dir) === dir);
|
|
66
|
+
}
|
|
67
|
+
export function registerProject(dir, name) {
|
|
68
|
+
ensureLoomHome();
|
|
69
|
+
const reg = readJson(registryFile(), { projects: [] });
|
|
70
|
+
const resolved = path.resolve(dir);
|
|
71
|
+
const existing = reg.projects.find((p) => path.resolve(p.dir) === resolved);
|
|
72
|
+
if (existing)
|
|
73
|
+
return existing;
|
|
74
|
+
const info = { id: newId(), name, dir: resolved };
|
|
75
|
+
reg.projects.push(info);
|
|
76
|
+
writeJson(registryFile(), reg);
|
|
77
|
+
return info;
|
|
78
|
+
}
|
|
79
|
+
export function unregisterProject(id) {
|
|
80
|
+
const reg = readJson(registryFile(), { projects: [] });
|
|
81
|
+
reg.projects = reg.projects.filter((p) => p.id !== id);
|
|
82
|
+
writeJson(registryFile(), reg);
|
|
83
|
+
}
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
// Per-project files (<dir>/.loom/…)
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
export function projectLoomDir(projectDir) {
|
|
88
|
+
return path.join(projectDir, ".loom");
|
|
89
|
+
}
|
|
90
|
+
export function readProjectConfig(projectDir) {
|
|
91
|
+
const file = path.join(projectLoomDir(projectDir), "config.json");
|
|
92
|
+
if (!fs.existsSync(file))
|
|
93
|
+
return null;
|
|
94
|
+
return readJson(file, { name: path.basename(projectDir), agents: [] });
|
|
95
|
+
}
|
|
96
|
+
export function writeProjectConfig(projectDir, cfg) {
|
|
97
|
+
writeJson(path.join(projectLoomDir(projectDir), "config.json"), cfg);
|
|
98
|
+
}
|
|
99
|
+
export function readProjectState(projectDir) {
|
|
100
|
+
return readJson(path.join(projectLoomDir(projectDir), "state.json"), {
|
|
101
|
+
holder: null,
|
|
102
|
+
agents: {},
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
export function writeProjectState(projectDir, state) {
|
|
106
|
+
writeJson(path.join(projectLoomDir(projectDir), "state.json"), state);
|
|
107
|
+
}
|
|
108
|
+
/** Namespaced memory file Loom manages for an agent. Never a user file. */
|
|
109
|
+
export function memoryFile(projectDir, agentId) {
|
|
110
|
+
return path.join(projectLoomDir(projectDir), "memory", `${agentId}.md`);
|
|
111
|
+
}
|
|
112
|
+
export function writeMemoryFile(projectDir, agentId, content) {
|
|
113
|
+
const file = memoryFile(projectDir, agentId);
|
|
114
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
115
|
+
fs.writeFileSync(file, content);
|
|
116
|
+
return file;
|
|
117
|
+
}
|
|
118
|
+
export function daemonConfigFile() {
|
|
119
|
+
return path.join(loomHome(), "daemon.json");
|
|
120
|
+
}
|
|
121
|
+
export function readDaemonConfig() {
|
|
122
|
+
const file = daemonConfigFile();
|
|
123
|
+
if (!fs.existsSync(file))
|
|
124
|
+
return null;
|
|
125
|
+
return readJson(file, null);
|
|
126
|
+
}
|
|
127
|
+
export function writeDaemonConfig(cfg) {
|
|
128
|
+
ensureLoomHome();
|
|
129
|
+
writeJson(daemonConfigFile(), cfg, 0o600);
|
|
130
|
+
}
|
|
131
|
+
export function ensureDaemonConfig(defaults) {
|
|
132
|
+
const existing = readDaemonConfig();
|
|
133
|
+
if (existing)
|
|
134
|
+
return existing;
|
|
135
|
+
const cfg = {
|
|
136
|
+
host: defaults.host,
|
|
137
|
+
port: defaults.port,
|
|
138
|
+
adminToken: crypto.randomBytes(32).toString("hex"),
|
|
139
|
+
clients: [],
|
|
140
|
+
};
|
|
141
|
+
writeDaemonConfig(cfg);
|
|
142
|
+
return cfg;
|
|
143
|
+
}
|
|
144
|
+
// buildDefaultRoutes and defaultAgentConfigs live in core/ades.ts now: they are
|
|
145
|
+
// answers about ADEs, and asking them here meant registry → ades → an adapter →
|
|
146
|
+
// base → registry, a cycle whose module-init order is nobody's friend.
|
|
147
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/core/registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hop routers for dynamic routes — after each step, decide who works next.
|
|
3
|
+
*
|
|
4
|
+
* Two brains, one interface:
|
|
5
|
+
* - rulesRouter: deterministic role rules (free, offline, always available)
|
|
6
|
+
* - llmRouter: asks Claude (headless, JSON out) to pick the next hop,
|
|
7
|
+
* falling back to the rules on any failure
|
|
8
|
+
*
|
|
9
|
+
* Loom never lets a router run away: dynamic routes carry a hop budget and
|
|
10
|
+
* every decision lands in the event log with its reason.
|
|
11
|
+
*/
|
|
12
|
+
import type { AgentRole } from "../types.js";
|
|
13
|
+
export interface RouterAgent {
|
|
14
|
+
id: string;
|
|
15
|
+
role: AgentRole;
|
|
16
|
+
}
|
|
17
|
+
export interface RouterContext {
|
|
18
|
+
task: string;
|
|
19
|
+
/** Agent ids of hops taken so far, in order. */
|
|
20
|
+
hops: string[];
|
|
21
|
+
/** Adapter-tier agents eligible for the next hop. */
|
|
22
|
+
agents: RouterAgent[];
|
|
23
|
+
/** Recent conversation: author (agent id or "user"/"loom") + text. */
|
|
24
|
+
recent: Array<{
|
|
25
|
+
author: string;
|
|
26
|
+
text: string;
|
|
27
|
+
}>;
|
|
28
|
+
}
|
|
29
|
+
export interface HopDecision {
|
|
30
|
+
next: string | "done";
|
|
31
|
+
reason: string;
|
|
32
|
+
/** Which brain produced this decision. */
|
|
33
|
+
by: "rules" | "llm";
|
|
34
|
+
}
|
|
35
|
+
/** Deterministic plan → execute → review → (fix loop) → done. */
|
|
36
|
+
export declare function rulesRouter(ctx: RouterContext): HopDecision;
|
|
37
|
+
export interface LlmRouterOptions {
|
|
38
|
+
/** Model for routing decisions; small+fast is right. */
|
|
39
|
+
model?: string;
|
|
40
|
+
timeoutMs?: number;
|
|
41
|
+
}
|
|
42
|
+
export declare function llmRouter(ctx: RouterContext, opts?: LlmRouterOptions): Promise<HopDecision>;
|