toolroll 0.9.6 → 0.9.7

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 (84) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/dist/actor.d.ts +21 -0
  3. package/dist/actor.js +48 -0
  4. package/dist/assignment-adapters.d.ts +1 -0
  5. package/dist/assignment-ui.js +3 -1
  6. package/dist/assignment.d.ts +9 -0
  7. package/dist/assignment.js +62 -13
  8. package/dist/browser/workspace.css +2 -2
  9. package/dist/browser/workspace.js +28 -24
  10. package/dist/browser-workspace.d.ts +117 -2
  11. package/dist/builder.d.ts +1 -0
  12. package/dist/builder.js +33 -13
  13. package/dist/chat-actions.js +2 -1
  14. package/dist/chat-delivery.js +2 -0
  15. package/dist/chat-quiet.js +11 -1
  16. package/dist/chat-task-actions.d.ts +13 -0
  17. package/dist/chat-task-actions.js +19 -1
  18. package/dist/check-levels-ui.d.ts +21 -0
  19. package/dist/check-levels-ui.js +70 -0
  20. package/dist/check-levels.d.ts +67 -0
  21. package/dist/check-levels.js +171 -0
  22. package/dist/claim.d.ts +22 -0
  23. package/dist/claim.js +43 -0
  24. package/dist/cli.d.ts +1 -1
  25. package/dist/cli.js +4 -0
  26. package/dist/demo-chat.js +4 -1
  27. package/dist/dispatch.js +7 -2
  28. package/dist/dispose.d.ts +8 -0
  29. package/dist/dispose.js +7 -1
  30. package/dist/export.js +3 -2
  31. package/dist/first-run.d.ts +34 -0
  32. package/dist/first-run.js +37 -1
  33. package/dist/flow-gallery-ui.d.ts +30 -0
  34. package/dist/flow-gallery-ui.js +85 -0
  35. package/dist/flow-gallery.d.ts +111 -0
  36. package/dist/flow-gallery.js +445 -0
  37. package/dist/flow-share.d.ts +102 -0
  38. package/dist/flow-share.js +614 -0
  39. package/dist/flow-starters.d.ts +2 -0
  40. package/dist/flow-starters.js +1 -1
  41. package/dist/flow-steps.js +7 -0
  42. package/dist/flow-triggers.js +2 -0
  43. package/dist/flows-cli.d.ts +56 -0
  44. package/dist/flows-cli.js +109 -3
  45. package/dist/flows-ui.d.ts +15 -1
  46. package/dist/flows-ui.js +31 -6
  47. package/dist/guides.js +27 -2
  48. package/dist/lead-context.js +6 -2
  49. package/dist/mate-doors.d.ts +0 -15
  50. package/dist/mate-doors.js +5 -1
  51. package/dist/mate-tools.js +9 -4
  52. package/dist/models-ui.js +1 -1
  53. package/dist/operate.d.ts +4 -1
  54. package/dist/operate.js +601 -85
  55. package/dist/project-concurrency.d.ts +62 -0
  56. package/dist/project-concurrency.js +116 -0
  57. package/dist/project-delete-ui.d.ts +7 -0
  58. package/dist/project-delete-ui.js +19 -2
  59. package/dist/project-delete.js +4 -0
  60. package/dist/pull-request-flow.d.ts +9 -0
  61. package/dist/pull-request-flow.js +21 -3
  62. package/dist/result-follow-ups.d.ts +101 -0
  63. package/dist/result-follow-ups.js +333 -0
  64. package/dist/scope.js +11 -0
  65. package/dist/serve.d.ts +211 -2
  66. package/dist/serve.js +1009 -141
  67. package/dist/store.d.ts +76 -3
  68. package/dist/store.js +311 -22
  69. package/dist/surface.js +31 -8
  70. package/dist/tailnet.d.ts +11 -0
  71. package/dist/tailnet.js +47 -0
  72. package/dist/task-outcome-cli.js +2 -2
  73. package/dist/task-status.d.ts +8 -1
  74. package/dist/task-status.js +28 -10
  75. package/dist/team-cli.js +3 -0
  76. package/dist/telegram-status.js +2 -2
  77. package/dist/verification-evidence.js +21 -1
  78. package/dist/webhooks.js +5 -0
  79. package/dist/work-index.js +39 -6
  80. package/dist/work-summary.d.ts +0 -4
  81. package/dist/work-summary.js +2 -0
  82. package/dist/workspace-ui.d.ts +3 -0
  83. package/dist/workspace-ui.js +5 -3
  84. package/package.json +1 -1
@@ -0,0 +1,85 @@
1
+ /**
2
+ * The flow gallery in the console (Flows → New and Settings → Flows): cards grouped by what they're for, each with
3
+ * its promise, a small drawing of its zones, what it needs and "Use this". A template's page asks only what it
4
+ * needs, previews in plain words what it will do and never do, and creates it — the same answers it previewed.
5
+ */
6
+ import { BLANK, GALLERY, GALLERY_GROUPS, galleryDiagram, OUTDATED_COMMANDS } from "./flow-gallery.js";
7
+ const e = (value) => String(value ?? "").replace(/[&<>"']/g, c => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[c]);
8
+ export const GALLERY_CSS = `.gallery{max-width:1120px;min-width:0}.gallery h2{margin:28px 0 10px;font-size:.9375rem}.gallery h2:first-of-type{margin-top:8px}` +
9
+ `.gallery-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:12px}` +
10
+ `.gallery-card{display:flex;flex-direction:column;gap:8px;padding:16px;border:1px solid var(--so-line);border-radius:10px;background:var(--so-paper);min-width:0}` +
11
+ `.gallery-card h3{margin:0;font-size:.9375rem}.gallery-card p{margin:0;overflow-wrap:anywhere}.gallery-card .gallery-promise{line-height:1.5;flex:1}` +
12
+ `.gallery-zones{display:block;width:100%;height:56px;border-radius:8px;background:var(--so-raised)}.gallery-zones rect{fill:var(--so-paper);stroke:var(--so-input-line);stroke-width:1.5}` +
13
+ `.gallery-zones rect[data-person="true"]{fill:var(--so-signal-soft,var(--so-raised));stroke:var(--so-ink)}.gallery-zones line{stroke:var(--so-input-line);stroke-width:1.5}` +
14
+ `.gallery-needs{display:flex;flex-wrap:wrap;gap:4px;list-style:none;padding:0;margin:0}.gallery-needs li{font-size:11.5px;font-weight:500;line-height:18px;padding:1px 6px;border-radius:5px;background:var(--so-neutral-soft,var(--so-raised));color:var(--so-neutral-ink,var(--so-muted))}` +
15
+ // An outline action in a list (the Quiet List Rule), over the workspace's ink default for .button-link.
16
+ `.gallery .gallery-card a.button-link{align-self:flex-start;min-height:36px;background:var(--so-paper);color:var(--so-ink);border:1px solid var(--so-input-line)}` +
17
+ `.gallery-blank{margin-top:20px}` +
18
+ `.gallery-use{max-width:720px;min-width:0}.gallery-use .gallery-zones{height:88px;margin:4px 0 12px}.gallery-use form{display:grid;gap:12px;margin:0}.gallery-use label{display:grid;gap:4px;font-weight:500}` +
19
+ `.gallery-use label small{font-weight:400;color:var(--so-muted)}` +
20
+ `.gallery-use input:not([type=hidden]),.gallery-use select{box-sizing:border-box;width:100%;max-width:100%;min-height:32px;padding:0 10px;border:1px solid var(--so-input-line);border-radius:8px;background:var(--so-paper);color:var(--so-ink);font:inherit}` +
21
+ `.gallery-preview{display:grid;gap:8px;padding:16px;border:1px solid var(--so-line);border-radius:10px;background:var(--so-paper);min-width:0}.gallery-preview h2{margin:0;font-size:.9375rem}` +
22
+ `.gallery-preview ul{margin:0;padding-left:18px;line-height:1.55}.gallery-preview p{margin:0;overflow-wrap:anywhere}.gallery-preview .gallery-never{font-weight:600}` +
23
+ `.gallery-preview details ol{margin:6px 0 0;padding-left:20px;white-space:pre-line;font-size:.875rem;line-height:1.5;color:var(--so-muted)}.gallery-preview details li{margin:0 0 6px;overflow-wrap:anywhere}` +
24
+ `.gallery-preview summary{cursor:pointer;min-height:36px;display:flex;align-items:center;font-weight:500}` +
25
+ `.gallery-actions{display:flex;flex-wrap:wrap;gap:8px;margin:0}.gallery-actions button{min-height:36px;white-space:nowrap}` +
26
+ `.gallery-use .gallery-actions button[value=create]{background:var(--primary);color:var(--primary-foreground);border-color:var(--primary);font-weight:600}.gallery-heading{margin:36px 0 0;font-size:1.05rem}` +
27
+ `@media(max-width:760px){.gallery-grid{grid-template-columns:minmax(0,1fr)}.gallery .gallery-card a.button-link{min-height:44px;align-self:stretch;justify-content:center}` +
28
+ `.gallery-use input:not([type=hidden]),.gallery-use select{font-size:16px;min-height:44px}.gallery-actions button{flex:1 1 auto;min-height:44px}}`;
29
+ /** A small drawing of a flow's zones where they sit on its canvas, with its paths; a person's decisions stand out. */
30
+ export function zonesDiagram(definition, label) {
31
+ const zones = definition.stages.map(one => one.zone);
32
+ const minX = Math.min(...zones.map(one => one.x)), minY = Math.min(...zones.map(one => one.y));
33
+ const maxX = Math.max(...zones.map(one => one.x + one.w)), maxY = Math.max(...zones.map(one => one.y + one.h));
34
+ const pad = 40;
35
+ const box = `${minX - pad} ${minY - pad} ${maxX - minX + pad * 2} ${maxY - minY + pad * 2}`;
36
+ const centre = (id) => { const at = definition.stages.find(one => one.id === id)?.zone; return at === undefined ? null : { x: at.x + at.w / 2, y: at.y + at.h / 2 }; };
37
+ const lines = definition.stages.flatMap(stage => [stage.next, ...(stage.sort?.answers.map(one => one.to) ?? []), ...(stage.routes?.map(one => one.to) ?? [])].map(to => {
38
+ const from = centre(stage.id), end = centre(to);
39
+ return from === null || end === null ? "" : `<line x1="${from.x}" y1="${from.y}" x2="${end.x}" y2="${end.y}" vector-effect="non-scaling-stroke"/>`;
40
+ })).join("");
41
+ const rects = definition.stages.map(one => `<rect x="${one.zone.x}" y="${one.zone.y}" width="${one.zone.w}" height="${one.zone.h}" rx="36" data-person="${one.kind === "approval"}" vector-effect="non-scaling-stroke"/>`).join("");
42
+ return `<svg class="gallery-zones" viewBox="${box}" preserveAspectRatio="xMidYMid meet" role="img" aria-label="${e(label)}">${lines}${rects}</svg>`;
43
+ }
44
+ const needsList = (template) => template.needs.length === 0 ? `<ul class="gallery-needs"><li>Nothing to connect</li></ul>`
45
+ : `<ul class="gallery-needs" aria-label="Needs">${template.needs.map(one => `<li>${e(one)}</li>`).join("")}</ul>`;
46
+ /** The gallery: every template, grouped, each with one "Use this". `repo` carries a chosen project to the template's page. */
47
+ export function galleryHtml(input) {
48
+ const href = (id) => `/flows/new/${id}${input.repo === null ? "" : `?repo=${encodeURIComponent(input.repo)}`}`;
49
+ const card = (template) => {
50
+ const definition = galleryDiagram(template);
51
+ return `<article class="gallery-card" data-template="${e(template.id)}"><h3>${e(template.name)}</h3><p class="gallery-promise">${e(template.promise)}</p>` +
52
+ zonesDiagram(definition, `Zones: ${definition.stages.map(one => one.title).join(", ")}`) + needsList(template) +
53
+ (input.canUse ? `<a class="button-link" href="${e(href(template.id))}" aria-label="Use ${e(template.name)}">Use this</a>` : "") + `</article>`;
54
+ };
55
+ const groups = GALLERY_GROUPS.map(group => `<h2 id="gallery-${group.id}">${e(group.label)}</h2><div class="gallery-grid" data-group="${group.id}">${GALLERY.filter(one => one.group === group.id).map(card).join("")}</div>`).join("");
56
+ return `<section class="gallery">${groups}${input.canUse ? `<p class="gallery-blank meta"><a href="${e(href(BLANK.id))}">Start from a blank flow</a></p>` : `<p class="meta">An approver creates flows.</p>`}</section>`;
57
+ }
58
+ /** A template's page: its questions, then what it will do and never do, then Create. */
59
+ export function galleryUseHtml(input) {
60
+ const { template } = input;
61
+ const field = (ask) => {
62
+ const value = input.answers[ask.key] ?? ask.default;
63
+ const hint = ask.hint === undefined ? "" : ` <small>${e(ask.hint)}</small>`;
64
+ if (ask.key === "outdated")
65
+ return `<label>${e(ask.label)}<select name="outdated">${Object.entries(OUTDATED_COMMANDS).map(([id, one]) => `<option value="${id}"${id === value ? " selected" : ""}>${e(one.label)}</option>`).join("")}</select></label>`;
66
+ return `<label>${e(ask.label)}${hint}<input name="${ask.key}" value="${e(value)}" required maxlength="${ask.key === "command" ? 500 : 100}"${ask.key === "command" ? ' spellcheck="false" autocapitalize="off"' : ""}></label>`;
67
+ };
68
+ const project = input.projects.length > 1
69
+ ? `<label>Project<select name="repo">${input.projects.map(one => `<option value="${e(one.path)}"${one.path === input.repo ? " selected" : ""}>${e(one.name)}</option>`).join("")}</select></label>`
70
+ : `<input type="hidden" name="repo" value="${e(input.repo)}">`;
71
+ const preview = input.preview === null ? "" : `<section class="gallery-preview" aria-labelledby="gallery-will">` +
72
+ `<h2 id="gallery-will">What it will do</h2><ul>${input.preview.built.does.map(one => `<li>${e(one)}</li>`).join("")}</ul>` +
73
+ `<p class="gallery-never">${e(input.preview.built.never)}</p>` +
74
+ (input.preview.startsFrom.length === 0 ? `<p class="meta">Cards start when you add them.</p>` : input.preview.startsFrom.map(one => `<p class="meta">Starts from: ${e(one)}</p>`).join("")) +
75
+ `<details><summary>Every step</summary><ol>${input.preview.steps.map(one => `<li>${e(one.replace(/^\d+\.\s/, ""))}</li>`).join("")}</ol></details></section>`;
76
+ const asks = template.asks.length > 0 || input.projects.length > 1;
77
+ return `<section class="gallery-use" data-gallery-template="${e(template.id)}">` +
78
+ (input.problem === null ? "" : `<p class="problem" role="alert">${e(input.problem)}</p>`) +
79
+ `<p>${e(template.promise)}</p>${input.diagram === null ? "" : zonesDiagram(input.diagram, `Zones: ${input.diagram.stages.map(one => one.title).join(", ")}`)}${needsList(template)}` +
80
+ `<form method="post" action="/flows/new/${e(template.id)}" data-gallery-use><input type="hidden" name="csrf" value="${e(input.csrf)}">` +
81
+ `<input type="hidden" name="previewed" value="${e(input.preview?.digest ?? "")}">${project}${template.asks.map(field).join("")}` +
82
+ `<label>Name<input name="name" value="${e(input.name)}" maxlength="80"></label>${preview}` +
83
+ `<p class="gallery-actions">${input.preview === null ? "" : `<button type="submit" name="intent" value="create">Create flow</button>`}${asks || input.preview === null ? `<button type="submit" name="intent" value="preview" class="secondary">${input.preview === null ? "Preview" : "Update preview"}</button>` : ""}</p>` +
84
+ `</form><p class="meta"><a href="/flows/new">All templates</a></p></section>`;
85
+ }
@@ -0,0 +1,111 @@
1
+ import { type FlowDefinition, type FlowStepInput } from "./flows.js";
2
+ import type { Store } from "./store.js";
3
+ export declare const GALLERY_GROUPS: readonly [{
4
+ readonly id: "ship";
5
+ readonly label: "Ship code";
6
+ }, {
7
+ readonly id: "health";
8
+ readonly label: "Keep it healthy";
9
+ }, {
10
+ readonly id: "users";
11
+ readonly label: "Hear from users";
12
+ }, {
13
+ readonly id: "ops";
14
+ readonly label: "Operations";
15
+ }];
16
+ export type GalleryGroup = (typeof GALLERY_GROUPS)[number]["id"];
17
+ /** What a template may ask, besides the project. */
18
+ export type AskKey = "label" | "branch" | "schedule" | "time" | "command" | "outdated";
19
+ export type GalleryAsk = {
20
+ key: AskKey;
21
+ default: string;
22
+ label: string;
23
+ hint?: string;
24
+ };
25
+ /** Weekly upkeep's package managers: what each runs. A script that fails (exits non-zero) when something is out of date. */
26
+ export declare const OUTDATED_COMMANDS: Record<"npm" | "pip" | "cargo", {
27
+ label: string;
28
+ command: string;
29
+ }>;
30
+ export type GalleryTemplate = {
31
+ id: string;
32
+ /** The flow's name, and the card's title. */
33
+ name: string;
34
+ group: GalleryGroup;
35
+ /** One line: what it promises. */
36
+ promise: string;
37
+ /** What it needs to work, in a word or two each. */
38
+ needs: string[];
39
+ /** Watches a GitHub repository, so the project must be on GitHub. */
40
+ github?: boolean;
41
+ asks: GalleryAsk[];
42
+ /** Its zones, as steps (laid out for it) or as a drawn flow. */
43
+ steps?: FlowStepInput[];
44
+ /** The steps in Toolroll's own repository, when they differ. */
45
+ ownSteps?: FlowStepInput[];
46
+ definition?: FlowDefinition;
47
+ triggers: Record<string, unknown>[];
48
+ scripts?: {
49
+ name: string;
50
+ about: string;
51
+ body: string;
52
+ timeoutMinutes?: number;
53
+ }[];
54
+ /** What it will do, in order; `{{github}}` is the project's GitHub repository. */
55
+ does: string[];
56
+ never: string;
57
+ };
58
+ export declare const GALLERY: readonly GalleryTemplate[];
59
+ /** The blank flow, for starting from nothing. */
60
+ export declare const BLANK: GalleryTemplate;
61
+ export declare const galleryTemplateOf: (id: string) => GalleryTemplate | null;
62
+ export type GalleryAnswers = Record<string, string>;
63
+ /** Each question's default for a project: the branch pull requests go to, when that's set up. */
64
+ export declare function galleryDefaults(store: Store, template: GalleryTemplate, repo: string | null): GalleryAnswers;
65
+ /** The answers, checked in plain words, with what they fill in. Throws. */
66
+ export declare function checkAnswers(template: GalleryTemplate, given: GalleryAnswers): GalleryAnswers;
67
+ export type GalleryBuilt = {
68
+ template: GalleryTemplate;
69
+ repo: string;
70
+ answers: GalleryAnswers;
71
+ definition: FlowDefinition;
72
+ triggers: Record<string, unknown>[];
73
+ scripts: {
74
+ name: string;
75
+ about: string;
76
+ body: string;
77
+ timeoutMinutes: number;
78
+ existing: boolean;
79
+ }[];
80
+ does: string[];
81
+ never: string;
82
+ };
83
+ /** A template made concrete for a project and its answers: its zones, triggers and scripts, checked. Throws in plain words. */
84
+ export declare function buildFromGallery(store: Store, template: GalleryTemplate, repo: string, given: GalleryAnswers): GalleryBuilt;
85
+ /** A template's zones as its defaults draw them, for the gallery's small drawing; no project needed. */
86
+ export declare function galleryDiagram(template: GalleryTemplate): FlowDefinition;
87
+ export type GalleryPreview = {
88
+ built: GalleryBuilt;
89
+ startsFrom: string[];
90
+ steps: string[];
91
+ digest: string;
92
+ };
93
+ /** What using a template will do, in words, before anything is made: its triggers checked as the flow will have them. Throws. */
94
+ export declare function previewGallery(store: Store, template: GalleryTemplate, repo: string, given: GalleryAnswers, actor: string, now: Date): GalleryPreview;
95
+ /** What a preview showed: the template, the project and the answers. Creating checks the person saw these. */
96
+ export declare function galleryDigest(id: string, repo: string, answers: GalleryAnswers): string;
97
+ export type GalleryUsed = {
98
+ ok: true;
99
+ flow: number;
100
+ said: string;
101
+ } | {
102
+ ok: false;
103
+ said: string;
104
+ };
105
+ /** Use a template in a project: its scripts, its flow and its triggers, together or not at all. */
106
+ export declare function useGalleryTemplate(store: Store, template: GalleryTemplate, repo: string, given: GalleryAnswers, input: {
107
+ name?: string | null;
108
+ by: string;
109
+ now: Date;
110
+ dir: string | null;
111
+ }): GalleryUsed;