studioflow 0.1.2 → 0.1.3

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/README.md CHANGED
@@ -1,6 +1,10 @@
1
- # StudioFlow CLI
1
+ # StudioFlow
2
2
 
3
- StudioFlow turns natural-language demo intent into deterministic UI run artifacts and executes them through Screen Studio.
3
+ StudioFlow turns a plain-language demo request into a deterministic recorded run.
4
+
5
+ This package includes:
6
+ - a CLI runtime (`studioflow`) for deterministic artifact execution
7
+ - bundled agent skills for Codex and Claude (`studioflow-investigate`, `studioflow-cli`)
4
8
 
5
9
  ## Requirements
6
10
 
@@ -22,11 +26,10 @@ npm install -g studioflow
22
26
  studioflow setup
23
27
  ```
24
28
 
25
- Bundled skills include:
26
- - `studioflow-investigate` (intent -> deterministic artifacts, including `artifacts/flow.json`)
27
- - `studioflow-cli` (artifact execution workflow)
29
+ ## Default Workflow (Agent-First)
28
30
 
29
- ## Quick Start
31
+ You should not need to manually invoke multiple skills or manually run CLI commands for a normal demo request.
32
+ Open Codex or Claude in your project and describe the demo you want.
30
33
 
31
34
  1. Start your agent from the project root.
32
35
 
@@ -46,37 +49,31 @@ claude
46
49
 
47
50
  If your launcher command differs, start your usual Codex or Claude session in this repo root.
48
51
 
49
- 2. Trigger `studioflow-investigate` to generate `artifacts/flow.json`.
52
+ 2. Ask for the demo in plain language.
50
53
 
51
- Paste this prompt:
54
+ Example:
52
55
 
53
56
  ```text
54
- Use StudioFlow skill studioflow-investigate.
55
- Intent: "Record a demo for onboarding and billing."
56
- Generate artifacts/flow.json for this repo.
57
- If intent details are missing, ask concise follow-up questions before authoring the flow.
57
+ Record a demo for onboarding and billing.
58
58
  ```
59
59
 
60
- `studioflow-investigate` automatically collects project context artifacts before creating the flow.
61
-
62
- 3. Validate and run:
63
-
64
- ```bash
65
- studioflow validate --flow artifacts/flow.json
66
- studioflow demo --flow artifacts/flow.json --intent "onboarding and billing demo"
67
- ```
60
+ 3. StudioFlow skills + CLI handle the rest:
61
+ - investigate the codebase
62
+ - generate deterministic artifacts (`artifacts/flow.json`, related context artifacts)
63
+ - validate the flow
64
+ - execute recording through the CLI runtime
68
65
 
69
66
  `run`/`demo` automatically performs Screen Studio preflight checks. Use manual prep only for troubleshooting.
70
67
  Headed runs auto-open a maximized browser window for cleaner recording composition.
71
68
  Runs do not auto-export by default; include `recorder_export` in flow steps only when export is explicitly needed.
72
69
 
73
- ## Optional: Trigger Runtime Skill In Agent
70
+ ## Advanced Manual Mode (Optional)
74
71
 
75
- If you want the agent to drive execution too, use:
72
+ If you want to run the runtime yourself:
76
73
 
77
- ```text
78
- Use StudioFlow skill studioflow-cli.
79
- Validate artifacts/flow.json, run the demo, and report run artifact paths.
74
+ ```bash
75
+ studioflow validate --flow artifacts/flow.json
76
+ studioflow demo --flow artifacts/flow.json --intent "onboarding and billing demo"
80
77
  ```
81
78
 
82
79
  ## Documentation
package/dist/index.js CHANGED
@@ -113,17 +113,17 @@ var require_visit = __commonJS({
113
113
  visit.BREAK = BREAK;
114
114
  visit.SKIP = SKIP;
115
115
  visit.REMOVE = REMOVE;
116
- function visit_(key, node, visitor, path12) {
117
- const ctrl = callVisitor(key, node, visitor, path12);
116
+ function visit_(key, node, visitor, path13) {
117
+ const ctrl = callVisitor(key, node, visitor, path13);
118
118
  if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
119
- replaceNode(key, path12, ctrl);
120
- return visit_(key, ctrl, visitor, path12);
119
+ replaceNode(key, path13, ctrl);
120
+ return visit_(key, ctrl, visitor, path13);
121
121
  }
122
122
  if (typeof ctrl !== "symbol") {
123
123
  if (identity.isCollection(node)) {
124
- path12 = Object.freeze(path12.concat(node));
124
+ path13 = Object.freeze(path13.concat(node));
125
125
  for (let i = 0; i < node.items.length; ++i) {
126
- const ci = visit_(i, node.items[i], visitor, path12);
126
+ const ci = visit_(i, node.items[i], visitor, path13);
127
127
  if (typeof ci === "number")
128
128
  i = ci - 1;
129
129
  else if (ci === BREAK)
@@ -134,13 +134,13 @@ var require_visit = __commonJS({
134
134
  }
135
135
  }
136
136
  } else if (identity.isPair(node)) {
137
- path12 = Object.freeze(path12.concat(node));
138
- const ck = visit_("key", node.key, visitor, path12);
137
+ path13 = Object.freeze(path13.concat(node));
138
+ const ck = visit_("key", node.key, visitor, path13);
139
139
  if (ck === BREAK)
140
140
  return BREAK;
141
141
  else if (ck === REMOVE)
142
142
  node.key = null;
143
- const cv = visit_("value", node.value, visitor, path12);
143
+ const cv = visit_("value", node.value, visitor, path13);
144
144
  if (cv === BREAK)
145
145
  return BREAK;
146
146
  else if (cv === REMOVE)
@@ -161,17 +161,17 @@ var require_visit = __commonJS({
161
161
  visitAsync.BREAK = BREAK;
162
162
  visitAsync.SKIP = SKIP;
163
163
  visitAsync.REMOVE = REMOVE;
164
- async function visitAsync_(key, node, visitor, path12) {
165
- const ctrl = await callVisitor(key, node, visitor, path12);
164
+ async function visitAsync_(key, node, visitor, path13) {
165
+ const ctrl = await callVisitor(key, node, visitor, path13);
166
166
  if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
167
- replaceNode(key, path12, ctrl);
168
- return visitAsync_(key, ctrl, visitor, path12);
167
+ replaceNode(key, path13, ctrl);
168
+ return visitAsync_(key, ctrl, visitor, path13);
169
169
  }
170
170
  if (typeof ctrl !== "symbol") {
171
171
  if (identity.isCollection(node)) {
172
- path12 = Object.freeze(path12.concat(node));
172
+ path13 = Object.freeze(path13.concat(node));
173
173
  for (let i = 0; i < node.items.length; ++i) {
174
- const ci = await visitAsync_(i, node.items[i], visitor, path12);
174
+ const ci = await visitAsync_(i, node.items[i], visitor, path13);
175
175
  if (typeof ci === "number")
176
176
  i = ci - 1;
177
177
  else if (ci === BREAK)
@@ -182,13 +182,13 @@ var require_visit = __commonJS({
182
182
  }
183
183
  }
184
184
  } else if (identity.isPair(node)) {
185
- path12 = Object.freeze(path12.concat(node));
186
- const ck = await visitAsync_("key", node.key, visitor, path12);
185
+ path13 = Object.freeze(path13.concat(node));
186
+ const ck = await visitAsync_("key", node.key, visitor, path13);
187
187
  if (ck === BREAK)
188
188
  return BREAK;
189
189
  else if (ck === REMOVE)
190
190
  node.key = null;
191
- const cv = await visitAsync_("value", node.value, visitor, path12);
191
+ const cv = await visitAsync_("value", node.value, visitor, path13);
192
192
  if (cv === BREAK)
193
193
  return BREAK;
194
194
  else if (cv === REMOVE)
@@ -215,23 +215,23 @@ var require_visit = __commonJS({
215
215
  }
216
216
  return visitor;
217
217
  }
218
- function callVisitor(key, node, visitor, path12) {
218
+ function callVisitor(key, node, visitor, path13) {
219
219
  if (typeof visitor === "function")
220
- return visitor(key, node, path12);
220
+ return visitor(key, node, path13);
221
221
  if (identity.isMap(node))
222
- return visitor.Map?.(key, node, path12);
222
+ return visitor.Map?.(key, node, path13);
223
223
  if (identity.isSeq(node))
224
- return visitor.Seq?.(key, node, path12);
224
+ return visitor.Seq?.(key, node, path13);
225
225
  if (identity.isPair(node))
226
- return visitor.Pair?.(key, node, path12);
226
+ return visitor.Pair?.(key, node, path13);
227
227
  if (identity.isScalar(node))
228
- return visitor.Scalar?.(key, node, path12);
228
+ return visitor.Scalar?.(key, node, path13);
229
229
  if (identity.isAlias(node))
230
- return visitor.Alias?.(key, node, path12);
230
+ return visitor.Alias?.(key, node, path13);
231
231
  return void 0;
232
232
  }
233
- function replaceNode(key, path12, node) {
234
- const parent = path12[path12.length - 1];
233
+ function replaceNode(key, path13, node) {
234
+ const parent = path13[path13.length - 1];
235
235
  if (identity.isCollection(parent)) {
236
236
  parent.items[key] = node;
237
237
  } else if (identity.isPair(parent)) {
@@ -839,10 +839,10 @@ var require_Collection = __commonJS({
839
839
  var createNode = require_createNode();
840
840
  var identity = require_identity();
841
841
  var Node = require_Node();
842
- function collectionFromPath(schema, path12, value) {
842
+ function collectionFromPath(schema, path13, value) {
843
843
  let v = value;
844
- for (let i = path12.length - 1; i >= 0; --i) {
845
- const k = path12[i];
844
+ for (let i = path13.length - 1; i >= 0; --i) {
845
+ const k = path13[i];
846
846
  if (typeof k === "number" && Number.isInteger(k) && k >= 0) {
847
847
  const a = [];
848
848
  a[k] = v;
@@ -861,7 +861,7 @@ var require_Collection = __commonJS({
861
861
  sourceObjects: /* @__PURE__ */ new Map()
862
862
  });
863
863
  }
864
- var isEmptyPath = (path12) => path12 == null || typeof path12 === "object" && !!path12[Symbol.iterator]().next().done;
864
+ var isEmptyPath = (path13) => path13 == null || typeof path13 === "object" && !!path13[Symbol.iterator]().next().done;
865
865
  var Collection = class extends Node.NodeBase {
866
866
  constructor(type, schema) {
867
867
  super(type);
@@ -891,11 +891,11 @@ var require_Collection = __commonJS({
891
891
  * be a Pair instance or a `{ key, value }` object, which may not have a key
892
892
  * that already exists in the map.
893
893
  */
894
- addIn(path12, value) {
895
- if (isEmptyPath(path12))
894
+ addIn(path13, value) {
895
+ if (isEmptyPath(path13))
896
896
  this.add(value);
897
897
  else {
898
- const [key, ...rest] = path12;
898
+ const [key, ...rest] = path13;
899
899
  const node = this.get(key, true);
900
900
  if (identity.isCollection(node))
901
901
  node.addIn(rest, value);
@@ -909,8 +909,8 @@ var require_Collection = __commonJS({
909
909
  * Removes a value from the collection.
910
910
  * @returns `true` if the item was found and removed.
911
911
  */
912
- deleteIn(path12) {
913
- const [key, ...rest] = path12;
912
+ deleteIn(path13) {
913
+ const [key, ...rest] = path13;
914
914
  if (rest.length === 0)
915
915
  return this.delete(key);
916
916
  const node = this.get(key, true);
@@ -924,8 +924,8 @@ var require_Collection = __commonJS({
924
924
  * scalar values from their surrounding node; to disable set `keepScalar` to
925
925
  * `true` (collections are always returned intact).
926
926
  */
927
- getIn(path12, keepScalar) {
928
- const [key, ...rest] = path12;
927
+ getIn(path13, keepScalar) {
928
+ const [key, ...rest] = path13;
929
929
  const node = this.get(key, true);
930
930
  if (rest.length === 0)
931
931
  return !keepScalar && identity.isScalar(node) ? node.value : node;
@@ -943,8 +943,8 @@ var require_Collection = __commonJS({
943
943
  /**
944
944
  * Checks if the collection includes a value with the key `key`.
945
945
  */
946
- hasIn(path12) {
947
- const [key, ...rest] = path12;
946
+ hasIn(path13) {
947
+ const [key, ...rest] = path13;
948
948
  if (rest.length === 0)
949
949
  return this.has(key);
950
950
  const node = this.get(key, true);
@@ -954,8 +954,8 @@ var require_Collection = __commonJS({
954
954
  * Sets a value in this collection. For `!!set`, `value` needs to be a
955
955
  * boolean to add/remove the item from the set.
956
956
  */
957
- setIn(path12, value) {
958
- const [key, ...rest] = path12;
957
+ setIn(path13, value) {
958
+ const [key, ...rest] = path13;
959
959
  if (rest.length === 0) {
960
960
  this.set(key, value);
961
961
  } else {
@@ -3459,9 +3459,9 @@ var require_Document = __commonJS({
3459
3459
  this.contents.add(value);
3460
3460
  }
3461
3461
  /** Adds a value to the document. */
3462
- addIn(path12, value) {
3462
+ addIn(path13, value) {
3463
3463
  if (assertCollection(this.contents))
3464
- this.contents.addIn(path12, value);
3464
+ this.contents.addIn(path13, value);
3465
3465
  }
3466
3466
  /**
3467
3467
  * Create a new `Alias` node, ensuring that the target `node` has the required anchor.
@@ -3536,14 +3536,14 @@ var require_Document = __commonJS({
3536
3536
  * Removes a value from the document.
3537
3537
  * @returns `true` if the item was found and removed.
3538
3538
  */
3539
- deleteIn(path12) {
3540
- if (Collection.isEmptyPath(path12)) {
3539
+ deleteIn(path13) {
3540
+ if (Collection.isEmptyPath(path13)) {
3541
3541
  if (this.contents == null)
3542
3542
  return false;
3543
3543
  this.contents = null;
3544
3544
  return true;
3545
3545
  }
3546
- return assertCollection(this.contents) ? this.contents.deleteIn(path12) : false;
3546
+ return assertCollection(this.contents) ? this.contents.deleteIn(path13) : false;
3547
3547
  }
3548
3548
  /**
3549
3549
  * Returns item at `key`, or `undefined` if not found. By default unwraps
@@ -3558,10 +3558,10 @@ var require_Document = __commonJS({
3558
3558
  * scalar values from their surrounding node; to disable set `keepScalar` to
3559
3559
  * `true` (collections are always returned intact).
3560
3560
  */
3561
- getIn(path12, keepScalar) {
3562
- if (Collection.isEmptyPath(path12))
3561
+ getIn(path13, keepScalar) {
3562
+ if (Collection.isEmptyPath(path13))
3563
3563
  return !keepScalar && identity.isScalar(this.contents) ? this.contents.value : this.contents;
3564
- return identity.isCollection(this.contents) ? this.contents.getIn(path12, keepScalar) : void 0;
3564
+ return identity.isCollection(this.contents) ? this.contents.getIn(path13, keepScalar) : void 0;
3565
3565
  }
3566
3566
  /**
3567
3567
  * Checks if the document includes a value with the key `key`.
@@ -3572,10 +3572,10 @@ var require_Document = __commonJS({
3572
3572
  /**
3573
3573
  * Checks if the document includes a value at `path`.
3574
3574
  */
3575
- hasIn(path12) {
3576
- if (Collection.isEmptyPath(path12))
3575
+ hasIn(path13) {
3576
+ if (Collection.isEmptyPath(path13))
3577
3577
  return this.contents !== void 0;
3578
- return identity.isCollection(this.contents) ? this.contents.hasIn(path12) : false;
3578
+ return identity.isCollection(this.contents) ? this.contents.hasIn(path13) : false;
3579
3579
  }
3580
3580
  /**
3581
3581
  * Sets a value in this document. For `!!set`, `value` needs to be a
@@ -3592,13 +3592,13 @@ var require_Document = __commonJS({
3592
3592
  * Sets a value in this document. For `!!set`, `value` needs to be a
3593
3593
  * boolean to add/remove the item from the set.
3594
3594
  */
3595
- setIn(path12, value) {
3596
- if (Collection.isEmptyPath(path12)) {
3595
+ setIn(path13, value) {
3596
+ if (Collection.isEmptyPath(path13)) {
3597
3597
  this.contents = value;
3598
3598
  } else if (this.contents == null) {
3599
- this.contents = Collection.collectionFromPath(this.schema, Array.from(path12), value);
3599
+ this.contents = Collection.collectionFromPath(this.schema, Array.from(path13), value);
3600
3600
  } else if (assertCollection(this.contents)) {
3601
- this.contents.setIn(path12, value);
3601
+ this.contents.setIn(path13, value);
3602
3602
  }
3603
3603
  }
3604
3604
  /**
@@ -5550,9 +5550,9 @@ var require_cst_visit = __commonJS({
5550
5550
  visit.BREAK = BREAK;
5551
5551
  visit.SKIP = SKIP;
5552
5552
  visit.REMOVE = REMOVE;
5553
- visit.itemAtPath = (cst, path12) => {
5553
+ visit.itemAtPath = (cst, path13) => {
5554
5554
  let item = cst;
5555
- for (const [field, index] of path12) {
5555
+ for (const [field, index] of path13) {
5556
5556
  const tok = item?.[field];
5557
5557
  if (tok && "items" in tok) {
5558
5558
  item = tok.items[index];
@@ -5561,23 +5561,23 @@ var require_cst_visit = __commonJS({
5561
5561
  }
5562
5562
  return item;
5563
5563
  };
5564
- visit.parentCollection = (cst, path12) => {
5565
- const parent = visit.itemAtPath(cst, path12.slice(0, -1));
5566
- const field = path12[path12.length - 1][0];
5564
+ visit.parentCollection = (cst, path13) => {
5565
+ const parent = visit.itemAtPath(cst, path13.slice(0, -1));
5566
+ const field = path13[path13.length - 1][0];
5567
5567
  const coll = parent?.[field];
5568
5568
  if (coll && "items" in coll)
5569
5569
  return coll;
5570
5570
  throw new Error("Parent collection not found");
5571
5571
  };
5572
- function _visit(path12, item, visitor) {
5573
- let ctrl = visitor(item, path12);
5572
+ function _visit(path13, item, visitor) {
5573
+ let ctrl = visitor(item, path13);
5574
5574
  if (typeof ctrl === "symbol")
5575
5575
  return ctrl;
5576
5576
  for (const field of ["key", "value"]) {
5577
5577
  const token = item[field];
5578
5578
  if (token && "items" in token) {
5579
5579
  for (let i = 0; i < token.items.length; ++i) {
5580
- const ci = _visit(Object.freeze(path12.concat([[field, i]])), token.items[i], visitor);
5580
+ const ci = _visit(Object.freeze(path13.concat([[field, i]])), token.items[i], visitor);
5581
5581
  if (typeof ci === "number")
5582
5582
  i = ci - 1;
5583
5583
  else if (ci === BREAK)
@@ -5588,10 +5588,10 @@ var require_cst_visit = __commonJS({
5588
5588
  }
5589
5589
  }
5590
5590
  if (typeof ctrl === "function" && field === "key")
5591
- ctrl = ctrl(item, path12);
5591
+ ctrl = ctrl(item, path13);
5592
5592
  }
5593
5593
  }
5594
- return typeof ctrl === "function" ? ctrl(item, path12) : ctrl;
5594
+ return typeof ctrl === "function" ? ctrl(item, path13) : ctrl;
5595
5595
  }
5596
5596
  exports.visit = visit;
5597
5597
  }
@@ -6876,14 +6876,14 @@ var require_parser = __commonJS({
6876
6876
  case "scalar":
6877
6877
  case "single-quoted-scalar":
6878
6878
  case "double-quoted-scalar": {
6879
- const fs12 = this.flowScalar(this.type);
6879
+ const fs13 = this.flowScalar(this.type);
6880
6880
  if (atNextItem || it.value) {
6881
- map.items.push({ start, key: fs12, sep: [] });
6881
+ map.items.push({ start, key: fs13, sep: [] });
6882
6882
  this.onKeyLine = true;
6883
6883
  } else if (it.sep) {
6884
- this.stack.push(fs12);
6884
+ this.stack.push(fs13);
6885
6885
  } else {
6886
- Object.assign(it, { key: fs12, sep: [] });
6886
+ Object.assign(it, { key: fs13, sep: [] });
6887
6887
  this.onKeyLine = true;
6888
6888
  }
6889
6889
  return;
@@ -7011,13 +7011,13 @@ var require_parser = __commonJS({
7011
7011
  case "scalar":
7012
7012
  case "single-quoted-scalar":
7013
7013
  case "double-quoted-scalar": {
7014
- const fs12 = this.flowScalar(this.type);
7014
+ const fs13 = this.flowScalar(this.type);
7015
7015
  if (!it || it.value)
7016
- fc.items.push({ start: [], key: fs12, sep: [] });
7016
+ fc.items.push({ start: [], key: fs13, sep: [] });
7017
7017
  else if (it.sep)
7018
- this.stack.push(fs12);
7018
+ this.stack.push(fs13);
7019
7019
  else
7020
- Object.assign(it, { key: fs12, sep: [] });
7020
+ Object.assign(it, { key: fs13, sep: [] });
7021
7021
  return;
7022
7022
  }
7023
7023
  case "flow-map-end":
@@ -7325,6 +7325,11 @@ var require_dist = __commonJS({
7325
7325
  }
7326
7326
  });
7327
7327
 
7328
+ // src/index.ts
7329
+ import fs12 from "node:fs/promises";
7330
+ import path12 from "node:path";
7331
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
7332
+
7328
7333
  // ../../node_modules/.pnpm/kleur@4.1.5/node_modules/kleur/index.mjs
7329
7334
  var FORCE_COLOR;
7330
7335
  var NODE_DISABLE_COLORS;
@@ -8258,8 +8263,8 @@ function getErrorMap() {
8258
8263
 
8259
8264
  // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
8260
8265
  var makeIssue = (params) => {
8261
- const { data, path: path12, errorMaps, issueData } = params;
8262
- const fullPath = [...path12, ...issueData.path || []];
8266
+ const { data, path: path13, errorMaps, issueData } = params;
8267
+ const fullPath = [...path13, ...issueData.path || []];
8263
8268
  const fullIssue = {
8264
8269
  ...issueData,
8265
8270
  path: fullPath
@@ -8375,11 +8380,11 @@ var errorUtil;
8375
8380
 
8376
8381
  // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js
8377
8382
  var ParseInputLazyPath = class {
8378
- constructor(parent, value, path12, key) {
8383
+ constructor(parent, value, path13, key) {
8379
8384
  this._cachedPath = [];
8380
8385
  this.parent = parent;
8381
8386
  this.data = value;
8382
- this._path = path12;
8387
+ this._path = path13;
8383
8388
  this._key = key;
8384
8389
  }
8385
8390
  get path() {
@@ -13331,6 +13336,16 @@ async function setupCommand(opts = {}) {
13331
13336
  }
13332
13337
 
13333
13338
  // src/index.ts
13339
+ var cachedVersion = null;
13340
+ async function cliVersion() {
13341
+ if (cachedVersion) return cachedVersion;
13342
+ const dirname = path12.dirname(fileURLToPath3(import.meta.url));
13343
+ const packageJsonPath = path12.resolve(dirname, "../package.json");
13344
+ const raw = await fs12.readFile(packageJsonPath, "utf8");
13345
+ const parsed = JSON.parse(raw);
13346
+ cachedVersion = parsed.version ?? "0.0.0";
13347
+ return cachedVersion;
13348
+ }
13334
13349
  function readFlag(args, flag) {
13335
13350
  const index = args.indexOf(flag);
13336
13351
  if (index === -1) return void 0;
@@ -13375,6 +13390,10 @@ async function main() {
13375
13390
  const [, , command = "run", ...args] = process.argv;
13376
13391
  const normalizedArgs = args.filter((arg) => arg !== "--");
13377
13392
  try {
13393
+ if (command === "version" || command === "--version" || command === "-v") {
13394
+ console.log(await cliVersion());
13395
+ return;
13396
+ }
13378
13397
  if (command === "run" || command === "demo") {
13379
13398
  const flowPath = readFlag(normalizedArgs, "--flow");
13380
13399
  if (!flowPath) {