symbio 0.7.1 → 0.9.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 +41 -0
- package/README.md +8 -2
- package/dist/server/api.js +77 -3
- package/dist/server/attachments.js +104 -0
- package/dist/server/documents.js +166 -0
- package/dist/server/guard.js +20 -7
- package/dist/server/housekeeping.js +24 -6
- package/dist/server/inspect.js +14 -1
- package/dist/server/prompts.js +54 -5
- package/dist/server/remote.js +7 -6
- package/dist/server/repos.js +136 -0
- package/dist/server/self-update.js +2 -1
- package/dist/server/team.js +79 -9
- package/dist/server/tools.js +45 -3
- package/dist/server/updates.js +34 -1
- package/dist/server/workspaces.js +37 -28
- package/dist/web/assets/index-1TakGjpe.js +55 -0
- package/dist/web/assets/index-CSPrpGKc.css +1 -0
- package/dist/web/index.html +2 -2
- package/package.json +4 -2
- package/dist/web/assets/index-BmRTqm-A.css +0 -1
- package/dist/web/assets/index-Cila9fjW.js +0 -53
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
// Worker workspaces. In a git project a worker gets its own worktree on its own
|
|
2
2
|
// branch (isolated copy of the committed files), unless it is set to share the
|
|
3
3
|
// project folder. Without git, workers always share it. Workers in the project
|
|
4
|
-
// folder rely on shared-asset grants alone.
|
|
4
|
+
// folder rely on shared-asset grants alone. In a project made of several repositories
|
|
5
|
+
// (repos.ts), a helper's worktree comes from the repository it was given (agent.repo), and its
|
|
6
|
+
// merges happen there.
|
|
5
7
|
import { existsSync, promises as fs } from "node:fs";
|
|
6
8
|
import path from "node:path";
|
|
7
9
|
import { git } from "./git.js";
|
|
10
|
+
import { repoRoot } from "./repos.js";
|
|
8
11
|
import { globalDir } from "./store.js";
|
|
9
12
|
export async function isGitRepo(dir) {
|
|
10
13
|
return existsSync(path.join(dir, ".git"));
|
|
@@ -14,8 +17,9 @@ export function inProjectFolder(project, agent) {
|
|
|
14
17
|
const norm = (p) => (process.platform === "win32" ? path.resolve(p).toLowerCase() : path.resolve(p));
|
|
15
18
|
return norm(agent.directory) === norm(project.directory);
|
|
16
19
|
}
|
|
17
|
-
export async function createWorkspace(project, agentName, mode = "own") {
|
|
18
|
-
|
|
20
|
+
export async function createWorkspace(project, agentName, mode = "own", repo) {
|
|
21
|
+
const root = repoRoot(project, { repo });
|
|
22
|
+
if (mode === "shared" || !(await isGitRepo(root))) {
|
|
19
23
|
return { directory: project.directory, branch: null, base: null };
|
|
20
24
|
}
|
|
21
25
|
const slug = agentName.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "") || "agent";
|
|
@@ -24,14 +28,14 @@ export async function createWorkspace(project, agentName, mode = "own") {
|
|
|
24
28
|
if (existsSync(directory))
|
|
25
29
|
throw new Error(`A workspace for "${agentName}" already exists.`);
|
|
26
30
|
await fs.mkdir(path.dirname(directory), { recursive: true });
|
|
27
|
-
const base = await git(
|
|
31
|
+
const base = await git(root, "rev-parse", "HEAD").catch(() => "");
|
|
28
32
|
if (!base)
|
|
29
|
-
throw new Error(
|
|
30
|
-
const branchExists = await git(
|
|
31
|
-
await git(
|
|
32
|
-
const dirty = await git(
|
|
33
|
+
throw new Error(`The ${repo ? `"${repo}" ` : "project's "}git repository has no commits yet; commit the current files first.`);
|
|
34
|
+
const branchExists = await git(root, "rev-parse", "--verify", "--quiet", branch).then(() => true, () => false);
|
|
35
|
+
await git(root, "worktree", "add", ...(branchExists ? [directory, branch] : ["-b", branch, directory, base]));
|
|
36
|
+
const dirty = await git(root, "status", "--porcelain").catch(() => "");
|
|
33
37
|
const warning = dirty
|
|
34
|
-
?
|
|
38
|
+
? `The ${repo ? `"${repo}" repository` : "project folder"} has uncommitted changes; the new agent's workspace does not include them. Commit first if the agent needs them.`
|
|
35
39
|
: undefined;
|
|
36
40
|
return { directory, branch, base, warning };
|
|
37
41
|
}
|
|
@@ -66,19 +70,21 @@ export async function mergeWorkspace(project, agent) {
|
|
|
66
70
|
}
|
|
67
71
|
if (committed.length === 0)
|
|
68
72
|
return { ok: false, reason: `${agent.name} has no committed changes to merge.` };
|
|
69
|
-
const
|
|
73
|
+
const root = repoRoot(project, agent);
|
|
74
|
+
const where = agent.repo ? `The "${agent.repo}" repository` : "The project folder";
|
|
75
|
+
const mainDirty = await git(root, "status", "--porcelain").catch(() => "");
|
|
70
76
|
if (mainDirty) {
|
|
71
|
-
return { ok: false, reason:
|
|
77
|
+
return { ok: false, reason: `${where} has uncommitted changes. Commit or stash them before merging.` };
|
|
72
78
|
}
|
|
73
79
|
try {
|
|
74
|
-
await git(
|
|
80
|
+
await git(root, "merge", "--no-ff", "--no-edit", "-m", `Merge work from ${agent.name} (Symbio)`, agent.branch);
|
|
75
81
|
}
|
|
76
82
|
catch (err) {
|
|
77
|
-
const conflicts = (await git(
|
|
83
|
+
const conflicts = (await git(root, "diff", "--name-only", "--diff-filter=U").catch(() => ""))
|
|
78
84
|
.split("\n")
|
|
79
85
|
.filter(Boolean);
|
|
80
|
-
await git(
|
|
81
|
-
const main = await git(
|
|
86
|
+
await git(root, "merge", "--abort").catch(() => { });
|
|
87
|
+
const main = await git(root, "rev-parse", "--abbrev-ref", "HEAD").catch(() => "the main branch");
|
|
82
88
|
return {
|
|
83
89
|
ok: false,
|
|
84
90
|
reason: conflicts.length
|
|
@@ -95,7 +101,7 @@ export async function mergeWorkspace(project, agent) {
|
|
|
95
101
|
export async function syncWorkspace(project, agent) {
|
|
96
102
|
if (!agent.branch)
|
|
97
103
|
return null;
|
|
98
|
-
const head = await git(project
|
|
104
|
+
const head = await git(repoRoot(project, agent), "rev-parse", "HEAD");
|
|
99
105
|
await git(agent.directory, "merge", "--ff-only", head).catch(() => { });
|
|
100
106
|
return head;
|
|
101
107
|
}
|
|
@@ -108,11 +114,12 @@ export async function restoreWorkspace(project, agent) {
|
|
|
108
114
|
if (!agent.branch || existsSync(agent.directory)) {
|
|
109
115
|
return { directory: agent.directory, branch: agent.branch ?? null, base: agent.base ?? null, reset: false };
|
|
110
116
|
}
|
|
111
|
-
const
|
|
112
|
-
await git(
|
|
113
|
-
await git(
|
|
117
|
+
const root = repoRoot(project, agent);
|
|
118
|
+
const base = await git(root, "rev-parse", "HEAD");
|
|
119
|
+
await git(root, "worktree", "prune").catch(() => { });
|
|
120
|
+
await git(root, "branch", "-f", agent.branch, base);
|
|
114
121
|
await fs.mkdir(path.dirname(agent.directory), { recursive: true });
|
|
115
|
-
await git(
|
|
122
|
+
await git(root, "worktree", "add", agent.directory, agent.branch);
|
|
116
123
|
return { directory: agent.directory, branch: agent.branch, base, reset: true };
|
|
117
124
|
}
|
|
118
125
|
/**
|
|
@@ -121,18 +128,19 @@ export async function restoreWorkspace(project, agent) {
|
|
|
121
128
|
* moved. Returns false when there was nothing to do.
|
|
122
129
|
*/
|
|
123
130
|
export async function reattachWorkspace(project, agent) {
|
|
124
|
-
|
|
131
|
+
const root = repoRoot(project, agent);
|
|
132
|
+
if (!agent.branch || existsSync(agent.directory) || !(await isGitRepo(root)))
|
|
125
133
|
return false;
|
|
126
|
-
await git(
|
|
127
|
-
const branchExists = await git(
|
|
134
|
+
await git(root, "worktree", "prune").catch(() => { });
|
|
135
|
+
const branchExists = await git(root, "rev-parse", "--verify", "--quiet", agent.branch).then(() => true, () => false);
|
|
128
136
|
await fs.mkdir(path.dirname(agent.directory), { recursive: true });
|
|
129
|
-
await git(
|
|
137
|
+
await git(root, "worktree", "add", ...(branchExists ? [agent.directory, agent.branch] : ["-b", agent.branch, agent.directory, "HEAD"]));
|
|
130
138
|
return true;
|
|
131
139
|
}
|
|
132
140
|
export async function removeWorkspace(project, agent) {
|
|
133
141
|
if (!agent.branch)
|
|
134
142
|
return;
|
|
135
|
-
await git(project
|
|
143
|
+
await git(repoRoot(project, agent), "worktree", "remove", "--force", agent.directory).catch(() => { });
|
|
136
144
|
}
|
|
137
145
|
/**
|
|
138
146
|
* Takes a helper out of its own worktree so it can work in the project folder. Refuses while
|
|
@@ -148,9 +156,10 @@ export async function leaveWorkspace(project, agent) {
|
|
|
148
156
|
throw new Error(`${agent.name}'s own folder has work that isn't in the project yet (${files.slice(0, 8).join(", ")}${files.length > 8 ? ", …" : ""}). ` +
|
|
149
157
|
"Merge it (or discard it) first.");
|
|
150
158
|
}
|
|
151
|
-
|
|
152
|
-
await git(
|
|
153
|
-
await git(
|
|
159
|
+
const root = repoRoot(project, agent);
|
|
160
|
+
await git(root, "worktree", "remove", "--force", agent.directory).catch(() => { });
|
|
161
|
+
await git(root, "worktree", "prune").catch(() => { });
|
|
162
|
+
await git(root, "branch", "-d", agent.branch).catch(() => { });
|
|
154
163
|
}
|
|
155
164
|
/**
|
|
156
165
|
* Deletes helper folders left behind by projects that no longer exist (removing a project
|