@flowriki/agent 0.1.0-beta.50 → 0.1.0-beta.52

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.
@@ -17,7 +17,7 @@ import path3 from "path";
17
17
  import fs4 from "fs";
18
18
  import path4 from "path";
19
19
 
20
- // ../test-model/dist/chunk-QDXJLOQJ.js
20
+ // ../test-model/dist/chunk-I5COYCQ5.js
21
21
  import { z as z2 } from "zod";
22
22
  import { z } from "zod";
23
23
  var PLATFORMS = ["web", "android", "ios"];
@@ -86,8 +86,8 @@ var DeviceChoiceSchema = z.discriminatedUnion("kind", [
86
86
  }).strict()
87
87
  ]);
88
88
  var MobileConfigSchema = z.object({
89
- android: AndroidAppSchema.optional(),
90
- ios: IosAppSchema.optional(),
89
+ android: AndroidAppSchema.partial({ appPackage: true }).optional(),
90
+ ios: IosAppSchema.partial({ bundleId: true }).optional(),
91
91
  /** Between runs: keep the app's data (stays signed in), or start clean. */
92
92
  reset: z.enum(["keepData", "fresh"]).default("keepData"),
93
93
  /** Where runs go when nobody says — absent means whatever is available. */
@@ -98,6 +98,10 @@ var mobileOf = (config) => {
98
98
  const parsed = MobileConfigSchema.safeParse(config?.mobile ?? {});
99
99
  return parsed.success ? parsed.data : MobileConfigSchema.parse({});
100
100
  };
101
+ var EnvironmentAppsSchema = z.object({
102
+ android: AndroidAppSchema.optional(),
103
+ ios: IosAppSchema.optional()
104
+ });
101
105
  var DeviceInfoSchema = z.object({
102
106
  id: z.string(),
103
107
  name: z.string(),
@@ -1684,6 +1688,14 @@ var BugSchema = z7.object({
1684
1688
  updatedAt: z7.string()
1685
1689
  }).strict();
1686
1690
  var STOP = new Set("a an the and or of to in on for with by is are be as at it this that from user users can should must shall will when then given".split(" "));
1691
+ function exploreSummary(markdown) {
1692
+ const screens = /^Screens:\s*(\d+)\s*$/im.exec(markdown)?.[1];
1693
+ return {
1694
+ exploredAt: /^Explored:\s*(\S+)\s*$/im.exec(markdown)?.[1] ?? null,
1695
+ environment: /^Environment:\s*(.+?)\s*$/im.exec(markdown)?.[1] ?? null,
1696
+ screens: screens ? Number(screens) : null
1697
+ };
1698
+ }
1687
1699
  var CODEBASE_TAGS = ["all", "web", "android", "ios", "mobile", "other"];
1688
1700
  var CODEBASE_TAG_NAME = { all: "All combined", web: "Web", android: "Android", ios: "iOS", mobile: "Mobile", other: "Other" };
1689
1701
  var CodebaseIndexSchema = z8.object({
@@ -2518,11 +2530,11 @@ var CLOUD_HUBS = {
2518
2530
  function appOf(target) {
2519
2531
  if (target.platform === "android") {
2520
2532
  const app2 = target.mobile.android;
2521
- if (!app2) throw new Error("This project has no Android app set up \u2014 add it in Settings \u2192 Mobile.");
2533
+ if (!app2?.appPackage) throw new Error("This environment has not named its Android app \u2014 add its package in Settings \u2192 Environments.");
2522
2534
  return { id: app2.appPackage, ...app2.appActivity ? { activity: app2.appActivity } : {}, ...app2.build ? { build: app2.build } : {} };
2523
2535
  }
2524
2536
  const app = target.mobile.ios;
2525
- if (!app) throw new Error("This project has no iOS app set up \u2014 add it in Settings \u2192 Mobile.");
2537
+ if (!app?.bundleId) throw new Error("This environment has not named its iOS app \u2014 add its bundle ID in Settings \u2192 Environments.");
2526
2538
  return { id: app.bundleId, ...app.build ? { build: app.build } : {} };
2527
2539
  }
2528
2540
  async function localBuild(build) {
@@ -4053,6 +4065,8 @@ export {
4053
4065
  isMobile,
4054
4066
  stepRefusedOn,
4055
4067
  platformsOf,
4068
+ AndroidAppSchema,
4069
+ IosAppSchema,
4056
4070
  CLOUD_PROVIDERS,
4057
4071
  CLOUD_PROVIDER_NAME,
4058
4072
  DeviceChoiceSchema,
@@ -4086,6 +4100,7 @@ export {
4086
4100
  PrerequisiteProblem,
4087
4101
  expandForRun,
4088
4102
  DeliveredHelperSchema,
4103
+ exploreSummary,
4089
4104
  CODEBASE_TAG_NAME,
4090
4105
  codebaseIndexOf,
4091
4106
  runWhereOf,
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ AndroidAppSchema,
3
4
  ArtifactPolicySchema,
4
5
  AuthReferenceSchema,
5
6
  CHECKS,
@@ -13,6 +14,7 @@ import {
13
14
  ExecutionEnvironmentSchema,
14
15
  FeatureSchema,
15
16
  FlowDocumentSchema,
17
+ IosAppSchema,
16
18
  MobileLane,
17
19
  PrerequisiteProblem,
18
20
  RecordedAssertionSchema,
@@ -65,7 +67,7 @@ import {
65
67
  textIndependent,
66
68
  validateActors,
67
69
  webCloudOf
68
- } from "./chunk-GLZFHC64.js";
70
+ } from "./chunk-KDW65RBE.js";
69
71
 
70
72
  // src/engine/machine.ts
71
73
  import fs from "fs";
@@ -73,7 +75,7 @@ import os from "os";
73
75
  import path from "path";
74
76
  import { createRequire } from "module";
75
77
  import { spawnSync } from "child_process";
76
- var AGENT_VERSION = "0.1.0-beta.50";
78
+ var AGENT_VERSION = "0.1.0-beta.52";
77
79
  async function prepareBrowsers() {
78
80
  if (browsersReady()) {
79
81
  return { ok: true, already: true, message: "chromium is already installed" };
@@ -133,7 +135,7 @@ async function checkMachine() {
133
135
  }
134
136
  } catch {
135
137
  }
136
- const { mobileDoctor: mobileDoctor2 } = await import("./dist-Z3WSTACX.js");
138
+ const { mobileDoctor: mobileDoctor2 } = await import("./dist-R2I4D66B.js");
137
139
  return {
138
140
  agentVersion: AGENT_VERSION,
139
141
  node: process.versions.node,
@@ -6157,7 +6159,7 @@ function toSummary(row) {
6157
6159
  };
6158
6160
  }
6159
6161
 
6160
- // ../repository-store/dist/chunk-7ZZULR2K.js
6162
+ // ../repository-store/dist/chunk-QF7BTHZB.js
6161
6163
  var FLOWRIKI_DIR = ".flowriki";
6162
6164
  var FEATURES_DIR = `${FLOWRIKI_DIR}/features`;
6163
6165
  var CONFIG_PATH = `${FLOWRIKI_DIR}/config.json`;
@@ -6219,12 +6221,18 @@ function readEnvironments(raw) {
6219
6221
  const seen = /* @__PURE__ */ new Set();
6220
6222
  for (const entry of raw) {
6221
6223
  if (!entry || typeof entry !== "object") continue;
6222
- const { name, baseUrl } = entry;
6224
+ const { name, baseUrl, android, ios } = entry;
6223
6225
  if (typeof name !== "string" || !name.trim()) continue;
6224
6226
  const key = name.trim().toLowerCase();
6225
6227
  if (seen.has(key)) continue;
6226
6228
  seen.add(key);
6227
- out.push({ name: name.trim(), baseUrl: typeof baseUrl === "string" ? baseUrl : "" });
6229
+ const app = { android: AndroidAppSchema.safeParse(android), ios: IosAppSchema.safeParse(ios) };
6230
+ out.push({
6231
+ name: name.trim(),
6232
+ baseUrl: typeof baseUrl === "string" ? baseUrl : "",
6233
+ ...app.android.success ? { android: app.android.data } : {},
6234
+ ...app.ios.success ? { ios: app.ios.data } : {}
6235
+ });
6228
6236
  }
6229
6237
  return out;
6230
6238
  }
@@ -9726,6 +9734,7 @@ function assistantHandlers({ deps }) {
9726
9734
  const askId = randomUUID();
9727
9735
  const linked = foldersFor(project.id).filter((f) => f.exists).map((f) => f.path);
9728
9736
  const mode = command.args["mode"] === "ask" ? "ask" : command.args["mode"] === "do" ? "do" : void 0;
9737
+ const brief = typeof command.args["brief"] === "string" && command.args["brief"].trim() ? command.args["brief"].slice(0, 4e4) : void 0;
9729
9738
  const started = assistant.ask({
9730
9739
  askId,
9731
9740
  prompt,
@@ -9733,6 +9742,7 @@ function assistantHandlers({ deps }) {
9733
9742
  projectName: project.name,
9734
9743
  ...linked.length ? { folders: linked } : {},
9735
9744
  ...mode ? { mode } : {},
9745
+ ...brief ? { brief } : {},
9736
9746
  ...typeof sessionId === "string" ? { sessionId } : {},
9737
9747
  emit
9738
9748
  });
@@ -10068,10 +10078,13 @@ var ASK_MODE_TOOLS = [
10068
10078
  function buildAskArgs(opts) {
10069
10079
  const ask = opts.mode === "ask";
10070
10080
  const folders = opts.folders ?? [];
10081
+ const brief = opts.brief?.trim();
10071
10082
  const guidance = [
10072
- `You are the Flowriki assistant for the project "${opts.projectName}" (id ${opts.projectId}). Use the Flowriki tools, and pass projectId "${opts.projectId}" wherever a tool takes one. Start with project_brief: it is this project's memory.`,
10083
+ `You are the Flowriki assistant for the project "${opts.projectName}" (id ${opts.projectId}). Use the Flowriki tools, and pass projectId "${opts.projectId}" wherever a tool takes one. ` + (brief ? "The project brief below is this project's memory for this job: read it first." : "Start with project_brief: it is this project's memory."),
10073
10084
  ask ? "ASK MODE: answer the question; change nothing. Cite what you rely on as [[testCase:<its id, tc_\u2026>]], [[knowledge:features/checkout.md]] or [[file:path/in/repo.ts]]." : "DO MODE: do what is asked, on Claude's working branch, for a person to review. Ask in the chat and wait before publishing, deleting, or sending anything to another system. End by writing a short summary of what you did with write_knowledge (memory/sessions/<date>-<topic>.md, append) so the next session knows.",
10074
- folders.length ? `The project's code is in ${folders.join(" and ")}. Read it with Read, Grep and Glob when it helps; you cannot change it.` : ""
10085
+ folders.length ? `The project's code is in ${folders.join(" and ")}. Read it with Read, Grep and Glob when it helps; you cannot change it.` : "",
10086
+ brief ? `
10087
+ ${brief}` : ""
10075
10088
  ].filter(Boolean).join("\n");
10076
10089
  const flowriki = ask ? ASK_MODE_TOOLS.map((t) => `mcp__${MCP_SERVER_NAME}__${t}`) : [`mcp__${MCP_SERVER_NAME}`];
10077
10090
  const files = folders.length ? ["Read", "Grep", "Glob"] : [];
package/dist/cli.js CHANGED
@@ -23,7 +23,7 @@ import {
23
23
  runtimeValue,
24
24
  start,
25
25
  status
26
- } from "./chunk-KRWHHKSS.js";
26
+ } from "./chunk-Z3SL46OX.js";
27
27
  import {
28
28
  CLOUD_PROVIDERS,
29
29
  CLOUD_PROVIDER_NAME,
@@ -32,6 +32,7 @@ import {
32
32
  RecordedStepSchema,
33
33
  checkMobileAssertion,
34
34
  codebaseIndexOf,
35
+ exploreSummary,
35
36
  isMobile,
36
37
  listDevices,
37
38
  locate,
@@ -48,7 +49,7 @@ import {
48
49
  screenLines,
49
50
  stepRefusedOn,
50
51
  targetFor
51
- } from "./chunk-GLZFHC64.js";
52
+ } from "./chunk-KDW65RBE.js";
52
53
 
53
54
  // src/cli.ts
54
55
  import { parseArgs } from "util";
@@ -325,6 +326,10 @@ var ServiceClient = class {
325
326
  return this.call(this.scoped(`/bugs/${encodeURIComponent(id)}`, projectId), { method: "PATCH", body: JSON.stringify(body) });
326
327
  }
327
328
  // ── Knowledge, sources, requirements ──────────────────────
329
+ /** The project's brief for a job: its slice of the knowledge. */
330
+ brief(projectId, scope) {
331
+ return this.call(this.scoped("/brief", projectId, { ...scope.platform ? { platform: scope.platform } : {}, ...scope.features?.length ? { features: scope.features.join(",") } : {} }));
332
+ }
328
333
  knowledgePages(projectId) {
329
334
  return this.call(this.scoped("/knowledge", projectId));
330
335
  }
@@ -985,7 +990,7 @@ Review and publish it here: ${review}`;
985
990
  async startMobile(platform, catalog) {
986
991
  const draft = this.draft;
987
992
  const mobile = catalog.mobile;
988
- if (!mobile) throw new Error("this project has no phone apps set up \u2014 turn on Android or iOS in Settings \u2192 Mobile");
993
+ if (!mobile) throw new Error("this project has no phone apps set up \u2014 turn on Android or iOS in Settings \u2192 Platforms");
989
994
  const resolvers = draft.signIn || draft.device?.kind === "cloud" ? await this.prepare() : null;
990
995
  this.mobile = await openMobileSession({
991
996
  platform,
@@ -1233,7 +1238,10 @@ codebase/<folder>.md, which must start "# <folder>", then "Commit: <full sha>"
1233
1238
  and "Learned: <yyyy-mm-dd>" lines: that is how the Knowledge screen shows it
1234
1239
  as learned. codebase.json says which platform's code each folder holds (Web,
1235
1240
  Android, iOS, Mobile, All combined, Other): read the web folder's page for a
1236
- website test and the phone folder's for an app test. Keep what you learn with write_knowledge (features/<name>.md,
1241
+ website test and the phone folder's for an app test. explore/<platform>.md is
1242
+ what the assistant saw WALKING that platform \u2014 screens, the words on them,
1243
+ journeys, rules it saw refused, vocabulary: read it before writing a test for
1244
+ that platform, because it says what a person sees; the code does not. Keep what you learn with write_knowledge (features/<name>.md,
1237
1245
  memory/decisions.md, a session summary), on Claude's branch for review \u2014
1238
1246
  never a password, key or token.
1239
1247
 
@@ -1412,7 +1420,7 @@ It is now the project these tools work on. Open it: ${service.origin}/projects/$
1412
1420
  const here = phones.filter((d) => d.platform === p);
1413
1421
  const farms = (c.mobile?.cloud ?? []).map((f) => CLOUD_PROVIDER_NAME[f.provider]);
1414
1422
  const chosen = c.mobile?.devices[p];
1415
- return `${p === "android" ? "Android" : "iOS"} app: ${app ?? "(not set up \u2014 Settings \u2192 Mobile)"}
1423
+ return `${p === "android" ? "Android" : "iOS"} app: ${app ?? "(not named \u2014 Settings \u2192 Environments)"}
1416
1424
  Phones on this computer: ${here.length ? here.map((d) => `${d.name}${d.state === "ready" ? "" : ` (${d.state})`}`).join(", ") : "none"}` + (farms.length ? `
1417
1425
  Cloud device farms: ${farms.join(", ")}` : "") + `
1418
1426
  Runs: ${c.runWhere?.[p] === "cloud" ? "in the cloud" : "on this computer"}` + (chosen ? ` \u2014 ${chosen.kind === "local" ? chosen.name || chosen.id : `${CLOUD_PROVIDER_NAME[chosen.provider]}: ${chosen.device}`}` : "");
@@ -2009,11 +2017,19 @@ Use it with start_flow signIn {"profileId": "\u2026", "accountId": "${made.accou
2009
2017
  {
2010
2018
  name: "project_brief",
2011
2019
  title: "Read the project's memory",
2012
- description: "The project's own knowledge, in one call: About the product (product.md, and product/<web|android|ios>.md where a platform differs), memory/decisions.md, what each linked codebase folder was learned as and which platform's code it holds, and the list of feature pages and session summaries. Call it first in every conversation.",
2013
- inputSchema: { type: "object", properties: { projectId: { type: "string" } }, additionalProperties: false },
2020
+ description: "The project's own knowledge, in one call: About the product (product.md, and product/<web|android|ios>.md where a platform differs), memory/decisions.md, what each linked codebase folder was learned as and which platform's code it holds, what each platform looked like when explored (explore/<platform>.md), and the list of feature pages and session summaries. Call it first in every conversation. With platform and/or features, the brief is the slice for that job: that platform's part and exploration, those features' notes, the code tagged for the platform, and who can sign in.",
2021
+ inputSchema: { type: "object", properties: { projectId: { type: "string" }, platform: { type: "string", enum: ["web", "android", "ios"], description: "The platform the job is about." }, features: { type: "array", items: { type: "string" }, description: "The features (or case folders) the job is about, by name." } }, additionalProperties: false },
2014
2022
  annotations: { readOnlyHint: true },
2015
2023
  run: async (args) => {
2016
2024
  const projectId = await projectFor(args["projectId"]);
2025
+ const platform = str(args["platform"]);
2026
+ const features = Array.isArray(args["features"]) ? args["features"].filter((f) => typeof f === "string") : [];
2027
+ if (platform || features.length) {
2028
+ try {
2029
+ return text((await service.brief(projectId, { ...platform ? { platform } : {}, features })).brief);
2030
+ } catch {
2031
+ }
2032
+ }
2017
2033
  const { pages } = await service.knowledgePages(projectId);
2018
2034
  const read = async (path2) => pages.some((p) => p.path === path2) ? (await service.knowledgePage(projectId, path2)).content : null;
2019
2035
  const [product, glossary, decisions, web, android, ios, tagged] = await Promise.all(["product.md", "glossary.md", "memory/decisions.md", "product/web.md", "product/android.md", "product/ios.md", "codebase.json"].map(read));
@@ -2023,7 +2039,12 @@ Use it with start_flow signIn {"profileId": "\u2026", "accountId": "${made.accou
2023
2039
  const holds = tags[p.path.slice("codebase/".length, -".md".length)]?.tags ?? [];
2024
2040
  return `- ${p.path}${holds.length ? ` (${holds.map((t) => CODEBASE_TAG_NAME[t]).join(", ")} code)` : ""}: ${(await read(p.path))?.split("\n").filter((l) => l.trim()).slice(0, 3).join(" \xB7 ") ?? ""}`;
2025
2041
  }));
2026
- const shown = /* @__PURE__ */ new Set(["product.md", "glossary.md", "memory/decisions.md", "product/web.md", "product/android.md", "product/ios.md", "codebase.json", ...codebases.map((p) => p.path)]);
2042
+ const explored = pages.filter((p) => p.path.startsWith("explore/"));
2043
+ const walks = await Promise.all(explored.map(async (p) => {
2044
+ const s = exploreSummary(await read(p.path) ?? "");
2045
+ return `- ${p.path}: explored ${s.exploredAt ?? "?"}${s.environment ? ` in ${s.environment}` : ""}${s.screens !== null ? `, ${s.screens} screens` : ""} \u2014 read it with knowledge_read before testing that platform`;
2046
+ }));
2047
+ const shown = /* @__PURE__ */ new Set(["product.md", "glossary.md", "memory/decisions.md", "product/web.md", "product/android.md", "product/ios.md", "codebase.json", ...codebases.map((p) => p.path), ...explored.map((p) => p.path)]);
2027
2048
  const parts = [
2028
2049
  product ? `## About the product (product.md)
2029
2050
  ${product}` : "No About the product yet \u2014 the person writes it on the Knowledge screen; offer to help draft it.",
@@ -2039,6 +2060,8 @@ ${glossary}` : "",
2039
2060
  ${decisions}` : "",
2040
2061
  codebases.length ? `## Codebase, as learned
2041
2062
  ${headers.join("\n")}` : "No codebase learned yet.",
2063
+ walks.length ? `## The product, as explored
2064
+ ${walks.join("\n")}` : "",
2042
2065
  `## Other pages
2043
2066
  ${pages.filter((p) => !shown.has(p.path)).map((p) => `- ${p.path}`).join("\n") || "none"}`
2044
2067
  ];
@@ -2074,7 +2097,7 @@ ${pages.filter((p) => !shown.has(p.path)).map((p) => `- ${p.path}`).join("\n") |
2074
2097
  {
2075
2098
  name: "write_knowledge",
2076
2099
  title: "Write a knowledge page",
2077
- description: `Write what the project should remember, on Claude's working branch for review: product.md (About the product \u2014 the person's own description; write it only when they ask) and product/<web|android|ios>.md (what is particular to that platform's app), codebase/<folder>.md (what a linked folder holds \u2014 starting with "# <folder>", "Commit: <sha>" and "Learned: <yyyy-mm-dd>" lines, then routes, screens, forms and their validation, roles, APIs, flags; summarised, never copied code), features/<name>.md (screens, rules, quirks), memory/decisions.md (decisions and conventions), memory/sessions/<yyyy-mm-dd>-<topic>.md (what a job did). append: true adds to the end instead of replacing. Never a password, key or token \u2014 those are refused.`,
2100
+ description: `Write what the project should remember, on Claude's working branch for review: product.md (About the product \u2014 the person's own description; write it only when they ask) and product/<web|android|ios>.md (what is particular to that platform's app), explore/<web|android|ios>.md (what you saw walking that platform: "# Explored: <Platform>", "Explored: <yyyy-mm-dd>", "Environment: <name>", "Screens: <n>" lines, then Screens, Journeys, Rules observed, Vocabulary, Suggested features), codebase/<folder>.md (what a linked folder holds \u2014 starting with "# <folder>", "Commit: <sha>" and "Learned: <yyyy-mm-dd>" lines, then routes, screens, forms and their validation, roles, APIs, flags; summarised, never copied code), features/<name>.md (screens, rules, quirks), memory/decisions.md (decisions and conventions), memory/sessions/<yyyy-mm-dd>-<topic>.md (what a job did). append: true adds to the end instead of replacing. Never a password, key or token \u2014 those are refused.`,
2078
2101
  inputSchema: {
2079
2102
  type: "object",
2080
2103
  properties: { projectId: { type: "string" }, path: { type: "string" }, content: { type: "string" }, append: { type: "boolean" }, branch: { type: "string" } },
@@ -44,7 +44,7 @@ import {
44
44
  textOf,
45
45
  toolEnv,
46
46
  typesSecrets
47
- } from "./chunk-GLZFHC64.js";
47
+ } from "./chunk-KDW65RBE.js";
48
48
  export {
49
49
  AGENT_HOME,
50
50
  APPIUM_DIR,
package/dist/engine.js CHANGED
@@ -9,8 +9,8 @@ import {
9
9
  resolveCheckouts,
10
10
  start,
11
11
  status
12
- } from "./chunk-KRWHHKSS.js";
13
- import "./chunk-GLZFHC64.js";
12
+ } from "./chunk-Z3SL46OX.js";
13
+ import "./chunk-KDW65RBE.js";
14
14
  export {
15
15
  AGENT_VERSION,
16
16
  browsersReady,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowriki/agent",
3
- "version": "0.1.0-beta.50",
3
+ "version": "0.1.0-beta.52",
4
4
  "description": "Run Flowriki UI tests on your own computer. Connects this machine to your Flowriki account.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",