@webappwiz/arbor 0.0.28 → 0.0.30

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 (119) hide show
  1. package/AGENTS.md +7 -0
  2. package/README.md +34 -12
  3. package/add.test.ts +125 -0
  4. package/add.ts +192 -0
  5. package/age.ts +11 -0
  6. package/arbor.test.ts +145 -0
  7. package/arbor.ts +429 -0
  8. package/assets.d.ts +9 -0
  9. package/attachments.ts +87 -0
  10. package/build.ts +98 -0
  11. package/claim.test.ts +89 -0
  12. package/claim.ts +73 -0
  13. package/components.json +21 -0
  14. package/config.ts +58 -0
  15. package/dev/api.ts +120 -0
  16. package/dev/app.test.tsx +645 -0
  17. package/dev/app.tsx +80 -0
  18. package/dev/assets.ts +16 -0
  19. package/dev/build/main.txt +68 -0
  20. package/dev/build/shell.txt +21 -0
  21. package/dev/build/styles.txt +2263 -0
  22. package/dev/components/ui/badge.tsx +51 -0
  23. package/dev/components/ui/button.tsx +57 -0
  24. package/dev/components/ui/dialog.tsx +154 -0
  25. package/dev/components/ui/empty.tsx +100 -0
  26. package/dev/components/ui/input-group.tsx +155 -0
  27. package/dev/components/ui/input.tsx +19 -0
  28. package/dev/components/ui/progress.tsx +80 -0
  29. package/dev/components/ui/textarea.tsx +17 -0
  30. package/dev/components/ui/toast.tsx +227 -0
  31. package/dev/feed.ts +69 -0
  32. package/dev/files.tsx +191 -0
  33. package/dev/index.html +21 -0
  34. package/dev/lib/utils.ts +7 -0
  35. package/dev/main.tsx +22 -0
  36. package/dev/markdown.test.tsx +133 -0
  37. package/dev/markdown.tsx +124 -0
  38. package/dev/mentions.tsx +319 -0
  39. package/dev/shadcn.css +649 -0
  40. package/dev/styles.css +107 -0
  41. package/dev/tags.tsx +44 -0
  42. package/dev/tasks.tsx +151 -0
  43. package/dev/todos.tsx +759 -0
  44. package/dev.test.ts +439 -0
  45. package/dev.ts +386 -0
  46. package/e2e.test.ts +256 -0
  47. package/escalate.test.ts +76 -0
  48. package/escalate.ts +112 -0
  49. package/exit.test.ts +71 -0
  50. package/exit.ts +74 -0
  51. package/git.ts +294 -0
  52. package/index.ts +9 -0
  53. package/journal.ts +89 -0
  54. package/list.test.ts +82 -0
  55. package/list.ts +137 -0
  56. package/load-config.test.ts +121 -0
  57. package/load-config.ts +62 -0
  58. package/log.test.ts +65 -0
  59. package/log.ts +40 -0
  60. package/merge.test.ts +332 -0
  61. package/merge.ts +283 -0
  62. package/package.json +32 -15
  63. package/path.test.ts +35 -0
  64. package/path.ts +38 -0
  65. package/plan.test.ts +318 -0
  66. package/plan.ts +302 -0
  67. package/progress.test.ts +54 -0
  68. package/progress.ts +31 -0
  69. package/remove.test.ts +68 -0
  70. package/remove.ts +88 -0
  71. package/repository.ts +83 -0
  72. package/retry.test.ts +43 -0
  73. package/retry.ts +48 -0
  74. package/shell.ts +44 -0
  75. package/show.test.ts +91 -0
  76. package/show.ts +151 -0
  77. package/snapshot.test.ts +33 -0
  78. package/snapshot.ts +84 -0
  79. package/table.ts +20 -0
  80. package/testing.ts +229 -0
  81. package/todo.test.ts +490 -0
  82. package/todo.ts +834 -0
  83. package/wait.test.ts +61 -0
  84. package/wait.ts +77 -0
  85. package/worktree-service.test.ts +158 -0
  86. package/worktree-service.ts +193 -0
  87. package/worktree.ts +239 -0
  88. package/add.d.ts +0 -23
  89. package/age.d.ts +0 -2
  90. package/arbor.d.ts +0 -11
  91. package/attachments.d.ts +0 -36
  92. package/claim.d.ts +0 -11
  93. package/config.d.ts +0 -55
  94. package/config.js +0 -7
  95. package/dev/assets.d.ts +0 -11
  96. package/dev.d.ts +0 -47
  97. package/escalate.d.ts +0 -28
  98. package/exit.d.ts +0 -42
  99. package/git.d.ts +0 -77
  100. package/index.d.ts +0 -2
  101. package/index.js +0 -4669
  102. package/journal.d.ts +0 -29
  103. package/list.d.ts +0 -18
  104. package/load-config.d.ts +0 -15
  105. package/log.d.ts +0 -14
  106. package/merge.d.ts +0 -24
  107. package/path.d.ts +0 -13
  108. package/plan.d.ts +0 -83
  109. package/remove.d.ts +0 -19
  110. package/repository.d.ts +0 -25
  111. package/retry.d.ts +0 -17
  112. package/shell.d.ts +0 -17
  113. package/show.d.ts +0 -53
  114. package/snapshot.d.ts +0 -31
  115. package/table.d.ts +0 -5
  116. package/todo.d.ts +0 -230
  117. package/wait.d.ts +0 -22
  118. package/worktree-service.d.ts +0 -51
  119. package/worktree.d.ts +0 -95
package/journal.d.ts DELETED
@@ -1,29 +0,0 @@
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 DELETED
@@ -1,18 +0,0 @@
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>;
package/load-config.d.ts DELETED
@@ -1,15 +0,0 @@
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 DELETED
@@ -1,14 +0,0 @@
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 DELETED
@@ -1,24 +0,0 @@
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/path.d.ts DELETED
@@ -1,13 +0,0 @@
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 DELETED
@@ -1,83 +0,0 @@
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 DELETED
@@ -1,19 +0,0 @@
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>;
package/repository.d.ts DELETED
@@ -1,25 +0,0 @@
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 DELETED
@@ -1,17 +0,0 @@
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 DELETED
@@ -1,17 +0,0 @@
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 DELETED
@@ -1,53 +0,0 @@
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 DELETED
@@ -1,31 +0,0 @@
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
- /** Past this age a todo is offered for removal rather than recommended. */
13
- todoStalenessMs: number;
14
- todos: TodoState[];
15
- tasks: Details[];
16
- }
17
- /**
18
- * Reads the whole repo the way the CLI's read commands do. Takes no lease, so
19
- * the page can sit open on a live tree without disturbing the agent in it.
20
- */
21
- export declare function snapshot({ service, todos, fs, }: {
22
- service: WorktreeService;
23
- todos: Todos;
24
- fs: Fs;
25
- }): Promise<Snapshot>;
26
- /**
27
- * What the page is actually showing, minus the fields that move on their own.
28
- * `age` ticks every minute, and hashing it would push to every open page for
29
- * nothing.
30
- */
31
- export declare function fingerprint({ todos, tasks }: Snapshot): string;
package/table.d.ts DELETED
@@ -1,5 +0,0 @@
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;
package/todo.d.ts DELETED
@@ -1,230 +0,0 @@
1
- import { type IdProvider } from "webappwiz/id";
2
- import { type Logger } from "webappwiz/log";
3
- import { type Fs, type Lock, type Ps } from "webappwiz/system";
4
- import { type Attachment, Attachments } from "./attachments.js";
5
- /** What a todo is on disk: one file apiece, so two agents never rewrite one. */
6
- export interface TodoState {
7
- id: number;
8
- /** What is left to do, in a line. */
9
- subject: string;
10
- /** Whatever more there is to say about it, or empty when the line is enough. */
11
- text: string;
12
- /** Where it stands in the list, 1 at the top: the one to pick up first. */
13
- position: number;
14
- /** The task it came up in, or null when a person added it from the main tree. */
15
- from: string | null;
16
- createdAt: string;
17
- /** The task working on it now, or null while it waits to be picked up. */
18
- takenBy: string | null;
19
- /** Absolute paths of the files attached, in `todos/<id>/`. */
20
- files: string[];
21
- }
22
- /** A todo's words, place or files changed: new ones added, some of the old kept. */
23
- export interface TodoChange {
24
- /** A new line; the old one stays when this is absent. */
25
- subject?: string;
26
- /** New detail, empty for none; the old stays when this is absent. */
27
- text?: string;
28
- /** Where to move it in the list; it stays put when this is absent. */
29
- position?: number;
30
- /** Files to add. */
31
- files?: Attachment[];
32
- /** The paths of the files it has now to keep; absent keeps them all. */
33
- keep?: string[];
34
- }
35
- /** What a new todo has besides its subject, all of it optional. */
36
- export interface TodoNew {
37
- /** Whatever more there is to say about it. */
38
- text?: string;
39
- files?: Attachment[];
40
- /** Where it goes in the list, pushing those from there down; the bottom by default. */
41
- position?: number;
42
- }
43
- /**
44
- * Work deferred for later: what an agent notes when something outside its
45
- * task comes up, so it can move on instead of growing the task. Kept under the
46
- * shared `.git`, so every worktree sees a new one at once, with nothing to
47
- * commit and nothing to merge.
48
- */
49
- export declare class Todo {
50
- private readonly todos;
51
- readonly state: TodoState;
52
- constructor(todos: Todos, state: TodoState);
53
- get id(): number;
54
- get subject(): string;
55
- get text(): string;
56
- get position(): number;
57
- get from(): string | null;
58
- get takenBy(): string | null;
59
- get createdAt(): Date;
60
- /** How long it has waited, in milliseconds. */
61
- get waited(): number;
62
- /** Marks it as the work of `task`, refusing one another task already has. */
63
- take(task: string): Promise<Todo>;
64
- /** Puts it back on the list, as when the task that took it is removed. */
65
- release(): Promise<Todo>;
66
- get files(): string[];
67
- get attachments(): Attachments;
68
- /**
69
- * Says what is left to do in other words, with other files, or higher or
70
- * lower in the list, keeping its id and history.
71
- */
72
- update({ subject, text, position, files, keep, }: TodoChange): Promise<Todo>;
73
- remove(): Promise<void>;
74
- }
75
- /** What `Todos` is stored through; the real filesystem by default. */
76
- export interface TodosOptions {
77
- fs?: Fs;
78
- /** Names stored files apart; a test counts. */
79
- ids?: IdProvider;
80
- }
81
- /** Every todo in the repo, one JSON file each under `.git/arbor/todos`. */
82
- export declare class Todos {
83
- readonly dir: string;
84
- /**
85
- * Held while numbering a new todo, so two agents never share an id, and
86
- * while writing one, so a move renumbering the rest never loses to a
87
- * write of a position it just changed.
88
- */
89
- private readonly lock;
90
- private readonly fs;
91
- private readonly ids;
92
- constructor(dir: string,
93
- /**
94
- * Held while numbering a new todo, so two agents never share an id, and
95
- * while writing one, so a move renumbering the rest never loses to a
96
- * write of a position it just changed.
97
- */
98
- lock: Lock, opts?: TodosOptions);
99
- /** Where a todo's files live, beside its record. */
100
- attachments(id: number): Attachments;
101
- /** Whether `path` is a file some todo holds, and not a way out of here. */
102
- owns(path: string): boolean;
103
- add(subject: string, from: string | null, { text, files, position }?: TodoNew): Promise<Todo>;
104
- /**
105
- * Top of the list first. A file that will not parse is skipped rather than
106
- * fatal, and positions are counted again from 1 as they are read, so a
107
- * gap or a tie left by a crash, or a todo saved before they had any (it
108
- * goes below those that do, oldest first), never shows.
109
- */
110
- all(): Promise<Todo[]>;
111
- find(id: number): Promise<Todo>;
112
- /** The todos `task` has taken; what a merge or remove settles. */
113
- takenBy(task: string): Promise<Todo[]>;
114
- /**
115
- * Puts a todo at `position`, 1 at the top, past the bottom meaning the
116
- * bottom, and numbers the rest around it.
117
- */
118
- move(id: number, position: number): Promise<Todo>;
119
- /**
120
- * @internal Rewrites one todo as it stands on disk now, under the lock, so
121
- * a change to its words or who has it never undoes a move made meanwhile.
122
- */
123
- revise(id: number, change: (state: TodoState) => TodoState): Promise<Todo>;
124
- /** @internal Writes one todo. Rename makes the swap atomic for readers. */
125
- save(state: TodoState): Promise<Todo>;
126
- /** @internal Removes one, moving those below it up to close the gap. */
127
- delete(id: number): Promise<void>;
128
- private locked;
129
- /** One todo as it stands on disk; call it under the lock. */
130
- private state;
131
- /**
132
- * Slots `state` in at `position` among the rest and saves whichever moved.
133
- * Call it under the lock.
134
- */
135
- private place;
136
- /** Every todo in list order, with the positions it has on disk. */
137
- private stored;
138
- /** Saves each todo in `order` whose position on disk is not its place there. */
139
- private renumber;
140
- private path;
141
- private lastId;
142
- private read;
143
- }
144
- /** What a finished task should be followed by, and what should just go. */
145
- export interface Recommendation {
146
- /** The todo to pick up next, or null when there is nothing fresh left. */
147
- next: Todo | null;
148
- /** Todos left waiting so long they probably no longer apply. */
149
- stale: Todo[];
150
- }
151
- /**
152
- * Which todo to take up after `task`: one that came up in it first, since
153
- * whoever just finished it knows that context best, then the open one highest
154
- * on the list, which is how whoever keeps the list says what matters most.
155
- * Todos past `staleness` are never recommended, only offered for removal.
156
- */
157
- export declare function recommend(todos: Todos, task: string | null, staleness: number): Promise<Recommendation>;
158
- /** The lines `merge` ends with, or none when there is nothing to say. */
159
- export declare function recommendation({ next, stale }: Recommendation): string[];
160
- export interface TodoListOptions {
161
- /** Print the todos as JSON instead of a table. */
162
- json?: boolean;
163
- }
164
- export interface TodoFileOptions {
165
- /** Files to attach, relative to the current directory or absolute. */
166
- files?: string[];
167
- }
168
- export interface TodoAddOptions extends TodoFileOptions {
169
- /** Whatever more there is to say than the subject. */
170
- text?: string;
171
- /** Where it goes in the list, 1 at the top; the bottom by default. */
172
- position?: number;
173
- }
174
- export declare function todoAdd(deps: {
175
- todos: Todos;
176
- log: Logger;
177
- fs: Fs;
178
- ps: Ps;
179
- }, subject: string, from: string | null, { text, files, position }?: TodoAddOptions): Promise<Todo>;
180
- export declare function todoList({ todos, log }: {
181
- todos: Todos;
182
- log: Logger;
183
- }, { json }?: TodoListOptions): Promise<void>;
184
- export interface TodoShowOptions {
185
- /** Print the todo as JSON instead of prose. */
186
- json?: boolean;
187
- }
188
- /** One todo in full: what `todo list` shows of it, its files, and its detail. */
189
- export declare function todoShow({ todos, log }: {
190
- todos: Todos;
191
- log: Logger;
192
- }, id: number, { json }?: TodoShowOptions): Promise<void>;
193
- export interface TodoUpdateOptions extends TodoFileOptions {
194
- /** A new line; the old one stays when this is absent or empty. */
195
- subject?: string;
196
- /** New detail; the old stays when this is absent or empty. */
197
- text?: string;
198
- /** Where to move it in the list, 1 at the top; it stays put when absent. */
199
- position?: number;
200
- /** Attached files to drop, by path or by the name they were stored under. */
201
- removeFiles?: string[];
202
- }
203
- export declare function todoUpdate(deps: {
204
- todos: Todos;
205
- log: Logger;
206
- fs: Fs;
207
- ps: Ps;
208
- }, id: number, { subject, text, position, files, removeFiles, }?: TodoUpdateOptions): Promise<Todo>;
209
- /**
210
- * Takes up todos for `task` after it started, as when one turns out to be
211
- * part of the work. Every id is checked before any is taken, so one that is
212
- * gone or another task's refuses them all.
213
- */
214
- export declare function todoTake({ todos, log }: {
215
- todos: Todos;
216
- log: Logger;
217
- }, ids: number[], task: string | null): Promise<Todo[]>;
218
- /**
219
- * Puts todos back on the list, as when a task is landing without finishing
220
- * them: say what is left with `arbor todo update` first. From a worktree it
221
- * gives back only its own task's todos.
222
- */
223
- export declare function todoRelease({ todos, log }: {
224
- todos: Todos;
225
- log: Logger;
226
- }, ids: number[], task: string | null): Promise<Todo[]>;
227
- export declare function todoRemove({ todos, log }: {
228
- todos: Todos;
229
- log: Logger;
230
- }, id: number): Promise<void>;
package/wait.d.ts DELETED
@@ -1,22 +0,0 @@
1
- import { type Logger } from "webappwiz/log";
2
- import { Duration } from "webappwiz/time";
3
- import type { WorktreeService } from "./worktree-service.js";
4
- /** How long `wait` gives a task before handing the wait back to its caller. */
5
- export declare const DEFAULT_TIMEOUT: Duration;
6
- export interface WaitOptions {
7
- /** How long to wait before giving up. */
8
- timeout?: Duration;
9
- /** How long between reads of the record; the default suits a human's patience. */
10
- poll?: Duration;
11
- }
12
- /**
13
- * Blocks until a task stops moving: merged or removed (both leave the name
14
- * `removed`), escalated to a human, or broken. Waiting is for the agent whose
15
- * own work overlaps this one's and would rather rebase onto the result than
16
- * against it, which is why the timeout is short enough to come back and think
17
- * again rather than block a session for an afternoon.
18
- */
19
- export declare function wait({ service, log }: {
20
- service: WorktreeService;
21
- log: Logger;
22
- }, task: string, { timeout, poll }?: WaitOptions): Promise<void>;