@swmansion/argent 0.19.1-next.4 → 0.19.1-next.6
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/bin/argent-android-devtools-0.1.0.apk +0 -0
- package/bin/darwin/ax-service +0 -0
- package/bin/darwin/simulator-server +0 -0
- package/bin/darwin/tvos-ax-service +0 -0
- package/bin/darwin/tvos-hid-daemon +0 -0
- package/bin/tcp/ax-service +0 -0
- package/bin/win32/simulator-server.exe +0 -0
- package/dist/tool-server.cjs +160 -141
- package/dylibs/libArgentInjectionBootstrap.dylib +0 -0
- package/dylibs/libKeyboardPatch.dylib +0 -0
- package/dylibs/libNativeDevtoolsIos.dylib +0 -0
- package/dylibs/tcp/libArgentInjectionBootstrap.dylib +0 -0
- package/dylibs/tcp/libKeyboardPatch.dylib +0 -0
- package/dylibs/tcp/libNativeDevtoolsIos.dylib +0 -0
- package/dylibs/tvos/libArgentInjectionBootstrap.dylib +0 -0
- package/dylibs/tvos/libKeyboardPatch.dylib +0 -0
- package/dylibs/tvos/libNativeDevtoolsIos.dylib +0 -0
- package/package.json +1 -1
- package/rules/argent.md +12 -4
- package/skills/argent-create-flow/SKILL.md +29 -324
- package/skills/argent-create-flow/references/flow-yaml.md +205 -0
- package/skills/argent-create-flow/references/live-authoring.md +242 -0
- package/skills/argent-create-flow/references/reliability-and-recovery.md +149 -0
- package/skills/argent-device-interact/SKILL.md +33 -20
- package/skills/argent-qa-flows/SKILL.md +123 -0
- package/skills/argent-react-native-profiler/SKILL.md +1 -1
- package/skills/argent-test-ui-flow/SKILL.md +1 -1
|
Binary file
|
package/bin/darwin/ax-service
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/bin/tcp/ax-service
CHANGED
|
Binary file
|
|
Binary file
|
package/dist/tool-server.cjs
CHANGED
|
@@ -143904,13 +143904,16 @@ async function appendStepToFlow(session, step) {
|
|
|
143904
143904
|
if (session.persist === "host") {
|
|
143905
143905
|
const flowFile = await appendStep(session.filePath, step);
|
|
143906
143906
|
session.flow = parseFlow(flowFile);
|
|
143907
|
-
return {
|
|
143907
|
+
return { savedTo: session.filePath, stepCount: session.flow.steps.length };
|
|
143908
143908
|
}
|
|
143909
143909
|
session.flow.steps.push(step);
|
|
143910
143910
|
try {
|
|
143911
143911
|
validateFlow(session.flow);
|
|
143912
143912
|
const flowFile = serializeFlow(session.flow);
|
|
143913
|
-
return {
|
|
143913
|
+
return {
|
|
143914
|
+
savedTo: clientFileDirective(session.filePath, flowFile),
|
|
143915
|
+
stepCount: session.flow.steps.length
|
|
143916
|
+
};
|
|
143914
143917
|
} catch (err) {
|
|
143915
143918
|
session.flow.steps.pop();
|
|
143916
143919
|
throw err;
|
|
@@ -143964,7 +143967,9 @@ executionPrerequisite instead. Use flow-add-echo to add labels. Call
|
|
|
143964
143967
|
flow-finish-recording when done.
|
|
143965
143968
|
|
|
143966
143969
|
If a recorded step turns out to be wrong, you can edit the .yaml file directly
|
|
143967
|
-
to remove or reorder steps
|
|
143970
|
+
to remove or reorder steps. Against a remote client, only after
|
|
143971
|
+
flow-finish-recording: the in-memory copy is authoritative there, and every
|
|
143972
|
+
write serializes it over your edit.`,
|
|
143968
143973
|
zodSchema: zodSchema61,
|
|
143969
143974
|
fileInputs: fileInputs2,
|
|
143970
143975
|
services: () => ({}),
|
|
@@ -144022,10 +144027,141 @@ to remove or reorder steps.`,
|
|
|
144022
144027
|
|
|
144023
144028
|
// ../tool-server/src/tools/flows/flow-add-step.ts
|
|
144024
144029
|
init_zod();
|
|
144025
|
-
var
|
|
144030
|
+
var fs45 = __toESM(require("node:fs/promises"));
|
|
144026
144031
|
var path32 = __toESM(require("node:path"));
|
|
144027
144032
|
init_src();
|
|
144028
144033
|
|
|
144034
|
+
// ../tool-server/src/tools/flows/flow-finish-recording.ts
|
|
144035
|
+
init_zod();
|
|
144036
|
+
var fs44 = __toESM(require("node:fs/promises"));
|
|
144037
|
+
function selectorLabel(sel) {
|
|
144038
|
+
return JSON.stringify(selectorToYaml(sel));
|
|
144039
|
+
}
|
|
144040
|
+
function textConditionLabel(sel, expectedText, textMatch) {
|
|
144041
|
+
const selector = selectorLabel(sel);
|
|
144042
|
+
const expected = expectedText ?? "";
|
|
144043
|
+
return textMatch === "matches" ? `text ${selector} matches /${expected}/` : textMatch === "equals" ? `text ${selector} == ${JSON.stringify(expected)}` : `text ${selector} contains ${JSON.stringify(expected)}`;
|
|
144044
|
+
}
|
|
144045
|
+
var zodSchema62 = external_exports.object({
|
|
144046
|
+
name: external_exports.string().describe("Name of the flow being recorded \u2014 the one passed to flow-start-recording."),
|
|
144047
|
+
project_root: external_exports.string().describe(
|
|
144048
|
+
"Absolute path to the project root of the flow being recorded \u2014 the same value passed to flow-start-recording. Together with `name` it identifies which recording to finish."
|
|
144049
|
+
)
|
|
144050
|
+
});
|
|
144051
|
+
var flowFinishRecordingTool = {
|
|
144052
|
+
id: "flow-finish-recording",
|
|
144053
|
+
interaction: {
|
|
144054
|
+
// Name the flow: other recordings stay live across this call, so an
|
|
144055
|
+
// unqualified "Finishing flow recording" would not identify which one.
|
|
144056
|
+
startedMsg: ({ params }) => `Finishing recording of flow ${params.name}`,
|
|
144057
|
+
// `params.name` rather than the basename of `result.path`: the two are the
|
|
144058
|
+
// same string on every branch — `assertSafeFlowName` admits no dots or
|
|
144059
|
+
// separators, so `getFlowPath` produces `<name>.yaml` and nothing else —
|
|
144060
|
+
// and this spelling matches the two formatters either side of it.
|
|
144061
|
+
completedMsg: ({ params }) => `Saved recorded flow ${params.name}`,
|
|
144062
|
+
failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to finish recording of flow ${params.name}: ${failureSignal2.error_code}`
|
|
144063
|
+
},
|
|
144064
|
+
description: `Finish recording the flow named by \`name\` + \`project_root\`, leaving recordings under any other key untouched. Returns { message, path, executionPrerequisite, steps, summary, flowFile, savedTo } - a summary of all recorded steps plus the final YAML. Use when you have added all desired steps and want to finalize the flow file. Fails if that flow has no recording in progress.
|
|
144065
|
+
You can still edit the .yaml file directly afterwards to remove or reorder steps.`,
|
|
144066
|
+
zodSchema: zodSchema62,
|
|
144067
|
+
services: () => ({}),
|
|
144068
|
+
async execute(_services, params) {
|
|
144069
|
+
const { filePath, flowFile, savedTo, flow, summary } = await withFlowFileLock(
|
|
144070
|
+
params.project_root,
|
|
144071
|
+
params.name,
|
|
144072
|
+
async () => {
|
|
144073
|
+
const session = await requireRecordingSession(params.project_root, params.name);
|
|
144074
|
+
const filePath2 = session.filePath;
|
|
144075
|
+
let flowFile2;
|
|
144076
|
+
let savedTo2;
|
|
144077
|
+
if (session.persist === "client") {
|
|
144078
|
+
flowFile2 = serializeFlow(session.flow);
|
|
144079
|
+
savedTo2 = clientFileDirective(filePath2, flowFile2);
|
|
144080
|
+
} else {
|
|
144081
|
+
flowFile2 = await fs44.readFile(filePath2, "utf8");
|
|
144082
|
+
savedTo2 = filePath2;
|
|
144083
|
+
}
|
|
144084
|
+
const flow2 = parseFlow(flowFile2);
|
|
144085
|
+
const summary2 = summarizeSteps(flow2);
|
|
144086
|
+
clearRecordingSession(session);
|
|
144087
|
+
return { filePath: filePath2, flowFile: flowFile2, savedTo: savedTo2, flow: flow2, summary: summary2 };
|
|
144088
|
+
}
|
|
144089
|
+
);
|
|
144090
|
+
return {
|
|
144091
|
+
message: `Finished recording "${params.name}" flow (${flow.steps.length} steps)`,
|
|
144092
|
+
path: filePath,
|
|
144093
|
+
executionPrerequisite: flow.executionPrerequisite,
|
|
144094
|
+
steps: flow.steps.length,
|
|
144095
|
+
summary,
|
|
144096
|
+
flowFile,
|
|
144097
|
+
savedTo
|
|
144098
|
+
};
|
|
144099
|
+
}
|
|
144100
|
+
};
|
|
144101
|
+
function renderToolArgs(args) {
|
|
144102
|
+
try {
|
|
144103
|
+
return `${JSON.stringify(args)}`;
|
|
144104
|
+
} catch {
|
|
144105
|
+
return "[cyclic args]";
|
|
144106
|
+
}
|
|
144107
|
+
}
|
|
144108
|
+
function delayLabel(step) {
|
|
144109
|
+
if (!step.delayMs) return "";
|
|
144110
|
+
const ms = Number(step.delayMs);
|
|
144111
|
+
return Number.isFinite(ms) && ms >= 1 ? ` (after ${ms}ms)` : "";
|
|
144112
|
+
}
|
|
144113
|
+
function summarizeSteps(flow) {
|
|
144114
|
+
return flow.steps.map((step, i) => summarizeStep(step, i + 1));
|
|
144115
|
+
}
|
|
144116
|
+
function summarizeStep(step, n) {
|
|
144117
|
+
switch (step.kind) {
|
|
144118
|
+
case "echo":
|
|
144119
|
+
return `${n}. echo: ${step.message}`;
|
|
144120
|
+
case "launch":
|
|
144121
|
+
return `${n}. launch: ${typeof step.app === "string" ? step.app : JSON.stringify(step.app)}`;
|
|
144122
|
+
case "run":
|
|
144123
|
+
return `${n}. run: ${step.flow}`;
|
|
144124
|
+
case "tap":
|
|
144125
|
+
case "long-press": {
|
|
144126
|
+
const target = step.selector ? selectorLabel(step.selector) : `(${step.x}, ${step.y})`;
|
|
144127
|
+
const times = step.kind === "tap" && step.times !== void 0 && step.times > 1 ? ` \xD7${step.times}` : "";
|
|
144128
|
+
const held = step.kind === "long-press" && step.duration !== void 0 ? ` for ${step.duration}ms` : "";
|
|
144129
|
+
return `${n}. ${step.kind}: ${target}${times}${held}`;
|
|
144130
|
+
}
|
|
144131
|
+
case "type":
|
|
144132
|
+
return `${n}. type: ${selectorLabel(step.into)} \u2190 "${step.text}"`;
|
|
144133
|
+
case "await":
|
|
144134
|
+
case "assert": {
|
|
144135
|
+
const tail = step.condition === "text" ? textConditionLabel(step.selector, step.expectedText, step.textMatch) : `${step.condition} ${selectorLabel(step.selector)}`;
|
|
144136
|
+
return `${n}. ${step.kind}: ${tail}`;
|
|
144137
|
+
}
|
|
144138
|
+
case "wait":
|
|
144139
|
+
return `${n}. wait: ${step.ms}ms`;
|
|
144140
|
+
case "when": {
|
|
144141
|
+
const cond = step.condition.kind === "platform" ? `platform ${step.condition.platform}` : step.condition.condition === "text" ? textConditionLabel(
|
|
144142
|
+
step.condition.selector,
|
|
144143
|
+
step.condition.expectedText,
|
|
144144
|
+
step.condition.textMatch
|
|
144145
|
+
) : `${step.condition.condition} ${selectorLabel(step.condition.selector)}`;
|
|
144146
|
+
const count2 = step.steps.length;
|
|
144147
|
+
return `${n}. when: ${cond} (${count2} step${count2 === 1 ? "" : "s"})`;
|
|
144148
|
+
}
|
|
144149
|
+
case "scroll-to":
|
|
144150
|
+
return `${n}. scroll-to: ${selectorLabel(step.target)} (${step.direction})`;
|
|
144151
|
+
case "pinch":
|
|
144152
|
+
return `${n}. pinch: scale ${step.scale}${step.selector ? ` on ${selectorLabel(step.selector)}` : ""}`;
|
|
144153
|
+
case "rotate":
|
|
144154
|
+
return `${n}. rotate: by ${step.by}\xB0${step.selector ? ` on ${selectorLabel(step.selector)}` : ""}`;
|
|
144155
|
+
case "snapshot":
|
|
144156
|
+
return `${n}. snapshot: ${step.name}`;
|
|
144157
|
+
case "idle":
|
|
144158
|
+
return `${n}. await: screen idle`;
|
|
144159
|
+
case "tool":
|
|
144160
|
+
default:
|
|
144161
|
+
return `${n}. tool: ${step.name} ${renderToolArgs(step.args)}${delayLabel(step)}`;
|
|
144162
|
+
}
|
|
144163
|
+
}
|
|
144164
|
+
|
|
144029
144165
|
// ../tool-server/src/tools/flows/flow-device.ts
|
|
144030
144166
|
init_src();
|
|
144031
144167
|
var DEVICE_BIND_KEYS = ["udid", "device_id", "device"];
|
|
@@ -144585,7 +144721,7 @@ async function fetchFlowTree(registry2, device) {
|
|
|
144585
144721
|
}
|
|
144586
144722
|
|
|
144587
144723
|
// ../tool-server/src/tools/flows/flow-add-step.ts
|
|
144588
|
-
var
|
|
144724
|
+
var zodSchema63 = external_exports.object({
|
|
144589
144725
|
name: external_exports.string().describe("Name of the flow being recorded \u2014 the one passed to flow-start-recording."),
|
|
144590
144726
|
project_root: external_exports.string().describe(
|
|
144591
144727
|
"Absolute path to the project root of the flow being recorded \u2014 the same value passed to flow-start-recording. Together with `name` it identifies which recording this step belongs to."
|
|
@@ -144702,7 +144838,7 @@ async function captureRunTarget(session, args) {
|
|
|
144702
144838
|
}
|
|
144703
144839
|
try {
|
|
144704
144840
|
assertSafeFlowName(name);
|
|
144705
|
-
const realFlowPath = await
|
|
144841
|
+
const realFlowPath = await fs45.realpath(session.filePath);
|
|
144706
144842
|
const flowsDir = path32.dirname(realFlowPath);
|
|
144707
144843
|
const fragPath = path32.join(flowsDir, `${name}.yaml`);
|
|
144708
144844
|
const projectRoot = args.project_root;
|
|
@@ -144718,10 +144854,10 @@ async function captureRunTarget(session, args) {
|
|
|
144718
144854
|
warning: `kept the raw flow-execute step \u2014 no sibling is named "${name}.yaml" (this filesystem matched it case-insensitively to "${spelling.actual}"), so a run: ${name} step would name a flow no case-sensitive checkout can find \u2014 ${recovery}`
|
|
144719
144855
|
};
|
|
144720
144856
|
}
|
|
144721
|
-
parseFlow(await
|
|
144857
|
+
parseFlow(await fs45.readFile(fragPath, "utf8"));
|
|
144722
144858
|
let executedPath;
|
|
144723
144859
|
try {
|
|
144724
|
-
executedPath = await
|
|
144860
|
+
executedPath = await fs45.realpath(path32.join(flowsDirFor(projectRoot), `${name}.yaml`));
|
|
144725
144861
|
} catch {
|
|
144726
144862
|
executedPath = void 0;
|
|
144727
144863
|
}
|
|
@@ -144730,7 +144866,7 @@ async function captureRunTarget(session, args) {
|
|
|
144730
144866
|
warning: `kept the raw flow-execute step \u2014 could not verify which file the live flow-execute ran ("${name}" has no canonical file in project_root's flows dir to compare the sibling against)`
|
|
144731
144867
|
};
|
|
144732
144868
|
}
|
|
144733
|
-
if (executedPath !== await
|
|
144869
|
+
if (executedPath !== await fs45.realpath(fragPath)) {
|
|
144734
144870
|
return {
|
|
144735
144871
|
warning: `kept the raw flow-execute step \u2014 project_root "${projectRoot}" resolves "${name}" to "${executedPath}", not the recording's sibling "${fragPath}", so "${name}.yaml" beside the recording's real file is not the file the live flow-execute ran and a run: ${name} step would replay a different flow than the one that just ran`
|
|
144736
144872
|
};
|
|
@@ -144752,9 +144888,10 @@ function createFlowAddStepTool(registry2) {
|
|
|
144752
144888
|
completedMsg: ({ params }) => `Added ${params.command} step to flow ${params.name}`,
|
|
144753
144889
|
failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to add ${params.command} step to flow ${params.name}: ${failureSignal2.error_code}`
|
|
144754
144890
|
},
|
|
144755
|
-
description: `Execute a tool call and record it as a step in the flow named by \`name\` + \`project_root\` (the recording must already be open \u2014 see flow-start-recording). Use when recording a flow and you want to run and capture each action. A coordinate \`gesture-tap\` is recorded as a portable \`tap: { selector }\` step when the tapped element has stable text/identifier (otherwise coordinates are kept with a warning); a \`restart-app\` is recorded as a \`launch\` step (record one FIRST to make the flow a self-contained e2e flow; restart-app has no chromium support, so a chromium flow records as a fragment \u2014 add the \`launch: { chromium: <app path> }\` line to the YAML afterward, deleting the executionPrerequisite line if one was recorded: a flow that starts with a launch must not declare it).
|
|
144756
|
-
|
|
144757
|
-
|
|
144891
|
+
description: `Execute a tool call and record it as a step in the flow named by \`name\` + \`project_root\` (the recording must already be open \u2014 see flow-start-recording). Use when recording a flow and you want to run and capture each action. A coordinate \`gesture-tap\` is recorded as a portable \`tap: { selector }\` step when the tapped element has stable text/identifier (otherwise coordinates are kept with a warning); a \`restart-app\` is recorded as a \`launch\` step (record one FIRST to make the flow a self-contained e2e flow; restart-app has no chromium support, so a chromium flow records as a fragment \u2014 add the \`launch: { chromium: <app path> }\` line to the YAML afterward, deleting the executionPrerequisite line if one was recorded: a flow that starts with a launch must not declare it).
|
|
144892
|
+
Returns { message, toolResult, stepCount, recorded, savedTo } on success. If it fails an error is returned and nothing is recorded.
|
|
144893
|
+
If a step was recorded by mistake, edit the .yaml to remove it \u2014 against a remote client, only after \`flow-finish-recording\`: the in-memory copy is authoritative there, and every write serializes it over your edit.`,
|
|
144894
|
+
zodSchema: zodSchema63,
|
|
144758
144895
|
services: () => ({}),
|
|
144759
144896
|
async execute(_services, params, ctx) {
|
|
144760
144897
|
const session = await requireRecordingSession(params.project_root, params.name);
|
|
@@ -144795,11 +144932,15 @@ If a step was recorded by mistake, edit the .yaml file directly to remove it.`,
|
|
|
144795
144932
|
delayMs: params.delayMs
|
|
144796
144933
|
};
|
|
144797
144934
|
}
|
|
144798
|
-
const {
|
|
144935
|
+
const { savedTo, stepCount } = await appendStepToFlow(session, step);
|
|
144799
144936
|
return {
|
|
144800
144937
|
message: `Step added to "${params.name}" flow${warning ? ` \u2014 ${warning}` : ""}`,
|
|
144801
144938
|
toolResult,
|
|
144802
|
-
|
|
144939
|
+
stepCount,
|
|
144940
|
+
recorded: summarizeStep(step, stepCount),
|
|
144941
|
+
// Host mode: a path. Client mode: the directive that carries the YAML
|
|
144942
|
+
// to the client, which IS the persistence mechanism there — the one
|
|
144943
|
+
// place the full file still has to travel per step.
|
|
144803
144944
|
savedTo
|
|
144804
144945
|
};
|
|
144805
144946
|
}
|
|
@@ -144808,7 +144949,7 @@ If a step was recorded by mistake, edit the .yaml file directly to remove it.`,
|
|
|
144808
144949
|
|
|
144809
144950
|
// ../tool-server/src/tools/flows/flow-insert-echo.ts
|
|
144810
144951
|
init_zod();
|
|
144811
|
-
var
|
|
144952
|
+
var zodSchema64 = external_exports.object({
|
|
144812
144953
|
name: external_exports.string().describe("Name of the flow being recorded \u2014 the one passed to flow-start-recording."),
|
|
144813
144954
|
project_root: external_exports.string().describe(
|
|
144814
144955
|
"Absolute path to the project root of the flow being recorded \u2014 the same value passed to flow-start-recording. Together with `name` it identifies which recording this echo belongs to."
|
|
@@ -144826,145 +144967,23 @@ var flowInsertEchoTool = {
|
|
|
144826
144967
|
},
|
|
144827
144968
|
description: `Record an echo step in the flow named by \`name\` + \`project_root\`. Echo steps print a message when the flow is replayed \u2014 useful as labels between tool calls.
|
|
144828
144969
|
Use when you want to annotate a recorded flow with a human-readable label or checkpoint message.
|
|
144829
|
-
Returns { message,
|
|
144830
|
-
zodSchema:
|
|
144970
|
+
Returns { message, stepCount, savedTo }. Fails if that flow has no recording in progress.`,
|
|
144971
|
+
zodSchema: zodSchema64,
|
|
144831
144972
|
services: () => ({}),
|
|
144832
144973
|
async execute(_services, params) {
|
|
144833
144974
|
const session = await requireRecordingSession(params.project_root, params.name);
|
|
144834
|
-
const {
|
|
144975
|
+
const { savedTo, stepCount } = await appendStepToFlow(session, {
|
|
144835
144976
|
kind: "echo",
|
|
144836
144977
|
message: params.message
|
|
144837
144978
|
});
|
|
144838
144979
|
return {
|
|
144839
144980
|
message: `Echo added to "${params.name}" flow`,
|
|
144840
|
-
|
|
144981
|
+
stepCount,
|
|
144841
144982
|
savedTo
|
|
144842
144983
|
};
|
|
144843
144984
|
}
|
|
144844
144985
|
};
|
|
144845
144986
|
|
|
144846
|
-
// ../tool-server/src/tools/flows/flow-finish-recording.ts
|
|
144847
|
-
init_zod();
|
|
144848
|
-
var fs45 = __toESM(require("node:fs/promises"));
|
|
144849
|
-
function selectorLabel(sel) {
|
|
144850
|
-
return JSON.stringify(selectorToYaml(sel));
|
|
144851
|
-
}
|
|
144852
|
-
function textConditionLabel(sel, expectedText, textMatch) {
|
|
144853
|
-
const selector = selectorLabel(sel);
|
|
144854
|
-
const expected = expectedText ?? "";
|
|
144855
|
-
return textMatch === "matches" ? `text ${selector} matches /${expected}/` : textMatch === "equals" ? `text ${selector} == ${JSON.stringify(expected)}` : `text ${selector} contains ${JSON.stringify(expected)}`;
|
|
144856
|
-
}
|
|
144857
|
-
var zodSchema64 = external_exports.object({
|
|
144858
|
-
name: external_exports.string().describe("Name of the flow being recorded \u2014 the one passed to flow-start-recording."),
|
|
144859
|
-
project_root: external_exports.string().describe(
|
|
144860
|
-
"Absolute path to the project root of the flow being recorded \u2014 the same value passed to flow-start-recording. Together with `name` it identifies which recording to finish."
|
|
144861
|
-
)
|
|
144862
|
-
});
|
|
144863
|
-
var flowFinishRecordingTool = {
|
|
144864
|
-
id: "flow-finish-recording",
|
|
144865
|
-
interaction: {
|
|
144866
|
-
// Name the flow: other recordings stay live across this call, so an
|
|
144867
|
-
// unqualified "Finishing flow recording" would not identify which one.
|
|
144868
|
-
startedMsg: ({ params }) => `Finishing recording of flow ${params.name}`,
|
|
144869
|
-
// `params.name` rather than the basename of `result.path`: the two are the
|
|
144870
|
-
// same string on every branch — `assertSafeFlowName` admits no dots or
|
|
144871
|
-
// separators, so `getFlowPath` produces `<name>.yaml` and nothing else —
|
|
144872
|
-
// and this spelling matches the two formatters either side of it.
|
|
144873
|
-
completedMsg: ({ params }) => `Saved recorded flow ${params.name}`,
|
|
144874
|
-
failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to finish recording of flow ${params.name}: ${failureSignal2.error_code}`
|
|
144875
|
-
},
|
|
144876
|
-
description: `Finish recording the flow named by \`name\` + \`project_root\`, leaving recordings under any other key untouched. Returns { message, path, executionPrerequisite, steps, summary, flowFile, savedTo } - a summary of all recorded steps plus the final YAML. Use when you have added all desired steps and want to finalize the flow file. Fails if that flow has no recording in progress.
|
|
144877
|
-
You can still edit the .yaml file directly afterwards to remove or reorder steps.`,
|
|
144878
|
-
zodSchema: zodSchema64,
|
|
144879
|
-
services: () => ({}),
|
|
144880
|
-
async execute(_services, params) {
|
|
144881
|
-
const { filePath, flowFile, savedTo, flow, summary } = await withFlowFileLock(
|
|
144882
|
-
params.project_root,
|
|
144883
|
-
params.name,
|
|
144884
|
-
async () => {
|
|
144885
|
-
const session = await requireRecordingSession(params.project_root, params.name);
|
|
144886
|
-
const filePath2 = session.filePath;
|
|
144887
|
-
let flowFile2;
|
|
144888
|
-
let savedTo2;
|
|
144889
|
-
if (session.persist === "client") {
|
|
144890
|
-
flowFile2 = serializeFlow(session.flow);
|
|
144891
|
-
savedTo2 = clientFileDirective(filePath2, flowFile2);
|
|
144892
|
-
} else {
|
|
144893
|
-
flowFile2 = await fs45.readFile(filePath2, "utf8");
|
|
144894
|
-
savedTo2 = filePath2;
|
|
144895
|
-
}
|
|
144896
|
-
const flow2 = parseFlow(flowFile2);
|
|
144897
|
-
const summary2 = summarizeSteps(flow2);
|
|
144898
|
-
clearRecordingSession(session);
|
|
144899
|
-
return { filePath: filePath2, flowFile: flowFile2, savedTo: savedTo2, flow: flow2, summary: summary2 };
|
|
144900
|
-
}
|
|
144901
|
-
);
|
|
144902
|
-
return {
|
|
144903
|
-
message: `Finished recording "${params.name}" flow (${flow.steps.length} steps)`,
|
|
144904
|
-
path: filePath,
|
|
144905
|
-
executionPrerequisite: flow.executionPrerequisite,
|
|
144906
|
-
steps: flow.steps.length,
|
|
144907
|
-
summary,
|
|
144908
|
-
flowFile,
|
|
144909
|
-
savedTo
|
|
144910
|
-
};
|
|
144911
|
-
}
|
|
144912
|
-
};
|
|
144913
|
-
function renderToolArgs(args) {
|
|
144914
|
-
try {
|
|
144915
|
-
return `${JSON.stringify(args)}`;
|
|
144916
|
-
} catch {
|
|
144917
|
-
return "[cyclic args]";
|
|
144918
|
-
}
|
|
144919
|
-
}
|
|
144920
|
-
function summarizeSteps(flow) {
|
|
144921
|
-
return flow.steps.map((step, i) => {
|
|
144922
|
-
const n = i + 1;
|
|
144923
|
-
switch (step.kind) {
|
|
144924
|
-
case "echo":
|
|
144925
|
-
return `${n}. echo: ${step.message}`;
|
|
144926
|
-
case "launch":
|
|
144927
|
-
return `${n}. launch: ${typeof step.app === "string" ? step.app : JSON.stringify(step.app)}`;
|
|
144928
|
-
case "run":
|
|
144929
|
-
return `${n}. run: ${step.flow}`;
|
|
144930
|
-
case "tap":
|
|
144931
|
-
case "long-press":
|
|
144932
|
-
return `${n}. ${step.kind}: ${step.selector ? selectorLabel(step.selector) : `(${step.x}, ${step.y})`}`;
|
|
144933
|
-
case "type":
|
|
144934
|
-
return `${n}. type: ${selectorLabel(step.into)} \u2190 "${step.text}"`;
|
|
144935
|
-
case "await":
|
|
144936
|
-
case "assert": {
|
|
144937
|
-
const tail = step.condition === "text" ? textConditionLabel(step.selector, step.expectedText, step.textMatch) : `${step.condition} ${selectorLabel(step.selector)}`;
|
|
144938
|
-
return `${n}. ${step.kind}: ${tail}`;
|
|
144939
|
-
}
|
|
144940
|
-
case "wait":
|
|
144941
|
-
return `${n}. wait: ${step.ms}ms`;
|
|
144942
|
-
case "when": {
|
|
144943
|
-
const cond = step.condition.kind === "platform" ? `platform ${step.condition.platform}` : step.condition.condition === "text" ? textConditionLabel(
|
|
144944
|
-
step.condition.selector,
|
|
144945
|
-
step.condition.expectedText,
|
|
144946
|
-
step.condition.textMatch
|
|
144947
|
-
) : `${step.condition.condition} ${selectorLabel(step.condition.selector)}`;
|
|
144948
|
-
const count2 = step.steps.length;
|
|
144949
|
-
return `${n}. when: ${cond} (${count2} step${count2 === 1 ? "" : "s"})`;
|
|
144950
|
-
}
|
|
144951
|
-
case "scroll-to":
|
|
144952
|
-
return `${n}. scroll-to: ${selectorLabel(step.target)} (${step.direction})`;
|
|
144953
|
-
case "pinch":
|
|
144954
|
-
return `${n}. pinch: scale ${step.scale}${step.selector ? ` on ${selectorLabel(step.selector)}` : ""}`;
|
|
144955
|
-
case "rotate":
|
|
144956
|
-
return `${n}. rotate: by ${step.by}\xB0${step.selector ? ` on ${selectorLabel(step.selector)}` : ""}`;
|
|
144957
|
-
case "snapshot":
|
|
144958
|
-
return `${n}. snapshot: ${step.name}`;
|
|
144959
|
-
case "idle":
|
|
144960
|
-
return `${n}. await: screen idle`;
|
|
144961
|
-
case "tool":
|
|
144962
|
-
default:
|
|
144963
|
-
return `${n}. tool: ${step.name} ${renderToolArgs(step.args)}`;
|
|
144964
|
-
}
|
|
144965
|
-
});
|
|
144966
|
-
}
|
|
144967
|
-
|
|
144968
144987
|
// ../tool-server/src/tools/flows/flow-run.ts
|
|
144969
144988
|
init_zod();
|
|
144970
144989
|
var fs49 = __toESM(require("node:fs/promises"));
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
package/rules/argent.md
CHANGED
|
@@ -76,6 +76,7 @@ Decision order:
|
|
|
76
76
|
- Interaction tools (`gesture-tap`, `gesture-swipe`, `gesture-pinch`, `gesture-rotate`, `gesture-custom`, `launch-app`, etc.) return a screenshot automatically.
|
|
77
77
|
Call `screenshot` separately only for a baseline before any action or after a delay.
|
|
78
78
|
- Always open apps with `launch-app` or `open-url` — never tap home screen icons.
|
|
79
|
+
- If a task can require a saved flow, choose `argent-create-flow` or `argent-qa-flows` before the first launch or in-app action. Start the recorder before walking the path; recording is not retroactive.
|
|
79
80
|
- Always use `run-sequence` when performing multiple sequential device actions where you don't need to observe the screen between steps. More in `argent-device-interact` skill.
|
|
80
81
|
- When the session ends or the user says they are done: call `stop-all-simulator-servers` with `devices: [...]`
|
|
81
82
|
naming the devices this session actually used. One tool-server is shared by every other agent using this
|
|
@@ -124,6 +125,7 @@ TV INTERACTION (APPLE TV / ANDROID TV / FIRE TV)
|
|
|
124
125
|
Skill: `argent-tv-interact`
|
|
125
126
|
When: Any TV target — a `list-devices` entry with `runtimeKind: "tv"` (Apple TV simulator or Android TV emulator) or `platform:"vega"` / `kind:"vvd"` (Amazon Fire TV / VVD), or the user mentions Apple TV / tvOS / Android TV / leanback / Vega / Fire TV. A TV UI is focus-driven, not touch-driven: drive it with `describe` (read focus) + `tv-remote` (D-pad presses) + `keyboard` (type); `gesture-*` tools do NOT apply. Covers booting the target, app lifecycle, focus navigation, typing, screenshots, and (Vega) VVD lifecycle + Fast Refresh + JS-runtime debugging (evaluate, console logs, network inspector).
|
|
126
127
|
Prompt keywords: apple tv, tvos, android tv, leanback, vega, fire tv, vvd, d-pad
|
|
128
|
+
Saved artifacts: on Vega, a replayable path is `argent-create-flow` and an acceptance-criteria regression test is `argent-qa-flows` — both record D-pad navigation as `tool: tv-remote` steps. Apple TV and Android TV have no saved-flow support; report that limitation.
|
|
127
129
|
|
|
128
130
|
SCREENSHOT DIFF & VISUAL REGRESSION
|
|
129
131
|
Skill: `argent-screenshot-diff`
|
|
@@ -131,7 +133,7 @@ When: Explicit visual regression, screenshot diff, compare screenshots, before/a
|
|
|
131
133
|
|
|
132
134
|
SCREEN RECORDING (VIDEO CAPTURE)
|
|
133
135
|
Skill: `argent-screen-recording`
|
|
134
|
-
When: The user wants
|
|
136
|
+
When: The user wants an mp4 of an interaction, animation, or bug reproduction. Use `argent-create-flow` instead for a replayable sequence.
|
|
135
137
|
Prompt keywords: record, recording, screen recording, video, capture video, clip, mp4
|
|
136
138
|
|
|
137
139
|
RUNNING / BUILDING / DEBUGGING REACT NATIVE APP
|
|
@@ -154,15 +156,21 @@ PERFORMANCE OPTIMIZATION
|
|
|
154
156
|
Use skill: `argent-react-native-optimization`
|
|
155
157
|
When: App feels slow, user asks to optimize, reducing bundle size, improving startup time, fixing re-renders, optimizing lists/images/navigation, or any performance-related task. This is the entry-point skill for all performance work — it delegates to `argent-react-native-profiler` for measurement.
|
|
156
158
|
|
|
157
|
-
|
|
159
|
+
INTERACTIVE UI TESTING (ONE-OFF, NOT SAVED)
|
|
158
160
|
Skill: `argent-test-ui-flow`
|
|
159
|
-
When:
|
|
161
|
+
When: Running a one-off interact → screenshot → verify check with no saved regression artifact.
|
|
160
162
|
|
|
161
163
|
RECORDING & REPLAYING FLOWS
|
|
162
164
|
Use skill: `argent-create-flow`
|
|
163
|
-
When:
|
|
165
|
+
When: Saving or replaying a repeatable path for profiling, A/B comparison, retry, or reuse. For acceptance-driven regression tests, use `argent-qa-flows`.
|
|
164
166
|
Prompt keywords: flow, repeat, test X times
|
|
165
167
|
|
|
168
|
+
GENERATED QA REGRESSION TESTS
|
|
169
|
+
Use skill: `argent-qa-flows`
|
|
170
|
+
When: Saving a test case, ticket, or acceptance criteria as a repeatable regression test. Requires stable evidence and two unchanged full passes. iOS, Android, Chromium, and Vega (D-pad navigation records as `tool: tv-remote` steps); not Apple TV or Android TV.
|
|
171
|
+
Prompt keywords: QA test, regression test, test case, automate this test, automate an e2e test, keep this e2e test, generate a test
|
|
172
|
+
Routing: one-off check → `argent-test-ui-flow`; saved path → `argent-create-flow`; saved acceptance test → `argent-qa-flows`.
|
|
173
|
+
|
|
166
174
|
PROPOSING DESIGN VARIANTS FOR HUMAN SELECTION
|
|
167
175
|
Use skill: `argent-lens`
|
|
168
176
|
When: The user asks for design alternatives / options / A-B choices for a screen or component, or you have produced more than one candidate look for an element and want a human to pick before committing. Covers the build → navigate → screenshot → propose_variant loop and the single blocking await_user_selection call. (Gated behind the `argent-lens` flag, off by default — run `argent enable argent-lens` first.)
|