toolroll 0.9.94 → 0.9.96

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 (191) hide show
  1. package/CHANGELOG.md +17 -1
  2. package/dist/agent-fence.d.ts +6 -0
  3. package/dist/agent-fence.js +2 -2
  4. package/dist/assignment-status.d.ts +1 -1
  5. package/dist/assignment-ui.js +2 -0
  6. package/dist/attest.d.ts +3 -0
  7. package/dist/attest.js +10 -0
  8. package/dist/browser/terminal.js +1 -1
  9. package/dist/browser/workspace.css +1 -1
  10. package/dist/browser/workspace.js +24 -24
  11. package/dist/browser-crew.js +2 -0
  12. package/dist/browser-workspace.d.ts +5 -0
  13. package/dist/build-review.js +2 -0
  14. package/dist/builder.d.ts +7 -2
  15. package/dist/builder.js +183 -79
  16. package/dist/card-catalog.d.ts +209 -0
  17. package/dist/card-catalog.js +133 -0
  18. package/dist/card-cli.d.ts +43 -0
  19. package/dist/card-cli.js +44 -0
  20. package/dist/card-render.d.ts +43 -0
  21. package/dist/card-render.js +260 -0
  22. package/dist/card-sources.d.ts +68 -0
  23. package/dist/card-sources.js +234 -0
  24. package/dist/chat-actions.d.ts +95 -8
  25. package/dist/chat-actions.js +449 -45
  26. package/dist/chat-approval.d.ts +13 -0
  27. package/dist/chat-approval.js +39 -9
  28. package/dist/chat-card-part.d.ts +11 -0
  29. package/dist/chat-card-part.js +18 -0
  30. package/dist/chat-card.d.ts +46 -0
  31. package/dist/chat-card.js +99 -0
  32. package/dist/chat-channel.d.ts +2 -0
  33. package/dist/chat-channel.js +26 -14
  34. package/dist/chat-controls.d.ts +4 -0
  35. package/dist/chat-controls.js +4 -0
  36. package/dist/chat-decide.d.ts +3 -14
  37. package/dist/chat-decide.js +43 -54
  38. package/dist/chat-delivery.js +13 -4
  39. package/dist/chat-flow.d.ts +0 -27
  40. package/dist/chat-flow.js +31 -2
  41. package/dist/chat-quiet.js +1 -1
  42. package/dist/chat-task-actions.d.ts +3 -2
  43. package/dist/chat-task-actions.js +19 -5
  44. package/dist/chat-terms.d.ts +14 -2
  45. package/dist/chat-terms.js +35 -0
  46. package/dist/check-failure.d.ts +2 -0
  47. package/dist/check-failure.js +34 -2
  48. package/dist/check-repair.d.ts +47 -0
  49. package/dist/check-repair.js +240 -0
  50. package/dist/claim.d.ts +6 -0
  51. package/dist/claim.js +13 -2
  52. package/dist/cli-help.d.ts +1 -1
  53. package/dist/cli-help.js +2 -0
  54. package/dist/cli.js +4 -0
  55. package/dist/coding-provider.d.ts +1 -1
  56. package/dist/coding-provider.js +9 -5
  57. package/dist/coding-workspace.d.ts +1 -1
  58. package/dist/coding-workspace.js +4 -4
  59. package/dist/contracts/chat-actions.d.ts +83 -0
  60. package/dist/contracts/chat-actions.js +18 -0
  61. package/dist/contracts/chat-card.d.ts +91 -0
  62. package/dist/contracts/chat-card.js +54 -0
  63. package/dist/contracts/chat-content.d.ts +191 -0
  64. package/dist/contracts/chat-content.js +4 -0
  65. package/dist/contracts/console-api.d.ts +2 -0
  66. package/dist/contracts/console-api.js +2 -0
  67. package/dist/contracts/flow-propose.d.ts +8 -1
  68. package/dist/contracts/flow-propose.js +3 -1
  69. package/dist/contracts/flow-send.d.ts +126 -0
  70. package/dist/contracts/flow-send.js +3 -0
  71. package/dist/contracts/lead-tools.d.ts +173 -0
  72. package/dist/contracts/lead-tools.js +15 -0
  73. package/dist/contracts/store-json.d.ts +16 -0
  74. package/dist/discord-chat.js +14 -0
  75. package/dist/dispatch.d.ts +1 -1
  76. package/dist/dispatch.js +7 -1
  77. package/dist/dispose.d.ts +19 -0
  78. package/dist/dispose.js +47 -7
  79. package/dist/eval-oracle.js +4 -1
  80. package/dist/evidence-pair.d.ts +53 -0
  81. package/dist/evidence-pair.js +128 -0
  82. package/dist/exhaustion.d.ts +17 -4
  83. package/dist/exhaustion.js +44 -4
  84. package/dist/export.js +1 -1
  85. package/dist/failing-first.d.ts +31 -0
  86. package/dist/failing-first.js +44 -0
  87. package/dist/fallback-schema.d.ts +8 -0
  88. package/dist/fallback-schema.js +40 -0
  89. package/dist/fallback.d.ts +138 -0
  90. package/dist/fallback.js +605 -0
  91. package/dist/findings.d.ts +224 -0
  92. package/dist/findings.js +449 -0
  93. package/dist/fleet-schema.d.ts +17 -0
  94. package/dist/fleet-schema.js +135 -0
  95. package/dist/flow-browser.js +2 -2
  96. package/dist/flow-code.d.ts +2 -2
  97. package/dist/flow-code.js +9 -5
  98. package/dist/flow-result-card.d.ts +22 -0
  99. package/dist/flow-result-card.js +55 -0
  100. package/dist/flow-send.js +12 -9
  101. package/dist/flow-steps.js +1 -1
  102. package/dist/flow-triggers.js +1 -1
  103. package/dist/flow-unattended.d.ts +26 -0
  104. package/dist/flow-unattended.js +12 -3
  105. package/dist/flows-ui.js +10 -2
  106. package/dist/follow-through-brief.d.ts +12 -0
  107. package/dist/follow-through-brief.js +31 -0
  108. package/dist/follow-through-cli.d.ts +23 -0
  109. package/dist/follow-through-cli.js +153 -0
  110. package/dist/git-incident.d.ts +16 -0
  111. package/dist/git-incident.js +69 -6
  112. package/dist/handover-note.d.ts +33 -0
  113. package/dist/handover-note.js +106 -0
  114. package/dist/invoke.d.ts +20 -1
  115. package/dist/invoke.js +108 -44
  116. package/dist/lead-cli.js +26 -24
  117. package/dist/lead-contract.js +1 -0
  118. package/dist/lead-doors.d.ts +4 -0
  119. package/dist/lead-doors.js +2 -2
  120. package/dist/lead-progress.js +2 -0
  121. package/dist/lead-tools.js +111 -32
  122. package/dist/lead.js +1 -1
  123. package/dist/local-owner.js +5 -0
  124. package/dist/modes.d.ts +6 -6
  125. package/dist/modes.js +3 -1
  126. package/dist/needs-you.d.ts +5 -2
  127. package/dist/needs-you.js +14 -2
  128. package/dist/operate-remote-arguments.js +1 -0
  129. package/dist/operate-remote.js +1 -1
  130. package/dist/operate.d.ts +1 -1
  131. package/dist/operate.js +165 -14
  132. package/dist/plan-login.d.ts +14 -0
  133. package/dist/plan-login.js +58 -0
  134. package/dist/planner.d.ts +1 -1
  135. package/dist/planner.js +2 -1
  136. package/dist/project-delete-ui.d.ts +12 -0
  137. package/dist/project-delete-ui.js +18 -1
  138. package/dist/project-delete.js +6 -0
  139. package/dist/project-knowledge.d.ts +6 -6
  140. package/dist/project-tools.d.ts +4 -4
  141. package/dist/project-tools.js +23 -7
  142. package/dist/protected-storage-look.d.ts +1 -0
  143. package/dist/protected-storage-look.js +13 -0
  144. package/dist/protected-storage.d.ts +70 -39
  145. package/dist/protected-storage.js +196 -147
  146. package/dist/provider-capture.d.ts +34 -0
  147. package/dist/provider-capture.js +53 -0
  148. package/dist/release-carry.js +10 -1
  149. package/dist/release.js +9 -1
  150. package/dist/scope.d.ts +22 -3
  151. package/dist/scope.js +71 -20
  152. package/dist/scout.d.ts +1 -1
  153. package/dist/scout.js +2 -1
  154. package/dist/serve.js +2 -0
  155. package/dist/server/chat.js +2 -2
  156. package/dist/server/console.css +50 -0
  157. package/dist/server/render-chat.js +14 -1
  158. package/dist/server/render-follow-through.d.ts +11 -0
  159. package/dist/server/render-follow-through.js +68 -0
  160. package/dist/server/render-results.js +1 -1
  161. package/dist/server/render-tasks.d.ts +3 -0
  162. package/dist/server/render-tasks.js +24 -5
  163. package/dist/server/route-table.js +3 -0
  164. package/dist/server/settings.js +37 -12
  165. package/dist/server/task-views.js +5 -0
  166. package/dist/server/tasks.js +76 -0
  167. package/dist/settings-groups-ui.d.ts +1 -0
  168. package/dist/settings-groups-ui.js +3 -1
  169. package/dist/settings-kit.js +3 -2
  170. package/dist/settings-proofs.js +3 -1
  171. package/dist/setup-guide.d.ts +2 -2
  172. package/dist/slack-chat.js +14 -1
  173. package/dist/steer-mailbox.d.ts +8 -1
  174. package/dist/steer-mailbox.js +164 -6
  175. package/dist/store.d.ts +11 -4
  176. package/dist/store.js +93 -30
  177. package/dist/surface.js +24 -4
  178. package/dist/task-control.d.ts +2 -1
  179. package/dist/task-status.js +5 -0
  180. package/dist/teardown-git.d.ts +2 -0
  181. package/dist/teardown-git.js +75 -19
  182. package/dist/telegram-lead.js +23 -6
  183. package/dist/telegram.js +12 -1
  184. package/dist/toolroll-update.js +2 -0
  185. package/dist/work-index.js +22 -7
  186. package/dist/work-summary.d.ts +1 -1
  187. package/dist/work-summary.js +5 -2
  188. package/dist/workspace-ui.d.ts +3 -1
  189. package/dist/workspace-ui.js +10 -1
  190. package/dist/worktree.js +23 -1
  191. package/package.json +2 -1
@@ -0,0 +1,209 @@
1
+ import { z } from "zod";
2
+ export declare const CARD_SOURCES: readonly ["tasks", "results", "flows", "flow_zones", "spend", "projects", "builders", "mode", "integrations", "providers", "findings", "handover"];
3
+ export type CardSourceName = (typeof CARD_SOURCES)[number];
4
+ export declare const CARD_TASK_STATES: readonly ["queued", "running", "done", "failed", "cancelled"];
5
+ export declare const CARD_CATALOG: import("@json-render/core").Catalog<{
6
+ spec: import("@json-render/core").SchemaType<"object", {
7
+ root: import("@json-render/core").SchemaType<"string", unknown>;
8
+ elements: import("@json-render/core").SchemaType<"record", import("@json-render/core").SchemaType<"object", {
9
+ type: import("@json-render/core").SchemaType<"ref", string>;
10
+ props: import("@json-render/core").SchemaType<"propsOf", string>;
11
+ children: import("@json-render/core").SchemaType<"array", import("@json-render/core").SchemaType<"string", unknown>>;
12
+ }>>;
13
+ }>;
14
+ catalog: import("@json-render/core").SchemaType<"object", {
15
+ components: import("@json-render/core").SchemaType<"map", {
16
+ props: import("@json-render/core").SchemaType<"zod", unknown>;
17
+ description: import("@json-render/core").SchemaType<"string", unknown>;
18
+ }>;
19
+ }>;
20
+ }, {
21
+ components: {
22
+ Card: {
23
+ props: z.ZodObject<{}, z.core.$strict>;
24
+ description: string;
25
+ };
26
+ Text: {
27
+ props: z.ZodObject<{
28
+ text: z.ZodString;
29
+ }, z.core.$strict>;
30
+ description: string;
31
+ };
32
+ Fields: {
33
+ props: z.ZodObject<{
34
+ repo: z.ZodOptional<z.ZodString>;
35
+ task: z.ZodOptional<z.ZodString>;
36
+ state: z.ZodOptional<z.ZodEnum<{
37
+ cancelled: "cancelled";
38
+ done: "done";
39
+ failed: "failed";
40
+ queued: "queued";
41
+ running: "running";
42
+ }>>;
43
+ limit: z.ZodOptional<z.ZodInt>;
44
+ flow: z.ZodOptional<z.ZodInt>;
45
+ days: z.ZodOptional<z.ZodInt>;
46
+ source: z.ZodEnum<{
47
+ builders: "builders";
48
+ findings: "findings";
49
+ flow_zones: "flow_zones";
50
+ flows: "flows";
51
+ handover: "handover";
52
+ integrations: "integrations";
53
+ mode: "mode";
54
+ projects: "projects";
55
+ providers: "providers";
56
+ results: "results";
57
+ spend: "spend";
58
+ tasks: "tasks";
59
+ }>;
60
+ facts: z.ZodOptional<z.ZodArray<z.ZodString>>;
61
+ }, z.core.$strict>;
62
+ description: string;
63
+ };
64
+ Table: {
65
+ props: z.ZodObject<{
66
+ repo: z.ZodOptional<z.ZodString>;
67
+ task: z.ZodOptional<z.ZodString>;
68
+ state: z.ZodOptional<z.ZodEnum<{
69
+ cancelled: "cancelled";
70
+ done: "done";
71
+ failed: "failed";
72
+ queued: "queued";
73
+ running: "running";
74
+ }>>;
75
+ limit: z.ZodOptional<z.ZodInt>;
76
+ flow: z.ZodOptional<z.ZodInt>;
77
+ days: z.ZodOptional<z.ZodInt>;
78
+ source: z.ZodEnum<{
79
+ builders: "builders";
80
+ findings: "findings";
81
+ flow_zones: "flow_zones";
82
+ flows: "flows";
83
+ handover: "handover";
84
+ integrations: "integrations";
85
+ mode: "mode";
86
+ projects: "projects";
87
+ providers: "providers";
88
+ results: "results";
89
+ spend: "spend";
90
+ tasks: "tasks";
91
+ }>;
92
+ columns: z.ZodOptional<z.ZodArray<z.ZodString>>;
93
+ }, z.core.$strict>;
94
+ description: string;
95
+ };
96
+ Notes: {
97
+ props: z.ZodObject<{
98
+ repo: z.ZodOptional<z.ZodString>;
99
+ task: z.ZodOptional<z.ZodString>;
100
+ state: z.ZodOptional<z.ZodEnum<{
101
+ cancelled: "cancelled";
102
+ done: "done";
103
+ failed: "failed";
104
+ queued: "queued";
105
+ running: "running";
106
+ }>>;
107
+ limit: z.ZodOptional<z.ZodInt>;
108
+ flow: z.ZodOptional<z.ZodInt>;
109
+ days: z.ZodOptional<z.ZodInt>;
110
+ source: z.ZodEnum<{
111
+ builders: "builders";
112
+ findings: "findings";
113
+ flow_zones: "flow_zones";
114
+ flows: "flows";
115
+ handover: "handover";
116
+ integrations: "integrations";
117
+ mode: "mode";
118
+ projects: "projects";
119
+ providers: "providers";
120
+ results: "results";
121
+ spend: "spend";
122
+ tasks: "tasks";
123
+ }>;
124
+ }, z.core.$strict>;
125
+ description: string;
126
+ };
127
+ Chart: {
128
+ props: z.ZodObject<{
129
+ repo: z.ZodOptional<z.ZodString>;
130
+ task: z.ZodOptional<z.ZodString>;
131
+ state: z.ZodOptional<z.ZodEnum<{
132
+ cancelled: "cancelled";
133
+ done: "done";
134
+ failed: "failed";
135
+ queued: "queued";
136
+ running: "running";
137
+ }>>;
138
+ limit: z.ZodOptional<z.ZodInt>;
139
+ flow: z.ZodOptional<z.ZodInt>;
140
+ days: z.ZodOptional<z.ZodInt>;
141
+ source: z.ZodEnum<{
142
+ builders: "builders";
143
+ findings: "findings";
144
+ flow_zones: "flow_zones";
145
+ flows: "flows";
146
+ handover: "handover";
147
+ integrations: "integrations";
148
+ mode: "mode";
149
+ projects: "projects";
150
+ providers: "providers";
151
+ results: "results";
152
+ spend: "spend";
153
+ tasks: "tasks";
154
+ }>;
155
+ chart: z.ZodEnum<{
156
+ area: "area";
157
+ bar: "bar";
158
+ line: "line";
159
+ pie: "pie";
160
+ }>;
161
+ }, z.core.$strict>;
162
+ description: string;
163
+ };
164
+ Divider: {
165
+ props: z.ZodObject<{}, z.core.$strict>;
166
+ description: string;
167
+ };
168
+ Actions: {
169
+ props: z.ZodObject<{}, z.core.$strict>;
170
+ description: string;
171
+ };
172
+ Button: {
173
+ props: z.ZodObject<{
174
+ control: z.ZodOptional<z.ZodEnum<{
175
+ [x: string]: string;
176
+ }>>;
177
+ task: z.ZodOptional<z.ZodString>;
178
+ run: z.ZodOptional<z.ZodInt>;
179
+ repo: z.ZodOptional<z.ZodString>;
180
+ proposal: z.ZodOptional<z.ZodInt>;
181
+ }, z.core.$strict>;
182
+ description: string;
183
+ };
184
+ };
185
+ }>;
186
+ export type CardComponent = keyof typeof CARD_CATALOG.data.components;
187
+ export type CardElement = {
188
+ type: CardComponent;
189
+ props: Record<string, unknown>;
190
+ children: string[];
191
+ };
192
+ /** A checked spec, as the parts of its card in order. */
193
+ export type CardPlan = {
194
+ parts: {
195
+ type: CardComponent;
196
+ props: Record<string, unknown>;
197
+ }[];
198
+ buttons: Record<string, unknown>[];
199
+ };
200
+ /** A spec as a card plan, or every reason it isn't one, in words the lead can fix on its next step. */
201
+ export declare function readCardSpec(raw: unknown): {
202
+ ok: true;
203
+ plan: CardPlan;
204
+ } | {
205
+ ok: false;
206
+ issues: string[];
207
+ };
208
+ /** The catalog in words, for the lead's tool description: json-render's full prompt describes features a card refuses. */
209
+ export declare function cardCatalogWords(): string;
@@ -0,0 +1,133 @@
1
+ /**
2
+ * What a lead may put on a card (docs/plans/toolroll-cards.md): a json-render spec limited to this catalog.
3
+ *
4
+ * json-render defines the catalog and checks a spec's structure. Its own check lets any props through when a catalog has
5
+ * several components, so each element's props are read here, strictly, against its component. json-render's dynamic
6
+ * features ($state, repeat, visible, on) are refused: a card is data Toolroll fills, not a program.
7
+ *
8
+ * Nothing here can carry a figure or an address. Fields, Table and Chart name a source; Text is the one free field.
9
+ */
10
+ import { defineCatalog, defineSchema, validateSpec } from "@json-render/core";
11
+ import { z } from "zod";
12
+ import { CHAT_CONTROLS } from "./chat-controls.js";
13
+ import { CARD_CHART_TYPES, CARD_LIMITS } from "./contracts/chat-card.js";
14
+ export const CARD_SOURCES = ["tasks", "results", "flows", "flow_zones", "spend", "projects", "builders", "mode", "integrations", "providers", "findings", "handover"];
15
+ export const CARD_TASK_STATES = ["queued", "running", "done", "failed", "cancelled"];
16
+ const source = z.enum(CARD_SOURCES);
17
+ const repo = z.string().regex(/^r[0-9]{1,3}$/);
18
+ const pick = z.array(z.string().max(40)).min(1).max(CARD_LIMITS.columns);
19
+ /** What narrows a source: read by the sources, which check each against who is asking. */
20
+ const narrowing = { repo: repo.optional(), task: z.string().max(200).optional(), state: z.enum(CARD_TASK_STATES).optional(), limit: z.int().min(1).max(CARD_LIMITS.rows).optional(), flow: z.int().min(1).optional(), days: z.int().min(1).max(90).optional() };
21
+ const schema = defineSchema(s => ({
22
+ spec: s.object({
23
+ root: s.string(),
24
+ elements: s.record(s.object({ type: s.ref("catalog.components"), props: s.propsOf("catalog.components"), children: s.array(s.string()) })),
25
+ }),
26
+ catalog: s.object({ components: s.map({ props: s.zod(), description: s.string() }) }),
27
+ }));
28
+ export const CARD_CATALOG = defineCatalog(schema, {
29
+ components: {
30
+ Card: { props: z.strictObject({}), description: "The root. Its children are the card's parts in order. Its title comes from its first source." },
31
+ Text: { props: z.strictObject({ text: z.string().min(1).max(CARD_LIMITS.text) }), description: "Your own words, shown as a plain paragraph. Never a place for figures you want read as records." },
32
+ Fields: { props: z.strictObject({ source, facts: z.array(z.string().max(40)).min(1).max(CARD_LIMITS.facts).optional(), ...narrowing }), description: "Key facts from a source, filled by Toolroll. facts picks which." },
33
+ Table: { props: z.strictObject({ source, columns: pick.optional(), ...narrowing }), description: "Rows from a source, filled by Toolroll. columns picks which." },
34
+ Notes: { props: z.strictObject({ source, ...narrowing }), description: "Saved words from a source, whole, one to a line: findings or handover (with task)." },
35
+ Chart: { props: z.strictObject({ source, chart: z.enum(CARD_CHART_TYPES), ...narrowing }), description: "A chart of a source's numbers, filled by Toolroll: bar, pie, line or area." },
36
+ Divider: { props: z.strictObject({}), description: "A line between parts." },
37
+ Actions: { props: z.strictObject({}), description: "Holds the card's Buttons." },
38
+ Button: {
39
+ props: z.strictObject({ control: z.enum(Object.keys(CHAT_CONTROLS)).optional(), task: z.string().max(200).optional(), run: z.int().min(1).optional(), repo: repo.optional(), proposal: z.int().min(1).optional() }),
40
+ description: "control opens a fixed Toolroll page (as show_control). proposal points at one you proposed earlier this reply; its Confirm stays on its own message under the card.",
41
+ },
42
+ },
43
+ });
44
+ const issues = (lines) => ({ ok: false, issues: lines });
45
+ /** A spec as a card plan, or every reason it isn't one, in words the lead can fix on its next step. */
46
+ export function readCardSpec(raw) {
47
+ const shape = CARD_CATALOG.validate(raw);
48
+ if (!shape.success)
49
+ return issues((shape.error?.issues ?? []).map(one => `${one.path.join(".")}: ${one.message}`));
50
+ const spec = raw;
51
+ const structure = validateSpec(spec, { checkOrphans: true });
52
+ const refused = structure.issues.map(one => one.message);
53
+ if (refused.length > 0)
54
+ return issues(refused);
55
+ const parsed = new Map();
56
+ for (const key of Object.keys(spec.elements)) {
57
+ const element = spec.elements[key];
58
+ const extra = Object.keys(element).filter(one => one !== "type" && one !== "props" && one !== "children");
59
+ if (extra.length > 0) {
60
+ refused.push(`${key}: a card element has only type, props and children, not ${extra.join(", ")}`);
61
+ continue;
62
+ }
63
+ const type = element["type"];
64
+ const props = CARD_CATALOG.data.components[type].props.safeParse(element["props"]);
65
+ if (!props.success) {
66
+ refused.push(...props.error.issues.map(one => `${key}.props.${one.path.join(".")}: ${one.message}`));
67
+ continue;
68
+ }
69
+ parsed.set(key, { type, props: props.data, children: element["children"] });
70
+ }
71
+ if (refused.length > 0)
72
+ return issues(refused);
73
+ const own = (key) => Object.hasOwn(spec.elements, key) ? parsed.get(key) : undefined;
74
+ const root = own(spec.root);
75
+ if (root?.type !== "Card")
76
+ return issues(["root: the root must be a Card"]);
77
+ const seen = new Set([spec.root]);
78
+ const plan = { parts: [], buttons: [] };
79
+ const used = (key) => {
80
+ const element = own(key);
81
+ if (element === undefined) {
82
+ refused.push(`${key}: no such element`);
83
+ return null;
84
+ }
85
+ if (seen.has(key)) {
86
+ refused.push(`${key}: an element appears once`);
87
+ return null;
88
+ }
89
+ seen.add(key);
90
+ return element;
91
+ };
92
+ for (const key of root.children) {
93
+ const element = used(key);
94
+ if (element === null)
95
+ continue;
96
+ if (element.type === "Actions") {
97
+ for (const inner of element.children) {
98
+ const button = used(inner);
99
+ if (button === null)
100
+ continue;
101
+ if (button.type !== "Button")
102
+ refused.push(`${inner}: Actions holds Buttons`);
103
+ else if ((button.props["control"] === undefined) === (button.props["proposal"] === undefined))
104
+ refused.push(`${inner}: a Button names a control or a proposal, one of them`);
105
+ else
106
+ plan.buttons.push(button.props);
107
+ }
108
+ continue;
109
+ }
110
+ if (element.type === "Card" || element.type === "Button") {
111
+ refused.push(`${key}: a ${element.type} can't be a part of a card`);
112
+ continue;
113
+ }
114
+ if (element.children.length > 0) {
115
+ refused.push(`${key}: a ${element.type} has no children`);
116
+ continue;
117
+ }
118
+ plan.parts.push({ type: element.type, props: element.props });
119
+ }
120
+ if (seen.size !== parsed.size)
121
+ refused.push("every element must be reached from the root");
122
+ if (plan.parts.length > CARD_LIMITS.blocks)
123
+ refused.push(`a card has at most ${CARD_LIMITS.blocks} parts`);
124
+ if (plan.buttons.length > CARD_LIMITS.buttons + CARD_LIMITS.actions)
125
+ refused.push("too many Buttons");
126
+ if (!plan.parts.some(one => one.type === "Fields" || one.type === "Table" || one.type === "Notes" || one.type === "Chart"))
127
+ refused.push("a card shows data: give it Fields, a Table, Notes or a Chart. For words alone, just reply");
128
+ return refused.length > 0 ? issues(refused) : { ok: true, plan };
129
+ }
130
+ /** The catalog in words, for the lead's tool description: json-render's full prompt describes features a card refuses. */
131
+ export function cardCatalogWords() {
132
+ return Object.entries(CARD_CATALOG.data.components).map(([name, one]) => `${name}: ${one.description}`).join(" ");
133
+ }
@@ -0,0 +1,43 @@
1
+ import type { Store } from "./store.js";
2
+ export declare const CARD_DESCRIPTOR: {
3
+ invocation: string;
4
+ synopsis: string;
5
+ flags: readonly [{
6
+ readonly name: "repo";
7
+ readonly takesValue: true;
8
+ readonly meaning: "one project; default: all of yours";
9
+ }, {
10
+ readonly name: "task";
11
+ readonly takesValue: true;
12
+ readonly meaning: "findings and handover: the task";
13
+ }, {
14
+ readonly name: "state";
15
+ readonly takesValue: true;
16
+ readonly meaning: "tasks: queued, running, done, failed or cancelled";
17
+ }, {
18
+ readonly name: "limit";
19
+ readonly takesValue: true;
20
+ readonly meaning: "how many rows, up to 20";
21
+ }, {
22
+ readonly name: "flow";
23
+ readonly takesValue: true;
24
+ readonly meaning: "flow_zones: the flow's number";
25
+ }, {
26
+ readonly name: "days";
27
+ readonly takesValue: true;
28
+ readonly meaning: "flow_zones: how far back, up to 90";
29
+ }];
30
+ };
31
+ export type CardCliContext = {
32
+ store: Store;
33
+ write: (line: string) => void;
34
+ json: boolean;
35
+ now: Date;
36
+ actor: string | null;
37
+ repos: readonly string[];
38
+ evidenceRoot?: string;
39
+ /** The CLI's own envelope (operate.ts succeed/fail): one JSON document under --json, plain lines otherwise. */
40
+ ok: (result: Record<string, unknown>, lines: () => string[]) => number;
41
+ no: (reason: string, message: string) => number;
42
+ };
43
+ export declare function runCardCommand(positional: readonly string[], options: ReadonlyMap<string, string | true>, context: CardCliContext): number;
@@ -0,0 +1,44 @@
1
+ /** `toolroll card <source>` (docs/plans/toolroll-cards.md): the card chat shows, from the same records, on the command line. */
2
+ import { CARD_SOURCES } from "./card-catalog.js";
3
+ import { cardPlainText } from "./card-render.js";
4
+ import { readCardSource } from "./card-sources.js";
5
+ import { buildChatCard } from "./chat-card.js";
6
+ import { capabilitiesOf } from "./lead-capabilities.js";
7
+ const flags = [
8
+ { name: "repo", takesValue: true, meaning: "one project; default: all of yours" },
9
+ { name: "task", takesValue: true, meaning: "findings and handover: the task" },
10
+ { name: "state", takesValue: true, meaning: "tasks: queued, running, done, failed or cancelled" },
11
+ { name: "limit", takesValue: true, meaning: "how many rows, up to 20" },
12
+ { name: "flow", takesValue: true, meaning: "flow_zones: the flow's number" },
13
+ { name: "days", takesValue: true, meaning: "flow_zones: how far back, up to 90" },
14
+ ];
15
+ export const CARD_DESCRIPTOR = { invocation: "card", synopsis: `show Toolroll's own data as chat's cards do: ${CARD_SOURCES.join(", ")} (read only)`, flags };
16
+ export function runCardCommand(positional, options, context) {
17
+ const fail = (reason, message) => context.no(reason, message);
18
+ const source = positional[0];
19
+ if (source === undefined || !CARD_SOURCES.includes(source))
20
+ return fail("usage", `Use card <${CARD_SOURCES.join("|")}> [--repo PATH] [--task ID] [--state S] [--limit N] [--flow N] [--days N].`);
21
+ if (context.actor === null)
22
+ return fail("unauthenticated", "Sign in first: the remembered local login drives card.");
23
+ const { store, now } = context, actor = context.actor;
24
+ const capabilities = (repos) => capabilitiesOf(store, { repos, admitted: context.repos, actor, integrations: [], now });
25
+ const reader = { store, now, who: { name: actor, repos: context.repos }, ...(context.evidenceRoot === undefined ? {} : { evidenceRoot: context.evidenceRoot }),
26
+ providers: repos => capabilities(repos).projects.flatMap(one => one.agents.map(agent => ({ project: one.repo, provider: agent.provider, model: agent.model, roles: agent.roles, state: agent.state }))),
27
+ workers: repos => capabilities(repos).workers.map(one => ({ name: one.name, state: one.state, capacity: one.capacity, running: one.running.length })) };
28
+ const value = (name) => { const got = options.get(name); return typeof got === "string" ? got : undefined; };
29
+ const number = (name) => { const got = Number(value(name)); return value(name) === undefined || !Number.isSafeInteger(got) || got < 1 ? {} : { [name]: got }; };
30
+ const props = { source, ...(value("repo") === undefined ? {} : { repo: value("repo") }), ...(value("task") === undefined ? {} : { task: value("task") }), ...(value("state") === undefined ? {} : { state: value("state") }), ...number("limit"), ...number("flow"), ...number("days") };
31
+ const read = readCardSource(reader, source, { repo: value("repo") ?? null, ...(value("task") === undefined ? {} : { task: value("task") }), ...(value("state") === undefined ? {} : { state: value("state") }), ...number("limit"), ...number("flow"), ...number("days") });
32
+ if ("problem" in read)
33
+ return fail("refused", read.problem);
34
+ const plan = { buttons: [], parts: [
35
+ ...(read.facts.length === 0 ? [] : [{ type: "Fields", props }]),
36
+ { type: "Table", props },
37
+ ...(read.notes === undefined || read.notes.length === 0 ? [] : [{ type: "Notes", props }]),
38
+ ...(read.points.length === 0 ? [] : [{ type: "Chart", props: { ...props, chart: "bar" } }]),
39
+ ] };
40
+ const built = buildChatCard(reader, plan, id => typeof id === "string" ? id : null, () => ({ ok: true }));
41
+ if (!built.ok)
42
+ return fail("refused", built.message);
43
+ return context.ok({ card: built.card }, () => cardPlainText(built.card).split("\n"));
44
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * One card, in each app's own form (docs/plans/toolroll-cards.md). A card's values are already scrubbed; here they are
3
+ * made inert for each app, so a value can't become a link, a mention or formatting. Whatever an app can't show comes out
4
+ * as readable text, and every renderer has the same plain version to fall back on.
5
+ */
6
+ import type { ChatCard, ChatCardBlock } from "./contracts/chat-card.js";
7
+ import { type Html } from "./html.js";
8
+ type Table = Extract<ChatCardBlock, {
9
+ kind: "table";
10
+ }>;
11
+ /** A Text part is the lead's (a model's) words: always labelled as such, on every app, never as a record. */
12
+ export declare const LEAD_NOTE_WORDS = "The lead's note, not Toolroll's records";
13
+ /** The whole card as plain text: the version every app can show. */
14
+ export declare function cardPlainText(card: ChatCard): string;
15
+ /** The card as Block Kit, native where Slack has it: one data table and two charts a message, the rest as code blocks. */
16
+ export declare function slackCardBlocks(card: ChatCard, buttons: readonly Record<string, unknown>[]): Record<string, unknown>[];
17
+ /** The card as one embed: facts as fields, tables and charts as code blocks. `plain` escapes Discord's own syntax. */
18
+ export declare function discordCardEmbed(card: ChatCard, plain: (text: string) => string): Record<string, unknown>;
19
+ /** The card as one Telegram message: plain words, tables and charts as `pre` entities, so nothing in a value is parsed. */
20
+ export declare function telegramCardMessage(card: ChatCard): {
21
+ text: string;
22
+ entities: {
23
+ type: "pre" | "bold";
24
+ offset: number;
25
+ length: number;
26
+ }[];
27
+ };
28
+ /** The card as the web chat's own markup; every value is escaped by `html`. */
29
+ export declare function webCardHtml(card: ChatCard): Html;
30
+ /**
31
+ * A table's rows for a phone: each row's first value in full as its name, then its other values on one line ("queued ·
32
+ * standing-orders · 3h ago"; a number keeps its column's label: "Failed attempts 2"). A column with the same value in
33
+ * every shown row is said once above the rows instead ("All: queued · Failed attempts 0").
34
+ */
35
+ export declare function telegramTableRows(block: Table, keep: number): {
36
+ same: string;
37
+ rows: {
38
+ name: string;
39
+ rest: string;
40
+ }[];
41
+ more: number;
42
+ };
43
+ export {};