startup-builder 0.2.0 → 0.3.0
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.
- package/LICENSE +21 -0
- package/README.md +120 -0
- package/dist/chunk-ZR74VEI2.js +2322 -0
- package/dist/cli.js +338 -188
- package/dist/index.d.ts +660 -430
- package/dist/index.js +77 -17
- package/package.json +23 -16
- package/dist/chunk-NWM7TYW6.js +0 -1004
package/dist/chunk-NWM7TYW6.js
DELETED
|
@@ -1,1004 +0,0 @@
|
|
|
1
|
-
// src/machines/startup-builder.ts
|
|
2
|
-
import { setup, assign } from "xstate";
|
|
3
|
-
import {
|
|
4
|
-
icpSchema,
|
|
5
|
-
competitorSchema,
|
|
6
|
-
leanCanvasSchema,
|
|
7
|
-
hypothesisSchema,
|
|
8
|
-
differentiatorSchema,
|
|
9
|
-
storyBrandSchema,
|
|
10
|
-
agentSchema
|
|
11
|
-
} from "startups.studio/schemas";
|
|
12
|
-
var startupBuilderMachine = setup({
|
|
13
|
-
types: {
|
|
14
|
-
context: {},
|
|
15
|
-
events: {},
|
|
16
|
-
input: {}
|
|
17
|
-
},
|
|
18
|
-
guards: {
|
|
19
|
-
hasResearchArtifacts: ({ context }) => context.icpCount >= 1 && context.competitorCount >= 1,
|
|
20
|
-
hasCanvasArtifacts: ({ context }) => context.leanCanvasCount >= 1 && context.hypothesisCount >= 1,
|
|
21
|
-
hasPositioningArtifacts: ({ context }) => context.positioningSet && context.differentiatorCount >= 1,
|
|
22
|
-
hasMessagingArtifacts: ({ context }) => context.storyBrandCount >= 1,
|
|
23
|
-
hasServicesComplete: ({ context }) => context.serviceCount >= 1,
|
|
24
|
-
hasTeamArtifacts: ({ context }) => context.agentCount >= 1,
|
|
25
|
-
hasSiteComplete: ({ context }) => context.domainCount >= 1 && context.pageCount >= 1,
|
|
26
|
-
isSkipping: ({ event }) => event.type === "SKIP"
|
|
27
|
-
},
|
|
28
|
-
actions: {
|
|
29
|
-
recordSkip: assign({
|
|
30
|
-
skipReason: ({ event }) => event.type === "SKIP" ? event.reason : void 0
|
|
31
|
-
}),
|
|
32
|
-
updateArtifactCount: assign(({ context, event }) => {
|
|
33
|
-
if (event.type !== "ARTIFACT_CREATED") return context;
|
|
34
|
-
const map = {
|
|
35
|
-
icps: "icpCount",
|
|
36
|
-
competitors: "competitorCount",
|
|
37
|
-
"lean-canvases": "leanCanvasCount",
|
|
38
|
-
hypotheses: "hypothesisCount",
|
|
39
|
-
differentiators: "differentiatorCount",
|
|
40
|
-
"story-brands": "storyBrandCount",
|
|
41
|
-
services: "serviceCount",
|
|
42
|
-
agents: "agentCount",
|
|
43
|
-
domains: "domainCount",
|
|
44
|
-
pages: "pageCount"
|
|
45
|
-
};
|
|
46
|
-
const key = map[event.collection];
|
|
47
|
-
if (!key) return context;
|
|
48
|
-
return { ...context, [key]: event.count };
|
|
49
|
-
}),
|
|
50
|
-
setPositioning: assign({ positioningSet: true }),
|
|
51
|
-
setResult: assign(({ event }) => ({
|
|
52
|
-
result: event.type === "SALES_COMPLETE" ? event.result : void 0
|
|
53
|
-
}))
|
|
54
|
-
}
|
|
55
|
-
}).createMachine({
|
|
56
|
-
id: "startup-builder",
|
|
57
|
-
initial: "research",
|
|
58
|
-
context: ({ input }) => ({
|
|
59
|
-
startupId: input.startupId,
|
|
60
|
-
ideaId: input.ideaId,
|
|
61
|
-
sprintId: input.sprintId,
|
|
62
|
-
icpCount: 0,
|
|
63
|
-
competitorCount: 0,
|
|
64
|
-
leanCanvasCount: 0,
|
|
65
|
-
hypothesisCount: 0,
|
|
66
|
-
positioningSet: false,
|
|
67
|
-
differentiatorCount: 0,
|
|
68
|
-
storyBrandCount: 0,
|
|
69
|
-
serviceCount: 0,
|
|
70
|
-
agentCount: 0,
|
|
71
|
-
domainCount: 0,
|
|
72
|
-
pageCount: 0
|
|
73
|
-
}),
|
|
74
|
-
on: {
|
|
75
|
-
ARTIFACT_CREATED: { actions: "updateArtifactCount" }
|
|
76
|
-
},
|
|
77
|
-
states: {
|
|
78
|
-
research: {
|
|
79
|
-
description: "Define who you are building for and who you are competing with",
|
|
80
|
-
meta: {
|
|
81
|
-
requiredArtifacts: {
|
|
82
|
-
icps: { min: 1, description: "Ideal Customer Profiles" },
|
|
83
|
-
competitors: { min: 1, description: "Competitor analyses" }
|
|
84
|
-
},
|
|
85
|
-
commands: [
|
|
86
|
-
{
|
|
87
|
-
name: "Create ICP",
|
|
88
|
-
collection: "icps",
|
|
89
|
-
schema: icpSchema
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
name: "Create Competitor",
|
|
93
|
-
collection: "competitors",
|
|
94
|
-
schema: competitorSchema
|
|
95
|
-
}
|
|
96
|
-
]
|
|
97
|
-
},
|
|
98
|
-
on: {
|
|
99
|
-
ADVANCE: { guard: "hasResearchArtifacts", target: "canvas" },
|
|
100
|
-
SKIP: { target: "canvas", actions: "recordSkip" }
|
|
101
|
-
}
|
|
102
|
-
},
|
|
103
|
-
canvas: {
|
|
104
|
-
description: "Formulate your hypothesis and business model",
|
|
105
|
-
meta: {
|
|
106
|
-
requiredArtifacts: {
|
|
107
|
-
"lean-canvases": { min: 1, description: "Lean Canvas" },
|
|
108
|
-
hypotheses: { min: 1, description: "Testable hypotheses" }
|
|
109
|
-
},
|
|
110
|
-
commands: [
|
|
111
|
-
{
|
|
112
|
-
name: "Create Lean Canvas",
|
|
113
|
-
collection: "lean-canvases",
|
|
114
|
-
schema: leanCanvasSchema
|
|
115
|
-
},
|
|
116
|
-
{
|
|
117
|
-
name: "Create Hypothesis",
|
|
118
|
-
collection: "hypotheses",
|
|
119
|
-
schema: hypothesisSchema
|
|
120
|
-
}
|
|
121
|
-
]
|
|
122
|
-
},
|
|
123
|
-
on: {
|
|
124
|
-
ADVANCE: { guard: "hasCanvasArtifacts", target: "positioning" },
|
|
125
|
-
SKIP: { target: "positioning", actions: "recordSkip" }
|
|
126
|
-
}
|
|
127
|
-
},
|
|
128
|
-
positioning: {
|
|
129
|
-
description: "Define where you sit in the market",
|
|
130
|
-
meta: {
|
|
131
|
-
requiredArtifacts: {
|
|
132
|
-
positioning: { min: 1, description: "Positioning data on startup" },
|
|
133
|
-
differentiators: { min: 1, description: "Key differentiators" }
|
|
134
|
-
},
|
|
135
|
-
commands: [
|
|
136
|
-
{
|
|
137
|
-
name: "Set Positioning",
|
|
138
|
-
collection: "startups",
|
|
139
|
-
operation: "update",
|
|
140
|
-
schema: {
|
|
141
|
-
positioning: {
|
|
142
|
-
xAxis: "Label for horizontal axis (e.g. Generic to Specialized)",
|
|
143
|
-
yAxis: "Label for vertical axis (e.g. Manual to Automated)",
|
|
144
|
-
placements: [
|
|
145
|
-
{
|
|
146
|
-
name: "Company name",
|
|
147
|
-
x: "number: 0-100 position on x axis",
|
|
148
|
-
y: "number: 0-100 position on y axis"
|
|
149
|
-
}
|
|
150
|
-
]
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
},
|
|
154
|
-
{
|
|
155
|
-
name: "Create Differentiator",
|
|
156
|
-
collection: "differentiators",
|
|
157
|
-
schema: differentiatorSchema
|
|
158
|
-
}
|
|
159
|
-
]
|
|
160
|
-
},
|
|
161
|
-
on: {
|
|
162
|
-
ADVANCE: { guard: "hasPositioningArtifacts", target: "messaging" },
|
|
163
|
-
SKIP: { target: "messaging", actions: "recordSkip" }
|
|
164
|
-
}
|
|
165
|
-
},
|
|
166
|
-
messaging: {
|
|
167
|
-
description: "Craft your story using the StoryBrand framework",
|
|
168
|
-
meta: {
|
|
169
|
-
requiredArtifacts: {
|
|
170
|
-
"story-brands": { min: 1, description: "StoryBrand messaging" }
|
|
171
|
-
},
|
|
172
|
-
commands: [
|
|
173
|
-
{
|
|
174
|
-
name: "Create StoryBrand",
|
|
175
|
-
collection: "story-brands",
|
|
176
|
-
schema: storyBrandSchema
|
|
177
|
-
}
|
|
178
|
-
]
|
|
179
|
-
},
|
|
180
|
-
on: {
|
|
181
|
-
ADVANCE: { guard: "hasMessagingArtifacts", target: "services" },
|
|
182
|
-
SKIP: { target: "services", actions: "recordSkip" }
|
|
183
|
-
}
|
|
184
|
-
},
|
|
185
|
-
services: {
|
|
186
|
-
description: "Define your delivery model and monetization \u2014 invokes ServicesBuilder",
|
|
187
|
-
meta: {
|
|
188
|
-
invokedBuilder: "services-builder"
|
|
189
|
-
},
|
|
190
|
-
on: {
|
|
191
|
-
SERVICES_COMPLETE: { target: "team" },
|
|
192
|
-
SKIP: { target: "team", actions: "recordSkip" }
|
|
193
|
-
}
|
|
194
|
-
},
|
|
195
|
-
team: {
|
|
196
|
-
description: "Define your org structure, agent roster, and OKRs",
|
|
197
|
-
meta: {
|
|
198
|
-
requiredArtifacts: {
|
|
199
|
-
agents: { min: 1, description: "Agent team members with objectives" }
|
|
200
|
-
},
|
|
201
|
-
commands: [
|
|
202
|
-
{
|
|
203
|
-
name: "Create Agent",
|
|
204
|
-
collection: "agents",
|
|
205
|
-
schema: agentSchema
|
|
206
|
-
},
|
|
207
|
-
{
|
|
208
|
-
name: "Create Roster from Template",
|
|
209
|
-
command: "agents create-roster",
|
|
210
|
-
schema: {
|
|
211
|
-
template: "solo | lean | full"
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
]
|
|
215
|
-
},
|
|
216
|
-
on: {
|
|
217
|
-
ADVANCE: { guard: "hasTeamArtifacts", target: "site" },
|
|
218
|
-
SKIP: { target: "site", actions: "recordSkip" }
|
|
219
|
-
}
|
|
220
|
-
},
|
|
221
|
-
site: {
|
|
222
|
-
description: "Build your web presence \u2014 invokes SiteBuilder",
|
|
223
|
-
meta: {
|
|
224
|
-
invokedBuilder: "site-builder"
|
|
225
|
-
},
|
|
226
|
-
on: {
|
|
227
|
-
SITE_COMPLETE: { target: "sales" },
|
|
228
|
-
SKIP: { target: "sales", actions: "recordSkip" }
|
|
229
|
-
}
|
|
230
|
-
},
|
|
231
|
-
sales: {
|
|
232
|
-
description: "Drive demand and measure results \u2014 invokes SalesBuilder",
|
|
233
|
-
meta: {
|
|
234
|
-
invokedBuilder: "sales-builder"
|
|
235
|
-
},
|
|
236
|
-
on: {
|
|
237
|
-
SALES_COMPLETE: { target: "complete", actions: "setResult" },
|
|
238
|
-
SKIP: { target: "complete", actions: "recordSkip" }
|
|
239
|
-
}
|
|
240
|
-
},
|
|
241
|
-
complete: {
|
|
242
|
-
type: "final",
|
|
243
|
-
description: "Builder complete \u2014 startup is now operating"
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
// src/machines/services-builder.ts
|
|
249
|
-
import { setup as setup2, assign as assign2 } from "xstate";
|
|
250
|
-
import { storyBrandSchema as storyBrandSchema2, serviceSchema, productSchema } from "startups.studio/schemas";
|
|
251
|
-
var servicesBuilderMachine = setup2({
|
|
252
|
-
types: {
|
|
253
|
-
context: {},
|
|
254
|
-
events: {},
|
|
255
|
-
input: {}
|
|
256
|
-
},
|
|
257
|
-
guards: {
|
|
258
|
-
needsMessaging: ({ context }) => !context.hasStoryBrand,
|
|
259
|
-
hasMessaging: ({ context }) => context.hasStoryBrand,
|
|
260
|
-
hasModel: ({ context }) => context.deliveryModel !== void 0,
|
|
261
|
-
hasServices: ({ context }) => context.serviceCount >= 1,
|
|
262
|
-
hasPricing: ({ context }) => context.pricingSet,
|
|
263
|
-
hasProducts: ({ context }) => context.productCount >= 1
|
|
264
|
-
},
|
|
265
|
-
actions: {
|
|
266
|
-
setModel: assign2(({ event }) => ({
|
|
267
|
-
deliveryModel: event.type === "MODEL_SELECTED" ? event.model : void 0
|
|
268
|
-
})),
|
|
269
|
-
setPricing: assign2({ pricingSet: true }),
|
|
270
|
-
updateArtifactCount: assign2(({ context, event }) => {
|
|
271
|
-
if (event.type !== "ARTIFACT_CREATED") return context;
|
|
272
|
-
if (event.collection === "services") return { ...context, serviceCount: event.count };
|
|
273
|
-
if (event.collection === "products") return { ...context, productCount: event.count };
|
|
274
|
-
return context;
|
|
275
|
-
})
|
|
276
|
-
}
|
|
277
|
-
}).createMachine({
|
|
278
|
-
id: "services-builder",
|
|
279
|
-
initial: "checkPrerequisites",
|
|
280
|
-
context: ({ input }) => ({
|
|
281
|
-
startupId: input.startupId,
|
|
282
|
-
hasStoryBrand: input.hasStoryBrand,
|
|
283
|
-
serviceCount: 0,
|
|
284
|
-
pricingSet: false,
|
|
285
|
-
productCount: 0,
|
|
286
|
-
activationComplete: false
|
|
287
|
-
}),
|
|
288
|
-
on: {
|
|
289
|
-
ARTIFACT_CREATED: { actions: "updateArtifactCount" }
|
|
290
|
-
},
|
|
291
|
-
states: {
|
|
292
|
-
checkPrerequisites: {
|
|
293
|
-
always: [
|
|
294
|
-
{ guard: "needsMessaging", target: "messaging" },
|
|
295
|
-
{ guard: "hasMessaging", target: "model" }
|
|
296
|
-
]
|
|
297
|
-
},
|
|
298
|
-
messaging: {
|
|
299
|
-
description: "Prerequisite: Create StoryBrand messaging before defining services",
|
|
300
|
-
meta: {
|
|
301
|
-
prerequisite: true,
|
|
302
|
-
commands: [
|
|
303
|
-
{
|
|
304
|
-
name: "Create StoryBrand",
|
|
305
|
-
collection: "story-brands",
|
|
306
|
-
schema: storyBrandSchema2
|
|
307
|
-
}
|
|
308
|
-
]
|
|
309
|
-
},
|
|
310
|
-
on: {
|
|
311
|
-
ADVANCE: { target: "model" }
|
|
312
|
-
}
|
|
313
|
-
},
|
|
314
|
-
model: {
|
|
315
|
-
description: "Choose your delivery model",
|
|
316
|
-
meta: {
|
|
317
|
-
commands: [
|
|
318
|
-
{
|
|
319
|
-
name: "Select delivery model",
|
|
320
|
-
schema: {
|
|
321
|
-
model: "saas | iaas | paas | services-as-software"
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
],
|
|
325
|
-
options: {
|
|
326
|
-
saas: "Software-as-a-Service \u2014 hosted application, user accounts, subscription billing",
|
|
327
|
-
iaas: "Infrastructure-as-a-Service \u2014 cloud resources, APIs, usage-based billing",
|
|
328
|
-
paas: "Platform-as-a-Service \u2014 developer tools, SDKs, build on top of your platform",
|
|
329
|
-
"services-as-software": "Services-as-Software \u2014 AI agents do the work, delivered as a service"
|
|
330
|
-
}
|
|
331
|
-
},
|
|
332
|
-
on: {
|
|
333
|
-
MODEL_SELECTED: { target: "definition", actions: "setModel" },
|
|
334
|
-
SKIP: { target: "definition" }
|
|
335
|
-
}
|
|
336
|
-
},
|
|
337
|
-
definition: {
|
|
338
|
-
description: "Define what your service does",
|
|
339
|
-
meta: {
|
|
340
|
-
requiredArtifacts: {
|
|
341
|
-
services: { min: 1, description: "Service definitions" }
|
|
342
|
-
},
|
|
343
|
-
commands: [
|
|
344
|
-
{
|
|
345
|
-
name: "Create Service",
|
|
346
|
-
collection: "services",
|
|
347
|
-
schema: serviceSchema
|
|
348
|
-
}
|
|
349
|
-
]
|
|
350
|
-
},
|
|
351
|
-
on: {
|
|
352
|
-
ADVANCE: { guard: "hasServices", target: "pricing" },
|
|
353
|
-
SKIP: { target: "pricing" }
|
|
354
|
-
}
|
|
355
|
-
},
|
|
356
|
-
pricing: {
|
|
357
|
-
description: "Define pricing for your services",
|
|
358
|
-
meta: {
|
|
359
|
-
commands: [
|
|
360
|
-
{
|
|
361
|
-
name: "Set pricing on service",
|
|
362
|
-
collection: "services",
|
|
363
|
-
operation: "update",
|
|
364
|
-
schema: {
|
|
365
|
-
pricingModel: "subscription | usage | per-seat | per-agent | one-time",
|
|
366
|
-
price: "number: price in cents",
|
|
367
|
-
interval: "month | year (for subscription)",
|
|
368
|
-
tiers: [
|
|
369
|
-
{
|
|
370
|
-
name: "Tier name",
|
|
371
|
-
price: "number: price in cents",
|
|
372
|
-
features: ["What is included in this tier"]
|
|
373
|
-
}
|
|
374
|
-
]
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
]
|
|
378
|
-
},
|
|
379
|
-
on: {
|
|
380
|
-
PRICING_SET: { target: "packaging", actions: "setPricing" },
|
|
381
|
-
ADVANCE: { guard: "hasPricing", target: "packaging" },
|
|
382
|
-
SKIP: { target: "packaging" }
|
|
383
|
-
}
|
|
384
|
-
},
|
|
385
|
-
packaging: {
|
|
386
|
-
description: "Bundle services into products and configure Stripe",
|
|
387
|
-
meta: {
|
|
388
|
-
requiredArtifacts: {
|
|
389
|
-
products: { min: 1, description: "Products with Stripe Price" }
|
|
390
|
-
},
|
|
391
|
-
commands: [
|
|
392
|
-
{
|
|
393
|
-
name: "Create Product",
|
|
394
|
-
collection: "products",
|
|
395
|
-
schema: productSchema
|
|
396
|
-
}
|
|
397
|
-
]
|
|
398
|
-
},
|
|
399
|
-
on: {
|
|
400
|
-
ADVANCE: { guard: "hasProducts", target: "activation" },
|
|
401
|
-
SKIP: { target: "activation" }
|
|
402
|
-
}
|
|
403
|
-
},
|
|
404
|
-
activation: {
|
|
405
|
-
description: "Connect delivery infrastructure",
|
|
406
|
-
meta: {
|
|
407
|
-
commands: [
|
|
408
|
-
{
|
|
409
|
-
name: "Activate service delivery",
|
|
410
|
-
description: "Connect agents, workflows, or infrastructure to deliver the service",
|
|
411
|
-
schema: {
|
|
412
|
-
type: "agent | workflow | api | manual",
|
|
413
|
-
agent: "ID of agent that delivers this service (for services-as-software)",
|
|
414
|
-
workflow: "ID of workflow that executes delivery"
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
]
|
|
418
|
-
},
|
|
419
|
-
on: {
|
|
420
|
-
ACTIVATION_COMPLETE: { target: "complete" },
|
|
421
|
-
ADVANCE: { target: "complete" },
|
|
422
|
-
SKIP: { target: "complete" }
|
|
423
|
-
}
|
|
424
|
-
},
|
|
425
|
-
complete: {
|
|
426
|
-
type: "final",
|
|
427
|
-
description: "Services defined and activated"
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
});
|
|
431
|
-
|
|
432
|
-
// src/machines/site-builder.ts
|
|
433
|
-
import { setup as setup3, assign as assign3 } from "xstate";
|
|
434
|
-
import { pageSchema, domainSchema } from "startups.studio/schemas";
|
|
435
|
-
var siteBuilderMachine = setup3({
|
|
436
|
-
types: {
|
|
437
|
-
context: {},
|
|
438
|
-
events: {},
|
|
439
|
-
input: {}
|
|
440
|
-
},
|
|
441
|
-
guards: {
|
|
442
|
-
needsMessaging: ({ context }) => !context.hasStoryBrand,
|
|
443
|
-
needsServices: ({ context }) => context.hasStoryBrand && !context.hasServices,
|
|
444
|
-
hasPrerequisites: ({ context }) => context.hasStoryBrand && context.hasServices,
|
|
445
|
-
hasPages: ({ context }) => context.pageCount >= 1,
|
|
446
|
-
hasContent: ({ context }) => context.pagesWithContent >= context.pageCount && context.pageCount > 0,
|
|
447
|
-
hasDesign: ({ context }) => context.designApproved,
|
|
448
|
-
hasDomain: ({ context }) => context.domainCount >= 1
|
|
449
|
-
},
|
|
450
|
-
actions: {
|
|
451
|
-
updateArtifactCount: assign3(({ context, event }) => {
|
|
452
|
-
if (event.type !== "ARTIFACT_CREATED") return context;
|
|
453
|
-
if (event.collection === "pages") return { ...context, pageCount: event.count };
|
|
454
|
-
if (event.collection === "domains") return { ...context, domainCount: event.count };
|
|
455
|
-
return context;
|
|
456
|
-
}),
|
|
457
|
-
setContentComplete: assign3(({ event }) => ({
|
|
458
|
-
pagesWithContent: event.type === "CONTENT_COMPLETE" ? event.pagesWithContent : 0
|
|
459
|
-
})),
|
|
460
|
-
setDesignApproved: assign3({ designApproved: true }),
|
|
461
|
-
setPublished: assign3({ published: true })
|
|
462
|
-
}
|
|
463
|
-
}).createMachine({
|
|
464
|
-
id: "site-builder",
|
|
465
|
-
initial: "checkPrerequisites",
|
|
466
|
-
context: ({ input }) => ({
|
|
467
|
-
startupId: input.startupId,
|
|
468
|
-
hasStoryBrand: input.hasStoryBrand,
|
|
469
|
-
hasServices: input.hasServices,
|
|
470
|
-
pageCount: 0,
|
|
471
|
-
pagesWithContent: 0,
|
|
472
|
-
domainCount: 0,
|
|
473
|
-
designApproved: false,
|
|
474
|
-
published: false
|
|
475
|
-
}),
|
|
476
|
-
on: {
|
|
477
|
-
ARTIFACT_CREATED: { actions: "updateArtifactCount" }
|
|
478
|
-
},
|
|
479
|
-
states: {
|
|
480
|
-
checkPrerequisites: {
|
|
481
|
-
always: [
|
|
482
|
-
{ guard: "needsMessaging", target: "messaging" },
|
|
483
|
-
{ guard: "needsServices", target: "servicesPrereq" },
|
|
484
|
-
{ guard: "hasPrerequisites", target: "structure" }
|
|
485
|
-
]
|
|
486
|
-
},
|
|
487
|
-
messaging: {
|
|
488
|
-
description: "Prerequisite: Create StoryBrand messaging first",
|
|
489
|
-
meta: { prerequisite: true },
|
|
490
|
-
on: {
|
|
491
|
-
ADVANCE: { target: "servicesPrereq" }
|
|
492
|
-
}
|
|
493
|
-
},
|
|
494
|
-
servicesPrereq: {
|
|
495
|
-
description: "Prerequisite: Define services and pricing first",
|
|
496
|
-
meta: { prerequisite: true, invokedBuilder: "services-builder" },
|
|
497
|
-
on: {
|
|
498
|
-
ADVANCE: { target: "structure" }
|
|
499
|
-
}
|
|
500
|
-
},
|
|
501
|
-
structure: {
|
|
502
|
-
description: "Define your site pages and navigation",
|
|
503
|
-
meta: {
|
|
504
|
-
requiredArtifacts: {
|
|
505
|
-
pages: { min: 1, description: "Site pages" }
|
|
506
|
-
},
|
|
507
|
-
commands: [
|
|
508
|
-
{
|
|
509
|
-
name: "Create Page",
|
|
510
|
-
collection: "pages",
|
|
511
|
-
schema: pageSchema
|
|
512
|
-
},
|
|
513
|
-
{
|
|
514
|
-
name: "Assign Domain",
|
|
515
|
-
collection: "domains",
|
|
516
|
-
schema: domainSchema
|
|
517
|
-
}
|
|
518
|
-
]
|
|
519
|
-
},
|
|
520
|
-
on: {
|
|
521
|
-
ADVANCE: { guard: "hasPages", target: "content" },
|
|
522
|
-
SKIP: { target: "content" }
|
|
523
|
-
}
|
|
524
|
-
},
|
|
525
|
-
content: {
|
|
526
|
-
description: "Write content for each page using your StoryBrand and service definitions",
|
|
527
|
-
meta: {
|
|
528
|
-
commands: [
|
|
529
|
-
{
|
|
530
|
-
name: "Update Page Content",
|
|
531
|
-
collection: "pages",
|
|
532
|
-
operation: "update",
|
|
533
|
-
schema: {
|
|
534
|
-
content: "Page content \u2014 use StoryBrand for landing page, service details for pricing",
|
|
535
|
-
sections: [
|
|
536
|
-
{
|
|
537
|
-
type: "hero | features | pricing | testimonials | cta | faq",
|
|
538
|
-
content: "Section content in markdown"
|
|
539
|
-
}
|
|
540
|
-
]
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
]
|
|
544
|
-
},
|
|
545
|
-
on: {
|
|
546
|
-
CONTENT_COMPLETE: { actions: "setContentComplete" },
|
|
547
|
-
ADVANCE: { guard: "hasContent", target: "design" },
|
|
548
|
-
SKIP: { target: "design" }
|
|
549
|
-
}
|
|
550
|
-
},
|
|
551
|
-
design: {
|
|
552
|
-
description: "Style your site using mdxui components",
|
|
553
|
-
meta: {
|
|
554
|
-
commands: [
|
|
555
|
-
{
|
|
556
|
-
name: "Update Site Design",
|
|
557
|
-
collection: "sites",
|
|
558
|
-
operation: "update",
|
|
559
|
-
schema: {
|
|
560
|
-
theme: "Theme name or configuration",
|
|
561
|
-
layout: "default | minimal | bold | corporate",
|
|
562
|
-
colors: {
|
|
563
|
-
primary: "Hex color code",
|
|
564
|
-
secondary: "Hex color code",
|
|
565
|
-
accent: "Hex color code"
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
]
|
|
570
|
-
},
|
|
571
|
-
on: {
|
|
572
|
-
DESIGN_APPROVED: { actions: "setDesignApproved" },
|
|
573
|
-
ADVANCE: { target: "publish" },
|
|
574
|
-
SKIP: { target: "publish" }
|
|
575
|
-
}
|
|
576
|
-
},
|
|
577
|
-
publish: {
|
|
578
|
-
description: "Deploy your site to your domain",
|
|
579
|
-
meta: {
|
|
580
|
-
requiredArtifacts: {
|
|
581
|
-
domains: { min: 1, description: "Domain assigned" }
|
|
582
|
-
},
|
|
583
|
-
commands: [
|
|
584
|
-
{
|
|
585
|
-
name: "Publish Site",
|
|
586
|
-
command: "sites publish",
|
|
587
|
-
schema: {
|
|
588
|
-
startup: "Startup ID"
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
]
|
|
592
|
-
},
|
|
593
|
-
on: {
|
|
594
|
-
PUBLISHED: { target: "complete", actions: "setPublished" },
|
|
595
|
-
ADVANCE: { guard: "hasDomain", target: "complete" },
|
|
596
|
-
SKIP: { target: "complete" }
|
|
597
|
-
}
|
|
598
|
-
},
|
|
599
|
-
complete: {
|
|
600
|
-
type: "final",
|
|
601
|
-
description: "Site published and live"
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
});
|
|
605
|
-
|
|
606
|
-
// src/machines/sales-builder.ts
|
|
607
|
-
import { setup as setup4, assign as assign4 } from "xstate";
|
|
608
|
-
import {
|
|
609
|
-
campaignSchema,
|
|
610
|
-
sequenceSchema,
|
|
611
|
-
postSchema,
|
|
612
|
-
experimentSchema,
|
|
613
|
-
budgetSchema,
|
|
614
|
-
accountSchema
|
|
615
|
-
} from "startups.studio/schemas";
|
|
616
|
-
var salesBuilderMachine = setup4({
|
|
617
|
-
types: {
|
|
618
|
-
context: {},
|
|
619
|
-
events: {},
|
|
620
|
-
input: {}
|
|
621
|
-
},
|
|
622
|
-
guards: {
|
|
623
|
-
needsSite: ({ context }) => !context.hasSite || !context.hasDomain,
|
|
624
|
-
hasSite: ({ context }) => context.hasSite && context.hasDomain,
|
|
625
|
-
hasActiveChannel: ({ context }) => context.campaignCount >= 1 || context.sequenceCount >= 1 || context.postCount >= 1,
|
|
626
|
-
hasMetrics: ({ context }) => context.hasMetrics
|
|
627
|
-
},
|
|
628
|
-
actions: {
|
|
629
|
-
updateArtifactCount: assign4(({ context, event }) => {
|
|
630
|
-
if (event.type !== "ARTIFACT_CREATED") return context;
|
|
631
|
-
if (event.collection === "campaigns") return { ...context, campaignCount: event.count };
|
|
632
|
-
if (event.collection === "sequences") return { ...context, sequenceCount: event.count };
|
|
633
|
-
if (event.collection === "posts") return { ...context, postCount: event.count };
|
|
634
|
-
return context;
|
|
635
|
-
}),
|
|
636
|
-
setExperiment: assign4(({ event }) => ({
|
|
637
|
-
hasExperiment: true,
|
|
638
|
-
experimentId: event.type === "EXPERIMENT_CREATED" ? event.experimentId : void 0
|
|
639
|
-
})),
|
|
640
|
-
setMetrics: assign4({ hasMetrics: true })
|
|
641
|
-
}
|
|
642
|
-
}).createMachine({
|
|
643
|
-
id: "sales-builder",
|
|
644
|
-
initial: "checkPrerequisites",
|
|
645
|
-
context: ({ input }) => ({
|
|
646
|
-
startupId: input.startupId,
|
|
647
|
-
hasSite: input.hasSite,
|
|
648
|
-
hasDomain: input.hasDomain,
|
|
649
|
-
channels: {
|
|
650
|
-
paidAds: { ready: false, reason: "Requires payment method and approved budget" },
|
|
651
|
-
coldOutbound: { ready: false, reason: "Requires email-configured domain and contacts" },
|
|
652
|
-
contentSocial: { ready: false, reason: "Requires connected account for platform" },
|
|
653
|
-
blog: { ready: true }
|
|
654
|
-
},
|
|
655
|
-
campaignCount: 0,
|
|
656
|
-
sequenceCount: 0,
|
|
657
|
-
postCount: 0,
|
|
658
|
-
hasExperiment: false,
|
|
659
|
-
hasMetrics: false
|
|
660
|
-
}),
|
|
661
|
-
on: {
|
|
662
|
-
ARTIFACT_CREATED: { actions: "updateArtifactCount" }
|
|
663
|
-
},
|
|
664
|
-
states: {
|
|
665
|
-
checkPrerequisites: {
|
|
666
|
-
always: [
|
|
667
|
-
{ guard: "needsSite", target: "sitePrereq" },
|
|
668
|
-
{ guard: "hasSite", target: "launch" }
|
|
669
|
-
]
|
|
670
|
-
},
|
|
671
|
-
sitePrereq: {
|
|
672
|
-
description: "Prerequisite: Build and publish your site first",
|
|
673
|
-
meta: { prerequisite: true, invokedBuilder: "site-builder" },
|
|
674
|
-
on: {
|
|
675
|
-
ADVANCE: { target: "launch" }
|
|
676
|
-
}
|
|
677
|
-
},
|
|
678
|
-
launch: {
|
|
679
|
-
description: "Drive traffic to test your hypothesis",
|
|
680
|
-
meta: {
|
|
681
|
-
requiredArtifacts: {
|
|
682
|
-
_any: {
|
|
683
|
-
min: 1,
|
|
684
|
-
description: "At least one active campaign, sequence, or post",
|
|
685
|
-
collections: ["campaigns", "sequences", "posts"]
|
|
686
|
-
}
|
|
687
|
-
},
|
|
688
|
-
channelCommands: {
|
|
689
|
-
paidAds: {
|
|
690
|
-
prerequisiteLabel: "Requires: payment method, approved budget",
|
|
691
|
-
prerequisiteCommands: [
|
|
692
|
-
{
|
|
693
|
-
name: "Create Budget",
|
|
694
|
-
collection: "budgets",
|
|
695
|
-
schema: budgetSchema
|
|
696
|
-
}
|
|
697
|
-
],
|
|
698
|
-
command: {
|
|
699
|
-
name: "Create Campaign",
|
|
700
|
-
collection: "campaigns",
|
|
701
|
-
schema: campaignSchema
|
|
702
|
-
}
|
|
703
|
-
},
|
|
704
|
-
coldOutbound: {
|
|
705
|
-
prerequisiteLabel: "Requires: email-configured domain, contacts",
|
|
706
|
-
prerequisiteCommands: [
|
|
707
|
-
{
|
|
708
|
-
name: "Configure email on domain",
|
|
709
|
-
collection: "domains",
|
|
710
|
-
operation: "update",
|
|
711
|
-
schema: { email: "boolean: true" }
|
|
712
|
-
},
|
|
713
|
-
{
|
|
714
|
-
name: "Search contacts",
|
|
715
|
-
command: "contacts search",
|
|
716
|
-
api: "api.sb",
|
|
717
|
-
schema: {
|
|
718
|
-
industry: "NAICS code",
|
|
719
|
-
role: "Job title to target",
|
|
720
|
-
companySize: "number: employee count range",
|
|
721
|
-
limit: "number: max results"
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
],
|
|
725
|
-
command: {
|
|
726
|
-
name: "Create Sequence",
|
|
727
|
-
collection: "sequences",
|
|
728
|
-
schema: sequenceSchema
|
|
729
|
-
}
|
|
730
|
-
},
|
|
731
|
-
contentSocial: {
|
|
732
|
-
prerequisiteLabel: "Requires: connected account for platform",
|
|
733
|
-
prerequisiteCommands: [
|
|
734
|
-
{
|
|
735
|
-
name: "Connect account",
|
|
736
|
-
collection: "accounts",
|
|
737
|
-
schema: accountSchema
|
|
738
|
-
}
|
|
739
|
-
],
|
|
740
|
-
command: {
|
|
741
|
-
name: "Create Post",
|
|
742
|
-
collection: "posts",
|
|
743
|
-
schema: postSchema
|
|
744
|
-
}
|
|
745
|
-
},
|
|
746
|
-
blog: {
|
|
747
|
-
prerequisiteLabel: "Ready (published on your domain)",
|
|
748
|
-
command: {
|
|
749
|
-
name: "Create Blog Post",
|
|
750
|
-
collection: "posts",
|
|
751
|
-
schema: postSchema
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
},
|
|
756
|
-
on: {
|
|
757
|
-
ADVANCE: { guard: "hasActiveChannel", target: "measure" },
|
|
758
|
-
SKIP: { target: "measure" }
|
|
759
|
-
}
|
|
760
|
-
},
|
|
761
|
-
measure: {
|
|
762
|
-
description: "Collect data and track results against your OKRs",
|
|
763
|
-
meta: {
|
|
764
|
-
commands: [
|
|
765
|
-
{
|
|
766
|
-
name: "Create Experiment",
|
|
767
|
-
collection: "experiments",
|
|
768
|
-
schema: experimentSchema
|
|
769
|
-
},
|
|
770
|
-
{
|
|
771
|
-
name: "Record Metrics",
|
|
772
|
-
collection: "experiments",
|
|
773
|
-
operation: "update",
|
|
774
|
-
schema: {
|
|
775
|
-
metrics: {
|
|
776
|
-
visitors: "number: unique visitors",
|
|
777
|
-
signups: "number: signups or leads captured",
|
|
778
|
-
conversionRate: "number: percentage",
|
|
779
|
-
revenue: "number: dollars"
|
|
780
|
-
},
|
|
781
|
-
evidence: "What you observed"
|
|
782
|
-
}
|
|
783
|
-
},
|
|
784
|
-
{
|
|
785
|
-
name: "View current metrics",
|
|
786
|
-
command: "status",
|
|
787
|
-
description: "See current OKR progress and health scores"
|
|
788
|
-
}
|
|
789
|
-
]
|
|
790
|
-
},
|
|
791
|
-
on: {
|
|
792
|
-
METRICS_COLLECTED: { target: "complete", actions: "setMetrics" },
|
|
793
|
-
ADVANCE: { guard: "hasMetrics", target: "complete" },
|
|
794
|
-
SKIP: { target: "complete" }
|
|
795
|
-
}
|
|
796
|
-
},
|
|
797
|
-
complete: {
|
|
798
|
-
type: "final",
|
|
799
|
-
description: "Sales cycle complete \u2014 review results against OKRs to determine next steps"
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
});
|
|
803
|
-
|
|
804
|
-
// src/reconcile.ts
|
|
805
|
-
import { apiRequest } from "startups.studio";
|
|
806
|
-
var COLLECTION_MAP = [
|
|
807
|
-
{ collection: "icps", key: "icpCount" },
|
|
808
|
-
{ collection: "competitors", key: "competitorCount" },
|
|
809
|
-
{ collection: "lean-canvases", key: "leanCanvasCount" },
|
|
810
|
-
{ collection: "hypotheses", key: "hypothesisCount" },
|
|
811
|
-
{ collection: "differentiators", key: "differentiatorCount" },
|
|
812
|
-
{ collection: "story-brands", key: "storyBrandCount" },
|
|
813
|
-
{ collection: "services", key: "serviceCount" },
|
|
814
|
-
{ collection: "agents", key: "agentCount" },
|
|
815
|
-
{ collection: "domains", key: "domainCount" },
|
|
816
|
-
{ collection: "pages", key: "pageCount" }
|
|
817
|
-
];
|
|
818
|
-
async function fetchArtifactCounts(startupId, apiBaseUrl, apiKey) {
|
|
819
|
-
const counts = {
|
|
820
|
-
icpCount: 0,
|
|
821
|
-
competitorCount: 0,
|
|
822
|
-
leanCanvasCount: 0,
|
|
823
|
-
hypothesisCount: 0,
|
|
824
|
-
positioningSet: false,
|
|
825
|
-
differentiatorCount: 0,
|
|
826
|
-
storyBrandCount: 0,
|
|
827
|
-
serviceCount: 0,
|
|
828
|
-
agentCount: 0,
|
|
829
|
-
domainCount: 0,
|
|
830
|
-
pageCount: 0
|
|
831
|
-
};
|
|
832
|
-
const results = await Promise.allSettled(
|
|
833
|
-
COLLECTION_MAP.map(async ({ collection, key }) => {
|
|
834
|
-
const path = `/api/${collection}?where[startup][equals]=${encodeURIComponent(startupId)}&limit=0`;
|
|
835
|
-
const result = await apiRequest(apiBaseUrl, path, { apiKey });
|
|
836
|
-
if (result.ok) {
|
|
837
|
-
return { key, count: result.data.totalDocs };
|
|
838
|
-
}
|
|
839
|
-
return { key, count: 0 };
|
|
840
|
-
})
|
|
841
|
-
);
|
|
842
|
-
for (const result of results) {
|
|
843
|
-
if (result.status === "fulfilled") {
|
|
844
|
-
const { key, count } = result.value;
|
|
845
|
-
counts[key] = count;
|
|
846
|
-
}
|
|
847
|
-
}
|
|
848
|
-
const startupResult = await apiRequest(
|
|
849
|
-
apiBaseUrl,
|
|
850
|
-
`/api/startups/${encodeURIComponent(startupId)}?depth=0`,
|
|
851
|
-
{ apiKey }
|
|
852
|
-
);
|
|
853
|
-
if (startupResult.ok && startupResult.data.positioning) {
|
|
854
|
-
counts.positioningSet = true;
|
|
855
|
-
}
|
|
856
|
-
return counts;
|
|
857
|
-
}
|
|
858
|
-
function determineCurrentState(counts) {
|
|
859
|
-
const transitions = [
|
|
860
|
-
{
|
|
861
|
-
from: "research",
|
|
862
|
-
to: "canvas",
|
|
863
|
-
guard: (c) => c.icpCount >= 1 && c.competitorCount >= 1
|
|
864
|
-
},
|
|
865
|
-
{
|
|
866
|
-
from: "canvas",
|
|
867
|
-
to: "positioning",
|
|
868
|
-
guard: (c) => c.leanCanvasCount >= 1 && c.hypothesisCount >= 1
|
|
869
|
-
},
|
|
870
|
-
{
|
|
871
|
-
from: "positioning",
|
|
872
|
-
to: "messaging",
|
|
873
|
-
guard: (c) => c.positioningSet && c.differentiatorCount >= 1
|
|
874
|
-
},
|
|
875
|
-
{
|
|
876
|
-
from: "messaging",
|
|
877
|
-
to: "services",
|
|
878
|
-
guard: (c) => c.storyBrandCount >= 1
|
|
879
|
-
},
|
|
880
|
-
{
|
|
881
|
-
from: "services",
|
|
882
|
-
to: "team",
|
|
883
|
-
guard: (c) => c.serviceCount >= 1
|
|
884
|
-
},
|
|
885
|
-
{
|
|
886
|
-
from: "team",
|
|
887
|
-
to: "site",
|
|
888
|
-
guard: (c) => c.agentCount >= 1
|
|
889
|
-
},
|
|
890
|
-
{
|
|
891
|
-
from: "site",
|
|
892
|
-
to: "sales",
|
|
893
|
-
guard: (c) => c.domainCount >= 1 && c.pageCount >= 1
|
|
894
|
-
}
|
|
895
|
-
];
|
|
896
|
-
let current = "research";
|
|
897
|
-
for (const t of transitions) {
|
|
898
|
-
if (t.from !== current) continue;
|
|
899
|
-
if (t.guard(counts)) {
|
|
900
|
-
current = t.to;
|
|
901
|
-
} else {
|
|
902
|
-
break;
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
return current;
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
// src/runner.ts
|
|
909
|
-
var STATE_ORDER = [
|
|
910
|
-
{ key: "research", label: "Research" },
|
|
911
|
-
{ key: "canvas", label: "Canvas" },
|
|
912
|
-
{ key: "positioning", label: "Positioning" },
|
|
913
|
-
{ key: "messaging", label: "Messaging" },
|
|
914
|
-
{ key: "services", label: "Services" },
|
|
915
|
-
{ key: "team", label: "Team" },
|
|
916
|
-
{ key: "site", label: "Site" },
|
|
917
|
-
{ key: "sales", label: "Sales" },
|
|
918
|
-
{ key: "complete", label: "Complete" }
|
|
919
|
-
];
|
|
920
|
-
async function reconcileState(startupId, apiBaseUrl, apiKey) {
|
|
921
|
-
const counts = await fetchArtifactCounts(startupId, apiBaseUrl, apiKey);
|
|
922
|
-
const currentState = determineCurrentState(counts);
|
|
923
|
-
return { currentState, counts };
|
|
924
|
-
}
|
|
925
|
-
function getProgress(currentState) {
|
|
926
|
-
let found = false;
|
|
927
|
-
return STATE_ORDER.map(({ key, label }) => {
|
|
928
|
-
if (key === currentState) {
|
|
929
|
-
found = true;
|
|
930
|
-
return { state: key, label, status: "current" };
|
|
931
|
-
}
|
|
932
|
-
if (!found) {
|
|
933
|
-
return { state: key, label, status: "complete" };
|
|
934
|
-
}
|
|
935
|
-
return { state: key, label, status: "pending" };
|
|
936
|
-
});
|
|
937
|
-
}
|
|
938
|
-
function getNextCommands(currentState, startupId) {
|
|
939
|
-
const stateNode = startupBuilderMachine.config.states?.[currentState];
|
|
940
|
-
if (!stateNode) return [];
|
|
941
|
-
const meta = stateNode.meta;
|
|
942
|
-
if (!meta) return [];
|
|
943
|
-
const commands = meta.commands;
|
|
944
|
-
if (!commands || !Array.isArray(commands)) return [];
|
|
945
|
-
return commands.map((cmd) => {
|
|
946
|
-
const operation = cmd.operation || "create";
|
|
947
|
-
const collection = cmd.collection;
|
|
948
|
-
let cliCommand;
|
|
949
|
-
if (cmd.command) {
|
|
950
|
-
cliCommand = `startups.studio ${cmd.command} --startup ${startupId}`;
|
|
951
|
-
} else if (operation === "update") {
|
|
952
|
-
cliCommand = `startups.studio ${collection} update ${startupId}`;
|
|
953
|
-
} else {
|
|
954
|
-
cliCommand = `startups.studio ${collection} create --startup ${startupId}`;
|
|
955
|
-
}
|
|
956
|
-
const description = stateNode.description || "";
|
|
957
|
-
return {
|
|
958
|
-
name: cmd.name,
|
|
959
|
-
collection,
|
|
960
|
-
operation,
|
|
961
|
-
command: cliCommand,
|
|
962
|
-
schema: cmd.schema,
|
|
963
|
-
description
|
|
964
|
-
};
|
|
965
|
-
});
|
|
966
|
-
}
|
|
967
|
-
function getRequiredArtifacts(currentState, counts) {
|
|
968
|
-
const stateNode = startupBuilderMachine.config.states?.[currentState];
|
|
969
|
-
if (!stateNode) return [];
|
|
970
|
-
const meta = stateNode.meta;
|
|
971
|
-
if (!meta?.requiredArtifacts) return [];
|
|
972
|
-
const required = meta.requiredArtifacts;
|
|
973
|
-
const countMap = {
|
|
974
|
-
icps: counts.icpCount,
|
|
975
|
-
competitors: counts.competitorCount,
|
|
976
|
-
"lean-canvases": counts.leanCanvasCount,
|
|
977
|
-
hypotheses: counts.hypothesisCount,
|
|
978
|
-
differentiators: counts.differentiatorCount,
|
|
979
|
-
"story-brands": counts.storyBrandCount,
|
|
980
|
-
services: counts.serviceCount,
|
|
981
|
-
agents: counts.agentCount,
|
|
982
|
-
domains: counts.domainCount,
|
|
983
|
-
pages: counts.pageCount,
|
|
984
|
-
positioning: counts.positioningSet ? 1 : 0
|
|
985
|
-
};
|
|
986
|
-
return Object.entries(required).map(([collection, spec]) => ({
|
|
987
|
-
collection,
|
|
988
|
-
description: spec.description,
|
|
989
|
-
required: spec.min,
|
|
990
|
-
current: countMap[collection] ?? 0
|
|
991
|
-
}));
|
|
992
|
-
}
|
|
993
|
-
|
|
994
|
-
export {
|
|
995
|
-
startupBuilderMachine,
|
|
996
|
-
servicesBuilderMachine,
|
|
997
|
-
siteBuilderMachine,
|
|
998
|
-
salesBuilderMachine,
|
|
999
|
-
fetchArtifactCounts,
|
|
1000
|
-
reconcileState,
|
|
1001
|
-
getProgress,
|
|
1002
|
-
getNextCommands,
|
|
1003
|
-
getRequiredArtifacts
|
|
1004
|
-
};
|