@mayurdluffy/ccwf 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +110 -2
- package/bin/ccwf.js +7 -0
- package/lib/index.js +312 -0
- package/lib/installer.js +127 -0
- package/lib/machine.js +167 -0
- package/lib/merge.js +92 -0
- package/lib/modules.js +199 -0
- package/package.json +45 -4
- package/templates/agent-memory/MEMORY.md +4 -0
- package/templates/blocks/browser.md +9 -0
- package/templates/blocks/cbm.md +9 -0
- package/templates/blocks/core.md +35 -0
- package/templates/blocks/memory.md +7 -0
- package/templates/blocks/tracking.md +11 -0
- package/templates/blocks/workers.md +3 -0
- package/templates/cursor-cli.json +17 -0
- package/templates/cursor-rule.mdc +6 -0
- package/templates/docs/BACKLOG.md +4 -0
- package/templates/docs/DECISIONS.md +4 -0
- package/templates/docs/FEATURES.md +8 -0
- package/templates/docs/GLOSSARY.md +3 -0
- package/templates/docs/adr/.gitkeep +0 -0
- package/templates/docs/agents/domain.md +52 -0
- package/templates/docs/agents/issue-tracker.md +30 -0
- package/templates/docs/agents/setup.md +48 -0
- package/templates/docs/agents/triage-labels.md +15 -0
- package/templates/docs/agents/workflow.md +58 -0
- package/templates/machine.json +47 -0
- package/templates/skills/agent-browser/LICENSE +202 -0
- package/templates/skills/agent-browser/SKILL.md +51 -0
- package/templates/skills/commit/SKILL.md +77 -0
- package/templates/skills/orchestrate-workers/HC.md +10 -0
- package/templates/skills/orchestrate-workers/SKILL.md +138 -0
- package/templates/skills/orchestrate-workers/approve.sh +7 -0
- package/templates/skills/orchestrate-workers/claude-worker-settings.json +14 -0
- package/templates/skills/orchestrate-workers/end.sh +13 -0
- package/templates/skills/orchestrate-workers/grid.sh +24 -0
- package/templates/skills/orchestrate-workers/pane.sh +51 -0
- package/templates/skills/orchestrate-workers/watch.sh +42 -0
- package/templates/skills/orchestrate-workers/workers.conf +8 -0
- package/templates/skills/verify-ui/PLAYBOOK.md +81 -0
- package/templates/skills/verify-ui/SKILL.md +88 -0
- package/templates/skills/verify-ui/lib.sh +63 -0
- package/templates/skills/verify-ui/probe.sh +78 -0
- package/templates/skills/verify-ui/run.sh +113 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/usr/bin/env zsh
|
|
2
|
+
# Run browser scenarios with agent-browser and print one PASS/FAIL line each, then a SUITE line.
|
|
3
|
+
# Usage: run.sh <scenario.txt|dir> [...] BASE_URL comes from verify-ui.conf
|
|
4
|
+
# Exit 0 = all pass, 1 = a check failed, 2 = setup problem.
|
|
5
|
+
#
|
|
6
|
+
# Scenario lines (one per line; # comments and blank lines ignored; every expect waits up to 5s):
|
|
7
|
+
# open <path-or-url> navigate (a path is joined to BASE_URL)
|
|
8
|
+
# expect-text <text> the accessibility tree contains the text
|
|
9
|
+
# expect-no-text <text> the text stays absent for 1.5s
|
|
10
|
+
# expect-url <substring> the current URL contains it
|
|
11
|
+
# expect-visible <selector> the element is visible
|
|
12
|
+
# expect-count <selector> <n> exactly n elements match
|
|
13
|
+
# expect-value <selector> <text> an input holds exactly this value
|
|
14
|
+
# expect-storage <key> <substring> localStorage[key] contains it (what was really saved)
|
|
15
|
+
# expect-request <METHOD> <url-part> [status] the page made this request (the API was really called)
|
|
16
|
+
# expect-status <path> <code> HTTP status of a direct request, without the browser
|
|
17
|
+
# expect-eval <js expression> evaluates to true in the page
|
|
18
|
+
# expect-a11y [impacts] no axe violations of these impacts (default critical,serious)
|
|
19
|
+
# allow-console <regex> console errors matching this are expected in this scenario
|
|
20
|
+
# allow-request <regex> 4xx/5xx requests matching this are expected (e.g. a 401 check)
|
|
21
|
+
# shot <name> save a screenshot as evidence (a drawing, layout); path is printed
|
|
22
|
+
# <anything else> an agent-browser command, must succeed:
|
|
23
|
+
# find role button click --name "Save", fill, press, wait --text,
|
|
24
|
+
# set viewport 390 844, set offline on, network route <url> --abort
|
|
25
|
+
# Arguments with spaces: quote them where a command takes more than one (expect-value "#name" "Sam").
|
|
26
|
+
#
|
|
27
|
+
# After every scenario, automatically: no uncaught page errors, no console errors, no 4xx/5xx
|
|
28
|
+
# request to the app (unhandled promise rejections count as page errors). On FAIL the full tree, console,
|
|
29
|
+
# errors, network list and a screenshot are saved; the FAIL block prints the folder.
|
|
30
|
+
. ${0:A:h}/lib.sh
|
|
31
|
+
need_server
|
|
32
|
+
|
|
33
|
+
files=()
|
|
34
|
+
for a in "$@"; do
|
|
35
|
+
if [[ -d $a ]]; then files+=("$a"/*.txt(N)); else files+=("$a"); fi
|
|
36
|
+
done
|
|
37
|
+
(( ${#files} )) || { echo "SETUP: no scenario files"; exit 2; }
|
|
38
|
+
|
|
39
|
+
has_text() { tree | grep -qF -- "$1"; }
|
|
40
|
+
url_has() { [[ $(ab get url) == *$1* ]]; }
|
|
41
|
+
is_visible() { [[ $(ab is visible "$1") == true ]]; }
|
|
42
|
+
count_is() { [[ $(ab get count "$1") == $2 ]]; }
|
|
43
|
+
value_is() { [[ $(ab get value "$1") == $2 ]]; }
|
|
44
|
+
stored() { ab storage local "$1" | grep -qF -- "$2"; }
|
|
45
|
+
requested() { requests | grep -F -- "] $1 " | grep -F -- "$2" | grep -qE "\) ${3:-[0-9]*}\$"; }
|
|
46
|
+
js_true() { [[ $(ab eval -b "$(print -rn -- "$1" | base64)") == true ]]; }
|
|
47
|
+
|
|
48
|
+
passed=0; failed=0; S=""
|
|
49
|
+
trap '[[ -n $S ]] && ab close >/dev/null 2>&1' EXIT; trap 'exit 130' INT TERM # an interrupted run must not leave a browser behind
|
|
50
|
+
for f in $files; do
|
|
51
|
+
name=${f:t:r}; S=verify-$name-$$; out=$outroot/$name
|
|
52
|
+
step=0; fail=""; okcon=""; okreq=""; shots=()
|
|
53
|
+
rm -rf "$out"
|
|
54
|
+
while IFS= read -r line || [[ -n $line ]]; do
|
|
55
|
+
[[ -z ${line// } || $line == \#* ]] && continue
|
|
56
|
+
(( step++ ))
|
|
57
|
+
cmd=${line%% *}; arg=${line#* }; w=("${(@Q)${(z)arg}}")
|
|
58
|
+
case $cmd in
|
|
59
|
+
open) o=$(ab_open "$arg") || fail="open $arg: $(print -r -- "$o" | tail -1)" ;;
|
|
60
|
+
expect-text) poll has_text "$arg" || fail="text not found: $arg" ;;
|
|
61
|
+
expect-no-text) for i in 1 2 3; do has_text "$arg" && { fail="unexpected text: $arg"; break; }; sleep 0.5; done ;;
|
|
62
|
+
expect-url) poll url_has "$arg" || fail="url is $(ab get url), wanted *$arg*" ;;
|
|
63
|
+
expect-visible) poll is_visible "$arg" || fail="not visible: $arg" ;;
|
|
64
|
+
expect-count) poll count_is "${(j: :)w[1,-2]}" "$w[-1]" || fail="count of ${(j: :)w[1,-2]} is $(ab get count "${(j: :)w[1,-2]}"), wanted $w[-1]" ;;
|
|
65
|
+
expect-value) poll value_is "$w[1]" "${(j: :)w[2,-1]}" || fail="value of $w[1] is '$(ab get value "$w[1]")', wanted '${(j: :)w[2,-1]}'" ;;
|
|
66
|
+
expect-storage) poll stored "$w[1]" "${(j: :)w[2,-1]}" || fail="localStorage $w[1] lacks '${(j: :)w[2,-1]}': $(ab storage local "$w[1]" | short)" ;;
|
|
67
|
+
expect-request) poll requested "$w[1]" "$w[2]" "$w[3]" || fail="no $w[1] request matching '$w[2]'${w[3]:+ with status $w[3]}; api calls seen: $(api_requests | tail -3 | short | tr '\n' ' ')" ;;
|
|
68
|
+
expect-status) got=$(curl -s -o /dev/null -w '%{http_code}' --max-time 5 "$base$w[1]")
|
|
69
|
+
[[ $got == $w[2] ]] || fail="$w[1] returned $got, wanted $w[2]" ;;
|
|
70
|
+
expect-eval) poll js_true "$arg" || fail="not true: $arg" ;;
|
|
71
|
+
expect-a11y) [[ $arg == $cmd ]] && arg=critical,serious
|
|
72
|
+
v=$(ab a11y | grep -E "^\[(${arg//,/|})\]" | short)
|
|
73
|
+
[[ -n $v ]] && fail="accessibility: $(print -r -- "$v" | head -4 | tr '\n' ' ')" ;;
|
|
74
|
+
allow-console) okcon=${okcon:+$okcon|}$arg ;;
|
|
75
|
+
allow-request) okreq=${okreq:+$okreq|}$arg ;;
|
|
76
|
+
shot) mkdir -p "$out"; o=$(ab screenshot "$out/$arg.png") && shots+=("$out/$arg.png") || fail="screenshot: $o" ;;
|
|
77
|
+
*) o=$(eval "ab $line") || fail="'$line' failed: $(print -r -- "$o" | tail -2 | tr '\n' ' ')" ;;
|
|
78
|
+
esac
|
|
79
|
+
[[ -n $fail ]] && { fail="step $step: $fail"; break; }
|
|
80
|
+
done < "$f"
|
|
81
|
+
|
|
82
|
+
if [[ -z $fail ]]; then
|
|
83
|
+
e=$(page_errors | head -3 | short)
|
|
84
|
+
[[ -n $e ]] && fail="page errors: $e"
|
|
85
|
+
fi
|
|
86
|
+
if [[ -z $fail ]]; then
|
|
87
|
+
e=$(console_of error | grep -vE -- "${okcon:-^$}" | sort -u | head -3 | short)
|
|
88
|
+
[[ -n $e ]] && fail="console errors: $e"
|
|
89
|
+
fi
|
|
90
|
+
if [[ -z $fail ]]; then
|
|
91
|
+
e=$(bad_requests | grep -vE -- "${okreq:-^$}" | head -3 | short)
|
|
92
|
+
[[ -n $e ]] && fail="failed requests: $e"
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
# Build the result first and print it after the browser is closed, so a reader that stops
|
|
96
|
+
# early (| head) cannot strand the browser.
|
|
97
|
+
if [[ -n $fail ]]; then
|
|
98
|
+
rep="FAIL $name: $fail"$'\n'" url: $(ab get url)"$'\n'"$(itree | head -15 | short | sed 's/^/ | /')"
|
|
99
|
+
evidence "$out"; ab screenshot "$out/fail.png" >/dev/null
|
|
100
|
+
rep+=$'\n'" evidence: $out (tree.txt console.txt errors.txt network.txt fail.png)"
|
|
101
|
+
(( failed++ ))
|
|
102
|
+
else
|
|
103
|
+
warn=$(console_of warning | wc -l | tr -d ' ')
|
|
104
|
+
(( warn )) && warn=", $warn console warnings" || warn=""
|
|
105
|
+
rep="PASS $name ($step steps$warn)"
|
|
106
|
+
(( ${#shots} )) && rep+=$'\n'" shots: $shots"
|
|
107
|
+
(( passed++ ))
|
|
108
|
+
fi
|
|
109
|
+
ab close >/dev/null 2>&1; S=""
|
|
110
|
+
print -r -- "$rep"
|
|
111
|
+
done
|
|
112
|
+
echo "SUITE: $passed passed, $failed failed"
|
|
113
|
+
(( failed == 0 ))
|