@saadnvd1/agent-os 0.3.467 → 0.3.470

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 (40) hide show
  1. package/README.md +28 -1
  2. package/app/api/projects/clone/route.ts +4 -2
  3. package/app/api/projects/init/route.test.ts +46 -0
  4. package/app/api/projects/init/route.ts +24 -4
  5. package/components/CommandPalette/CommandPalette.tsx +3 -1
  6. package/components/CommandPalette/useAppCommands.ts +10 -0
  7. package/components/Projects/AddProject/AddProjectDialog.tsx +25 -5
  8. package/components/Projects/AddProject/CloneForm.tsx +3 -1
  9. package/components/Projects/AddProject/FolderForm.tsx +98 -4
  10. package/components/Projects/AddProject/NameForm.tsx +3 -1
  11. package/components/Projects/AddProject/WorkspacePick.tsx +56 -0
  12. package/components/Projects/AddProject/useAddExistingProject.ts +58 -0
  13. package/components/SessionList/WorkspaceSwitcher.tsx +10 -0
  14. package/components/SessionList/useDoneAction.ts +2 -1
  15. package/data/projects/add.ts +23 -3
  16. package/lib/chat/driver.ts +8 -0
  17. package/lib/chat/drivers/claude-approvals.ts +40 -0
  18. package/lib/chat/drivers/claude.ts +7 -10
  19. package/lib/chat/worker/extras.ts +1 -0
  20. package/lib/chat/worker/host.ts +4 -0
  21. package/lib/done/index.ts +1 -8
  22. package/lib/done/plan.ts +13 -2
  23. package/lib/done/worktree.ts +55 -33
  24. package/lib/orchestrator/add-project.ts +35 -0
  25. package/lib/orchestrator/brief.ts +3 -2
  26. package/lib/orchestrator/diff.ts +5 -1
  27. package/lib/orchestrator/serve.ts +6 -0
  28. package/lib/orchestrator/tool-names.ts +13 -6
  29. package/lib/orchestrator/tool-schemas.ts +15 -0
  30. package/lib/orchestrator/tools.ts +2 -0
  31. package/lib/orchestrator/worker.ts +4 -1
  32. package/lib/orchestrator/write-guard.ts +295 -0
  33. package/lib/project-add/index.ts +133 -14
  34. package/lib/project-add/name.ts +1 -0
  35. package/lib/secret-paths.ts +9 -0
  36. package/lib/tasks/finish.ts +8 -2
  37. package/lib/worktree-removal.ts +115 -0
  38. package/lib/worktrees.ts +30 -27
  39. package/package.json +1 -1
  40. package/stores/addProject.ts +12 -3
package/lib/worktrees.ts CHANGED
@@ -14,6 +14,14 @@ import {
14
14
  } from "./git";
15
15
  import { installWorktreeHooks } from "./worktree-hooks";
16
16
  import { gitResult, isGitError, worktreeList } from "./git-exec";
17
+ import {
18
+ markRemoving,
19
+ removalKey,
20
+ removalRunning,
21
+ removalSettled,
22
+ removals,
23
+ unmarkRemoving,
24
+ } from "./worktree-removal";
17
25
 
18
26
  const git = (cwd: string, args: string[], timeout: number) =>
19
27
  gitResult(cwd, args, { timeout });
@@ -252,28 +260,10 @@ async function undoFailedAdd(
252
260
  await quiet(["branch", "-D", "--", branch]);
253
261
  }
254
262
 
255
- // Removals in progress, by path, with the branch each one's worktree was
256
- // on. `git worktree remove` takes seconds on a tree with cloned
257
- // dependencies, and a tree read halfway through looks like every file it
258
- // already deleted is an uncommitted change.
259
- const removing = new Map<
260
- string,
261
- { done: Promise<void>; branch: Promise<string | null> }
262
- >();
263
-
264
- const removalKey = (p: string) => path.resolve(resolvePath(p));
265
-
266
- // Resolves once no removal of this worktree is running.
267
- export async function removalSettled(worktreePath: string): Promise<void> {
268
- await removing.get(removalKey(worktreePath))?.done.catch(() => {});
269
- }
270
-
271
- export const removalRunning = (worktreePath: string) =>
272
- removing.has(removalKey(worktreePath));
273
-
274
263
  /**
275
264
  * Delete a worktree and optionally its branch. A second call while one is
276
- * running joins it, and still deletes the branch when it asks to.
265
+ * running joins it, and still deletes the branch when it asks to; one
266
+ * another process is running is waited for first.
277
267
  */
278
268
  export function deleteWorktree(
279
269
  worktreePath: string,
@@ -282,22 +272,35 @@ export function deleteWorktree(
282
272
  ): Promise<void> {
283
273
  const key = removalKey(worktreePath);
284
274
  const repo = resolvePath(projectPath);
285
- const running = removing.get(key);
275
+ const running = removals.get(key);
286
276
  if (running)
287
277
  return deleteBranch
288
278
  ? running.done.then(async () => dropBranch(repo, await running.branch))
289
279
  : running.done;
280
+ // One another process is running goes first, and keeps its own marker.
281
+ const foreign = removalRunning(key)
282
+ ? removalSettled(key, undefined, { foreignOnly: true })
283
+ : null;
290
284
  // Read before anything is removed.
291
- const branch = git(key, ["rev-parse", "--abbrev-ref", "HEAD"], 5000).then(
292
- ({ stdout }) => stdout.trim() || null,
293
- () => null
294
- );
285
+ const branch = (async () => {
286
+ await foreign;
287
+ return git(key, ["rev-parse", "--abbrev-ref", "HEAD"], 5000).then(
288
+ ({ stdout }) => stdout.trim() || null,
289
+ () => null
290
+ );
291
+ })();
292
+ let marker: string | null = null;
295
293
  const done = (async () => {
296
294
  await branch;
295
+ // Before anything is removed, so no reader catches it half-deleted.
296
+ marker = markRemoving(key);
297
297
  await removeWorktree(key, repo);
298
298
  if (deleteBranch) await dropBranch(repo, await branch);
299
- })().finally(() => removing.delete(key));
300
- removing.set(key, { done, branch });
299
+ })().finally(() => {
300
+ removals.delete(key);
301
+ if (marker) unmarkRemoving(key, marker);
302
+ });
303
+ removals.set(key, { done, branch });
301
304
  return done;
302
305
  }
303
306
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saadnvd1/agent-os",
3
- "version": "0.3.467",
3
+ "version": "0.3.470",
4
4
  "description": "Self-hosted web UI for managing Claude Code sessions",
5
5
  "bin": {
6
6
  "agent-os": "scripts/agent-os"
@@ -1,15 +1,24 @@
1
1
  import { proxy } from "valtio";
2
+ import { sidebarUi } from "./sidebarUi";
2
3
 
3
4
  export type AddProjectKind = "folder" | "clone" | "name";
4
5
 
5
- // The add-project form on show, once a machine and a way are picked.
6
+ // The add-project form on show, once a machine and a way are picked. It
7
+ // adds to the workspace the sidebar is on, changeable in the form.
6
8
  export const addProjectUi = proxy<{
7
- open: { kind: AddProjectKind; hostId: string } | null;
9
+ open: {
10
+ kind: AddProjectKind;
11
+ hostId: string;
12
+ workspaceId: string | null;
13
+ } | null;
8
14
  }>({ open: null });
9
15
 
10
16
  export const addProjectActions = {
11
17
  show: (kind: AddProjectKind, hostId: string) => {
12
- addProjectUi.open = { kind, hostId };
18
+ addProjectUi.open = { kind, hostId, workspaceId: sidebarUi.workspaceId };
19
+ },
20
+ setWorkspace: (workspaceId: string | null) => {
21
+ if (addProjectUi.open) addProjectUi.open.workspaceId = workspaceId;
13
22
  },
14
23
  close: () => {
15
24
  addProjectUi.open = null;