@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.
Files changed (177) hide show
  1. package/ARCHITECTURE.md +211 -0
  2. package/CHANGELOG.md +470 -0
  3. package/LICENSE +21 -0
  4. package/README.md +630 -0
  5. package/dist/adapters/base.d.ts +71 -0
  6. package/dist/adapters/base.js +187 -0
  7. package/dist/adapters/base.js.map +1 -0
  8. package/dist/adapters/bridges/antigravity.d.ts +53 -0
  9. package/dist/adapters/bridges/antigravity.js +106 -0
  10. package/dist/adapters/bridges/antigravity.js.map +1 -0
  11. package/dist/adapters/bridges/cdp.d.ts +82 -0
  12. package/dist/adapters/bridges/cdp.js +222 -0
  13. package/dist/adapters/bridges/cdp.js.map +1 -0
  14. package/dist/adapters/bridges/gui-chat.d.ts +113 -0
  15. package/dist/adapters/bridges/gui-chat.js +498 -0
  16. package/dist/adapters/bridges/gui-chat.js.map +1 -0
  17. package/dist/adapters/bridges/kiro.d.ts +19 -0
  18. package/dist/adapters/bridges/kiro.js +22 -0
  19. package/dist/adapters/bridges/kiro.js.map +1 -0
  20. package/dist/adapters/bridges/profiles.d.ts +82 -0
  21. package/dist/adapters/bridges/profiles.js +125 -0
  22. package/dist/adapters/bridges/profiles.js.map +1 -0
  23. package/dist/adapters/claude-code.d.ts +26 -0
  24. package/dist/adapters/claude-code.js +209 -0
  25. package/dist/adapters/claude-code.js.map +1 -0
  26. package/dist/adapters/codex.d.ts +51 -0
  27. package/dist/adapters/codex.js +291 -0
  28. package/dist/adapters/codex.js.map +1 -0
  29. package/dist/adapters/echo.d.ts +20 -0
  30. package/dist/adapters/echo.js +79 -0
  31. package/dist/adapters/echo.js.map +1 -0
  32. package/dist/adapters/grok.d.ts +61 -0
  33. package/dist/adapters/grok.js +217 -0
  34. package/dist/adapters/grok.js.map +1 -0
  35. package/dist/adapters/index.d.ts +9 -0
  36. package/dist/adapters/index.js +33 -0
  37. package/dist/adapters/index.js.map +1 -0
  38. package/dist/adapters/opencode.d.ts +64 -0
  39. package/dist/adapters/opencode.js +426 -0
  40. package/dist/adapters/opencode.js.map +1 -0
  41. package/dist/cli/common.d.ts +11 -0
  42. package/dist/cli/common.js +40 -0
  43. package/dist/cli/common.js.map +1 -0
  44. package/dist/cli/doctor.d.ts +12 -0
  45. package/dist/cli/doctor.js +181 -0
  46. package/dist/cli/doctor.js.map +1 -0
  47. package/dist/cli/index.d.ts +8 -0
  48. package/dist/cli/index.js +641 -0
  49. package/dist/cli/index.js.map +1 -0
  50. package/dist/cli/tui-model.d.ts +73 -0
  51. package/dist/cli/tui-model.js +312 -0
  52. package/dist/cli/tui-model.js.map +1 -0
  53. package/dist/cli/tui.d.ts +15 -0
  54. package/dist/cli/tui.js +451 -0
  55. package/dist/cli/tui.js.map +1 -0
  56. package/dist/cli/ui.d.ts +10 -0
  57. package/dist/cli/ui.js +135 -0
  58. package/dist/cli/ui.js.map +1 -0
  59. package/dist/core/ades.d.ts +79 -0
  60. package/dist/core/ades.js +130 -0
  61. package/dist/core/ades.js.map +1 -0
  62. package/dist/core/baton.d.ts +37 -0
  63. package/dist/core/baton.js +92 -0
  64. package/dist/core/baton.js.map +1 -0
  65. package/dist/core/brain-extract.d.ts +111 -0
  66. package/dist/core/brain-extract.js +248 -0
  67. package/dist/core/brain-extract.js.map +1 -0
  68. package/dist/core/brain-index.d.ts +128 -0
  69. package/dist/core/brain-index.js +338 -0
  70. package/dist/core/brain-index.js.map +1 -0
  71. package/dist/core/brain.d.ts +214 -0
  72. package/dist/core/brain.js +425 -0
  73. package/dist/core/brain.js.map +1 -0
  74. package/dist/core/claude-cli.d.ts +12 -0
  75. package/dist/core/claude-cli.js +47 -0
  76. package/dist/core/claude-cli.js.map +1 -0
  77. package/dist/core/distill.d.ts +19 -0
  78. package/dist/core/distill.js +88 -0
  79. package/dist/core/distill.js.map +1 -0
  80. package/dist/core/eventlog.d.ts +43 -0
  81. package/dist/core/eventlog.js +211 -0
  82. package/dist/core/eventlog.js.map +1 -0
  83. package/dist/core/git.d.ts +203 -0
  84. package/dist/core/git.js +460 -0
  85. package/dist/core/git.js.map +1 -0
  86. package/dist/core/logbook.d.ts +59 -0
  87. package/dist/core/logbook.js +118 -0
  88. package/dist/core/logbook.js.map +1 -0
  89. package/dist/core/memory.d.ts +34 -0
  90. package/dist/core/memory.js +124 -0
  91. package/dist/core/memory.js.map +1 -0
  92. package/dist/core/notify.d.ts +11 -0
  93. package/dist/core/notify.js +30 -0
  94. package/dist/core/notify.js.map +1 -0
  95. package/dist/core/projection.d.ts +20 -0
  96. package/dist/core/projection.js +96 -0
  97. package/dist/core/projection.js.map +1 -0
  98. package/dist/core/registry.d.ts +76 -0
  99. package/dist/core/registry.js +147 -0
  100. package/dist/core/registry.js.map +1 -0
  101. package/dist/core/router.d.ts +42 -0
  102. package/dist/core/router.js +99 -0
  103. package/dist/core/router.js.map +1 -0
  104. package/dist/core/routes.d.ts +93 -0
  105. package/dist/core/routes.js +399 -0
  106. package/dist/core/routes.js.map +1 -0
  107. package/dist/core/search.d.ts +63 -0
  108. package/dist/core/search.js +0 -0
  109. package/dist/core/search.js.map +1 -0
  110. package/dist/core/setup.d.ts +70 -0
  111. package/dist/core/setup.js +250 -0
  112. package/dist/core/setup.js.map +1 -0
  113. package/dist/core/suggestions.d.ts +15 -0
  114. package/dist/core/suggestions.js +38 -0
  115. package/dist/core/suggestions.js.map +1 -0
  116. package/dist/core/worktree.d.ts +32 -0
  117. package/dist/core/worktree.js +83 -0
  118. package/dist/core/worktree.js.map +1 -0
  119. package/dist/daemon/app-page.d.ts +32 -0
  120. package/dist/daemon/app-page.js +6363 -0
  121. package/dist/daemon/app-page.js.map +1 -0
  122. package/dist/daemon/auth.d.ts +51 -0
  123. package/dist/daemon/auth.js +135 -0
  124. package/dist/daemon/auth.js.map +1 -0
  125. package/dist/daemon/board.d.ts +117 -0
  126. package/dist/daemon/board.js +202 -0
  127. package/dist/daemon/board.js.map +1 -0
  128. package/dist/daemon/brand-icons.d.ts +29 -0
  129. package/dist/daemon/brand-icons.js +44 -0
  130. package/dist/daemon/brand-icons.js.map +1 -0
  131. package/dist/daemon/client.d.ts +127 -0
  132. package/dist/daemon/client.js +237 -0
  133. package/dist/daemon/client.js.map +1 -0
  134. package/dist/daemon/geist-font.d.ts +10 -0
  135. package/dist/daemon/geist-font.js +783 -0
  136. package/dist/daemon/geist-font.js.map +1 -0
  137. package/dist/daemon/guards.d.ts +18 -0
  138. package/dist/daemon/guards.js +40 -0
  139. package/dist/daemon/guards.js.map +1 -0
  140. package/dist/daemon/linear.d.ts +59 -0
  141. package/dist/daemon/linear.js +117 -0
  142. package/dist/daemon/linear.js.map +1 -0
  143. package/dist/daemon/push.d.ts +22 -0
  144. package/dist/daemon/push.js +58 -0
  145. package/dist/daemon/push.js.map +1 -0
  146. package/dist/daemon/runtime.d.ts +299 -0
  147. package/dist/daemon/runtime.js +1016 -0
  148. package/dist/daemon/runtime.js.map +1 -0
  149. package/dist/daemon/server.d.ts +125 -0
  150. package/dist/daemon/server.js +1728 -0
  151. package/dist/daemon/server.js.map +1 -0
  152. package/dist/daemon/tasks.d.ts +153 -0
  153. package/dist/daemon/tasks.js +264 -0
  154. package/dist/daemon/tasks.js.map +1 -0
  155. package/dist/daemon/terminals.d.ts +82 -0
  156. package/dist/daemon/terminals.js +372 -0
  157. package/dist/daemon/terminals.js.map +1 -0
  158. package/dist/index.d.ts +20 -0
  159. package/dist/index.js +20 -0
  160. package/dist/index.js.map +1 -0
  161. package/dist/sdk.d.ts +17 -0
  162. package/dist/sdk.js +17 -0
  163. package/dist/sdk.js.map +1 -0
  164. package/dist/types.d.ts +236 -0
  165. package/dist/types.js +18 -0
  166. package/dist/types.js.map +1 -0
  167. package/docs/SETUP.md +265 -0
  168. package/docs/adapters.md +132 -0
  169. package/docs/design-system.md +245 -0
  170. package/docs/img/board-linear.png +0 -0
  171. package/docs/img/board-projects.png +0 -0
  172. package/docs/img/brain.png +0 -0
  173. package/docs/img/settings.png +0 -0
  174. package/docs/img/workspace.png +0 -0
  175. package/docs/integration-notes.md +76 -0
  176. package/docs/proposals/brain.md +262 -0
  177. 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":"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"}
@@ -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>;