@orangeworks/orangetree 0.4.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/CHANGELOG.md ADDED
@@ -0,0 +1,44 @@
1
+ # @orangeworks/orangetree
2
+
3
+ ## 0.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - fa6c7d9: Add one-click bug reporting and copy-to-clipboard. Copy any assistant message or the report-form body to the clipboard, and file a bug report (with optional image attachments) from the toolbar during an online session — the report is forwarded to the cloud and filed as a GitHub issue. The report button appears only in an online session signed in through the cloud.
8
+ - 780ab82: Add cloud-fronted remote sign-in for remote mode: open your home PC from the dashboard via a one-time magic link, and every request is gated by your cloud session. Opening the app's address directly without a session is now bounced to a "open from the dashboard" page instead of being allowed in — closing a gap where loading the page could grant access.
9
+ - 73e5ce2: Connect a PC to the cloud directly from the app: click "Connect", approve on your phone or in your browser, and it pairs automatically — no environment variables and no onboarding-token copy-paste. The remote-access tunnel component is now downloaded on demand, and your setup is remembered between launches. A new Connection settings panel lets you choose your work folder and adjust remote options from the app instead of editing environment variables.
10
+
11
+ ### Patch Changes
12
+
13
+ - be0a76b: Fix the subscription usage indicator failing to load. The 5-hour and weekly rate-limit usage now refreshes correctly instead of silently failing on every check.
14
+ - 8e153c7: Remote sign-in sessions can now be ended immediately: signing out (or revoking access from the cloud) drops the remote session right away instead of after a delay, borrowed-PC sessions expire automatically a couple of hours after last use, and there is a sign-out action for a remote session. Signing out one session no longer disturbs your other active sessions.
15
+ - 23d55cf: Fix the interface showing the wrong language on startup. When the response language was set to English (or Japanese) but the browser's own language differed, the app could boot with an English selector yet a Korean interface — including the chat's "start a conversation" placeholder. The interface language now follows the selected language on startup, and the empty chat view updates when you switch languages.
16
+
17
+ ## 0.3.0
18
+
19
+ ### Minor Changes
20
+
21
+ - e557f20: Add explorer search and a per-node related-documents view. The explorer now has a search box that live-filters both files (by name/path) and nodes (by title, decision, or purpose) — click a result to open the file or jump to the node. Right-clicking a node offers "Related docs", a popover listing that node's documents (its artifact, the docs it touched, and a parent's done children's docs) that open straight in the viewer.
22
+ - d7d25ad: Add UI multilingual support (Korean / English / Japanese). The single language selector now switches both the model's response language and the interface itself — buttons, menus, the explorer, chat, and dialogs — backed by a built-in locale catalog (no external dependency). Your choice is remembered and applied instantly on load, and `auto` follows the browser's language.
23
+
24
+ ### Patch Changes
25
+
26
+ - 962df32: Make saving more reliable on Windows. Project state is written atomically (write-then-rename), and on Windows that rename can momentarily fail when antivirus or another reader briefly holds the file — which could drop a save. The rename now retries with a short backoff, so a transient lock no longer loses your work.
27
+
28
+ ## 0.2.0
29
+
30
+ ### Minor Changes
31
+
32
+ - 5c7883a: Surface overlapping code changes between nodes. A node now records which files it edits, and when another node touches one of those files the affected node gets a "▶ 지금 반영" card plus a canvas badge, and its next turn folds the change in once (no loss, no extra cost) — so parallel work no longer silently conflicts or duplicates.
33
+ - f0b3837: Add soft file locks for shared (non-worktree) folders. While a node edits a file in a shared folder it holds a turn-lifetime lock, shown as a 🔒 with the holder's id in the explorer; you can click to release it. This surfaces who's touching what without blocking — worktree nodes are already isolated, so they don't take locks.
34
+ - c537427: Round out cross-node code awareness. Editing a file another node is editing at the same moment now surfaces a soft "concurrent edit" note in the next turn (read the other node to align), and every node sees an ambient digest of other nodes' recent edits — so even work that doesn't directly overlap stays visible without manual checking.
35
+ - b033efb: Add result return-flow for detour branches. When you fork a child with "return result to parent", completing that child surfaces a "▶ 이어받아 진행" card in the parent and folds the child's decision into the parent's next turn (once), so a quick side-question rejoins the original line of work without losing context.
36
+ - 6878f8e: Add isolated git worktrees for parallel branches. Forking with "격리" runs the child in its own git worktree and branch, so siblings editing the same files never clobber each other. Completing the node auto-commits its work and merges the branch back with --no-ff, then cleans up the worktree; on a merge conflict it aborts cleanly, preserves the worktree, and drops the node to review with a ⚠ badge. Orphaned worktrees are reconciled on startup.
37
+ - e75bf3e: Add the branching session tree. Work is a canvas of Claude conversations where any answer can be forked into a child node; each node streams live with collapsible tool and thinking detail, supports file attachments and slash commands, and moves through a clear lifecycle (in progress, blocked, handoff, review, done) with manual completion.
38
+ - e75bf3e: Add project workspaces, settings, and theming. Group work into projects with their own folders (read-only or read-write), per-project instructions, and context files; create, switch, and configure projects from the explorer and link node artifacts to files; view the base guidelines and open a node's linked document; and switch between light, dark, and system themes.
39
+ - 4b89756: Nodes in the same tree now see each other's recorded decisions directly in their working context, and a new read_decisions tool pulls the fuller decision history (outputs, touched files) on demand without bloating every turn. Branch seeds are also more reliably preserved and refreshed across a child node's first turn.
40
+
41
+ ### Patch Changes
42
+
43
+ - c7e8b15: Refine cross-node context sharing to follow the graph. A node's turns now see decisions from its ancestors and its explicit dependencies (with an on-demand tool to pull the rest), instead of a flat list of every node in the tree — keeping shared context focused and its cost flat as a project grows.
44
+ - bbfe04d: Fix live Claude turns failing to start on Windows. The CLI is now detected as its native executable instead of an unlaunchable npm shim, so chatting with a node works out of the box.
package/LICENSE ADDED
@@ -0,0 +1,8 @@
1
+ Copyright (c) 2026 Orangeworks (orangeworks.kr)
2
+
3
+ All rights reserved.
4
+
5
+ NOTE: This is a placeholder license. The canonical license terms are pending the
6
+ commercial-restriction decision tracked in otree-docs (920-distribution OPEN-1).
7
+ Until that decision is finalized, no license to use, copy, modify, or distribute
8
+ this software is granted. Do not redistribute.
package/README.md ADDED
@@ -0,0 +1,55 @@
1
+ # Orange Tree (`@orangeworks/orangetree`)
2
+
3
+ Branching session-tree work-tracking tool. Local-first desktop app: break work into a DAG of nodes,
4
+ run each node as an isolated Claude session, sync results graph-locally, and track progress on a canvas.
5
+
6
+ Runs on the user's PC against their own `claude` CLI (zero inference cost). The reference implementation
7
+ is the JS-based `orange-tree`; this repo is the all-TypeScript rebuild (canonical: `otree-docs/docs`).
8
+
9
+ > Status: **0.2.0** — core feature set in place. A draggable canvas of branching Claude sessions
10
+ > (fork, lifecycle, live streaming); projects with read-only/read-write folders, per-project
11
+ > instructions and context files; a document explorer + viewer; light/dark/system theming; and the
12
+ > 120-COLLAB collaboration engine — graph-local context sync, result return-flow, code-change
13
+ > propagation, and isolated git worktrees with auto-merge + AI conflict resolution.
14
+ > Design canon lives in `otree-docs/docs/20-design`.
15
+
16
+ ## Requirements
17
+
18
+ - Node.js >= 20
19
+ - A working `claude` CLI on `PATH` (or set `OTREE_CLAUDE_BIN`)
20
+
21
+ ## Develop
22
+
23
+ ```sh
24
+ pnpm install
25
+ git config core.hooksPath .githooks # enable the commit gate (integrity + tsc --noEmit)
26
+
27
+ pnpm dev # esbuild-watch the frontend + node --watch the server
28
+ pnpm build # one-shot build -> dist/
29
+ pnpm start # run the built server (dist/server.js)
30
+ pnpm typecheck # tsc --noEmit (also runs in the pre-commit hook)
31
+ ```
32
+
33
+ The app serves its UI on `127.0.0.1` (loopback only) and opens a browser.
34
+
35
+ ## Runtime state (git-excluded)
36
+
37
+ All per-project runtime state lives in **app-data**, outside any repo (DES-PLATFORM-002):
38
+
39
+ ```
40
+ <OTREE_DATA>/ # default ~/.orangetree (env OTREE_DATA wins) — a DIRECTORY
41
+ global.json # project registry + projectless nodes + settings
42
+ scratch/ # cwd for folderless sessions
43
+ projects/<projectId>/
44
+ tree.json # this project's node graph
45
+ decisions.md # audit log (append-only)
46
+ ```
47
+
48
+ ## Environment
49
+
50
+ | Var | Meaning |
51
+ |---|---|
52
+ | `OTREE_DATA` | app-data root directory (default `~/.orangetree`) |
53
+ | `OTREE_CLAUDE_BIN` | explicit path to the `claude` executable |
54
+ | `OTREE_PORT` | preferred port (default 4100, scans upward if taken) |
55
+ | `OTREE_OPEN` | set `0` to not auto-open the browser |
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env node
2
+
3
+ // bin/orangetree.ts
4
+ import { homedir } from "node:os";
5
+ import { join as join2 } from "node:path";
6
+
7
+ // lib/config.ts
8
+ import { existsSync, readFileSync } from "node:fs";
9
+ import { join } from "node:path";
10
+ var CONFIG_VERSION = 1;
11
+ var CONFIG_FILE = "config.json";
12
+ function configPath(appDataRoot) {
13
+ return join(appDataRoot, CONFIG_FILE);
14
+ }
15
+ function defaults() {
16
+ return {
17
+ version: CONFIG_VERSION,
18
+ workRoot: null,
19
+ remoteEnabled: false,
20
+ cloudApiBase: null,
21
+ remoteAuth: "cloud",
22
+ port: null,
23
+ bindHost: null,
24
+ openBrowser: true,
25
+ pairing: { onboardingToken: null, consumedAt: null }
26
+ };
27
+ }
28
+ function asTrimmedString(v) {
29
+ return typeof v === "string" && v.trim() !== "" ? v.trim() : null;
30
+ }
31
+ function asHttpUrl(v) {
32
+ const s = asTrimmedString(v);
33
+ return s && /^https?:\/\//i.test(s) ? s.replace(/\/+$/, "") : null;
34
+ }
35
+ function asBool(v, fallback) {
36
+ return typeof v === "boolean" ? v : fallback;
37
+ }
38
+ function asPort(v) {
39
+ return typeof v === "number" && Number.isInteger(v) && v > 0 && v < 65536 ? v : null;
40
+ }
41
+ function normalize(raw) {
42
+ const d = defaults();
43
+ if (raw === null || typeof raw !== "object") return d;
44
+ const o = raw;
45
+ const p = o.pairing ?? {};
46
+ return {
47
+ version: typeof o.version === "number" ? o.version : CONFIG_VERSION,
48
+ workRoot: asTrimmedString(o.workRoot),
49
+ remoteEnabled: asBool(o.remoteEnabled, d.remoteEnabled),
50
+ cloudApiBase: asHttpUrl(o.cloudApiBase),
51
+ remoteAuth: o.remoteAuth === "token" ? "token" : "cloud",
52
+ port: asPort(o.port),
53
+ bindHost: asTrimmedString(o.bindHost),
54
+ openBrowser: asBool(o.openBrowser, d.openBrowser),
55
+ pairing: {
56
+ onboardingToken: asTrimmedString(p.onboardingToken),
57
+ consumedAt: typeof p.consumedAt === "number" ? p.consumedAt : null
58
+ }
59
+ };
60
+ }
61
+ function loadConfig(appDataRoot) {
62
+ const path = configPath(appDataRoot);
63
+ if (!existsSync(path)) return defaults();
64
+ try {
65
+ return normalize(JSON.parse(readFileSync(path, "utf8")));
66
+ } catch {
67
+ return defaults();
68
+ }
69
+ }
70
+ function applyConfigToEnv(appDataRoot) {
71
+ const cfg = loadConfig(appDataRoot);
72
+ const fill = (key, value) => {
73
+ if (value != null && process.env[key] === void 0) process.env[key] = value;
74
+ };
75
+ fill("OTREE_CLOUD_API", cfg.cloudApiBase);
76
+ if (cfg.remoteEnabled) {
77
+ fill("OTREE_REMOTE_AUTH", cfg.remoteAuth);
78
+ if (cfg.workRoot && existsSync(cfg.workRoot)) fill("OTREE_ROOT", cfg.workRoot);
79
+ }
80
+ fill("OTREE_PORT", cfg.port != null ? String(cfg.port) : null);
81
+ fill("OTREE_BIND", cfg.bindHost);
82
+ if (cfg.openBrowser === false) fill("OTREE_OPEN", "0");
83
+ return cfg;
84
+ }
85
+
86
+ // bin/orangetree.ts
87
+ process.env.OTREE_DATA ??= join2(homedir(), ".orangetree");
88
+ applyConfigToEnv(process.env.OTREE_DATA);
89
+ await import("../server.js");
@@ -0,0 +1,33 @@
1
+ function toast(message, type = "info", ms = 3e3) {
2
+ let host = document.querySelector("#ui-toasts");
3
+ if (!host) {
4
+ host = document.createElement("div");
5
+ host.id = "ui-toasts";
6
+ document.body.append(host);
7
+ }
8
+ const t = document.createElement("div");
9
+ t.className = `ui-toast ${type}`;
10
+ t.textContent = message;
11
+ host.append(t);
12
+ setTimeout(() => {
13
+ t.classList.add("out");
14
+ setTimeout(() => t.remove(), 300);
15
+ }, ms);
16
+ }
17
+ const api = async (method, path, body) => {
18
+ const res = await fetch(`/api${path}`, {
19
+ method,
20
+ ...body !== void 0 ? { headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) } : {}
21
+ });
22
+ const data = await res.json().catch(() => ({}));
23
+ if (!res.ok) {
24
+ const message = data.error ?? `${res.status} ${res.statusText}`;
25
+ toast(message, "error");
26
+ throw new Error(message);
27
+ }
28
+ return data;
29
+ };
30
+ export {
31
+ api,
32
+ toast
33
+ };