@exodus/xqa 1.2.3 → 1.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/dist/skills/xqa-spec/AGENTS.md +94 -0
- package/dist/skills/xqa-spec/SKILL.md +100 -0
- package/dist/skills/xqa-spec/metadata.json +5 -0
- package/dist/xqa.cjs +492 -287
- package/package.json +4 -3
package/dist/xqa.cjs
CHANGED
|
@@ -3587,7 +3587,7 @@ var require_index_cjs = __commonJS({
|
|
|
3587
3587
|
}, reject);
|
|
3588
3588
|
}
|
|
3589
3589
|
}
|
|
3590
|
-
var
|
|
3590
|
+
var ResultAsync9 = class _ResultAsync {
|
|
3591
3591
|
constructor(res) {
|
|
3592
3592
|
this._promise = res;
|
|
3593
3593
|
}
|
|
@@ -3726,14 +3726,14 @@ var require_index_cjs = __commonJS({
|
|
|
3726
3726
|
}
|
|
3727
3727
|
};
|
|
3728
3728
|
function okAsync8(value) {
|
|
3729
|
-
return new
|
|
3729
|
+
return new ResultAsync9(Promise.resolve(new Ok(value)));
|
|
3730
3730
|
}
|
|
3731
3731
|
function errAsync7(err17) {
|
|
3732
|
-
return new
|
|
3732
|
+
return new ResultAsync9(Promise.resolve(new Err(err17)));
|
|
3733
3733
|
}
|
|
3734
|
-
var fromPromise =
|
|
3735
|
-
var fromSafePromise2 =
|
|
3736
|
-
var fromAsyncThrowable9 =
|
|
3734
|
+
var fromPromise = ResultAsync9.fromPromise;
|
|
3735
|
+
var fromSafePromise2 = ResultAsync9.fromSafePromise;
|
|
3736
|
+
var fromAsyncThrowable9 = ResultAsync9.fromThrowable;
|
|
3737
3737
|
var combineResultList = (resultList) => {
|
|
3738
3738
|
let acc = ok17([]);
|
|
3739
3739
|
for (const result of resultList) {
|
|
@@ -3746,7 +3746,7 @@ var require_index_cjs = __commonJS({
|
|
|
3746
3746
|
}
|
|
3747
3747
|
return acc;
|
|
3748
3748
|
};
|
|
3749
|
-
var combineResultAsyncList = (asyncResultList) =>
|
|
3749
|
+
var combineResultAsyncList = (asyncResultList) => ResultAsync9.fromSafePromise(Promise.all(asyncResultList)).andThen(combineResultList);
|
|
3750
3750
|
var combineResultListWithAllErrors = (resultList) => {
|
|
3751
3751
|
let acc = ok17([]);
|
|
3752
3752
|
for (const result of resultList) {
|
|
@@ -3760,9 +3760,9 @@ var require_index_cjs = __commonJS({
|
|
|
3760
3760
|
}
|
|
3761
3761
|
return acc;
|
|
3762
3762
|
};
|
|
3763
|
-
var combineResultAsyncListWithAllErrors = (asyncResultList) =>
|
|
3763
|
+
var combineResultAsyncListWithAllErrors = (asyncResultList) => ResultAsync9.fromSafePromise(Promise.all(asyncResultList)).andThen(combineResultListWithAllErrors);
|
|
3764
3764
|
exports2.Result = void 0;
|
|
3765
|
-
(function(
|
|
3765
|
+
(function(Result3) {
|
|
3766
3766
|
function fromThrowable11(fn, errorFn) {
|
|
3767
3767
|
return (...args) => {
|
|
3768
3768
|
try {
|
|
@@ -3773,15 +3773,15 @@ var require_index_cjs = __commonJS({
|
|
|
3773
3773
|
}
|
|
3774
3774
|
};
|
|
3775
3775
|
}
|
|
3776
|
-
|
|
3776
|
+
Result3.fromThrowable = fromThrowable11;
|
|
3777
3777
|
function combine(resultList) {
|
|
3778
3778
|
return combineResultList(resultList);
|
|
3779
3779
|
}
|
|
3780
|
-
|
|
3780
|
+
Result3.combine = combine;
|
|
3781
3781
|
function combineWithAllErrors(resultList) {
|
|
3782
3782
|
return combineResultListWithAllErrors(resultList);
|
|
3783
3783
|
}
|
|
3784
|
-
|
|
3784
|
+
Result3.combineWithAllErrors = combineWithAllErrors;
|
|
3785
3785
|
})(exports2.Result || (exports2.Result = {}));
|
|
3786
3786
|
function ok17(value) {
|
|
3787
3787
|
return new Ok(value);
|
|
@@ -3792,7 +3792,7 @@ var require_index_cjs = __commonJS({
|
|
|
3792
3792
|
function safeTry(body) {
|
|
3793
3793
|
const n3 = body().next();
|
|
3794
3794
|
if (n3 instanceof Promise) {
|
|
3795
|
-
return new
|
|
3795
|
+
return new ResultAsync9(n3.then((r3) => r3.value));
|
|
3796
3796
|
}
|
|
3797
3797
|
return n3.value;
|
|
3798
3798
|
}
|
|
@@ -3843,7 +3843,7 @@ var require_index_cjs = __commonJS({
|
|
|
3843
3843
|
return f6(this.value).map(() => this.value);
|
|
3844
3844
|
}
|
|
3845
3845
|
asyncMap(f6) {
|
|
3846
|
-
return
|
|
3846
|
+
return ResultAsync9.fromSafePromise(f6(this.value));
|
|
3847
3847
|
}
|
|
3848
3848
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
3849
3849
|
unwrapOr(_v) {
|
|
@@ -3947,7 +3947,7 @@ var require_index_cjs = __commonJS({
|
|
|
3947
3947
|
var fromThrowable10 = exports2.Result.fromThrowable;
|
|
3948
3948
|
exports2.Err = Err;
|
|
3949
3949
|
exports2.Ok = Ok;
|
|
3950
|
-
exports2.ResultAsync =
|
|
3950
|
+
exports2.ResultAsync = ResultAsync9;
|
|
3951
3951
|
exports2.err = err16;
|
|
3952
3952
|
exports2.errAsync = errAsync7;
|
|
3953
3953
|
exports2.fromAsyncThrowable = fromAsyncThrowable9;
|
|
@@ -48106,7 +48106,7 @@ function dismissalsPath(baseDirectory, override) {
|
|
|
48106
48106
|
}
|
|
48107
48107
|
|
|
48108
48108
|
// ../../packages/pipeline/dist/index.js
|
|
48109
|
-
var
|
|
48109
|
+
var import_neverthrow28 = __toESM(require_index_cjs(), 1);
|
|
48110
48110
|
var import_promises14 = require("node:timers/promises");
|
|
48111
48111
|
|
|
48112
48112
|
// ../../agents/analyser/dist/index.js
|
|
@@ -55763,10 +55763,10 @@ function runConsolidator(input, config3) {
|
|
|
55763
55763
|
}
|
|
55764
55764
|
|
|
55765
55765
|
// ../../packages/pipeline/dist/index.js
|
|
55766
|
-
var import_neverthrow28 = __toESM(require_index_cjs(), 1);
|
|
55767
55766
|
var import_neverthrow29 = __toESM(require_index_cjs(), 1);
|
|
55768
|
-
var import_node_fs3 = require("node:fs");
|
|
55769
55767
|
var import_neverthrow30 = __toESM(require_index_cjs(), 1);
|
|
55768
|
+
var import_node_fs3 = require("node:fs");
|
|
55769
|
+
var import_neverthrow31 = __toESM(require_index_cjs(), 1);
|
|
55770
55770
|
var import_promises15 = require("node:timers/promises");
|
|
55771
55771
|
|
|
55772
55772
|
// ../../agents/explorer/dist/index.js
|
|
@@ -55776,16 +55776,17 @@ var import_node_path4 = __toESM(require("node:path"), 1);
|
|
|
55776
55776
|
var import_neverthrow14 = __toESM(require_index_cjs(), 1);
|
|
55777
55777
|
var import_promises8 = require("node:fs/promises");
|
|
55778
55778
|
var import_neverthrow15 = __toESM(require_index_cjs(), 1);
|
|
55779
|
-
var import_promises9 = require("node:timers/promises");
|
|
55780
55779
|
var import_neverthrow16 = __toESM(require_index_cjs(), 1);
|
|
55780
|
+
var import_promises9 = require("node:timers/promises");
|
|
55781
55781
|
var import_neverthrow17 = __toESM(require_index_cjs(), 1);
|
|
55782
|
+
var import_neverthrow18 = __toESM(require_index_cjs(), 1);
|
|
55782
55783
|
var import_promises10 = require("node:fs/promises");
|
|
55783
55784
|
var import_node_path5 = __toESM(require("node:path"), 1);
|
|
55784
|
-
var
|
|
55785
|
+
var import_neverthrow19 = __toESM(require_index_cjs(), 1);
|
|
55785
55786
|
var import_node_child_process4 = require("node:child_process");
|
|
55786
55787
|
var import_promises11 = require("node:fs/promises");
|
|
55787
55788
|
var import_node_path6 = __toESM(require("node:path"), 1);
|
|
55788
|
-
var
|
|
55789
|
+
var import_neverthrow20 = __toESM(require_index_cjs(), 1);
|
|
55789
55790
|
async function runFfmpeg(arguments_) {
|
|
55790
55791
|
const { promise: promise2, resolve, reject } = Promise.withResolvers();
|
|
55791
55792
|
(0, import_node_child_process3.execFile)("ffmpeg", arguments_, (error48) => {
|
|
@@ -55883,6 +55884,277 @@ function speedUpVideo({
|
|
|
55883
55884
|
() => outputPath
|
|
55884
55885
|
);
|
|
55885
55886
|
}
|
|
55887
|
+
var DEV_ENVIRONMENT_SECTION = `## Environment
|
|
55888
|
+
|
|
55889
|
+
This is a development build. Debug overlays and internal messages are expected artifacts \u2014 do not report them as findings.`;
|
|
55890
|
+
var WORKING_STATE_SECTION = `## Working State
|
|
55891
|
+
|
|
55892
|
+
At every reasoning step, maintain a mental ledger:
|
|
55893
|
+
- VISITED: screen names confirmed via \`view_ui\` this session
|
|
55894
|
+
- QUEUE: screen names seen as reachable but not yet explored
|
|
55895
|
+
- PATH: your current navigation stack from root (e.g. Home > Settings > Privacy)
|
|
55896
|
+
|
|
55897
|
+
Consult the ledger before every action. Always prefer navigating to a QUEUE screen over a VISITED one.`;
|
|
55898
|
+
var BACK_NAV_RULE = `- After navigating forward to any new screen: tap back, call \`view_ui\`, confirm you returned to the expected parent in PATH \u2014 if not, emit a \`back-nav-failure\` finding, then navigate forward again to continue`;
|
|
55899
|
+
var STUCK_LOOP_RULE = `- Stuck loop: emit a \`stuck-loop\` finding when any of these occur: (1) \`view_ui\` returns the same screen state 3 or more consecutive steps, (2) the same element has been tapped more than twice with no screen change, (3) PATH shows the same screen at two non-adjacent positions \u2014 before emitting, try one alternative action (scroll, long-press, swipe) to rule out a gesture mismatch`;
|
|
55900
|
+
var CLIPPED_ELEMENT_RULE = `- Never tap an element tagged \`[clipped-top]\`, \`[clipped-bottom]\`, \`[clipped-left]\`, or \`[clipped-right]\` \u2014 scroll to fully reveal it first, then re-call \`view_ui\` before tapping`;
|
|
55901
|
+
var WHAT_TO_TEST_SECTION = `## What to Test
|
|
55902
|
+
|
|
55903
|
+
Test navigation elements first, interactions second.
|
|
55904
|
+
|
|
55905
|
+
**Tier 1 \u2014 test every one, every time:**
|
|
55906
|
+
- Tab bar items, drawer menu items, hamburger menus
|
|
55907
|
+
- Back buttons, close buttons (X), modal dismiss controls
|
|
55908
|
+
- Bottom sheet handles and drag gestures
|
|
55909
|
+
- Navigation links and "Go to X" CTAs
|
|
55910
|
+
|
|
55911
|
+
**Tier 2 \u2014 test one representative per type per screen:**
|
|
55912
|
+
- Primary action buttons
|
|
55913
|
+
- Form inputs and toggles
|
|
55914
|
+
- Segmented controls and dropdowns
|
|
55915
|
+
|
|
55916
|
+
**Tier 3 \u2014 skip unless visibly broken:**
|
|
55917
|
+
- Static labels, decorative images, dividers
|
|
55918
|
+
|
|
55919
|
+
If an interaction produces no observable change, retry once before flagging.`;
|
|
55920
|
+
var DEAD_END_SECTION = `## Dead End and Modal Detection
|
|
55921
|
+
|
|
55922
|
+
**Dead end** \u2014 when \`view_ui\` shows no interactive exit affordance, attempt ALL of before emitting a finding: (1) any visible back/close button, (2) swipe from the left edge (back gesture), (3) swipe down (dismiss gesture). If all fail, emit a \`dead-end\` finding describing what was visible and what was attempted.
|
|
55923
|
+
|
|
55924
|
+
**Stuck modal** \u2014 when a modal or bottom sheet blocks the screen, attempt dismissal in order: (1) close/X button if present, (2) tap outside the modal, (3) swipe down, (4) swipe from the left edge. If all fail, emit a \`stuck-modal\` finding listing the modal, the screen it appeared on, and the methods attempted.`;
|
|
55925
|
+
var SPEC_MODE_TEMPLATE = (specContent, options) => {
|
|
55926
|
+
const appSection = options.userPrompt ? `## Application
|
|
55927
|
+
|
|
55928
|
+
${options.userPrompt}
|
|
55929
|
+
|
|
55930
|
+
` : "";
|
|
55931
|
+
const environmentSection = options.buildEnv === "dev" ? `
|
|
55932
|
+
|
|
55933
|
+
${DEV_ENVIRONMENT_SECTION}` : "";
|
|
55934
|
+
return `You are a navigation and interaction testing agent. Your role is to find broken navigation flows and non-functional interactive elements. Do not report content bugs, copy errors, or visual style issues unless they directly prevent a navigation action from completing.
|
|
55935
|
+
|
|
55936
|
+
Verify app against specs below.
|
|
55937
|
+
|
|
55938
|
+
${appSection}${WORKING_STATE_SECTION}
|
|
55939
|
+
|
|
55940
|
+
## Rules
|
|
55941
|
+
|
|
55942
|
+
- ALWAYS call \`view_ui\` after every action before deciding what to do next \u2014 it is your only way to observe the screen
|
|
55943
|
+
- ${BACK_NAV_RULE.slice(2)}
|
|
55944
|
+
- Before selecting any action, prefer navigating to a QUEUE screen over re-exploring a VISITED one
|
|
55945
|
+
- ${STUCK_LOOP_RULE.slice(2)}
|
|
55946
|
+
- ${CLIPPED_ELEMENT_RULE.slice(2)}
|
|
55947
|
+
- Each item in \`**Assertions**\` is a mandatory pass/fail check \u2014 verify using \`view_ui\`; if the accessibility tree cannot confirm, emit a \`spec-deviation\` finding based on what is observable
|
|
55948
|
+
- Flag unexpected navigation failures, broken interactions, or crash dialogs encountered during step execution, even if not listed as assertions
|
|
55949
|
+
|
|
55950
|
+
## Exploration Strategy
|
|
55951
|
+
|
|
55952
|
+
Navigate to verify each spec's scenarios. When choosing how to reach a screen, prefer breadth-first paths \u2014 map sibling screens before going deeper into any one branch.
|
|
55953
|
+
|
|
55954
|
+
${WHAT_TO_TEST_SECTION}
|
|
55955
|
+
|
|
55956
|
+
${DEAD_END_SECTION}
|
|
55957
|
+
|
|
55958
|
+
## Specs
|
|
55959
|
+
|
|
55960
|
+
${specContent}${environmentSection}
|
|
55961
|
+
|
|
55962
|
+
## Output
|
|
55963
|
+
|
|
55964
|
+
CRITICAL: Call \`set_output\` each time your findings change \u2014 when you discover something new, confirm a false positive, or revise a finding. Each call replaces the previous output entirely, so always pass the full current list. Do not reply in plain text.`;
|
|
55965
|
+
};
|
|
55966
|
+
var FREESTYLE_TEMPLATE = (options) => {
|
|
55967
|
+
const { userPrompt, buildEnv } = options ?? {};
|
|
55968
|
+
const appSection = userPrompt ? `## Application
|
|
55969
|
+
|
|
55970
|
+
${userPrompt}
|
|
55971
|
+
|
|
55972
|
+
` : "";
|
|
55973
|
+
const environmentSection = buildEnv === "dev" ? `
|
|
55974
|
+
|
|
55975
|
+
${DEV_ENVIRONMENT_SECTION}` : "";
|
|
55976
|
+
return `You are a navigation and interaction testing agent. Your role is to find broken navigation flows and non-functional interactive elements. Do not report content bugs, copy errors, or visual style issues unless they directly prevent a navigation action from completing.
|
|
55977
|
+
|
|
55978
|
+
${appSection}${WORKING_STATE_SECTION}
|
|
55979
|
+
|
|
55980
|
+
## Rules
|
|
55981
|
+
|
|
55982
|
+
- ALWAYS call \`view_ui\` after every action before deciding what to do next \u2014 it is your only way to observe the screen
|
|
55983
|
+
- ${BACK_NAV_RULE.slice(2)}
|
|
55984
|
+
- Before selecting any action, prefer navigating to a QUEUE screen over re-exploring a VISITED one
|
|
55985
|
+
- ${STUCK_LOOP_RULE.slice(2)}
|
|
55986
|
+
- ${CLIPPED_ELEMENT_RULE.slice(2)}
|
|
55987
|
+
|
|
55988
|
+
## Exploration Strategy
|
|
55989
|
+
|
|
55990
|
+
Explore breadth-first: map all screens reachable from the current screen before descending into any one path. Visit all siblings at the current depth before going deeper.
|
|
55991
|
+
|
|
55992
|
+
${WHAT_TO_TEST_SECTION}
|
|
55993
|
+
|
|
55994
|
+
${DEAD_END_SECTION}${environmentSection}
|
|
55995
|
+
|
|
55996
|
+
## Output
|
|
55997
|
+
|
|
55998
|
+
CRITICAL: Call \`set_output\` each time your findings change \u2014 when you discover something new, confirm a false positive, or revise a finding. Each call replaces the previous output entirely, so always pass the full current list. Do not reply in plain text.`;
|
|
55999
|
+
};
|
|
56000
|
+
function generateExplorerPrompt({
|
|
56001
|
+
mode,
|
|
56002
|
+
specs,
|
|
56003
|
+
userPrompt,
|
|
56004
|
+
buildEnv
|
|
56005
|
+
}) {
|
|
56006
|
+
return mode === "spec" ? buildSpecModePrompt(specs, { userPrompt, buildEnv }) : FREESTYLE_TEMPLATE({ userPrompt, buildEnv });
|
|
56007
|
+
}
|
|
56008
|
+
function renderStep(step, index) {
|
|
56009
|
+
const stepNumber = String(index + 1);
|
|
56010
|
+
const base = `${stepNumber}. ${step.action}`;
|
|
56011
|
+
return step.assertion === void 0 ? base : `${base} \u2192 ${step.assertion}`;
|
|
56012
|
+
}
|
|
56013
|
+
function collectAssertions(spec) {
|
|
56014
|
+
return spec.assertions;
|
|
56015
|
+
}
|
|
56016
|
+
function renderSpec(spec) {
|
|
56017
|
+
const stepsBlock = spec.steps.map((step, index) => renderStep(step, index)).join("\n");
|
|
56018
|
+
const allAssertions = collectAssertions(spec);
|
|
56019
|
+
const assertionsBlock = allAssertions.length > 0 ? `
|
|
56020
|
+
|
|
56021
|
+
**Assertions**
|
|
56022
|
+
${allAssertions.map((assertion) => `- ${assertion}`).join("\n")}` : "";
|
|
56023
|
+
return `### ${spec.name}
|
|
56024
|
+
|
|
56025
|
+
**Setup**
|
|
56026
|
+
${spec.setup}
|
|
56027
|
+
|
|
56028
|
+
**Steps**
|
|
56029
|
+
${stepsBlock}${assertionsBlock}`;
|
|
56030
|
+
}
|
|
56031
|
+
function buildSpecModePrompt(specs, options) {
|
|
56032
|
+
const specContent = specs.map((spec) => renderSpec(spec)).join("\n\n---\n\n");
|
|
56033
|
+
return SPEC_MODE_TEMPLATE(specContent, options);
|
|
56034
|
+
}
|
|
56035
|
+
var FRONTMATTER_FENCE = "---";
|
|
56036
|
+
var INLINE_ASSERTION_DELIMITER = " \u2192 ";
|
|
56037
|
+
var NUMBERED_STEP_PREFIX = /^\d+\.\s+/;
|
|
56038
|
+
function parseTagsValue(value) {
|
|
56039
|
+
if (!value.startsWith("[") || !value.endsWith("]")) {
|
|
56040
|
+
return (0, import_neverthrow16.err)({ type: "MALFORMED_FRONTMATTER", cause: `Invalid tags format: ${value}` });
|
|
56041
|
+
}
|
|
56042
|
+
return (0, import_neverthrow16.ok)(
|
|
56043
|
+
value.slice(1, -1).split(",").map((tag) => tag.trim()).filter((tag) => tag.length > 0)
|
|
56044
|
+
);
|
|
56045
|
+
}
|
|
56046
|
+
function parseTimeoutValue(value) {
|
|
56047
|
+
const parsed = Number(value);
|
|
56048
|
+
if (Number.isNaN(parsed) || parsed <= 0) {
|
|
56049
|
+
return (0, import_neverthrow16.err)({ type: "MALFORMED_FRONTMATTER", cause: `Invalid timeout: ${value}` });
|
|
56050
|
+
}
|
|
56051
|
+
return (0, import_neverthrow16.ok)(parsed);
|
|
56052
|
+
}
|
|
56053
|
+
function parseFrontmatterLine(line) {
|
|
56054
|
+
const colonIndex = line.indexOf(":");
|
|
56055
|
+
if (colonIndex === -1) {
|
|
56056
|
+
return (0, import_neverthrow16.err)({ type: "MALFORMED_FRONTMATTER", cause: `Invalid line: ${line}` });
|
|
56057
|
+
}
|
|
56058
|
+
const key = line.slice(0, colonIndex).trim();
|
|
56059
|
+
const value = line.slice(colonIndex + 1).trim();
|
|
56060
|
+
switch (key) {
|
|
56061
|
+
case "description": {
|
|
56062
|
+
return (0, import_neverthrow16.ok)({ description: value });
|
|
56063
|
+
}
|
|
56064
|
+
case "tags": {
|
|
56065
|
+
return parseTagsValue(value).map((tags) => ({ tags }));
|
|
56066
|
+
}
|
|
56067
|
+
case "timeout": {
|
|
56068
|
+
return parseTimeoutValue(value).map((timeout) => ({ timeout }));
|
|
56069
|
+
}
|
|
56070
|
+
default: {
|
|
56071
|
+
return (0, import_neverthrow16.ok)({});
|
|
56072
|
+
}
|
|
56073
|
+
}
|
|
56074
|
+
}
|
|
56075
|
+
function normalizeFrontmatter(raw) {
|
|
56076
|
+
return {
|
|
56077
|
+
description: raw.description,
|
|
56078
|
+
tags: raw.tags ?? [],
|
|
56079
|
+
timeout: raw.timeout
|
|
56080
|
+
};
|
|
56081
|
+
}
|
|
56082
|
+
function emptyFrontmatter() {
|
|
56083
|
+
return { description: void 0, tags: [], timeout: void 0 };
|
|
56084
|
+
}
|
|
56085
|
+
function mergePartials(partials) {
|
|
56086
|
+
return Object.fromEntries(partials.flatMap((partial2) => Object.entries(partial2)));
|
|
56087
|
+
}
|
|
56088
|
+
function combineFrontmatterLines(lines) {
|
|
56089
|
+
const lineResults = lines.filter((line) => line.trim().length > 0).map((line) => parseFrontmatterLine(line));
|
|
56090
|
+
return import_neverthrow16.Result.combine(lineResults).map((partials) => mergePartials(partials)).map((merged) => normalizeFrontmatter(merged));
|
|
56091
|
+
}
|
|
56092
|
+
function extractFrontmatter(content) {
|
|
56093
|
+
const trimmed = content.trimStart();
|
|
56094
|
+
if (!trimmed.startsWith(FRONTMATTER_FENCE)) {
|
|
56095
|
+
return (0, import_neverthrow16.ok)({ frontmatter: emptyFrontmatter(), body: content });
|
|
56096
|
+
}
|
|
56097
|
+
const afterOpenFence = trimmed.slice(FRONTMATTER_FENCE.length);
|
|
56098
|
+
const closeIndex = afterOpenFence.indexOf(`
|
|
56099
|
+
${FRONTMATTER_FENCE}`);
|
|
56100
|
+
if (closeIndex === -1) {
|
|
56101
|
+
return (0, import_neverthrow16.err)({ type: "MALFORMED_FRONTMATTER", cause: "Unclosed frontmatter fence" });
|
|
56102
|
+
}
|
|
56103
|
+
const rawFrontmatter = afterOpenFence.slice(0, closeIndex);
|
|
56104
|
+
const body = afterOpenFence.slice(closeIndex + FRONTMATTER_FENCE.length + 1);
|
|
56105
|
+
return combineFrontmatterLines(rawFrontmatter.split("\n")).map((frontmatter) => ({
|
|
56106
|
+
frontmatter,
|
|
56107
|
+
body
|
|
56108
|
+
}));
|
|
56109
|
+
}
|
|
56110
|
+
function splitIntoSections(body) {
|
|
56111
|
+
const parts = ("\n" + body).split(/\n## /).slice(1);
|
|
56112
|
+
return Object.fromEntries(
|
|
56113
|
+
parts.map((part) => {
|
|
56114
|
+
const newlineIndex = part.indexOf("\n");
|
|
56115
|
+
const heading = newlineIndex === -1 ? part.trim() : part.slice(0, newlineIndex).trim();
|
|
56116
|
+
const sectionContent = newlineIndex === -1 ? "" : part.slice(newlineIndex + 1).trim();
|
|
56117
|
+
return [heading, sectionContent];
|
|
56118
|
+
})
|
|
56119
|
+
);
|
|
56120
|
+
}
|
|
56121
|
+
function parseStep(line) {
|
|
56122
|
+
const withoutNumber = line.replace(NUMBERED_STEP_PREFIX, "");
|
|
56123
|
+
const delimiterIndex = withoutNumber.indexOf(INLINE_ASSERTION_DELIMITER);
|
|
56124
|
+
if (delimiterIndex === -1) {
|
|
56125
|
+
return { action: withoutNumber.trim(), assertion: void 0 };
|
|
56126
|
+
}
|
|
56127
|
+
return {
|
|
56128
|
+
action: withoutNumber.slice(0, delimiterIndex).trim(),
|
|
56129
|
+
assertion: withoutNumber.slice(delimiterIndex + INLINE_ASSERTION_DELIMITER.length).trim()
|
|
56130
|
+
};
|
|
56131
|
+
}
|
|
56132
|
+
function parseSteps(stepsSection) {
|
|
56133
|
+
return stepsSection.split("\n").filter((line) => NUMBERED_STEP_PREFIX.test(line)).map((line) => parseStep(line));
|
|
56134
|
+
}
|
|
56135
|
+
function parseAssertions(assertionsSection) {
|
|
56136
|
+
return assertionsSection.split("\n").filter((line) => line.startsWith("- ")).map((line) => line.slice(2).trim());
|
|
56137
|
+
}
|
|
56138
|
+
function parseTestSpec(name, content) {
|
|
56139
|
+
return extractFrontmatter(content).andThen(({ frontmatter, body }) => {
|
|
56140
|
+
const sections = splitIntoSections(body);
|
|
56141
|
+
const setup = sections.Setup;
|
|
56142
|
+
if (setup === void 0) {
|
|
56143
|
+
return (0, import_neverthrow16.err)({ type: "MISSING_SETUP_SECTION" });
|
|
56144
|
+
}
|
|
56145
|
+
const stepsSection = sections.Steps;
|
|
56146
|
+
if (stepsSection === void 0) {
|
|
56147
|
+
return (0, import_neverthrow16.err)({ type: "MISSING_STEPS_SECTION" });
|
|
56148
|
+
}
|
|
56149
|
+
return (0, import_neverthrow16.ok)({
|
|
56150
|
+
name,
|
|
56151
|
+
frontmatter,
|
|
56152
|
+
setup,
|
|
56153
|
+
steps: parseSteps(stepsSection),
|
|
56154
|
+
assertions: sections.Assertions === void 0 ? [] : parseAssertions(sections.Assertions)
|
|
56155
|
+
});
|
|
56156
|
+
});
|
|
56157
|
+
}
|
|
55886
56158
|
var VIEW_UI_TOOL_NAME = "mcp__perception__view_ui";
|
|
55887
56159
|
var VIEW_UI_DESCRIPTION = `Capture the current screen state: accessibility snapshot (element list with positions) and screenshot. Call this after every action to observe the result.
|
|
55888
56160
|
|
|
@@ -55898,7 +56170,7 @@ function deriveScreenLabel(tree, stepIndex) {
|
|
|
55898
56170
|
}
|
|
55899
56171
|
async function persistScreenshot(params) {
|
|
55900
56172
|
const screenshotPath = import_node_path5.default.join(params.screenshotsDirectory, `${params.screenLabel}.png`);
|
|
55901
|
-
const safeWriteFile = (0,
|
|
56173
|
+
const safeWriteFile = (0, import_neverthrow19.fromAsyncThrowable)(
|
|
55902
56174
|
import_promises10.writeFile,
|
|
55903
56175
|
(cause) => ({ type: "WRITE_FAILED", cause })
|
|
55904
56176
|
);
|
|
@@ -55963,7 +56235,7 @@ function buildSnapshotRecord(rawElements, { stepIndex, context }) {
|
|
|
55963
56235
|
}
|
|
55964
56236
|
async function handleScreenshotData(data, params) {
|
|
55965
56237
|
if (params.context.screenshotsDir !== void 0) {
|
|
55966
|
-
const safeWrite2 = (0,
|
|
56238
|
+
const safeWrite2 = (0, import_neverthrow19.fromAsyncThrowable)(
|
|
55967
56239
|
import_promises10.writeFile,
|
|
55968
56240
|
(cause) => ({ type: "WRITE_FAILED", cause })
|
|
55969
56241
|
);
|
|
@@ -56133,23 +56405,23 @@ function processMessage(message, state) {
|
|
|
56133
56405
|
}
|
|
56134
56406
|
if (message.type === "result") {
|
|
56135
56407
|
if (message.subtype !== "success" && !state.timedOut.value && !state.aborted.value) {
|
|
56136
|
-
return (0,
|
|
56408
|
+
return (0, import_neverthrow18.err)(message.errors.join("; "));
|
|
56137
56409
|
}
|
|
56138
|
-
return (0,
|
|
56410
|
+
return (0, import_neverthrow18.ok)(true);
|
|
56139
56411
|
}
|
|
56140
|
-
return (0,
|
|
56412
|
+
return (0, import_neverthrow18.ok)(false);
|
|
56141
56413
|
}
|
|
56142
56414
|
async function processMessages(queryRunner, state) {
|
|
56143
56415
|
for await (const message of queryRunner) {
|
|
56144
56416
|
const result = processMessage(message, state);
|
|
56145
56417
|
if (result.isErr()) {
|
|
56146
|
-
return (0,
|
|
56418
|
+
return (0, import_neverthrow18.err)(result.error);
|
|
56147
56419
|
}
|
|
56148
56420
|
if (result.value) {
|
|
56149
56421
|
break;
|
|
56150
56422
|
}
|
|
56151
56423
|
}
|
|
56152
|
-
return (0,
|
|
56424
|
+
return (0, import_neverthrow18.ok)(null);
|
|
56153
56425
|
}
|
|
56154
56426
|
var MessageQueue = class {
|
|
56155
56427
|
pending = [];
|
|
@@ -56409,7 +56681,7 @@ var INTERRUPT_DRAIN_TIMEOUT_MS = 1e4;
|
|
|
56409
56681
|
async function interruptOrTimeout(queryRunner) {
|
|
56410
56682
|
await Promise.race([queryRunner.interrupt(), (0, import_promises9.setTimeout)(INTERRUPT_DRAIN_TIMEOUT_MS)]);
|
|
56411
56683
|
}
|
|
56412
|
-
var safeInterruptOrTimeout =
|
|
56684
|
+
var safeInterruptOrTimeout = import_neverthrow17.ResultAsync.fromThrowable(
|
|
56413
56685
|
interruptOrTimeout,
|
|
56414
56686
|
(error48) => error48
|
|
56415
56687
|
);
|
|
@@ -56460,18 +56732,18 @@ function startQueryTimers(config3, context) {
|
|
|
56460
56732
|
}
|
|
56461
56733
|
function awaitMessagesAndResolve({ queryRunner, state }, { cleanup: cleanup2, getOutput }) {
|
|
56462
56734
|
const messagesPromise = processMessages(queryRunner, state);
|
|
56463
|
-
return
|
|
56735
|
+
return import_neverthrow17.ResultAsync.fromPromise(messagesPromise, String).andThen((innerResult) => {
|
|
56464
56736
|
cleanup2();
|
|
56465
56737
|
if (innerResult.isErr()) {
|
|
56466
|
-
return (0,
|
|
56738
|
+
return (0, import_neverthrow17.err)(innerResult.error);
|
|
56467
56739
|
}
|
|
56468
|
-
return (0,
|
|
56740
|
+
return (0, import_neverthrow17.ok)(getOutput());
|
|
56469
56741
|
}).orElse((sdkError) => {
|
|
56470
56742
|
cleanup2();
|
|
56471
56743
|
if (!state.timedOut.value && !state.aborted.value) {
|
|
56472
|
-
return (0,
|
|
56744
|
+
return (0, import_neverthrow17.err)(sdkError);
|
|
56473
56745
|
}
|
|
56474
|
-
return (0,
|
|
56746
|
+
return (0, import_neverthrow17.ok)(getOutput());
|
|
56475
56747
|
});
|
|
56476
56748
|
}
|
|
56477
56749
|
function executeQuery({
|
|
@@ -56485,9 +56757,9 @@ function executeQuery({
|
|
|
56485
56757
|
message: { role: "user", content: prompt },
|
|
56486
56758
|
parent_tool_use_id: null
|
|
56487
56759
|
});
|
|
56488
|
-
const queryRunnerResult = (0,
|
|
56760
|
+
const queryRunnerResult = (0, import_neverthrow17.fromThrowable)(Qs, String)({ prompt: inputQueue, options });
|
|
56489
56761
|
if (queryRunnerResult.isErr()) {
|
|
56490
|
-
return (0,
|
|
56762
|
+
return (0, import_neverthrow17.errAsync)(queryRunnerResult.error);
|
|
56491
56763
|
}
|
|
56492
56764
|
const queryRunner = queryRunnerResult.value;
|
|
56493
56765
|
const cleanup2 = startQueryTimers(config3, { state, queryRunner, inputQueue, linkedController });
|
|
@@ -56502,7 +56774,7 @@ function executeQuery({
|
|
|
56502
56774
|
function runQuery(prompt, config3) {
|
|
56503
56775
|
const outputTools = createOutputTool({ findings: external_exports.array(EXPLORER_FINDING_SCHEMA) });
|
|
56504
56776
|
const setupPromise = setupQuery(config3, outputTools);
|
|
56505
|
-
return
|
|
56777
|
+
return import_neverthrow17.ResultAsync.fromPromise(setupPromise, String).andThen(
|
|
56506
56778
|
(setup) => executeQuery({ prompt, config: config3, outputTools, setup })
|
|
56507
56779
|
);
|
|
56508
56780
|
}
|
|
@@ -56549,25 +56821,25 @@ function direntToSpecEntry(directory, entry) {
|
|
|
56549
56821
|
return [];
|
|
56550
56822
|
}
|
|
56551
56823
|
function scanDirectory(directory) {
|
|
56552
|
-
const
|
|
56824
|
+
const safeReaddir2 = (0, import_neverthrow20.fromAsyncThrowable)(
|
|
56553
56825
|
async () => (0, import_promises11.readdir)(directory, { withFileTypes: true }),
|
|
56554
56826
|
(cause) => ({ type: "DIR_READ_FAILED", dir: directory, cause })
|
|
56555
56827
|
);
|
|
56556
|
-
return
|
|
56828
|
+
return safeReaddir2().orElse((error48) => isNotFound(error48.cause) ? (0, import_neverthrow20.okAsync)([]) : (0, import_neverthrow20.errAsync)(error48)).map(
|
|
56557
56829
|
(entries) => entries.flatMap((entry) => direntToSpecEntry(directory, entry))
|
|
56558
56830
|
);
|
|
56559
56831
|
}
|
|
56560
56832
|
function readEntries(entries) {
|
|
56561
|
-
return
|
|
56833
|
+
return import_neverthrow20.ResultAsync.combine(entries.map((entry) => readEntry(entry))).map(
|
|
56562
56834
|
(results) => results.flat()
|
|
56563
56835
|
);
|
|
56564
56836
|
}
|
|
56565
56837
|
function readEntry(entry) {
|
|
56566
|
-
const safeReadFile5 = (0,
|
|
56838
|
+
const safeReadFile5 = (0, import_neverthrow20.fromAsyncThrowable)(
|
|
56567
56839
|
async () => (0, import_promises11.readFile)(entry.path, "utf8"),
|
|
56568
56840
|
(cause) => ({ type: "FILE_READ_FAILED", path: entry.path, cause })
|
|
56569
56841
|
);
|
|
56570
|
-
return safeReadFile5().map((content) => [{ name: entry.name, content }]).orElse((error48) => entry.required ? (0,
|
|
56842
|
+
return safeReadFile5().map((content) => [{ name: entry.name, content }]).orElse((error48) => entry.required ? (0, import_neverthrow20.errAsync)(error48) : (0, import_neverthrow20.okAsync)([]));
|
|
56571
56843
|
}
|
|
56572
56844
|
function specNameFromPath(filePath) {
|
|
56573
56845
|
const parts = filePath.split("/");
|
|
@@ -56583,132 +56855,6 @@ function filterByNames(specs, specNames) {
|
|
|
56583
56855
|
}
|
|
56584
56856
|
return specs.filter((spec) => specNames.includes(spec.name));
|
|
56585
56857
|
}
|
|
56586
|
-
var DEV_ENVIRONMENT_SECTION = `## Environment
|
|
56587
|
-
|
|
56588
|
-
This is a development build. Debug overlays and internal messages are expected artifacts \u2014 do not report them as findings.`;
|
|
56589
|
-
var WORKING_STATE_SECTION = `## Working State
|
|
56590
|
-
|
|
56591
|
-
At every reasoning step, maintain a mental ledger:
|
|
56592
|
-
- VISITED: screen names confirmed via \`view_ui\` this session
|
|
56593
|
-
- QUEUE: screen names seen as reachable but not yet explored
|
|
56594
|
-
- PATH: your current navigation stack from root (e.g. Home > Settings > Privacy)
|
|
56595
|
-
|
|
56596
|
-
Consult the ledger before every action. Always prefer navigating to a QUEUE screen over a VISITED one.`;
|
|
56597
|
-
var BACK_NAV_RULE = `- After navigating forward to any new screen: tap back, call \`view_ui\`, confirm you returned to the expected parent in PATH \u2014 if not, emit a \`back-nav-failure\` finding, then navigate forward again to continue`;
|
|
56598
|
-
var STUCK_LOOP_RULE = `- Stuck loop: emit a \`stuck-loop\` finding when any of these occur: (1) \`view_ui\` returns the same screen state 3 or more consecutive steps, (2) the same element has been tapped more than twice with no screen change, (3) PATH shows the same screen at two non-adjacent positions \u2014 before emitting, try one alternative action (scroll, long-press, swipe) to rule out a gesture mismatch`;
|
|
56599
|
-
var CLIPPED_ELEMENT_RULE = `- Never tap an element tagged \`[clipped-top]\`, \`[clipped-bottom]\`, \`[clipped-left]\`, or \`[clipped-right]\` \u2014 scroll to fully reveal it first, then re-call \`view_ui\` before tapping`;
|
|
56600
|
-
var WHAT_TO_TEST_SECTION = `## What to Test
|
|
56601
|
-
|
|
56602
|
-
Test navigation elements first, interactions second.
|
|
56603
|
-
|
|
56604
|
-
**Tier 1 \u2014 test every one, every time:**
|
|
56605
|
-
- Tab bar items, drawer menu items, hamburger menus
|
|
56606
|
-
- Back buttons, close buttons (X), modal dismiss controls
|
|
56607
|
-
- Bottom sheet handles and drag gestures
|
|
56608
|
-
- Navigation links and "Go to X" CTAs
|
|
56609
|
-
|
|
56610
|
-
**Tier 2 \u2014 test one representative per type per screen:**
|
|
56611
|
-
- Primary action buttons
|
|
56612
|
-
- Form inputs and toggles
|
|
56613
|
-
- Segmented controls and dropdowns
|
|
56614
|
-
|
|
56615
|
-
**Tier 3 \u2014 skip unless visibly broken:**
|
|
56616
|
-
- Static labels, decorative images, dividers
|
|
56617
|
-
|
|
56618
|
-
If an interaction produces no observable change, retry once before flagging.`;
|
|
56619
|
-
var DEAD_END_SECTION = `## Dead End and Modal Detection
|
|
56620
|
-
|
|
56621
|
-
**Dead end** \u2014 when \`view_ui\` shows no interactive exit affordance, attempt ALL of before emitting a finding: (1) any visible back/close button, (2) swipe from the left edge (back gesture), (3) swipe down (dismiss gesture). If all fail, emit a \`dead-end\` finding describing what was visible and what was attempted.
|
|
56622
|
-
|
|
56623
|
-
**Stuck modal** \u2014 when a modal or bottom sheet blocks the screen, attempt dismissal in order: (1) close/X button if present, (2) tap outside the modal, (3) swipe down, (4) swipe from the left edge. If all fail, emit a \`stuck-modal\` finding listing the modal, the screen it appeared on, and the methods attempted.`;
|
|
56624
|
-
var SPEC_MODE_TEMPLATE = (specContent, options) => {
|
|
56625
|
-
const appSection = options.userPrompt ? `## Application
|
|
56626
|
-
|
|
56627
|
-
${options.userPrompt}
|
|
56628
|
-
|
|
56629
|
-
` : "";
|
|
56630
|
-
const environmentSection = options.buildEnv === "dev" ? `
|
|
56631
|
-
|
|
56632
|
-
${DEV_ENVIRONMENT_SECTION}` : "";
|
|
56633
|
-
return `You are a navigation and interaction testing agent. Your role is to find broken navigation flows and non-functional interactive elements. Do not report content bugs, copy errors, or visual style issues unless they directly prevent a navigation action from completing.
|
|
56634
|
-
|
|
56635
|
-
Verify app against specs below.
|
|
56636
|
-
|
|
56637
|
-
${appSection}${WORKING_STATE_SECTION}
|
|
56638
|
-
|
|
56639
|
-
## Rules
|
|
56640
|
-
|
|
56641
|
-
- ALWAYS call \`view_ui\` after every action before deciding what to do next \u2014 it is your only way to observe the screen
|
|
56642
|
-
- ${BACK_NAV_RULE.slice(2)}
|
|
56643
|
-
- Before selecting any action, prefer navigating to a QUEUE screen over re-exploring a VISITED one
|
|
56644
|
-
- ${STUCK_LOOP_RULE.slice(2)}
|
|
56645
|
-
- ${CLIPPED_ELEMENT_RULE.slice(2)}
|
|
56646
|
-
- Each item in \`## Assertions\` is a mandatory pass/fail check \u2014 verify using \`view_ui\` first; fall back to \`screenshot\` only when no accessibility node is present
|
|
56647
|
-
|
|
56648
|
-
## Exploration Strategy
|
|
56649
|
-
|
|
56650
|
-
Navigate to verify each spec's scenarios. When choosing how to reach a screen, prefer breadth-first paths \u2014 map sibling screens before going deeper into any one branch.
|
|
56651
|
-
|
|
56652
|
-
${WHAT_TO_TEST_SECTION}
|
|
56653
|
-
|
|
56654
|
-
${DEAD_END_SECTION}
|
|
56655
|
-
|
|
56656
|
-
## Specs
|
|
56657
|
-
|
|
56658
|
-
${specContent}${environmentSection}
|
|
56659
|
-
|
|
56660
|
-
## Output
|
|
56661
|
-
|
|
56662
|
-
CRITICAL: Call \`set_output\` each time your findings change \u2014 when you discover something new, confirm a false positive, or revise a finding. Each call replaces the previous output entirely, so always pass the full current list. Do not reply in plain text.`;
|
|
56663
|
-
};
|
|
56664
|
-
var FREESTYLE_TEMPLATE = (options) => {
|
|
56665
|
-
const { userPrompt, buildEnv } = options ?? {};
|
|
56666
|
-
const appSection = userPrompt ? `## Application
|
|
56667
|
-
|
|
56668
|
-
${userPrompt}
|
|
56669
|
-
|
|
56670
|
-
` : "";
|
|
56671
|
-
const environmentSection = buildEnv === "dev" ? `
|
|
56672
|
-
|
|
56673
|
-
${DEV_ENVIRONMENT_SECTION}` : "";
|
|
56674
|
-
return `You are a navigation and interaction testing agent. Your role is to find broken navigation flows and non-functional interactive elements. Do not report content bugs, copy errors, or visual style issues unless they directly prevent a navigation action from completing.
|
|
56675
|
-
|
|
56676
|
-
${appSection}${WORKING_STATE_SECTION}
|
|
56677
|
-
|
|
56678
|
-
## Rules
|
|
56679
|
-
|
|
56680
|
-
- ALWAYS call \`view_ui\` after every action before deciding what to do next \u2014 it is your only way to observe the screen
|
|
56681
|
-
- ${BACK_NAV_RULE.slice(2)}
|
|
56682
|
-
- Before selecting any action, prefer navigating to a QUEUE screen over re-exploring a VISITED one
|
|
56683
|
-
- ${STUCK_LOOP_RULE.slice(2)}
|
|
56684
|
-
- ${CLIPPED_ELEMENT_RULE.slice(2)}
|
|
56685
|
-
|
|
56686
|
-
## Exploration Strategy
|
|
56687
|
-
|
|
56688
|
-
Explore breadth-first: map all screens reachable from the current screen before descending into any one path. Visit all siblings at the current depth before going deeper.
|
|
56689
|
-
|
|
56690
|
-
${WHAT_TO_TEST_SECTION}
|
|
56691
|
-
|
|
56692
|
-
${DEAD_END_SECTION}${environmentSection}
|
|
56693
|
-
|
|
56694
|
-
## Output
|
|
56695
|
-
|
|
56696
|
-
CRITICAL: Call \`set_output\` each time your findings change \u2014 when you discover something new, confirm a false positive, or revise a finding. Each call replaces the previous output entirely, so always pass the full current list. Do not reply in plain text.`;
|
|
56697
|
-
};
|
|
56698
|
-
function generateExplorerPrompt({
|
|
56699
|
-
mode,
|
|
56700
|
-
specs,
|
|
56701
|
-
userPrompt,
|
|
56702
|
-
buildEnv
|
|
56703
|
-
}) {
|
|
56704
|
-
return mode === "spec" ? buildSpecModePrompt(specs, { userPrompt, buildEnv }) : FREESTYLE_TEMPLATE({ userPrompt, buildEnv });
|
|
56705
|
-
}
|
|
56706
|
-
function buildSpecModePrompt(specs, options) {
|
|
56707
|
-
const specContent = specs.map((spec) => `### ${spec.name}
|
|
56708
|
-
|
|
56709
|
-
${spec.content}`).join("\n\n---\n\n");
|
|
56710
|
-
return SPEC_MODE_TEMPLATE(specContent, options);
|
|
56711
|
-
}
|
|
56712
56858
|
var SPEED_2X = 2;
|
|
56713
56859
|
var SPEED_4X = 4;
|
|
56714
56860
|
var ISO_DATE_LENGTH = 10;
|
|
@@ -56720,6 +56866,15 @@ function buildPrompt(safeConfig, specs) {
|
|
|
56720
56866
|
buildEnv: safeConfig.buildEnv
|
|
56721
56867
|
});
|
|
56722
56868
|
}
|
|
56869
|
+
function parseSpecs(resolvedSpecs) {
|
|
56870
|
+
return import_neverthrow15.Result.combine(
|
|
56871
|
+
resolvedSpecs.map(
|
|
56872
|
+
(spec) => parseTestSpec(spec.name, spec.content).mapErr(
|
|
56873
|
+
(cause) => ({ type: "SPEC_PARSE_FAILED", specName: spec.name, cause })
|
|
56874
|
+
)
|
|
56875
|
+
)
|
|
56876
|
+
);
|
|
56877
|
+
}
|
|
56723
56878
|
function toArtifacts(result, runPaths) {
|
|
56724
56879
|
return {
|
|
56725
56880
|
findings: result.findings,
|
|
@@ -56757,7 +56912,7 @@ function runPipeline({
|
|
|
56757
56912
|
runPaths,
|
|
56758
56913
|
start
|
|
56759
56914
|
}) {
|
|
56760
|
-
return resolveSpecs(safeConfig).mapErr((cause) => ({ type: "SPEC_RESOLVE_FAILED", cause })).
|
|
56915
|
+
return resolveSpecs(safeConfig).mapErr((cause) => ({ type: "SPEC_RESOLVE_FAILED", cause })).andThen((specs) => parseSpecs(specs)).map((parsedSpecs) => buildPrompt(safeConfig, parsedSpecs)).map((prompt) => {
|
|
56761
56916
|
safeConfig.onEvent?.({ type: "SYSTEM_PROMPT", agent: "explorer", prompt });
|
|
56762
56917
|
return prompt;
|
|
56763
56918
|
}).andThen((prompt) => collectAndFinalize({ safeConfig, prompt, runPaths, start }));
|
|
@@ -56841,17 +56996,17 @@ function runWithRecording(handle, collectOutput) {
|
|
|
56841
56996
|
}
|
|
56842
56997
|
|
|
56843
56998
|
// ../../packages/pipeline/dist/index.js
|
|
56844
|
-
var
|
|
56999
|
+
var import_neverthrow32 = __toESM(require_index_cjs(), 1);
|
|
56845
57000
|
|
|
56846
57001
|
// ../../agents/inspector/dist/index.js
|
|
56847
|
-
var import_neverthrow20 = __toESM(require_index_cjs(), 1);
|
|
56848
|
-
var import_promises12 = require("node:fs/promises");
|
|
56849
57002
|
var import_neverthrow21 = __toESM(require_index_cjs(), 1);
|
|
57003
|
+
var import_promises12 = require("node:fs/promises");
|
|
56850
57004
|
var import_neverthrow22 = __toESM(require_index_cjs(), 1);
|
|
56851
57005
|
var import_neverthrow23 = __toESM(require_index_cjs(), 1);
|
|
56852
57006
|
var import_neverthrow24 = __toESM(require_index_cjs(), 1);
|
|
56853
|
-
var import_sharp2 = __toESM(require("sharp"), 1);
|
|
56854
57007
|
var import_neverthrow25 = __toESM(require_index_cjs(), 1);
|
|
57008
|
+
var import_sharp2 = __toESM(require("sharp"), 1);
|
|
57009
|
+
var import_neverthrow26 = __toESM(require_index_cjs(), 1);
|
|
56855
57010
|
var import_promises13 = require("node:fs/promises");
|
|
56856
57011
|
var import_node_path7 = __toESM(require("node:path"), 1);
|
|
56857
57012
|
|
|
@@ -59480,7 +59635,7 @@ var jsYaml = {
|
|
|
59480
59635
|
};
|
|
59481
59636
|
|
|
59482
59637
|
// ../../agents/inspector/dist/index.js
|
|
59483
|
-
var
|
|
59638
|
+
var import_neverthrow27 = __toESM(require_index_cjs(), 1);
|
|
59484
59639
|
var MS_PER_DAY = 864e5;
|
|
59485
59640
|
function checkStaleness({ lastUpdated, thresholdDays, now }) {
|
|
59486
59641
|
if (!lastUpdated) {
|
|
@@ -59557,7 +59712,7 @@ function mapRawFinding(item) {
|
|
|
59557
59712
|
function parseJson(raw) {
|
|
59558
59713
|
return JSON.parse(raw);
|
|
59559
59714
|
}
|
|
59560
|
-
var safeJsonParse2 = (0,
|
|
59715
|
+
var safeJsonParse2 = (0, import_neverthrow24.fromThrowable)(parseJson);
|
|
59561
59716
|
function parseClaudeResponse(raw) {
|
|
59562
59717
|
const parseResult = safeJsonParse2(raw);
|
|
59563
59718
|
if (parseResult.isErr()) {
|
|
@@ -59584,7 +59739,7 @@ async function downscaleBuffer(buffer) {
|
|
|
59584
59739
|
return (0, import_sharp2.default)(buffer).resize({ width: targetWidth }).toBuffer();
|
|
59585
59740
|
}
|
|
59586
59741
|
function downscale(buffer) {
|
|
59587
|
-
return (0,
|
|
59742
|
+
return (0, import_neverthrow25.fromAsyncThrowable)(
|
|
59588
59743
|
downscaleBuffer,
|
|
59589
59744
|
(cause) => ({ type: "CLAUDE_API_FAILED", cause })
|
|
59590
59745
|
)(buffer);
|
|
@@ -59657,7 +59812,7 @@ async function fetchClaudeText({
|
|
|
59657
59812
|
return block?.type === "text" ? block.text : "";
|
|
59658
59813
|
}
|
|
59659
59814
|
function callClaude(options) {
|
|
59660
|
-
return (0,
|
|
59815
|
+
return (0, import_neverthrow23.fromAsyncThrowable)(
|
|
59661
59816
|
fetchClaudeText,
|
|
59662
59817
|
(cause) => ({ type: "CLAUDE_API_FAILED", cause })
|
|
59663
59818
|
)(options);
|
|
@@ -59669,9 +59824,9 @@ function toFindings(text) {
|
|
|
59669
59824
|
const cleaned = stripCodeFences(text);
|
|
59670
59825
|
const findings = parseClaudeResponse(cleaned);
|
|
59671
59826
|
if (!findings) {
|
|
59672
|
-
return (0,
|
|
59827
|
+
return (0, import_neverthrow23.errAsync)({ type: "CLAUDE_RESPONSE_INVALID", raw: cleaned });
|
|
59673
59828
|
}
|
|
59674
|
-
return (0,
|
|
59829
|
+
return (0, import_neverthrow23.okAsync)(findings);
|
|
59675
59830
|
}
|
|
59676
59831
|
function buildResolveMessages(screenshotBase64, artboardNames) {
|
|
59677
59832
|
return [
|
|
@@ -59708,12 +59863,12 @@ async function fetchResolveName({
|
|
|
59708
59863
|
}
|
|
59709
59864
|
function resolveArtboard(screenshot, artboardNames) {
|
|
59710
59865
|
if (artboardNames.length === 0) {
|
|
59711
|
-
return (0,
|
|
59866
|
+
return (0, import_neverthrow23.okAsync)(void 0);
|
|
59712
59867
|
}
|
|
59713
59868
|
const anthropic = new Anthropic();
|
|
59714
59869
|
return downscale(screenshot).andThen((scaled) => {
|
|
59715
59870
|
const screenshotBase64 = scaled.toString("base64");
|
|
59716
|
-
return (0,
|
|
59871
|
+
return (0, import_neverthrow23.fromAsyncThrowable)(
|
|
59717
59872
|
fetchResolveName,
|
|
59718
59873
|
(cause) => ({ type: "CLAUDE_API_FAILED", cause })
|
|
59719
59874
|
)({ anthropic, screenshotBase64, artboardNames }).map((name) => {
|
|
@@ -59776,7 +59931,7 @@ var SEVERITY_CONFIDENCE = {
|
|
|
59776
59931
|
medium: CONFIDENCE_MEDIUM2,
|
|
59777
59932
|
low: CONFIDENCE_LOW2
|
|
59778
59933
|
};
|
|
59779
|
-
var safeJsonParse22 = (0,
|
|
59934
|
+
var safeJsonParse22 = (0, import_neverthrow26.fromThrowable)(JSON.parse);
|
|
59780
59935
|
function buildFindCandidatesMessages(screenshotBase64, artboardNames) {
|
|
59781
59936
|
return [
|
|
59782
59937
|
{
|
|
@@ -59823,12 +59978,12 @@ async function fetchCandidateNames({
|
|
|
59823
59978
|
}
|
|
59824
59979
|
function findCandidates(screenshot, artboardNames) {
|
|
59825
59980
|
if (artboardNames.length === 0) {
|
|
59826
|
-
return (0,
|
|
59981
|
+
return (0, import_neverthrow26.okAsync)([]);
|
|
59827
59982
|
}
|
|
59828
59983
|
const anthropic = new Anthropic();
|
|
59829
59984
|
return downscale(screenshot).andThen((scaled) => {
|
|
59830
59985
|
const screenshotBase64 = scaled.toString("base64");
|
|
59831
|
-
return (0,
|
|
59986
|
+
return (0, import_neverthrow26.fromAsyncThrowable)(
|
|
59832
59987
|
fetchCandidateNames,
|
|
59833
59988
|
(cause) => ({ type: "CLAUDE_API_FAILED", cause })
|
|
59834
59989
|
)({ anthropic, screenshotBase64, artboardNames });
|
|
@@ -59906,14 +60061,14 @@ async function fetchDesignContextText({
|
|
|
59906
60061
|
function toDesignContextFindings(text) {
|
|
59907
60062
|
const findings = parseConservativeResponse(text);
|
|
59908
60063
|
if (!findings) {
|
|
59909
|
-
return (0,
|
|
60064
|
+
return (0, import_neverthrow26.errAsync)({ type: "CLAUDE_RESPONSE_INVALID", raw: text });
|
|
59910
60065
|
}
|
|
59911
|
-
return (0,
|
|
60066
|
+
return (0, import_neverthrow26.okAsync)(findings);
|
|
59912
60067
|
}
|
|
59913
60068
|
async function downscaleAll(buffers) {
|
|
59914
60069
|
return Promise.all(buffers.map(async (buf) => downscaleBuffer(buf)));
|
|
59915
60070
|
}
|
|
59916
|
-
var downscaleAllBuffers = (0,
|
|
60071
|
+
var downscaleAllBuffers = (0, import_neverthrow26.fromAsyncThrowable)(
|
|
59917
60072
|
downscaleAll,
|
|
59918
60073
|
(cause) => ({ type: "CLAUDE_API_FAILED", cause })
|
|
59919
60074
|
);
|
|
@@ -59923,7 +60078,7 @@ function compareWithDesignContext(screenshot, artboards) {
|
|
|
59923
60078
|
const screenshotBase64 = scaledScreenshot.toString("base64");
|
|
59924
60079
|
return downscaleAllBuffers(artboards).andThen((scaledArtboards) => {
|
|
59925
60080
|
const artboardBase64s = scaledArtboards.map((buf) => buf.toString("base64"));
|
|
59926
|
-
return (0,
|
|
60081
|
+
return (0, import_neverthrow26.fromAsyncThrowable)(
|
|
59927
60082
|
fetchDesignContextText,
|
|
59928
60083
|
(cause) => ({ type: "CLAUDE_API_FAILED", cause })
|
|
59929
60084
|
)({ anthropic, screenshotBase64, artboardBase64s }).andThen(toDesignContextFindings);
|
|
@@ -60156,7 +60311,7 @@ async function initArtboardNames({ designStore, config: config3, state }) {
|
|
|
60156
60311
|
return state.artboardNamesPromise;
|
|
60157
60312
|
}
|
|
60158
60313
|
function readScreenshot(screenshotPath, stepIndex) {
|
|
60159
|
-
return
|
|
60314
|
+
return import_neverthrow22.ResultAsync.fromThrowable(
|
|
60160
60315
|
import_promises12.readFile,
|
|
60161
60316
|
(cause) => ({ type: "SCREENSHOT_READ_FAILED", stepIndex, cause })
|
|
60162
60317
|
)(screenshotPath);
|
|
@@ -60231,7 +60386,7 @@ function buildInspector(state, context) {
|
|
|
60231
60386
|
const { promise: promise2, resolve } = Promise.withResolvers();
|
|
60232
60387
|
state.resolve = resolve;
|
|
60233
60388
|
applyTryResolve(state);
|
|
60234
|
-
return (0,
|
|
60389
|
+
return (0, import_neverthrow21.fromSafePromise)(promise2);
|
|
60235
60390
|
}
|
|
60236
60391
|
};
|
|
60237
60392
|
}
|
|
@@ -60255,10 +60410,10 @@ async function readAndParseSidecar(sidecarPath) {
|
|
|
60255
60410
|
return parseMeta(raw);
|
|
60256
60411
|
}
|
|
60257
60412
|
function readSidecarFile(sidecarPath) {
|
|
60258
|
-
return (0,
|
|
60413
|
+
return (0, import_neverthrow27.fromAsyncThrowable)(
|
|
60259
60414
|
readAndParseSidecar,
|
|
60260
60415
|
() => ({})
|
|
60261
|
-
)(sidecarPath).orElse(() => (0,
|
|
60416
|
+
)(sidecarPath).orElse(() => (0, import_neverthrow27.okAsync)({}));
|
|
60262
60417
|
}
|
|
60263
60418
|
function isEnoent(error48) {
|
|
60264
60419
|
return error48?.code === "ENOENT";
|
|
@@ -60267,13 +60422,13 @@ function wrapFsError(cause) {
|
|
|
60267
60422
|
return { type: "FS_ERROR", cause };
|
|
60268
60423
|
}
|
|
60269
60424
|
function toFsError(fsError) {
|
|
60270
|
-
return (0,
|
|
60425
|
+
return (0, import_neverthrow27.errAsync)(fsError);
|
|
60271
60426
|
}
|
|
60272
60427
|
function missingBuffer() {
|
|
60273
|
-
return (0,
|
|
60428
|
+
return (0, import_neverthrow27.okAsync)(void 0);
|
|
60274
60429
|
}
|
|
60275
60430
|
function missingArtboard() {
|
|
60276
|
-
return (0,
|
|
60431
|
+
return (0, import_neverthrow27.okAsync)(void 0);
|
|
60277
60432
|
}
|
|
60278
60433
|
var FsDesignStore = class {
|
|
60279
60434
|
designsDirectory;
|
|
@@ -60281,12 +60436,12 @@ var FsDesignStore = class {
|
|
|
60281
60436
|
this.designsDirectory = designsDirectory;
|
|
60282
60437
|
}
|
|
60283
60438
|
listArtboards() {
|
|
60284
|
-
return (0,
|
|
60439
|
+
return (0, import_neverthrow27.fromAsyncThrowable)(
|
|
60285
60440
|
import_promises13.readdir,
|
|
60286
60441
|
wrapFsError
|
|
60287
60442
|
)(this.designsDirectory).orElse((fsError) => {
|
|
60288
60443
|
if (fsError.type === "FS_ERROR" && isEnoent(fsError.cause)) {
|
|
60289
|
-
return (0,
|
|
60444
|
+
return (0, import_neverthrow27.okAsync)([]);
|
|
60290
60445
|
}
|
|
60291
60446
|
return toFsError(fsError);
|
|
60292
60447
|
}).map(
|
|
@@ -60296,7 +60451,7 @@ var FsDesignStore = class {
|
|
|
60296
60451
|
getArtboard(filename) {
|
|
60297
60452
|
const pngPath = import_node_path7.default.join(this.designsDirectory, `${filename}.png`);
|
|
60298
60453
|
const sidecarPath = import_node_path7.default.join(this.designsDirectory, `${filename}.meta.yaml`);
|
|
60299
|
-
return (0,
|
|
60454
|
+
return (0, import_neverthrow27.fromAsyncThrowable)(
|
|
60300
60455
|
import_promises13.readFile,
|
|
60301
60456
|
wrapFsError
|
|
60302
60457
|
)(pngPath).orElse((fsError) => {
|
|
@@ -60323,10 +60478,10 @@ function attemptRetry(options) {
|
|
|
60323
60478
|
const { factory, config: config3, delayFunction, onRetry, attempt } = options;
|
|
60324
60479
|
return factory().orElse((error48) => {
|
|
60325
60480
|
if (attempt >= config3.maxAttempts) {
|
|
60326
|
-
return (0,
|
|
60481
|
+
return (0, import_neverthrow30.errAsync)(error48);
|
|
60327
60482
|
}
|
|
60328
60483
|
const delay = config3.baseDelayMs * Math.pow(2, attempt - 1);
|
|
60329
|
-
return
|
|
60484
|
+
return import_neverthrow30.ResultAsync.fromPromise(
|
|
60330
60485
|
(onRetry?.({ attempt, maxAttempts: config3.maxAttempts, delayMs: delay, error: error48 }), delayFunction(delay)),
|
|
60331
60486
|
() => error48
|
|
60332
60487
|
).andThen(
|
|
@@ -60346,7 +60501,7 @@ function withRetry(factory, options) {
|
|
|
60346
60501
|
var CONSOLIDATOR_AGENT = "consolidator";
|
|
60347
60502
|
function analyserFallback(artifacts, onEvent) {
|
|
60348
60503
|
onEvent?.({ type: "AGENT_FAILED_NON_CRITICAL", agent: "analyser", attempts: RETRY_MAX_ATTEMPTS });
|
|
60349
|
-
return (0,
|
|
60504
|
+
return (0, import_neverthrow29.okAsync)(artifacts.findings);
|
|
60350
60505
|
}
|
|
60351
60506
|
function runAnalyserWithRetry(params) {
|
|
60352
60507
|
const { artifacts, config: config3, onEvent } = params;
|
|
@@ -60376,7 +60531,7 @@ function resolveVisualFindings({
|
|
|
60376
60531
|
onEvent
|
|
60377
60532
|
}) {
|
|
60378
60533
|
if (config3.analyser === void 0 || config3.signal?.aborted) {
|
|
60379
|
-
return (0,
|
|
60534
|
+
return (0, import_neverthrow29.okAsync)(artifacts.findings);
|
|
60380
60535
|
}
|
|
60381
60536
|
return runAnalyserWithRetry({ artifacts, config: config3, onEvent });
|
|
60382
60537
|
}
|
|
@@ -60386,7 +60541,7 @@ function unmergedFallback(allFindings, onEvent) {
|
|
|
60386
60541
|
agent: CONSOLIDATOR_AGENT,
|
|
60387
60542
|
message: "Consolidation failed, returning unmerged findings"
|
|
60388
60543
|
});
|
|
60389
|
-
return (0,
|
|
60544
|
+
return (0, import_neverthrow29.okAsync)({ findings: allFindings, dismissed: [] });
|
|
60390
60545
|
}
|
|
60391
60546
|
function mergeWithFallback(options) {
|
|
60392
60547
|
const {
|
|
@@ -60413,7 +60568,7 @@ function consolidate(options) {
|
|
|
60413
60568
|
const { artifacts, inspectorFindings, runId, dismissals, config: config3, consolidatorConfig, onEvent } = options;
|
|
60414
60569
|
return resolveVisualFindings({ artifacts, config: config3, onEvent }).andThen((visualFindings) => {
|
|
60415
60570
|
if (config3.signal?.aborted) {
|
|
60416
|
-
return (0,
|
|
60571
|
+
return (0, import_neverthrow29.okAsync)({ findings: artifacts.findings, dismissed: [] });
|
|
60417
60572
|
}
|
|
60418
60573
|
return mergeWithFallback({
|
|
60419
60574
|
artifacts,
|
|
@@ -60426,8 +60581,8 @@ function consolidate(options) {
|
|
|
60426
60581
|
});
|
|
60427
60582
|
});
|
|
60428
60583
|
}
|
|
60429
|
-
var safeReadFile = (0,
|
|
60430
|
-
var safeParseJson = (0,
|
|
60584
|
+
var safeReadFile = (0, import_neverthrow31.fromThrowable)((filePath) => (0, import_node_fs3.readFileSync)(filePath, "utf8"));
|
|
60585
|
+
var safeParseJson = (0, import_neverthrow31.fromThrowable)(JSON.parse);
|
|
60431
60586
|
function isEnoent2(error48) {
|
|
60432
60587
|
if (!(error48 instanceof Error)) {
|
|
60433
60588
|
return false;
|
|
@@ -60439,19 +60594,19 @@ function loadDismissals(filePath) {
|
|
|
60439
60594
|
const readResult = safeReadFile(filePath);
|
|
60440
60595
|
if (readResult.isErr()) {
|
|
60441
60596
|
if (isEnoent2(readResult.error)) {
|
|
60442
|
-
return (0,
|
|
60597
|
+
return (0, import_neverthrow31.ok)([]);
|
|
60443
60598
|
}
|
|
60444
|
-
return (0,
|
|
60599
|
+
return (0, import_neverthrow31.err)({ type: "DISMISSALS_LOAD_FAILED", cause: readResult.error });
|
|
60445
60600
|
}
|
|
60446
60601
|
return safeParseJson(readResult.value).mapErr((cause) => ({ type: "DISMISSALS_LOAD_FAILED", cause })).andThen((data) => {
|
|
60447
60602
|
const store = data;
|
|
60448
60603
|
if (!Array.isArray(store.dismissed)) {
|
|
60449
|
-
return (0,
|
|
60604
|
+
return (0, import_neverthrow31.err)({
|
|
60450
60605
|
type: "DISMISSALS_LOAD_FAILED",
|
|
60451
60606
|
cause: "invalid shape: dismissed is not an array"
|
|
60452
60607
|
});
|
|
60453
60608
|
}
|
|
60454
|
-
return (0,
|
|
60609
|
+
return (0, import_neverthrow31.ok)(store.dismissed);
|
|
60455
60610
|
});
|
|
60456
60611
|
}
|
|
60457
60612
|
function toInspectorStepEvent(event) {
|
|
@@ -60521,7 +60676,7 @@ function runExplorerWithTeardown(explorerConfig, udid) {
|
|
|
60521
60676
|
return runExplorer(explorerConfig).mapErr((cause) => ({ type: "EXPLORER_FAILED", cause })).andThen(
|
|
60522
60677
|
(artifacts) => disableTouchIndicators(udid).mapErr(toSimulatorError).map(() => artifacts)
|
|
60523
60678
|
).orElse(
|
|
60524
|
-
(error48) => disableTouchIndicators(udid).mapErr(toSimulatorError).andThen(() => (0,
|
|
60679
|
+
(error48) => disableTouchIndicators(udid).mapErr(toSimulatorError).andThen(() => (0, import_neverthrow32.errAsync)(error48)).orElse(() => (0, import_neverthrow32.errAsync)(error48))
|
|
60525
60680
|
);
|
|
60526
60681
|
}
|
|
60527
60682
|
function runExplorerWithRetry(options) {
|
|
@@ -60547,12 +60702,12 @@ async function drainAfterExplorer(options) {
|
|
|
60547
60702
|
inspector?.close();
|
|
60548
60703
|
const inspectorFindings = inspector ? await collectInspectorFindings({ inspector, onEvent, totalSteps: enqueuedCount.value }) : [];
|
|
60549
60704
|
if (explorerResult.isErr()) {
|
|
60550
|
-
return (0,
|
|
60705
|
+
return (0, import_neverthrow32.err)(explorerResult.error);
|
|
60551
60706
|
}
|
|
60552
|
-
return (0,
|
|
60707
|
+
return (0, import_neverthrow32.ok)({ artifacts: explorerResult.value, inspectorFindings });
|
|
60553
60708
|
}
|
|
60554
60709
|
function runExplorerAndDrain(options) {
|
|
60555
|
-
return new
|
|
60710
|
+
return new import_neverthrow32.ResultAsync(drainAfterExplorer(options));
|
|
60556
60711
|
}
|
|
60557
60712
|
var require2 = (0, import_node_module.createRequire)(__importMetaUrl);
|
|
60558
60713
|
function createMobileMcpServer() {
|
|
@@ -60570,7 +60725,7 @@ function runAnalysis(artifacts, config3) {
|
|
|
60570
60725
|
}
|
|
60571
60726
|
var ISO_DATE_LENGTH2 = 10;
|
|
60572
60727
|
var RUN_ID_PAD_LENGTH = 4;
|
|
60573
|
-
var safeReaddirSync = (0,
|
|
60728
|
+
var safeReaddirSync = (0, import_neverthrow28.fromThrowable)((directory) => (0, import_node_fs2.readdirSync)(directory));
|
|
60574
60729
|
function nextRunId(outputDirectory, date5) {
|
|
60575
60730
|
const entries = safeReaddirSync(`${outputDirectory}/${date5}`).unwrapOr([]);
|
|
60576
60731
|
let max = 0;
|
|
@@ -60582,7 +60737,7 @@ function nextRunId(outputDirectory, date5) {
|
|
|
60582
60737
|
}
|
|
60583
60738
|
return String(max + 1).padStart(RUN_ID_PAD_LENGTH, "0");
|
|
60584
60739
|
}
|
|
60585
|
-
var writeOutputFile = (0,
|
|
60740
|
+
var writeOutputFile = (0, import_neverthrow28.fromThrowable)(
|
|
60586
60741
|
(params) => {
|
|
60587
60742
|
const { findingsPath, outputDirectory, json: json3 } = params;
|
|
60588
60743
|
(0, import_node_fs2.mkdirSync)(outputDirectory, { recursive: true });
|
|
@@ -60595,15 +60750,15 @@ function validatePipelineConfig(config3) {
|
|
|
60595
60750
|
const runId = config3.runId ?? nextRunId(config3.outputDir, date5);
|
|
60596
60751
|
const runPathsResult = resolveRunPaths({ outputDirectory: config3.outputDir, runId, date: date5 });
|
|
60597
60752
|
if (runPathsResult.isErr()) {
|
|
60598
|
-
return (0,
|
|
60753
|
+
return (0, import_neverthrow28.err)({ type: "RUN_PATHS_FAILED", cause: runPathsResult.error });
|
|
60599
60754
|
}
|
|
60600
60755
|
const dismissalsResult = loadDismissals(
|
|
60601
60756
|
dismissalsPath(config3.outputDir, process.env.QA_DISMISSALS_PATH)
|
|
60602
60757
|
);
|
|
60603
60758
|
if (dismissalsResult.isErr()) {
|
|
60604
|
-
return (0,
|
|
60759
|
+
return (0, import_neverthrow28.err)(dismissalsResult.error);
|
|
60605
60760
|
}
|
|
60606
|
-
return (0,
|
|
60761
|
+
return (0, import_neverthrow28.ok)({ runId, date: date5, runPaths: runPathsResult.value, dismissals: dismissalsResult.value });
|
|
60607
60762
|
}
|
|
60608
60763
|
function buildExplorerConfig({
|
|
60609
60764
|
config: config3,
|
|
@@ -60638,11 +60793,11 @@ function buildOutput(consolidationResult, options) {
|
|
|
60638
60793
|
function buildPipelineSetup(config3) {
|
|
60639
60794
|
const validatedResult = validatePipelineConfig(config3);
|
|
60640
60795
|
if (validatedResult.isErr()) {
|
|
60641
|
-
return (0,
|
|
60796
|
+
return (0, import_neverthrow28.err)(validatedResult.error);
|
|
60642
60797
|
}
|
|
60643
60798
|
const { runId, date: date5, runPaths, dismissals } = validatedResult.value;
|
|
60644
60799
|
const { inspector, explorerOnEvent, enqueuedCount } = buildInspectorSetup(config3);
|
|
60645
|
-
return (0,
|
|
60800
|
+
return (0, import_neverthrow28.ok)({
|
|
60646
60801
|
runId,
|
|
60647
60802
|
udid: config3.simulatorUdid ?? "booted",
|
|
60648
60803
|
runPaths,
|
|
@@ -60686,19 +60841,19 @@ function executePipeline(setup, config3) {
|
|
|
60686
60841
|
function runPipeline2(config3) {
|
|
60687
60842
|
const setupResult = buildPipelineSetup(config3);
|
|
60688
60843
|
if (setupResult.isErr()) {
|
|
60689
|
-
return (0,
|
|
60844
|
+
return (0, import_neverthrow28.errAsync)(setupResult.error);
|
|
60690
60845
|
}
|
|
60691
60846
|
return executePipeline(setupResult.value, config3);
|
|
60692
60847
|
}
|
|
60693
60848
|
|
|
60694
60849
|
// src/commands/analyse-command.ts
|
|
60695
|
-
var
|
|
60850
|
+
var import_neverthrow33 = __toESM(require_index_cjs(), 1);
|
|
60696
60851
|
var JSON_INDENT = 2;
|
|
60697
60852
|
function buildArtifacts(videoPath) {
|
|
60698
60853
|
return { videoPath, videoPath2x: "", videoPath4x: videoPath, findings: [], snapshots: [] };
|
|
60699
60854
|
}
|
|
60700
60855
|
async function checkVideoPathExists(videoPath) {
|
|
60701
|
-
const safeAccess = (0,
|
|
60856
|
+
const safeAccess = (0, import_neverthrow33.fromAsyncThrowable)(import_promises16.access, () => ({ type: "FILE_NOT_FOUND" }));
|
|
60702
60857
|
const result = await safeAccess(videoPath);
|
|
60703
60858
|
return result.isOk();
|
|
60704
60859
|
}
|
|
@@ -60742,7 +60897,7 @@ async function runAnalyseCommand(videoPath, config3) {
|
|
|
60742
60897
|
}
|
|
60743
60898
|
|
|
60744
60899
|
// src/core/completion-generator.ts
|
|
60745
|
-
var
|
|
60900
|
+
var import_neverthrow34 = __toESM(require_index_cjs(), 1);
|
|
60746
60901
|
function extractLongFlags(flags) {
|
|
60747
60902
|
return flags.split(/[\s,]+/).filter((token) => token.startsWith("--"));
|
|
60748
60903
|
}
|
|
@@ -60832,9 +60987,9 @@ complete -F _xqa_completion xqa`;
|
|
|
60832
60987
|
}
|
|
60833
60988
|
function generateCompletion(commands, shell) {
|
|
60834
60989
|
if (shell !== "bash" && shell !== "zsh") {
|
|
60835
|
-
return (0,
|
|
60990
|
+
return (0, import_neverthrow34.err)({ type: "UNSUPPORTED_SHELL", shell });
|
|
60836
60991
|
}
|
|
60837
|
-
return (0,
|
|
60992
|
+
return (0, import_neverthrow34.ok)(shell === "zsh" ? generateZshCompletion(commands) : generateBashCompletion(commands));
|
|
60838
60993
|
}
|
|
60839
60994
|
|
|
60840
60995
|
// src/commands/completion-command.ts
|
|
@@ -60893,16 +61048,16 @@ var DEFAULT_ABORT_EXIT_CODE = 130;
|
|
|
60893
61048
|
// src/core/last-path.ts
|
|
60894
61049
|
var import_node_fs4 = require("node:fs");
|
|
60895
61050
|
var import_node_path8 = __toESM(require("node:path"), 1);
|
|
60896
|
-
var
|
|
61051
|
+
var import_neverthrow35 = __toESM(require_index_cjs(), 1);
|
|
60897
61052
|
function resolveLastPath(argument, stateContent) {
|
|
60898
61053
|
if (argument !== void 0) {
|
|
60899
|
-
return (0,
|
|
61054
|
+
return (0, import_neverthrow35.ok)(argument);
|
|
60900
61055
|
}
|
|
60901
61056
|
const trimmed = stateContent?.trim();
|
|
60902
61057
|
if (trimmed) {
|
|
60903
|
-
return (0,
|
|
61058
|
+
return (0, import_neverthrow35.ok)(trimmed);
|
|
60904
61059
|
}
|
|
60905
|
-
return (0,
|
|
61060
|
+
return (0, import_neverthrow35.err)({ type: "NO_ARG_AND_NO_STATE" });
|
|
60906
61061
|
}
|
|
60907
61062
|
function lastPathFilePath(xqaDirectoryectory) {
|
|
60908
61063
|
return import_node_path8.default.join(xqaDirectoryectory, "last-findings-path");
|
|
@@ -60914,7 +61069,7 @@ function writeLastPath(xqaDirectory, findingsPath) {
|
|
|
60914
61069
|
// src/shell/instructions.ts
|
|
60915
61070
|
var import_promises17 = require("node:fs/promises");
|
|
60916
61071
|
var import_node_path9 = __toESM(require("node:path"), 1);
|
|
60917
|
-
var
|
|
61072
|
+
var import_neverthrow36 = __toESM(require_index_cjs(), 1);
|
|
60918
61073
|
var HTML_COMMENT_PATTERN = /<!--[\s\S]*?-->/g;
|
|
60919
61074
|
function isEnoentError(value) {
|
|
60920
61075
|
return value !== null && typeof value === "object" && "code" in value && value.code === "ENOENT";
|
|
@@ -60924,9 +61079,9 @@ function toInstructionsError(cause) {
|
|
|
60924
61079
|
}
|
|
60925
61080
|
function absentInstructions() {
|
|
60926
61081
|
const absent = void 0;
|
|
60927
|
-
return (0,
|
|
61082
|
+
return (0, import_neverthrow36.ok)(absent);
|
|
60928
61083
|
}
|
|
60929
|
-
var safeReadFile2 =
|
|
61084
|
+
var safeReadFile2 = import_neverthrow36.ResultAsync.fromThrowable(
|
|
60930
61085
|
async (filePath) => (0, import_promises17.readFile)(filePath, "utf8"),
|
|
60931
61086
|
toInstructionsError
|
|
60932
61087
|
);
|
|
@@ -60940,7 +61095,7 @@ function readInstructions(xqaDirectory) {
|
|
|
60940
61095
|
if (isEnoentError(error48.cause)) {
|
|
60941
61096
|
return absentInstructions();
|
|
60942
61097
|
}
|
|
60943
|
-
return (0,
|
|
61098
|
+
return (0, import_neverthrow36.err)(error48);
|
|
60944
61099
|
});
|
|
60945
61100
|
}
|
|
60946
61101
|
|
|
@@ -61029,8 +61184,10 @@ ${cause}
|
|
|
61029
61184
|
}
|
|
61030
61185
|
|
|
61031
61186
|
// src/commands/init-command.ts
|
|
61187
|
+
var import_node_child_process5 = require("node:child_process");
|
|
61032
61188
|
var import_node_fs5 = require("node:fs");
|
|
61033
61189
|
var import_node_path11 = __toESM(require("node:path"), 1);
|
|
61190
|
+
var import_node_url = require("node:url");
|
|
61034
61191
|
var GITIGNORE_CONTENT = `/output
|
|
61035
61192
|
/last-findings-path
|
|
61036
61193
|
`;
|
|
@@ -61050,17 +61207,27 @@ Example: The app starts on the home screen with a wallet already loaded.
|
|
|
61050
61207
|
If this file contains a mnemonic phrase, add .xqa/instructions.md to your .gitignore.
|
|
61051
61208
|
-->
|
|
61052
61209
|
`;
|
|
61210
|
+
function resolveSkillPath(skillName) {
|
|
61211
|
+
const packageDistributionDirectory = import_node_path11.default.dirname((0, import_node_url.fileURLToPath)(__importMetaUrl));
|
|
61212
|
+
return import_node_path11.default.join(packageDistributionDirectory, "skills", skillName);
|
|
61213
|
+
}
|
|
61053
61214
|
function runInitCommand() {
|
|
61054
61215
|
const xqaDirectory = import_node_path11.default.join(process.cwd(), ".xqa");
|
|
61216
|
+
(0, import_node_child_process5.spawnSync)("npx", ["skills", "add", resolveSkillPath("xqa-spec"), "--all", "-y"], {
|
|
61217
|
+
stdio: "inherit"
|
|
61218
|
+
});
|
|
61055
61219
|
if ((0, import_node_fs5.existsSync)(xqaDirectory)) {
|
|
61056
|
-
process.
|
|
61220
|
+
process.stdout.write(`Skills updated. .xqa already exists, skipping project init.
|
|
61057
61221
|
`);
|
|
61058
|
-
process.exit(1);
|
|
61059
61222
|
return;
|
|
61060
61223
|
}
|
|
61061
61224
|
(0, import_node_fs5.mkdirSync)(xqaDirectory);
|
|
61062
61225
|
(0, import_node_fs5.writeFileSync)(import_node_path11.default.join(xqaDirectory, ".gitignore"), GITIGNORE_CONTENT);
|
|
61063
61226
|
(0, import_node_fs5.writeFileSync)(import_node_path11.default.join(xqaDirectory, "instructions.md"), INSTRUCTIONS_TEMPLATE);
|
|
61227
|
+
for (const subdir of ["designs", "specs", "suites"]) {
|
|
61228
|
+
(0, import_node_fs5.mkdirSync)(import_node_path11.default.join(xqaDirectory, subdir));
|
|
61229
|
+
(0, import_node_fs5.writeFileSync)(import_node_path11.default.join(xqaDirectory, subdir, ".gitkeep"), "");
|
|
61230
|
+
}
|
|
61064
61231
|
process.stdout.write(`Initialized xqa project: ${xqaDirectory}
|
|
61065
61232
|
`);
|
|
61066
61233
|
process.stdout.write(`Edit .xqa/instructions.md to describe your app.
|
|
@@ -61070,7 +61237,7 @@ function runInitCommand() {
|
|
|
61070
61237
|
// src/commands/review-command.ts
|
|
61071
61238
|
var import_node_fs6 = require("node:fs");
|
|
61072
61239
|
var import_node_path13 = __toESM(require("node:path"), 1);
|
|
61073
|
-
var
|
|
61240
|
+
var import_neverthrow38 = __toESM(require_index_cjs(), 1);
|
|
61074
61241
|
|
|
61075
61242
|
// ../../node_modules/.pnpm/@inquirer+core@10.3.2_@types+node@22.19.15/node_modules/@inquirer/core/dist/esm/lib/key.js
|
|
61076
61243
|
var isUpKey = (key, keybindings = []) => (
|
|
@@ -63475,7 +63642,7 @@ var esm_default11 = createPrompt((config3, done) => {
|
|
|
63475
63642
|
});
|
|
63476
63643
|
|
|
63477
63644
|
// src/review-session.ts
|
|
63478
|
-
var
|
|
63645
|
+
var import_neverthrow37 = __toESM(require_index_cjs(), 1);
|
|
63479
63646
|
var CONFIDENCE_PERCENT = 100;
|
|
63480
63647
|
var FLOW_COL_WIDTH = 35;
|
|
63481
63648
|
var TRIGGER_COL_WIDTH = 16;
|
|
@@ -63628,15 +63795,15 @@ async function runInteractiveLoop(findings, existing) {
|
|
|
63628
63795
|
}
|
|
63629
63796
|
return { staged: state.staged, undoneKeys: state.undoneKeys };
|
|
63630
63797
|
}
|
|
63631
|
-
var safeRunInteractiveLoop = (0,
|
|
63798
|
+
var safeRunInteractiveLoop = (0, import_neverthrow37.fromAsyncThrowable)(
|
|
63632
63799
|
runInteractiveLoop,
|
|
63633
63800
|
(error48) => error48 instanceof Error && error48.name === "ExitPromptError" ? "exit-prompt" : "unexpected"
|
|
63634
63801
|
);
|
|
63635
63802
|
|
|
63636
63803
|
// src/commands/review-command.ts
|
|
63637
|
-
var safeReadFile3 = (0,
|
|
63638
|
-
var safeParseJson2 = (0,
|
|
63639
|
-
var safeWrite = (0,
|
|
63804
|
+
var safeReadFile3 = (0, import_neverthrow38.fromThrowable)((filePath) => (0, import_node_fs6.readFileSync)(filePath, "utf8"));
|
|
63805
|
+
var safeParseJson2 = (0, import_neverthrow38.fromThrowable)(JSON.parse);
|
|
63806
|
+
var safeWrite = (0, import_neverthrow38.fromThrowable)((filePath, content) => {
|
|
63640
63807
|
(0, import_node_fs6.writeFileSync)(filePath, content);
|
|
63641
63808
|
});
|
|
63642
63809
|
function readLastPath(xqaDirectory) {
|
|
@@ -63653,13 +63820,13 @@ function isPipelineOutput(data) {
|
|
|
63653
63820
|
function readFindings(filePath) {
|
|
63654
63821
|
const readResult = safeReadFile3(filePath);
|
|
63655
63822
|
if (readResult.isErr()) {
|
|
63656
|
-
return (0,
|
|
63823
|
+
return (0, import_neverthrow38.err)("not-found");
|
|
63657
63824
|
}
|
|
63658
63825
|
return safeParseJson2(readResult.value).mapErr(() => "invalid").andThen((data) => {
|
|
63659
63826
|
if (!isPipelineOutput(data)) {
|
|
63660
|
-
return (0,
|
|
63827
|
+
return (0, import_neverthrow38.err)("invalid");
|
|
63661
63828
|
}
|
|
63662
|
-
return (0,
|
|
63829
|
+
return (0, import_neverthrow38.ok)(data);
|
|
63663
63830
|
});
|
|
63664
63831
|
}
|
|
63665
63832
|
function loadExistingDismissals(filePath) {
|
|
@@ -63732,7 +63899,7 @@ function resolveAndReadFindings(findingsPath, xqaDirectory) {
|
|
|
63732
63899
|
"No findings path provided and no last path found. Run: xqa review <findings-path>\n"
|
|
63733
63900
|
);
|
|
63734
63901
|
process.exit(1);
|
|
63735
|
-
return (0,
|
|
63902
|
+
return (0, import_neverthrow38.err)();
|
|
63736
63903
|
}
|
|
63737
63904
|
const resolvedPath = resolvedPathResult.value;
|
|
63738
63905
|
const findingsResult = readFindings(resolvedPath);
|
|
@@ -63745,9 +63912,9 @@ function resolveAndReadFindings(findingsPath, xqaDirectory) {
|
|
|
63745
63912
|
`);
|
|
63746
63913
|
}
|
|
63747
63914
|
process.exit(1);
|
|
63748
|
-
return (0,
|
|
63915
|
+
return (0, import_neverthrow38.err)();
|
|
63749
63916
|
}
|
|
63750
|
-
return (0,
|
|
63917
|
+
return (0, import_neverthrow38.ok)({ resolvedPath, output: findingsResult.value });
|
|
63751
63918
|
}
|
|
63752
63919
|
async function runReviewLoop({
|
|
63753
63920
|
findings,
|
|
@@ -63800,45 +63967,42 @@ async function runReviewCommand(findingsPath, xqaDirectory) {
|
|
|
63800
63967
|
// src/commands/spec-command.ts
|
|
63801
63968
|
var import_node_fs7 = require("node:fs");
|
|
63802
63969
|
var import_node_path15 = __toESM(require("node:path"), 1);
|
|
63803
|
-
var
|
|
63970
|
+
var import_neverthrow40 = __toESM(require_index_cjs(), 1);
|
|
63804
63971
|
|
|
63805
63972
|
// src/spec-frontmatter.ts
|
|
63806
|
-
var
|
|
63973
|
+
var import_neverthrow39 = __toESM(require_index_cjs(), 1);
|
|
63807
63974
|
var FRONTMATTER_OPEN_LEN = 4;
|
|
63808
63975
|
var FRONTMATTER_MARKER_LEN = 3;
|
|
63809
63976
|
function extractFrontmatterBlock(content) {
|
|
63810
63977
|
const normalized = content.replaceAll("\r\n", "\n");
|
|
63811
63978
|
if (!normalized.startsWith("---")) {
|
|
63812
|
-
return (0,
|
|
63979
|
+
return (0, import_neverthrow39.err)({ type: "MISSING_FRONTMATTER" });
|
|
63813
63980
|
}
|
|
63814
63981
|
const end = normalized.indexOf("\n---", FRONTMATTER_MARKER_LEN);
|
|
63815
63982
|
if (end === -1) {
|
|
63816
|
-
return (0,
|
|
63983
|
+
return (0, import_neverthrow39.err)({ type: "MISSING_FRONTMATTER" });
|
|
63817
63984
|
}
|
|
63818
|
-
return (0,
|
|
63985
|
+
return (0, import_neverthrow39.ok)(normalized.slice(FRONTMATTER_OPEN_LEN, end));
|
|
63819
63986
|
}
|
|
63820
63987
|
function parseMaxSteps(fields) {
|
|
63821
63988
|
const maxStepsRaw = fields.get("max_steps");
|
|
63822
63989
|
if (maxStepsRaw === void 0) {
|
|
63823
|
-
return (0,
|
|
63990
|
+
return (0, import_neverthrow39.ok)(maxStepsRaw);
|
|
63824
63991
|
}
|
|
63825
63992
|
const parsed = Number(maxStepsRaw);
|
|
63826
63993
|
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
63827
|
-
return (0,
|
|
63994
|
+
return (0, import_neverthrow39.err)({ type: "PARSE_ERROR", cause: `invalid max_steps: ${maxStepsRaw}` });
|
|
63828
63995
|
}
|
|
63829
|
-
return (0,
|
|
63996
|
+
return (0, import_neverthrow39.ok)(parsed);
|
|
63830
63997
|
}
|
|
63831
63998
|
function parseSpecFrontmatter(content) {
|
|
63832
63999
|
return extractFrontmatterBlock(content).andThen((block) => {
|
|
63833
64000
|
const fields = parseYamlFields(block);
|
|
63834
64001
|
const feature = fields.get("feature");
|
|
63835
64002
|
if (feature === void 0) {
|
|
63836
|
-
return (0,
|
|
64003
|
+
return (0, import_neverthrow39.err)({ type: "MISSING_FIELD", field: "feature" });
|
|
63837
64004
|
}
|
|
63838
64005
|
const entry = fields.get("entry");
|
|
63839
|
-
if (entry === void 0) {
|
|
63840
|
-
return (0, import_neverthrow38.err)({ type: "MISSING_FIELD", field: "entry" });
|
|
63841
|
-
}
|
|
63842
64006
|
return parseMaxSteps(fields).map((maxSteps) => ({ feature, entry, maxSteps }));
|
|
63843
64007
|
});
|
|
63844
64008
|
}
|
|
@@ -63877,14 +64041,22 @@ function stripExtensions(filename) {
|
|
|
63877
64041
|
}
|
|
63878
64042
|
|
|
63879
64043
|
// src/commands/spec-command.ts
|
|
63880
|
-
var safeReadFile4 = (0,
|
|
64044
|
+
var safeReadFile4 = (0, import_neverthrow40.fromThrowable)((filePath) => (0, import_node_fs7.readFileSync)(filePath, "utf8"));
|
|
64045
|
+
var safeReaddir = (0, import_neverthrow40.fromThrowable)(
|
|
64046
|
+
(directory) => (0, import_node_fs7.readdirSync)(directory, { recursive: true, encoding: "utf8" })
|
|
64047
|
+
);
|
|
64048
|
+
var CANCEL = "xqa:cancel";
|
|
64049
|
+
var safeSelect = import_neverthrow40.ResultAsync.fromThrowable(
|
|
64050
|
+
esm_default11,
|
|
64051
|
+
(error48) => error48 instanceof Error && error48.name === "ExitPromptError" ? "cancelled" : "failed"
|
|
64052
|
+
);
|
|
63881
64053
|
function buildSpecExplorer(input, context) {
|
|
63882
64054
|
return {
|
|
63883
64055
|
mode: "spec",
|
|
63884
64056
|
specFiles: [context.absolutePath],
|
|
63885
64057
|
mcpServers: createDefaultMcpServers(),
|
|
63886
64058
|
allowedTools: ALLOWED_TOOLS,
|
|
63887
|
-
userPrompt: `Navigate to \`${context.entry}\` before beginning spec verification
|
|
64059
|
+
userPrompt: context.entry ? `Navigate to \`${context.entry}\` before beginning spec verification.` : void 0,
|
|
63888
64060
|
buildEnv: context.config.QA_BUILD_ENV
|
|
63889
64061
|
};
|
|
63890
64062
|
}
|
|
@@ -63937,33 +64109,66 @@ ${cause}
|
|
|
63937
64109
|
`);
|
|
63938
64110
|
process.exit(1);
|
|
63939
64111
|
}
|
|
64112
|
+
function findSpecFiles(xqaDirectory) {
|
|
64113
|
+
const specsDirectory = import_node_path15.default.join(xqaDirectory, "specs");
|
|
64114
|
+
return safeReaddir(specsDirectory).unwrapOr([]).filter((file2) => file2.endsWith(".test.md")).map((file2) => import_node_path15.default.join(specsDirectory, file2));
|
|
64115
|
+
}
|
|
64116
|
+
async function promptForSpec(specFiles, xqaDirectory) {
|
|
64117
|
+
const result = await safeSelect({
|
|
64118
|
+
message: "Select a spec",
|
|
64119
|
+
choices: [
|
|
64120
|
+
...specFiles.map((specFile) => ({
|
|
64121
|
+
name: import_node_path15.default.relative(xqaDirectory, specFile),
|
|
64122
|
+
value: specFile
|
|
64123
|
+
})),
|
|
64124
|
+
new Separator(),
|
|
64125
|
+
{ name: "Cancel", value: CANCEL }
|
|
64126
|
+
]
|
|
64127
|
+
});
|
|
64128
|
+
if (result.isErr() || result.value === CANCEL) {
|
|
64129
|
+
process.exit(0);
|
|
64130
|
+
return void 0;
|
|
64131
|
+
}
|
|
64132
|
+
return result.value;
|
|
64133
|
+
}
|
|
64134
|
+
async function resolveSpecFile(specFile, xqaDirectory) {
|
|
64135
|
+
if (specFile !== void 0) {
|
|
64136
|
+
return specFile;
|
|
64137
|
+
}
|
|
64138
|
+
const specFiles = findSpecFiles(xqaDirectory);
|
|
64139
|
+
if (specFiles.length === 0) {
|
|
64140
|
+
process.stderr.write("No spec files found in .xqa/specs/. Create one with /xqa-spec.\n");
|
|
64141
|
+
process.exit(1);
|
|
64142
|
+
return void 0;
|
|
64143
|
+
}
|
|
64144
|
+
return promptForSpec(specFiles, xqaDirectory);
|
|
64145
|
+
}
|
|
64146
|
+
async function executeSpec(input, context) {
|
|
64147
|
+
const result = await runPipeline2(buildPipelineConfig2(input, context));
|
|
64148
|
+
result.match((output) => {
|
|
64149
|
+
handleSpecSuccess(context.xqaDirectory, output);
|
|
64150
|
+
}, handleSpecError);
|
|
64151
|
+
}
|
|
63940
64152
|
async function runSpecCommand(input, options) {
|
|
63941
64153
|
const { config: config3, xqaDirectory } = options;
|
|
63942
|
-
const
|
|
64154
|
+
const resolvedSpecFile = await resolveSpecFile(input.specFile, xqaDirectory);
|
|
64155
|
+
if (resolvedSpecFile === void 0) {
|
|
64156
|
+
return;
|
|
64157
|
+
}
|
|
64158
|
+
const absolutePath = import_node_path15.default.resolve(resolvedSpecFile);
|
|
63943
64159
|
const frontmatter = readAndParseSpec(absolutePath);
|
|
63944
64160
|
if (frontmatter === void 0) {
|
|
63945
64161
|
return;
|
|
63946
64162
|
}
|
|
63947
64163
|
const slug = deriveSpecSlug(absolutePath);
|
|
63948
|
-
|
|
63949
|
-
buildPipelineConfig2(input, {
|
|
63950
|
-
config: config3,
|
|
63951
|
-
xqaDirectory,
|
|
63952
|
-
absolutePath,
|
|
63953
|
-
entry: frontmatter.entry,
|
|
63954
|
-
slug
|
|
63955
|
-
})
|
|
63956
|
-
);
|
|
63957
|
-
result.match((output) => {
|
|
63958
|
-
handleSpecSuccess(xqaDirectory, output);
|
|
63959
|
-
}, handleSpecError);
|
|
64164
|
+
await executeSpec(input, { config: config3, xqaDirectory, absolutePath, entry: frontmatter.entry, slug });
|
|
63960
64165
|
}
|
|
63961
64166
|
|
|
63962
64167
|
// src/config.ts
|
|
63963
64168
|
var import_node_path16 = __toESM(require("node:path"), 1);
|
|
63964
|
-
var
|
|
64169
|
+
var import_node_url2 = require("node:url");
|
|
63965
64170
|
var import_dotenv = __toESM(require_main(), 1);
|
|
63966
|
-
var
|
|
64171
|
+
var import_neverthrow41 = __toESM(require_index_cjs(), 1);
|
|
63967
64172
|
|
|
63968
64173
|
// ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
|
|
63969
64174
|
var external_exports2 = {};
|
|
@@ -68016,7 +68221,7 @@ var configSchema = external_exports2.object({
|
|
|
68016
68221
|
});
|
|
68017
68222
|
|
|
68018
68223
|
// src/config.ts
|
|
68019
|
-
var packageDirectory = import_node_path16.default.dirname((0,
|
|
68224
|
+
var packageDirectory = import_node_path16.default.dirname((0, import_node_url2.fileURLToPath)(__importMetaUrl));
|
|
68020
68225
|
function loadConfig() {
|
|
68021
68226
|
(0, import_dotenv.config)({ path: import_node_path16.default.resolve(packageDirectory, "..", ".env.local") });
|
|
68022
68227
|
const result = configSchema.safeParse(process.env);
|
|
@@ -68024,15 +68229,15 @@ function loadConfig() {
|
|
|
68024
68229
|
const messages = result.error.issues.map(
|
|
68025
68230
|
(issue2) => ` - ${issue2.path.join(".")}: ${issue2.message}`
|
|
68026
68231
|
);
|
|
68027
|
-
return (0,
|
|
68232
|
+
return (0, import_neverthrow41.err)({ type: "INVALID_CONFIG", message: `Configuration error:
|
|
68028
68233
|
${messages.join("\n")}` });
|
|
68029
68234
|
}
|
|
68030
|
-
return (0,
|
|
68235
|
+
return (0, import_neverthrow41.ok)(result.data);
|
|
68031
68236
|
}
|
|
68032
68237
|
|
|
68033
68238
|
// src/pid-lock.ts
|
|
68034
68239
|
var import_node_fs8 = require("node:fs");
|
|
68035
|
-
var
|
|
68240
|
+
var import_neverthrow42 = __toESM(require_index_cjs(), 1);
|
|
68036
68241
|
var PID_FILE = "/tmp/xqa.pid";
|
|
68037
68242
|
var SIGINT_EXIT_CODE = 130;
|
|
68038
68243
|
var SIGTERM_EXIT_CODE = 143;
|
|
@@ -68041,7 +68246,7 @@ var HARD_TIMEOUT_MS = 1e4;
|
|
|
68041
68246
|
var cleanup = () => {
|
|
68042
68247
|
(0, import_node_fs8.rmSync)(PID_FILE, { force: true });
|
|
68043
68248
|
};
|
|
68044
|
-
var checkProcessRunning = (0,
|
|
68249
|
+
var checkProcessRunning = (0, import_neverthrow42.fromThrowable)(
|
|
68045
68250
|
(pid) => {
|
|
68046
68251
|
process.kill(pid, 0);
|
|
68047
68252
|
return true;
|
|
@@ -68107,17 +68312,17 @@ function acquireLock() {
|
|
|
68107
68312
|
// src/shell/xqa-directory.ts
|
|
68108
68313
|
var import_node_fs9 = require("node:fs");
|
|
68109
68314
|
var import_node_path17 = __toESM(require("node:path"), 1);
|
|
68110
|
-
var
|
|
68315
|
+
var import_neverthrow43 = __toESM(require_index_cjs(), 1);
|
|
68111
68316
|
function findXqaDirectory(startDirectory) {
|
|
68112
68317
|
let current = startDirectory;
|
|
68113
68318
|
for (; ; ) {
|
|
68114
68319
|
const candidate = import_node_path17.default.join(current, ".xqa");
|
|
68115
68320
|
if ((0, import_node_fs9.existsSync)(candidate)) {
|
|
68116
|
-
return (0,
|
|
68321
|
+
return (0, import_neverthrow43.ok)(candidate);
|
|
68117
68322
|
}
|
|
68118
68323
|
const parent = import_node_path17.default.dirname(current);
|
|
68119
68324
|
if (parent === current) {
|
|
68120
|
-
return (0,
|
|
68325
|
+
return (0, import_neverthrow43.err)({ type: "XQA_NOT_INITIALIZED" });
|
|
68121
68326
|
}
|
|
68122
68327
|
current = parent;
|
|
68123
68328
|
}
|
|
@@ -68162,7 +68367,7 @@ program2.command("review").description("Review findings and mark false positives
|
|
|
68162
68367
|
const xqaDirectory = resolveXqaDirectory();
|
|
68163
68368
|
void runReviewCommand(findingsPath, xqaDirectory);
|
|
68164
68369
|
});
|
|
68165
|
-
program2.command("spec").description("Run the explorer agent against a spec file").argument("
|
|
68370
|
+
program2.command("spec").description("Run the explorer agent against a spec file").argument("[spec-file]", "Path to the spec markdown file; omit to pick interactively").option("--verbose", "Log tool call results").action((specFile, options) => {
|
|
68166
68371
|
const xqaDirectory = resolveXqaDirectory();
|
|
68167
68372
|
void runSpecCommand(
|
|
68168
68373
|
{ specFile, verbose: options.verbose ?? false, signal: controller.signal },
|