@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,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Searching a project: its code, and its conversations.
|
|
3
|
+
*
|
|
4
|
+
* The rail could find a file by name and nothing else, which is the least
|
|
5
|
+
* useful half of search — you almost always remember a line, not a filename.
|
|
6
|
+
* And the thread, which is where the actual reasoning of a project lives, was
|
|
7
|
+
* not searchable at all.
|
|
8
|
+
*
|
|
9
|
+
* ## Code: git grep, then a walk
|
|
10
|
+
*
|
|
11
|
+
* `git grep` is the right tool in a repo and it isn't close: it's fast, it
|
|
12
|
+
* already ignores .gitignore, and it never descends into node_modules. Outside
|
|
13
|
+
* a repo there's no index to lean on, so the fallback walks — with hard caps,
|
|
14
|
+
* because a search box that hangs the daemon on a home directory is worse than
|
|
15
|
+
* one that says "too many results".
|
|
16
|
+
*
|
|
17
|
+
* ## Chats: the event log
|
|
18
|
+
*
|
|
19
|
+
* Messages are events, so searching chats is scanning the log. No index: a
|
|
20
|
+
* project's log is thousands of rows, not millions, and a substring scan over
|
|
21
|
+
* that is microseconds. When that stops being true, this is the function to
|
|
22
|
+
* change, and it will be obvious.
|
|
23
|
+
*/
|
|
24
|
+
import type { EventStore } from "./eventlog.js";
|
|
25
|
+
export interface CodeHit {
|
|
26
|
+
path: string;
|
|
27
|
+
line: number;
|
|
28
|
+
text: string;
|
|
29
|
+
}
|
|
30
|
+
export interface ChatHit {
|
|
31
|
+
chat: string;
|
|
32
|
+
eventId: number;
|
|
33
|
+
at: number;
|
|
34
|
+
kind: string;
|
|
35
|
+
agentId: string | null;
|
|
36
|
+
/** The matching text, trimmed around the hit. */
|
|
37
|
+
snippet: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Search file contents.
|
|
41
|
+
*
|
|
42
|
+
* Case-insensitive and fixed-string, not regex: the box is a search box, and
|
|
43
|
+
* someone typing `foo(bar)` means those characters. A regex there would turn a
|
|
44
|
+
* paren into a syntax error and blame them for it.
|
|
45
|
+
*/
|
|
46
|
+
export declare function searchCode(dir: string, query: string): Promise<{
|
|
47
|
+
hits: CodeHit[];
|
|
48
|
+
truncated: boolean;
|
|
49
|
+
}>;
|
|
50
|
+
/**
|
|
51
|
+
* Search the conversations.
|
|
52
|
+
*
|
|
53
|
+
* Every kind of event with text in it, not just your messages: an agent's
|
|
54
|
+
* reply, the tool it ran, the error it hit. What you remember about a project
|
|
55
|
+
* is rarely the sentence you typed — it's the answer you got.
|
|
56
|
+
*/
|
|
57
|
+
export declare function searchChats(log: Pick<EventStore, "list">, query: string, opts?: {
|
|
58
|
+
chat?: string;
|
|
59
|
+
limit?: number;
|
|
60
|
+
}): {
|
|
61
|
+
hits: ChatHit[];
|
|
62
|
+
truncated: boolean;
|
|
63
|
+
};
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/core/search.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a new machine still needs — one answer, for the CLI and the app both.
|
|
3
|
+
*
|
|
4
|
+
* `loom doctor` and the onboarding screen ask the same question, so they share
|
|
5
|
+
* a source. The alternative is what this codebase already learned the hard way
|
|
6
|
+
* with agent kinds: two lists that agree until they don't, and a UI confidently
|
|
7
|
+
* telling you something the code stopped believing months ago.
|
|
8
|
+
*
|
|
9
|
+
* Everything here is per-platform because the answers genuinely differ — a
|
|
10
|
+
* Windows firewall prompt and a macOS notification permission are not the same
|
|
11
|
+
* item worded differently.
|
|
12
|
+
*/
|
|
13
|
+
export type Platform = "darwin" | "win32" | "linux";
|
|
14
|
+
export declare function platform(): Platform;
|
|
15
|
+
export interface AgentStatus {
|
|
16
|
+
kind: string;
|
|
17
|
+
label: string;
|
|
18
|
+
found: boolean;
|
|
19
|
+
/** How to get it, when it's missing. */
|
|
20
|
+
install: string;
|
|
21
|
+
/**
|
|
22
|
+
* Is it actually signed in?
|
|
23
|
+
*
|
|
24
|
+
* `null` when we couldn't tell in the time we were willing to wait — an
|
|
25
|
+
* honest "don't know", not a cheerful yes.
|
|
26
|
+
*/
|
|
27
|
+
authed: boolean | null;
|
|
28
|
+
/** What the tool said, when it said no. */
|
|
29
|
+
authDetail?: string;
|
|
30
|
+
/** The command that fixes it. */
|
|
31
|
+
auth: string;
|
|
32
|
+
}
|
|
33
|
+
export interface BridgeStatus {
|
|
34
|
+
kind: string;
|
|
35
|
+
label: string;
|
|
36
|
+
port: number;
|
|
37
|
+
/** Something is answering the debugger. */
|
|
38
|
+
reachable: boolean;
|
|
39
|
+
/** ...and it has a chat we could actually type into. */
|
|
40
|
+
driveable: boolean;
|
|
41
|
+
/** Why not, in words worth showing a person. */
|
|
42
|
+
reason?: string;
|
|
43
|
+
/** How to start it with a debugger, on this OS. */
|
|
44
|
+
launch: string;
|
|
45
|
+
}
|
|
46
|
+
export interface PermissionItem {
|
|
47
|
+
title: string;
|
|
48
|
+
why: string;
|
|
49
|
+
how: string;
|
|
50
|
+
/** True when Loom deliberately does NOT want this, despite expectations. */
|
|
51
|
+
refused?: boolean;
|
|
52
|
+
}
|
|
53
|
+
export interface SetupReport {
|
|
54
|
+
platform: Platform;
|
|
55
|
+
node: {
|
|
56
|
+
version: string;
|
|
57
|
+
ok: boolean;
|
|
58
|
+
needed: string;
|
|
59
|
+
};
|
|
60
|
+
agents: AgentStatus[];
|
|
61
|
+
bridges: BridgeStatus[];
|
|
62
|
+
permissions: PermissionItem[];
|
|
63
|
+
/** Nothing to drive: the one state that makes Loom useless. */
|
|
64
|
+
ready: boolean;
|
|
65
|
+
}
|
|
66
|
+
/** Is this Node new enough for the event log to be real? */
|
|
67
|
+
export declare function nodeOk(version?: string): boolean;
|
|
68
|
+
export declare function setupReport(): Promise<SetupReport>;
|
|
69
|
+
/** For the "and this is your machine" line in onboarding. */
|
|
70
|
+
export declare function machineName(): string;
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a new machine still needs — one answer, for the CLI and the app both.
|
|
3
|
+
*
|
|
4
|
+
* `loom doctor` and the onboarding screen ask the same question, so they share
|
|
5
|
+
* a source. The alternative is what this codebase already learned the hard way
|
|
6
|
+
* with agent kinds: two lists that agree until they don't, and a UI confidently
|
|
7
|
+
* telling you something the code stopped believing months ago.
|
|
8
|
+
*
|
|
9
|
+
* Everything here is per-platform because the answers genuinely differ — a
|
|
10
|
+
* Windows firewall prompt and a macOS notification permission are not the same
|
|
11
|
+
* item worded differently.
|
|
12
|
+
*/
|
|
13
|
+
import { execFile } from "node:child_process";
|
|
14
|
+
import os from "node:os";
|
|
15
|
+
import { GuiChatDriver } from "../adapters/bridges/gui-chat.js";
|
|
16
|
+
import { codexBin } from "../adapters/codex.js";
|
|
17
|
+
import { profileFor } from "../adapters/bridges/profiles.js";
|
|
18
|
+
import { ADES, detectAdes } from "./ades.js";
|
|
19
|
+
export function platform() {
|
|
20
|
+
return process.platform === "darwin" ? "darwin" : process.platform === "win32" ? "win32" : "linux";
|
|
21
|
+
}
|
|
22
|
+
const INSTALL = {
|
|
23
|
+
"claude-code": "npm i -g @anthropic-ai/claude-code",
|
|
24
|
+
codex: "install Codex.app, or npm i -g @openai/codex",
|
|
25
|
+
opencode: "curl -fsSL https://opencode.ai/install | bash",
|
|
26
|
+
"grok-code": "install the grok CLI from docs.x.ai",
|
|
27
|
+
};
|
|
28
|
+
const AUTH = {
|
|
29
|
+
"claude-code": "run `claude`, then /login",
|
|
30
|
+
codex: "codex login",
|
|
31
|
+
opencode: "opencode auth login",
|
|
32
|
+
"grok-code": "run `grok` once and sign in",
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Ask each tool whether it's actually signed in.
|
|
36
|
+
*
|
|
37
|
+
* I claimed this was impossible earlier in the day — that installed and
|
|
38
|
+
* authenticated look identical from out here — and then watched a whole
|
|
39
|
+
* afternoon go into "claude is installed" while `claude` answered "Not logged
|
|
40
|
+
* in · Please run /login" to anyone who asked. It isn't impossible. It just
|
|
41
|
+
* needs asking the right question.
|
|
42
|
+
*
|
|
43
|
+
* Every probe here is free and offline-ish:
|
|
44
|
+
* codex — `login status` reports without touching a model
|
|
45
|
+
* opencode — `auth list` reads its own credentials file
|
|
46
|
+
* claude — a `-p` probe; when signed out it refuses in ~30ms having called
|
|
47
|
+
* nothing. When signed IN it would cost a token or two, so it is
|
|
48
|
+
* capped hard and a slow answer counts as "signed in": only the
|
|
49
|
+
* instant refusal is diagnostic.
|
|
50
|
+
* grok — has no status command; unknown rather than guessed.
|
|
51
|
+
*/
|
|
52
|
+
async function probeAuth(kind) {
|
|
53
|
+
const run = (cmd, args, ms) => new Promise((resolve) => {
|
|
54
|
+
let done = false;
|
|
55
|
+
const child = execFile(cmd, args, { timeout: ms }, (_err, stdout, stderr) => {
|
|
56
|
+
if (done)
|
|
57
|
+
return;
|
|
58
|
+
done = true;
|
|
59
|
+
resolve({ code: 0, out: `${stdout}${stderr}` });
|
|
60
|
+
});
|
|
61
|
+
child.stdin?.end();
|
|
62
|
+
child.on("error", () => {
|
|
63
|
+
if (!done) {
|
|
64
|
+
done = true;
|
|
65
|
+
resolve({ code: null, out: "" });
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
setTimeout(() => {
|
|
69
|
+
if (!done) {
|
|
70
|
+
done = true;
|
|
71
|
+
try {
|
|
72
|
+
child.kill("SIGKILL");
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
/* gone */
|
|
76
|
+
}
|
|
77
|
+
resolve({ code: null, out: "" });
|
|
78
|
+
}
|
|
79
|
+
}, ms + 200).unref?.();
|
|
80
|
+
});
|
|
81
|
+
try {
|
|
82
|
+
if (kind === "codex") {
|
|
83
|
+
const bin = codexBin();
|
|
84
|
+
if (!bin)
|
|
85
|
+
return { authed: null };
|
|
86
|
+
const { out } = await run(bin, ["login", "status"], 6000);
|
|
87
|
+
if (/logged in/i.test(out))
|
|
88
|
+
return { authed: true };
|
|
89
|
+
if (/not logged in|no auth/i.test(out))
|
|
90
|
+
return { authed: false, detail: out.trim().slice(0, 60) };
|
|
91
|
+
return { authed: null };
|
|
92
|
+
}
|
|
93
|
+
if (kind === "opencode") {
|
|
94
|
+
const { out } = await run("opencode", ["auth", "list"], 8000);
|
|
95
|
+
// its list marks each stored credential with a bullet
|
|
96
|
+
return /●/.test(out) ? { authed: true } : { authed: null };
|
|
97
|
+
}
|
|
98
|
+
if (kind === "claude-code") {
|
|
99
|
+
// Signed out, this refuses instantly and for free. Signed in, it would
|
|
100
|
+
// start a real turn — so the timeout is the budget, and hitting it is a
|
|
101
|
+
// yes, not a maybe.
|
|
102
|
+
const { out } = await run("claude", ["-p", "hi", "--output-format", "json"], 6000);
|
|
103
|
+
if (/not logged in|please run \/login|invalid api key/i.test(out)) {
|
|
104
|
+
return { authed: false, detail: "the CLI says: Not logged in" };
|
|
105
|
+
}
|
|
106
|
+
return { authed: true };
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
/* fall through to unknown */
|
|
111
|
+
}
|
|
112
|
+
return { authed: null };
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* The permissions Loom actually needs, and the ones it pointedly doesn't.
|
|
116
|
+
*
|
|
117
|
+
* The refusals are listed on purpose. People expect a tool that drives other
|
|
118
|
+
* apps to demand Accessibility, and something that asks for it while claiming
|
|
119
|
+
* to be Loom is worth being suspicious of. Loom talks to a debugging port; it
|
|
120
|
+
* never pretends to be a mouse, so it never needs control of your machine.
|
|
121
|
+
*/
|
|
122
|
+
function permissionsFor(p) {
|
|
123
|
+
const shared = [
|
|
124
|
+
{
|
|
125
|
+
title: "Accessibility / Screen Recording / Automation",
|
|
126
|
+
why: "Loom drives GUI agents through their debug port, not by faking clicks",
|
|
127
|
+
how: "Not needed — don't grant it. If something asks for this in Loom's name, it isn't Loom.",
|
|
128
|
+
refused: true,
|
|
129
|
+
},
|
|
130
|
+
];
|
|
131
|
+
if (p === "darwin") {
|
|
132
|
+
return [
|
|
133
|
+
{
|
|
134
|
+
title: "Notifications",
|
|
135
|
+
why: "so you hear when an agent needs you and you're in another window",
|
|
136
|
+
how: "Allowed on first ask · System Settings → Notifications → Loom",
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
title: "Opening an unsigned app",
|
|
140
|
+
why: "Loom.app isn't notarized yet, so Gatekeeper blocks a double-click",
|
|
141
|
+
how: "Right-click the app → Open, once. Double-clicking gives a dead end.",
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
title: "Local Network",
|
|
145
|
+
why: "your phone reaching the daemon over the LAN",
|
|
146
|
+
how: "Prompted on first connection · System Settings → Privacy & Security → Local Network",
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
title: "Run at login",
|
|
150
|
+
why: "the daemon survives closing the window, but not a reboot",
|
|
151
|
+
how: "System Settings → General → Login Items → + → Loom",
|
|
152
|
+
},
|
|
153
|
+
...shared,
|
|
154
|
+
];
|
|
155
|
+
}
|
|
156
|
+
if (p === "win32") {
|
|
157
|
+
return [
|
|
158
|
+
{
|
|
159
|
+
title: "Firewall — private networks",
|
|
160
|
+
why: "your phone reaching the daemon; without it the tailnet can't connect",
|
|
161
|
+
how: "Windows Defender prompts on first `loom up` — tick Private, leave Public off",
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
title: "SmartScreen",
|
|
165
|
+
why: "the installer isn't signed yet",
|
|
166
|
+
how: "More info → Run anyway",
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
title: "Notifications",
|
|
170
|
+
why: "so you hear when an agent needs you",
|
|
171
|
+
how: "Settings → System → Notifications → Loom",
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
title: "Run at login",
|
|
175
|
+
why: "the daemon survives closing the window, but not a reboot",
|
|
176
|
+
how: "Win+R → shell:startup → put a shortcut to Loom in there",
|
|
177
|
+
},
|
|
178
|
+
...shared,
|
|
179
|
+
];
|
|
180
|
+
}
|
|
181
|
+
return [
|
|
182
|
+
{
|
|
183
|
+
title: "Notifications",
|
|
184
|
+
why: "so you hear when an agent needs you",
|
|
185
|
+
how: "your desktop's notification settings — Loom uses libnotify",
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
title: "Run at login",
|
|
189
|
+
why: "the daemon survives closing the window, but not a reboot",
|
|
190
|
+
how: "a user systemd unit running `loom up`",
|
|
191
|
+
},
|
|
192
|
+
...shared,
|
|
193
|
+
];
|
|
194
|
+
}
|
|
195
|
+
/** Is this Node new enough for the event log to be real? */
|
|
196
|
+
export function nodeOk(version = process.versions.node) {
|
|
197
|
+
const [major, minor] = version.split(".").map(Number);
|
|
198
|
+
return (major ?? 0) > 22 || ((major ?? 0) === 22 && (minor ?? 0) >= 5);
|
|
199
|
+
}
|
|
200
|
+
export async function setupReport() {
|
|
201
|
+
const p = platform();
|
|
202
|
+
const available = await detectAdes();
|
|
203
|
+
const agents = await Promise.all(ADES.filter((a) => a.tier === "adapter").map(async (a) => {
|
|
204
|
+
const found = Boolean(available[a.kind]);
|
|
205
|
+
const auth = found ? await probeAuth(a.kind) : { authed: null };
|
|
206
|
+
return {
|
|
207
|
+
kind: a.kind,
|
|
208
|
+
label: a.label,
|
|
209
|
+
found,
|
|
210
|
+
install: INSTALL[a.kind] ?? "",
|
|
211
|
+
authed: auth.authed,
|
|
212
|
+
...(auth.detail ? { authDetail: auth.detail } : {}),
|
|
213
|
+
auth: AUTH[a.kind] ?? "",
|
|
214
|
+
};
|
|
215
|
+
}));
|
|
216
|
+
// Bridges are probed live: "installed" says nothing useful about an app whose
|
|
217
|
+
// debugger has to be asked for at launch.
|
|
218
|
+
const bridges = await Promise.all(ADES.filter((a) => a.tier === "bridge").map(async (a) => {
|
|
219
|
+
const profile = profileFor(a.kind);
|
|
220
|
+
const driver = new GuiChatDriver(profile.name, {}, a.kind);
|
|
221
|
+
const reachable = await driver.reachable().catch(() => false);
|
|
222
|
+
const drive = reachable ? await driver.driveable() : { ok: false, reason: undefined };
|
|
223
|
+
return {
|
|
224
|
+
kind: a.kind,
|
|
225
|
+
label: profile.name,
|
|
226
|
+
port: profile.defaultPort,
|
|
227
|
+
reachable,
|
|
228
|
+
driveable: drive.ok,
|
|
229
|
+
...(drive.ok ? {} : { reason: drive.reason ?? "not running" }),
|
|
230
|
+
launch: profile.launch[p],
|
|
231
|
+
};
|
|
232
|
+
}));
|
|
233
|
+
return {
|
|
234
|
+
platform: p,
|
|
235
|
+
node: { version: process.versions.node, ok: nodeOk(), needed: "22.5" },
|
|
236
|
+
agents,
|
|
237
|
+
bridges,
|
|
238
|
+
permissions: permissionsFor(p),
|
|
239
|
+
// Ready means a turn can actually happen. An installed-but-signed-out
|
|
240
|
+
// roster is not ready, and saying it is was the exact shape of today's
|
|
241
|
+
// wasted afternoon. Unknown counts as ready — don't cry wolf over a probe
|
|
242
|
+
// that timed out.
|
|
243
|
+
ready: agents.some((a) => a.found && a.authed !== false),
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
/** For the "and this is your machine" line in onboarding. */
|
|
247
|
+
export function machineName() {
|
|
248
|
+
return os.hostname().replace(/\.local$/, "");
|
|
249
|
+
}
|
|
250
|
+
//# sourceMappingURL=setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/core/setup.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Suggested handoffs — routing stays manual (the user confirms every baton
|
|
3
|
+
* pass), but Loom watches the stream for natural boundaries and *offers*
|
|
4
|
+
* the next hop. This is the stepping stone toward auto-routing.
|
|
5
|
+
*/
|
|
6
|
+
import type { LoomEvent, ProjectConfig } from "../types.js";
|
|
7
|
+
export interface Suggestion {
|
|
8
|
+
to: string;
|
|
9
|
+
reason: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Inspect a freshly appended event; return a handoff suggestion or null.
|
|
13
|
+
* Only message events from the current baton holder are considered.
|
|
14
|
+
*/
|
|
15
|
+
export declare function suggestHandoff(event: LoomEvent, cfg: ProjectConfig, holder: string | null): Suggestion | null;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Suggested handoffs — routing stays manual (the user confirms every baton
|
|
3
|
+
* pass), but Loom watches the stream for natural boundaries and *offers*
|
|
4
|
+
* the next hop. This is the stepping stone toward auto-routing.
|
|
5
|
+
*/
|
|
6
|
+
const PLAN_DONE = /\b(plan(?:ning)?\s+(?:is\s+)?(?:complete|ready|done|final(?:ized)?)|ready\s+(?:for|to)\s+(?:implement|execution|execute|build)|implementation\s+plan\b.*\b(?:below|follows|ready))/i;
|
|
7
|
+
const EXEC_DONE = /\b(implementation\s+(?:is\s+)?(?:complete|done|finished)|all\s+tests?\s+pass(?:ing)?|ready\s+for\s+review)\b/i;
|
|
8
|
+
function firstWithRole(cfg, role, excludeId) {
|
|
9
|
+
return cfg.agents.find((a) => a.role === role && a.id !== excludeId);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Inspect a freshly appended event; return a handoff suggestion or null.
|
|
13
|
+
* Only message events from the current baton holder are considered.
|
|
14
|
+
*/
|
|
15
|
+
export function suggestHandoff(event, cfg, holder) {
|
|
16
|
+
if (event.kind !== "message" || !event.agentId)
|
|
17
|
+
return null;
|
|
18
|
+
if (holder && event.agentId !== holder)
|
|
19
|
+
return null;
|
|
20
|
+
const speaker = cfg.agents.find((a) => a.id === event.agentId);
|
|
21
|
+
if (!speaker)
|
|
22
|
+
return null;
|
|
23
|
+
const text = String(event.payload.text ?? "");
|
|
24
|
+
if (speaker.role === "planner" && PLAN_DONE.test(text)) {
|
|
25
|
+
const executor = firstWithRole(cfg, "executor", speaker.id);
|
|
26
|
+
if (executor) {
|
|
27
|
+
return { to: executor.id, reason: "plan looks complete — hand to the executor?" };
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (speaker.role === "executor" && EXEC_DONE.test(text)) {
|
|
31
|
+
const reviewer = firstWithRole(cfg, "reviewer", speaker.id) ?? firstWithRole(cfg, "planner", speaker.id);
|
|
32
|
+
if (reviewer) {
|
|
33
|
+
return { to: reviewer.id, reason: "execution looks done — hand back for review?" };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=suggestions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"suggestions.js","sourceRoot":"","sources":["../../src/core/suggestions.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,MAAM,SAAS,GACb,qLAAqL,CAAC;AACxL,MAAM,SAAS,GACb,+GAA+G,CAAC;AAElH,SAAS,aAAa,CAAC,GAAkB,EAAE,IAAe,EAAE,SAAkB;IAC5E,OAAO,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,IAAI,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC;AACvE,CAAC;AAOD;;;GAGG;AACH,MAAM,UAAU,cAAc,CAC5B,KAAgB,EAChB,GAAkB,EAClB,MAAqB;IAErB,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,KAAK,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC5D,IAAI,MAAM,IAAI,KAAK,CAAC,OAAO,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC;IAEpD,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,OAAO,CAAC,CAAC;IAC/D,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC1B,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;IAE9C,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACvD,MAAM,QAAQ,GAAG,aAAa,CAAC,GAAG,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;QAC5D,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,6CAA6C,EAAE,CAAC;QACpF,CAAC;IACH,CAAC;IACD,IAAI,OAAO,CAAC,IAAI,KAAK,UAAU,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACxD,MAAM,QAAQ,GACZ,aAAa,CAAC,GAAG,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,CAAC,IAAI,aAAa,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;QAC1F,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,8CAA8C,EAAE,CAAC;QACrF,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Working-tree inspection — the data behind "what code changed, per prompt".
|
|
3
|
+
* Read-only git plumbing; every function degrades to empty results outside
|
|
4
|
+
* a git repo.
|
|
5
|
+
*/
|
|
6
|
+
export interface ChangedFile {
|
|
7
|
+
status: string;
|
|
8
|
+
path: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function parsePorcelain(porcelain: string): ChangedFile[];
|
|
11
|
+
export declare function porcelainStatus(dir: string): Promise<string>;
|
|
12
|
+
export interface TurnDiff {
|
|
13
|
+
files: ChangedFile[];
|
|
14
|
+
added: number;
|
|
15
|
+
removed: number;
|
|
16
|
+
patch: string;
|
|
17
|
+
truncated: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Changes attributable to one turn: files whose porcelain line differs from
|
|
21
|
+
* the pre-turn snapshot, with a patch limited to those files.
|
|
22
|
+
*/
|
|
23
|
+
export declare function diffSinceSnapshot(dir: string, before: string): Promise<TurnDiff | null>;
|
|
24
|
+
export interface WorkingTree {
|
|
25
|
+
git: boolean;
|
|
26
|
+
branch?: string;
|
|
27
|
+
files: ChangedFile[];
|
|
28
|
+
patch: string;
|
|
29
|
+
truncated: boolean;
|
|
30
|
+
}
|
|
31
|
+
/** The project's current uncommitted state, for the Changes/Working-tree views. */
|
|
32
|
+
export declare function workingTree(dir: string): Promise<WorkingTree>;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Working-tree inspection — the data behind "what code changed, per prompt".
|
|
3
|
+
* Read-only git plumbing; every function degrades to empty results outside
|
|
4
|
+
* a git repo.
|
|
5
|
+
*/
|
|
6
|
+
import { execFile } from "node:child_process";
|
|
7
|
+
const PATCH_EVENT_LIMIT = 12_000; // per-turn patch stored in the event log
|
|
8
|
+
const PATCH_VIEW_LIMIT = 64_000; // full working-tree patch served to apps
|
|
9
|
+
function git(args, cwd) {
|
|
10
|
+
return new Promise((resolve) => {
|
|
11
|
+
execFile("git", args, { cwd, maxBuffer: 8 * 1024 * 1024 }, (err, stdout) => resolve(err ? "" : stdout));
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
export function parsePorcelain(porcelain) {
|
|
15
|
+
return porcelain
|
|
16
|
+
.split("\n")
|
|
17
|
+
.filter(Boolean)
|
|
18
|
+
.map((line) => ({ status: line.slice(0, 2), path: line.slice(3).trim() }));
|
|
19
|
+
}
|
|
20
|
+
export async function porcelainStatus(dir) {
|
|
21
|
+
// -uall lists files inside untracked directories (not just "?? dir/").
|
|
22
|
+
return (await git(["status", "--porcelain", "-uall"], dir)).trimEnd();
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Changes attributable to one turn: files whose porcelain line differs from
|
|
26
|
+
* the pre-turn snapshot, with a patch limited to those files.
|
|
27
|
+
*/
|
|
28
|
+
export async function diffSinceSnapshot(dir, before) {
|
|
29
|
+
const after = await porcelainStatus(dir);
|
|
30
|
+
const beforeSet = new Set(before.split("\n").filter(Boolean));
|
|
31
|
+
const changedLines = after.split("\n").filter((l) => l && !beforeSet.has(l));
|
|
32
|
+
if (!changedLines.length)
|
|
33
|
+
return null;
|
|
34
|
+
const files = parsePorcelain(changedLines.join("\n"));
|
|
35
|
+
const paths = files.map((f) => f.path);
|
|
36
|
+
let added = 0;
|
|
37
|
+
let removed = 0;
|
|
38
|
+
const numstat = await git(["diff", "HEAD", "--numstat", "--", ...paths], dir);
|
|
39
|
+
for (const line of numstat.split("\n")) {
|
|
40
|
+
const [a, r] = line.split("\t");
|
|
41
|
+
added += Number(a) || 0;
|
|
42
|
+
removed += Number(r) || 0;
|
|
43
|
+
}
|
|
44
|
+
let patch = await git(["diff", "HEAD", "--", ...paths], dir);
|
|
45
|
+
const untracked = files.filter((f) => f.status === "??").map((f) => f.path);
|
|
46
|
+
if (untracked.length) {
|
|
47
|
+
patch += (patch ? "\n" : "") + untracked.map((p) => `?? new file: ${p}`).join("\n");
|
|
48
|
+
}
|
|
49
|
+
const truncated = patch.length > PATCH_EVENT_LIMIT;
|
|
50
|
+
return {
|
|
51
|
+
files,
|
|
52
|
+
added,
|
|
53
|
+
removed,
|
|
54
|
+
patch: truncated ? patch.slice(0, PATCH_EVENT_LIMIT) + "\n… (truncated)" : patch,
|
|
55
|
+
truncated,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/** The project's current uncommitted state, for the Changes/Working-tree views. */
|
|
59
|
+
export async function workingTree(dir) {
|
|
60
|
+
const inside = (await git(["rev-parse", "--is-inside-work-tree"], dir)).trim() === "true";
|
|
61
|
+
if (!inside)
|
|
62
|
+
return { git: false, files: [], patch: "", truncated: false };
|
|
63
|
+
const [branch, porcelain, rawPatch] = await Promise.all([
|
|
64
|
+
git(["rev-parse", "--abbrev-ref", "HEAD"], dir),
|
|
65
|
+
porcelainStatus(dir),
|
|
66
|
+
git(["diff", "HEAD"], dir),
|
|
67
|
+
]);
|
|
68
|
+
const files = parsePorcelain(porcelain);
|
|
69
|
+
let patch = rawPatch;
|
|
70
|
+
const untracked = files.filter((f) => f.status === "??").map((f) => f.path);
|
|
71
|
+
if (untracked.length) {
|
|
72
|
+
patch += (patch ? "\n" : "") + untracked.map((p) => `?? new file: ${p}`).join("\n");
|
|
73
|
+
}
|
|
74
|
+
const truncated = patch.length > PATCH_VIEW_LIMIT;
|
|
75
|
+
return {
|
|
76
|
+
git: true,
|
|
77
|
+
branch: branch.trim(),
|
|
78
|
+
files,
|
|
79
|
+
patch: truncated ? patch.slice(0, PATCH_VIEW_LIMIT) + "\n… (truncated)" : patch,
|
|
80
|
+
truncated,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=worktree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worktree.js","sourceRoot":"","sources":["../../src/core/worktree.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Loom web app — a single-file app served by the daemon at /app.
|
|
3
|
+
* Reachable over the tailnet, paired via the `loom pair` QR deep link
|
|
4
|
+
* (…/app#pair=<one-time-token>), installable to the Android home screen,
|
|
5
|
+
* and wrapped by the Electron shell on desktop.
|
|
6
|
+
*
|
|
7
|
+
* Served publicly (it's just a shell); every API call it makes carries the
|
|
8
|
+
* paired client's bearer token. No frameworks, no build step, no CDN.
|
|
9
|
+
*
|
|
10
|
+
* Design: the "quiet graphite" system adapted from Orca (github.com/stablyai/
|
|
11
|
+
* orca, MIT) — neutral monochrome chrome, hairline borders, Geist type, and
|
|
12
|
+
* color reserved for state: thread cyan = live, shuttle magenta = the baton.
|
|
13
|
+
* Desktop (>=900px) is the Orca workspace shell: projects/agents tree in the
|
|
14
|
+
* left sidebar, a tabbed center pane (Thread | Tasks | Brain | Board), a diff
|
|
15
|
+
* dock right of the chat, a 4-view right rail, a terminal dock, and a status
|
|
16
|
+
* bar. Mobile keeps the single-column thread. See docs/design-system.md.
|
|
17
|
+
*/
|
|
18
|
+
export declare const APP_MANIFEST: {
|
|
19
|
+
name: string;
|
|
20
|
+
short_name: string;
|
|
21
|
+
start_url: string;
|
|
22
|
+
display: string;
|
|
23
|
+
background_color: string;
|
|
24
|
+
theme_color: string;
|
|
25
|
+
icons: {
|
|
26
|
+
src: string;
|
|
27
|
+
sizes: string;
|
|
28
|
+
type: string;
|
|
29
|
+
purpose: string;
|
|
30
|
+
}[];
|
|
31
|
+
};
|
|
32
|
+
export declare const APP_HTML: string;
|