@webappwiz/arbor 0.0.30 → 0.0.31

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 (118) hide show
  1. package/add.d.ts +23 -0
  2. package/age.d.ts +2 -0
  3. package/arbor.d.ts +11 -0
  4. package/attachments.d.ts +36 -0
  5. package/claim.d.ts +11 -0
  6. package/config.d.ts +55 -0
  7. package/config.js +7 -0
  8. package/dev/assets.d.ts +11 -0
  9. package/dev.d.ts +48 -0
  10. package/escalate.d.ts +28 -0
  11. package/exit.d.ts +42 -0
  12. package/git.d.ts +77 -0
  13. package/index.d.ts +2 -0
  14. package/index.js +4712 -0
  15. package/journal.d.ts +29 -0
  16. package/list.d.ts +18 -0
  17. package/load-config.d.ts +15 -0
  18. package/log.d.ts +14 -0
  19. package/merge.d.ts +24 -0
  20. package/package.json +15 -32
  21. package/path.d.ts +13 -0
  22. package/plan.d.ts +83 -0
  23. package/remove.d.ts +19 -0
  24. package/repository.d.ts +25 -0
  25. package/retry.d.ts +17 -0
  26. package/shell.d.ts +17 -0
  27. package/show.d.ts +53 -0
  28. package/snapshot.d.ts +35 -0
  29. package/table.d.ts +5 -0
  30. package/todo.d.ts +264 -0
  31. package/wait.d.ts +22 -0
  32. package/worktree-service.d.ts +51 -0
  33. package/worktree.d.ts +95 -0
  34. package/AGENTS.md +0 -7
  35. package/add.test.ts +0 -125
  36. package/add.ts +0 -192
  37. package/age.ts +0 -11
  38. package/arbor.test.ts +0 -145
  39. package/arbor.ts +0 -429
  40. package/assets.d.ts +0 -9
  41. package/attachments.ts +0 -87
  42. package/build.ts +0 -98
  43. package/claim.test.ts +0 -89
  44. package/claim.ts +0 -73
  45. package/components.json +0 -21
  46. package/config.ts +0 -58
  47. package/dev/api.ts +0 -120
  48. package/dev/app.test.tsx +0 -645
  49. package/dev/app.tsx +0 -80
  50. package/dev/assets.ts +0 -16
  51. package/dev/build/main.txt +0 -68
  52. package/dev/build/shell.txt +0 -21
  53. package/dev/build/styles.txt +0 -2263
  54. package/dev/components/ui/badge.tsx +0 -51
  55. package/dev/components/ui/button.tsx +0 -57
  56. package/dev/components/ui/dialog.tsx +0 -154
  57. package/dev/components/ui/empty.tsx +0 -100
  58. package/dev/components/ui/input-group.tsx +0 -155
  59. package/dev/components/ui/input.tsx +0 -19
  60. package/dev/components/ui/progress.tsx +0 -80
  61. package/dev/components/ui/textarea.tsx +0 -17
  62. package/dev/components/ui/toast.tsx +0 -227
  63. package/dev/feed.ts +0 -69
  64. package/dev/files.tsx +0 -191
  65. package/dev/index.html +0 -21
  66. package/dev/lib/utils.ts +0 -7
  67. package/dev/main.tsx +0 -22
  68. package/dev/markdown.test.tsx +0 -133
  69. package/dev/markdown.tsx +0 -124
  70. package/dev/mentions.tsx +0 -319
  71. package/dev/shadcn.css +0 -649
  72. package/dev/styles.css +0 -107
  73. package/dev/tags.tsx +0 -44
  74. package/dev/tasks.tsx +0 -151
  75. package/dev/todos.tsx +0 -759
  76. package/dev.test.ts +0 -439
  77. package/dev.ts +0 -386
  78. package/e2e.test.ts +0 -256
  79. package/escalate.test.ts +0 -76
  80. package/escalate.ts +0 -112
  81. package/exit.test.ts +0 -71
  82. package/exit.ts +0 -74
  83. package/git.ts +0 -294
  84. package/index.ts +0 -9
  85. package/journal.ts +0 -89
  86. package/list.test.ts +0 -82
  87. package/list.ts +0 -137
  88. package/load-config.test.ts +0 -121
  89. package/load-config.ts +0 -62
  90. package/log.test.ts +0 -65
  91. package/log.ts +0 -40
  92. package/merge.test.ts +0 -332
  93. package/merge.ts +0 -283
  94. package/path.test.ts +0 -35
  95. package/path.ts +0 -38
  96. package/plan.test.ts +0 -318
  97. package/plan.ts +0 -302
  98. package/progress.test.ts +0 -54
  99. package/progress.ts +0 -31
  100. package/remove.test.ts +0 -68
  101. package/remove.ts +0 -88
  102. package/repository.ts +0 -83
  103. package/retry.test.ts +0 -43
  104. package/retry.ts +0 -48
  105. package/shell.ts +0 -44
  106. package/show.test.ts +0 -91
  107. package/show.ts +0 -151
  108. package/snapshot.test.ts +0 -33
  109. package/snapshot.ts +0 -84
  110. package/table.ts +0 -20
  111. package/testing.ts +0 -229
  112. package/todo.test.ts +0 -490
  113. package/todo.ts +0 -834
  114. package/wait.test.ts +0 -61
  115. package/wait.ts +0 -77
  116. package/worktree-service.test.ts +0 -158
  117. package/worktree-service.ts +0 -193
  118. package/worktree.ts +0 -239
package/journal.d.ts ADDED
@@ -0,0 +1,29 @@
1
+ import { type Fs } from "webappwiz/system";
2
+ /** One thing that was done to a task, and how it went. */
3
+ export interface Entry {
4
+ at: string;
5
+ action: string;
6
+ /** The task it was about, or null when the command names none. */
7
+ task: string | null;
8
+ /** The refusal reason, or null when the command succeeded. */
9
+ reason: string | null;
10
+ }
11
+ /**
12
+ * What has been done in this repo, oldest first. Entries outlive their tasks:
13
+ * `merge` and `remove` take the record with them, so this is the only thing that
14
+ * remembers a task existed at all.
15
+ */
16
+ /** What a `Journal` is stored through; the real filesystem by default. */
17
+ export interface JournalOptions {
18
+ fs?: Fs;
19
+ }
20
+ export declare class Journal {
21
+ private readonly path;
22
+ private readonly capacity;
23
+ private readonly fs;
24
+ constructor(path: string, capacity: number, opts?: JournalOptions);
25
+ record<T>(action: string, task: string | null, run: () => Promise<T>): Promise<T>;
26
+ tail(count: number): Promise<Entry[]>;
27
+ private append;
28
+ private entries;
29
+ }
package/list.d.ts ADDED
@@ -0,0 +1,18 @@
1
+ import { type Logger } from "webappwiz/log";
2
+ import type { Fs } from "webappwiz/system";
3
+ import type { WorktreeService } from "./worktree-service.js";
4
+ export interface ListOptions {
5
+ /** Print the rows as JSON instead of a table. */
6
+ json?: boolean;
7
+ /**
8
+ * Add each task's changed and planned files, which is what an agent checks
9
+ * its own plan against before starting. Off by default: it costs git calls
10
+ * and a file read per task.
11
+ */
12
+ files?: boolean;
13
+ }
14
+ export declare function list({ service, log, fs }: {
15
+ service: WorktreeService;
16
+ log: Logger;
17
+ fs: Fs;
18
+ }, { json, files }?: ListOptions): Promise<void>;
@@ -0,0 +1,15 @@
1
+ import { type Fs } from "webappwiz/system";
2
+ import type { Config } from "./config.js";
3
+ import { Git } from "./git.js";
4
+ export interface LoadConfigOptions {
5
+ /** What `arbor.config.ts` is looked for through; the real one by default. */
6
+ fs?: Fs;
7
+ /** What the trunk is detected through; the repo's own git by default. */
8
+ git?: Git;
9
+ }
10
+ /**
11
+ * The settings arbor runs a repo with: what `arbor.config.ts` names, then the
12
+ * branch `origin/HEAD` points at for the trunk, then arbor's own defaults.
13
+ * Throws when the config file is there but cannot be imported.
14
+ */
15
+ export declare function loadConfig(root: string, opts?: LoadConfigOptions): Promise<Config>;
package/log.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ import { type Logger } from "webappwiz/log";
2
+ import type { Journal } from "./journal.js";
3
+ /** Enough to cover a session's worth of work without scrolling. */
4
+ export declare const DEFAULT_COUNT = 20;
5
+ export interface LogOptions {
6
+ /** How many of the most recent entries to show. */
7
+ count?: number;
8
+ /** Print the entries as JSON instead of a table. */
9
+ json?: boolean;
10
+ }
11
+ export declare function log({ journal, log }: {
12
+ journal: Journal;
13
+ log: Logger;
14
+ }, { count, json }?: LogOptions): Promise<void>;
package/merge.d.ts ADDED
@@ -0,0 +1,24 @@
1
+ import { type Logger } from "webappwiz/log";
2
+ import type { Fs, Lock } from "webappwiz/system";
3
+ import type { Config } from "./config.js";
4
+ import type { Git } from "./git.js";
5
+ import type { Shell } from "./shell.js";
6
+ import { type Todos } from "./todo.js";
7
+ import type { WorktreeService } from "./worktree-service.js";
8
+ /**
9
+ * Lands the current worktree's branch on its base branch, trunk unless the
10
+ * task was created with `--base`. Never a merge commit: it rebases onto the
11
+ * base, runs the gate there, and fast-forwards the base with
12
+ * `git merge --ff-only`, in whichever worktree has the base checked out.
13
+ * History stays linear.
14
+ */
15
+ export declare function merge({ service, git, lock, shell, config, log, todos, fs, }: {
16
+ service: WorktreeService;
17
+ git: Git;
18
+ lock: Lock;
19
+ shell: Shell;
20
+ config: Config;
21
+ log: Logger;
22
+ todos: Todos;
23
+ fs: Fs;
24
+ }, cwd: string): Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webappwiz/arbor",
3
- "version": "0.0.30",
3
+ "version": "0.0.31",
4
4
  "description": "Runs several AI coding agents on one repository at once, each in its own git worktree",
5
5
  "license": "MIT",
6
6
  "author": "Jared Johnson",
@@ -10,14 +10,6 @@
10
10
  "directory": "packages/arbor"
11
11
  },
12
12
  "type": "module",
13
- "module": "index.ts",
14
- "bin": {
15
- "arbor": "./index.ts"
16
- },
17
- "exports": {
18
- ".": "./index.ts",
19
- "./config": "./config.ts"
20
- },
21
13
  "imports": {
22
14
  "#dev/*": "./dev/*"
23
15
  },
@@ -28,31 +20,22 @@
28
20
  "access": "public"
29
21
  },
30
22
  "dependencies": {
31
- "webappwiz": "0.0.29",
23
+ "webappwiz": "^0.0.31",
32
24
  "zod": "^4.4.3"
33
25
  },
34
- "devDependencies": {
35
- "@base-ui/react": "^1.8.0",
36
- "@dnd-kit/core": "^6.3.1",
37
- "@dnd-kit/modifiers": "^9.0.0",
38
- "@dnd-kit/sortable": "^10.0.0",
39
- "@dnd-kit/utilities": "^3.2.2",
40
- "@tailwindcss/oxide": "^4.3.3",
41
- "@testing-library/react": "^16.3.1",
42
- "@types/react": "^19.2.18",
43
- "@types/react-dom": "^19.2.4",
44
- "@webappwiz/react": "0.0.29",
45
- "class-variance-authority": "^0.7.1",
46
- "clsx": "^2.1.1",
47
- "lucide-react": "^1.49.0",
48
- "markdown-to-jsx": "^9.10.3",
49
- "react": "^19.2.8",
50
- "react-dom": "^19.2.8",
51
- "tailwind-merge": "^3.7.0",
52
- "tailwindcss": "^4.3.3",
53
- "tw-animate-css": "^1.4.0"
26
+ "main": "./index.js",
27
+ "types": "./index.d.ts",
28
+ "exports": {
29
+ ".": {
30
+ "types": "./index.d.ts",
31
+ "default": "./index.js"
32
+ },
33
+ "./config": {
34
+ "types": "./config.d.ts",
35
+ "default": "./config.js"
36
+ }
54
37
  },
55
- "scripts": {
56
- "build": "bun run build.ts"
38
+ "bin": {
39
+ "arbor": "./index.js"
57
40
  }
58
41
  }
package/path.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ import type { Logger } from "webappwiz/log";
2
+ import type { WorktreeService } from "./worktree-service.js";
3
+ /**
4
+ * Where a task lives, or (with no task) the main tree. Nothing but the path
5
+ * on stdout, so it composes: `cd "$(arbor path)"`, `zed -a "$(arbor path x)"`.
6
+ * Moving between trees is `cd` and nothing else, and the main tree is the one
7
+ * path a process standing in a worktree cannot otherwise name: git's
8
+ * `--show-toplevel` hands back the worktree it is already in.
9
+ */
10
+ export declare function path({ service, log }: {
11
+ service: WorktreeService;
12
+ log: Logger;
13
+ }, task?: string): Promise<void>;
package/plan.d.ts ADDED
@@ -0,0 +1,83 @@
1
+ /** The plan each task keeps at its worktree root, excluded from git. */
2
+ export declare const PLAN_FILE = "ARBOR.md";
3
+ /**
4
+ * The paths an `ARBOR.md` says its task will touch: each top-level bullet
5
+ * under `## Files`, taken as written. Prose around the list is left out, and a
6
+ * plan with no such section plans nothing.
7
+ */
8
+ export declare function plannedFiles(text: string): string[];
9
+ /**
10
+ * One `- [ ] 9.` item under `## Blocked`: something only a person can do.
11
+ * The item's line is its subject; lines indented under it are its body, and
12
+ * `- (a) ...` or `- [a] ...` lines among them are the answers it offers.
13
+ */
14
+ export interface Question {
15
+ /** As the plan numbers it, `9`: the name a reply goes by. */
16
+ number: string;
17
+ /** Checked off, which the agent does once it has acted on the reply. */
18
+ done: boolean;
19
+ /** The item's own line, without its checkbox, number or reply. */
20
+ text: string;
21
+ /**
22
+ * The markdown indented under it, dedented, choices left out: detail,
23
+ * code blocks, `![shot](/abs/path.png)` images. Empty when there is none.
24
+ */
25
+ body: string;
26
+ /** Whatever follows ` → `, or null when nobody has answered yet. */
27
+ reply: string | null;
28
+ /**
29
+ * What a person added once the reply was read, oldest first: each an
30
+ * indented `→ ` line under the question.
31
+ */
32
+ followUps: string[];
33
+ /**
34
+ * The answers it offers, in order. Empty for a question answered in words
35
+ * alone. A reply can always add words to its picks, or answer in words
36
+ * instead.
37
+ */
38
+ choices: Choice[];
39
+ /**
40
+ * `one` for `- (a)` choices, at most one of which a reply picks; `any` for
41
+ * `- [a]` choices, where it picks all that apply. Null without choices.
42
+ */
43
+ pick: "one" | "any" | null;
44
+ }
45
+ /** One answer a question offers: `- (b) Migrate on next login`. */
46
+ export interface Choice {
47
+ /** The letter it goes by: `b`. */
48
+ key: string;
49
+ text: string;
50
+ }
51
+ /**
52
+ * Every numbered item under `## Blocked`, open or checked off, each with the
53
+ * indented lines under it. The reply is read off the item's own line: an
54
+ * answer is one line, written by `arbor reply` or by hand after the arrow.
55
+ */
56
+ export declare function questions(text: string): Question[];
57
+ /**
58
+ * The plan with a new open question at the end of `## Blocked`, the section
59
+ * made if missing, numbered after the highest there. Returns the plan and
60
+ * the number it went by.
61
+ */
62
+ export declare function withQuestion(text: string, subject: string, body?: string): {
63
+ plan: string;
64
+ number: string;
65
+ };
66
+ /**
67
+ * A question's number however a person typed it: `9`, `9.`, `Q9` and `q9:`
68
+ * all mean `9`. Null for anything that is not a number at all.
69
+ */
70
+ export declare function questionNumber(raw: string): string | null;
71
+ export interface PlanOptions {
72
+ /** The task name the title is expected to match. */
73
+ task: string;
74
+ /** Whether the task has escalated, which makes `## Blocked` required. */
75
+ escalated?: boolean;
76
+ }
77
+ /**
78
+ * Every way a task's `ARBOR.md` departs from the shape the agent skill
79
+ * prescribes, phrased for the agent that wrote it. Advisory only: a resumable
80
+ * plan is the point, and a malformed one still beats none, so nothing here
81
+ * blocks a merge.
82
+ */
83
+ export declare function checkPlan(text: string, { task, escalated }: PlanOptions): string[];
package/remove.d.ts ADDED
@@ -0,0 +1,19 @@
1
+ import { type Logger } from "webappwiz/log";
2
+ import type { Todos } from "./todo.js";
3
+ import type { WorktreeService } from "./worktree-service.js";
4
+ export interface RemoveOptions {
5
+ /** Discard the tree even when another agent holds its lease. */
6
+ force?: boolean;
7
+ }
8
+ /**
9
+ * Discards a whole task: `git worktree remove` plus the branch and the
10
+ * record.
11
+ *
12
+ * Throwing a task away and redoing it against current trunk is usually cheaper
13
+ * than a hard rebase, so this is meant to be used freely.
14
+ */
15
+ export declare function remove({ service, log, todos, }: {
16
+ service: WorktreeService;
17
+ log: Logger;
18
+ todos: Todos;
19
+ }, task: string, { force }?: RemoveOptions): Promise<void>;
@@ -0,0 +1,25 @@
1
+ import type { Deps, Middleware } from "webappwiz/cmd";
2
+ import { type Fs, type Lock } from "webappwiz/system";
3
+ import type { Config } from "./config.js";
4
+ import { Git } from "./git.js";
5
+ import { Journal } from "./journal.js";
6
+ import { Shell } from "./shell.js";
7
+ import { Todos } from "./todo.js";
8
+ import { WorktreeService } from "./worktree-service.js";
9
+ /** What a command gets to work with, once there is a repository to work in. */
10
+ export interface Repository {
11
+ config: Config;
12
+ git: Git;
13
+ service: WorktreeService;
14
+ lock: Lock;
15
+ shell: Shell;
16
+ journal: Journal;
17
+ todos: Todos;
18
+ }
19
+ /**
20
+ * Runs the action against the repository `cwd` sits in, and refuses to run it
21
+ * at all when that is not a repository.
22
+ */
23
+ export declare function repository<C extends Deps & {
24
+ fs: Fs;
25
+ }>(at?: string): Middleware<C, C & Repository>;
package/retry.d.ts ADDED
@@ -0,0 +1,17 @@
1
+ import { type Logger } from "webappwiz/log";
2
+ import type { Config } from "./config.js";
3
+ import type { WorktreeService } from "./worktree-service.js";
4
+ /**
5
+ * The way back from `budget_exhausted`. A task that has spent its merge
6
+ * attempts gets another `mergeRetryCount` of them, instead of `remove` and redo.
7
+ *
8
+ * Only from `escalated`, and deliberately: the budget's whole job is to make an
9
+ * agent stop and hand the task over. An agent that could grant itself more
10
+ * would be back to grinding against a moving trunk forever, so the price of a
11
+ * fresh budget is that a human has looked at the tree first.
12
+ */
13
+ export declare function retry({ service, config, log, }: {
14
+ service: WorktreeService;
15
+ config: Config;
16
+ log: Logger;
17
+ }, task: string): Promise<void>;
package/shell.d.ts ADDED
@@ -0,0 +1,17 @@
1
+ import { type Ps, type SpawnCaptureResult, type SpawnOptions, type SpawnResult } from "webappwiz/system";
2
+ /** What a `Shell` spawns through; the real process by default. */
3
+ export interface ShellOptions {
4
+ ps?: Ps;
5
+ }
6
+ /**
7
+ * Runs the commands a repo configures (the lifecycle hooks) as shell strings
8
+ * rather than argv, since that is how a repo writes them.
9
+ */
10
+ export declare class Shell {
11
+ private readonly ps;
12
+ constructor(opts?: ShellOptions);
13
+ /** Captures output, for commands whose failure has to be reported back. */
14
+ run(command: string, cwd: string, opts?: SpawnOptions): Promise<SpawnCaptureResult>;
15
+ /** Inherits stdio, for commands the agent should watch as they run. */
16
+ stream(command: string, cwd: string, opts?: SpawnOptions): Promise<SpawnResult>;
17
+ }
package/show.d.ts ADDED
@@ -0,0 +1,53 @@
1
+ import { type Logger } from "webappwiz/log";
2
+ import { type Fs } from "webappwiz/system";
3
+ import type { Worktree } from "./worktree.js";
4
+ import type { WorktreeService } from "./worktree-service.js";
5
+ export interface Details {
6
+ task: string;
7
+ status: string;
8
+ branch: string;
9
+ base: string;
10
+ worktree: string;
11
+ lease: "held" | "stale" | "none";
12
+ ahead: number | null;
13
+ added: number | null;
14
+ removed: number | null;
15
+ age: string | null;
16
+ escalation: string | null;
17
+ /**
18
+ * While escalated to be approved, the question that asks for it, `4`;
19
+ * null otherwise.
20
+ */
21
+ review: string | null;
22
+ plan: string | null;
23
+ /** How the `ARBOR.md` departs from the shape the skill prescribes. */
24
+ planProblems: string[];
25
+ }
26
+ export interface ShowOptions {
27
+ /** Print the details as JSON instead of prose. */
28
+ json?: boolean;
29
+ }
30
+ /**
31
+ * One task in full: what `list` shows for it, plus the `ARBOR.md` its agent
32
+ * left at the worktree root. Reading a tree this way takes no lease, so it
33
+ * cannot knock the agent driving it off its own work.
34
+ */
35
+ export declare function show({ service, fs, log }: {
36
+ service: WorktreeService;
37
+ fs: Fs;
38
+ log: Logger;
39
+ }, task: string, opts?: ShowOptions): Promise<void>;
40
+ /** Configures how {@link TaskDetails} gathers a task's info. */
41
+ export interface TaskDetailsOptions {
42
+ /** What the worktree is read through; the real filesystem by default. */
43
+ fs?: Fs;
44
+ }
45
+ /**
46
+ * Everything there is to say about one task. Split out so `dev` can render the
47
+ * same fields this prints, rather than assembling its own and drifting.
48
+ */
49
+ export declare class TaskDetails {
50
+ private readonly fs;
51
+ constructor(opts?: TaskDetailsOptions);
52
+ get(worktree: Worktree): Promise<Details>;
53
+ }
package/snapshot.d.ts ADDED
@@ -0,0 +1,35 @@
1
+ import type { Fs } from "webappwiz/system";
2
+ import { type Details } from "./show.js";
3
+ import type { TodoState, Todos } from "./todo.js";
4
+ import type { WorktreeService } from "./worktree-service.js";
5
+ /**
6
+ * Everything one page shows: `todo list`, and `list` and `show` for each
7
+ * task.
8
+ */
9
+ export interface Snapshot {
10
+ /** The repository's directory name, so a page among many says whose it is. */
11
+ repo: string;
12
+ /** Where the repository sits, with the home directory as `~`. */
13
+ path: string;
14
+ /** Past this age a todo is offered for removal rather than recommended. */
15
+ todoStalenessMs: number;
16
+ todos: TodoState[];
17
+ tasks: Details[];
18
+ }
19
+ /**
20
+ * Reads the whole repo the way the CLI's read commands do. Takes no lease, so
21
+ * the page can sit open on a live tree without disturbing the agent in it.
22
+ */
23
+ export declare function snapshot({ service, todos, fs, home, }: {
24
+ service: WorktreeService;
25
+ todos: Todos;
26
+ fs: Fs;
27
+ /** The home directory to show as `~`, when there is one. */
28
+ home?: string;
29
+ }): Promise<Snapshot>;
30
+ /**
31
+ * What the page is actually showing, minus the fields that move on their own.
32
+ * `age` ticks every minute, and hashing it would push to every open page for
33
+ * nothing.
34
+ */
35
+ export declare function fingerprint({ todos, tasks }: Snapshot): string;
package/table.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Columns padded to their widest cell. Padding goes by visible width, not
3
+ * string length: cells carry color codes.
4
+ */
5
+ export declare function table(header: string[], rows: string[][]): string;