@jonit-dev/night-watch-cli 1.7.9 → 1.7.11
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 +1 -1
- package/dist/cli.js +3 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/dashboard/tab-config.d.ts.map +1 -1
- package/dist/commands/dashboard/tab-config.js +9 -1
- package/dist/commands/dashboard/tab-config.js.map +1 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +3 -0
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +131 -19
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/install.d.ts +4 -0
- package/dist/commands/install.d.ts.map +1 -1
- package/dist/commands/install.js +24 -0
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/qa.d.ts +30 -0
- package/dist/commands/qa.d.ts.map +1 -0
- package/dist/commands/qa.js +159 -0
- package/dist/commands/qa.js.map +1 -0
- package/dist/commands/review.d.ts +5 -0
- package/dist/commands/review.d.ts.map +1 -1
- package/dist/commands/review.js +40 -0
- package/dist/commands/review.js.map +1 -1
- package/dist/commands/run.d.ts +11 -0
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +56 -1
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +3 -0
- package/dist/commands/status.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +155 -1
- package/dist/config.js.map +1 -1
- package/dist/constants.d.ts +20 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +40 -0
- package/dist/constants.js.map +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +47 -5
- package/dist/server/index.js.map +1 -1
- package/dist/shared/types.d.ts +223 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/dist/shared/types.js +7 -0
- package/dist/shared/types.js.map +1 -0
- package/dist/src/agents/soul-compiler.d.ts +11 -0
- package/dist/src/agents/soul-compiler.d.ts.map +1 -0
- package/dist/src/agents/soul-compiler.js +103 -0
- package/dist/src/agents/soul-compiler.js.map +1 -0
- package/dist/src/board/factory.d.ts +3 -0
- package/dist/src/board/factory.d.ts.map +1 -0
- package/dist/src/board/factory.js +10 -0
- package/dist/src/board/factory.js.map +1 -0
- package/dist/src/board/providers/github-graphql.d.ts +16 -0
- package/dist/src/board/providers/github-graphql.d.ts.map +1 -0
- package/dist/src/board/providers/github-graphql.js +43 -0
- package/dist/src/board/providers/github-graphql.js.map +1 -0
- package/dist/src/board/providers/github-projects.d.ts +51 -0
- package/dist/src/board/providers/github-projects.d.ts.map +1 -0
- package/dist/src/board/providers/github-projects.js +672 -0
- package/dist/src/board/providers/github-projects.js.map +1 -0
- package/dist/src/board/types.d.ts +60 -0
- package/dist/src/board/types.d.ts.map +1 -0
- package/dist/src/board/types.js +4 -0
- package/dist/src/board/types.js.map +1 -0
- package/dist/src/cli.d.ts +3 -0
- package/dist/src/cli.d.ts.map +1 -0
- package/dist/src/cli.js +80 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/commands/board.d.ts +9 -0
- package/dist/src/commands/board.d.ts.map +1 -0
- package/dist/src/commands/board.js +294 -0
- package/dist/src/commands/board.js.map +1 -0
- package/dist/src/commands/cancel.d.ts +46 -0
- package/dist/src/commands/cancel.d.ts.map +1 -0
- package/dist/src/commands/cancel.js +241 -0
- package/dist/src/commands/cancel.js.map +1 -0
- package/dist/src/commands/dashboard/tab-actions.d.ts +10 -0
- package/dist/src/commands/dashboard/tab-actions.d.ts.map +1 -0
- package/dist/src/commands/dashboard/tab-actions.js +245 -0
- package/dist/src/commands/dashboard/tab-actions.js.map +1 -0
- package/dist/src/commands/dashboard/tab-config.d.ts +21 -0
- package/dist/src/commands/dashboard/tab-config.d.ts.map +1 -0
- package/dist/src/commands/dashboard/tab-config.js +829 -0
- package/dist/src/commands/dashboard/tab-config.js.map +1 -0
- package/dist/src/commands/dashboard/tab-logs.d.ts +10 -0
- package/dist/src/commands/dashboard/tab-logs.d.ts.map +1 -0
- package/dist/src/commands/dashboard/tab-logs.js +178 -0
- package/dist/src/commands/dashboard/tab-logs.js.map +1 -0
- package/dist/src/commands/dashboard/tab-schedules.d.ts +21 -0
- package/dist/src/commands/dashboard/tab-schedules.d.ts.map +1 -0
- package/dist/src/commands/dashboard/tab-schedules.js +304 -0
- package/dist/src/commands/dashboard/tab-schedules.js.map +1 -0
- package/dist/src/commands/dashboard/tab-status.d.ts +32 -0
- package/dist/src/commands/dashboard/tab-status.d.ts.map +1 -0
- package/dist/src/commands/dashboard/tab-status.js +421 -0
- package/dist/src/commands/dashboard/tab-status.js.map +1 -0
- package/dist/src/commands/dashboard/types.d.ts +43 -0
- package/dist/src/commands/dashboard/types.d.ts.map +1 -0
- package/dist/src/commands/dashboard/types.js +5 -0
- package/dist/src/commands/dashboard/types.js.map +1 -0
- package/dist/src/commands/dashboard.d.ts +11 -0
- package/dist/src/commands/dashboard.d.ts.map +1 -0
- package/dist/src/commands/dashboard.js +239 -0
- package/dist/src/commands/dashboard.js.map +1 -0
- package/dist/src/commands/doctor.d.ts +16 -0
- package/dist/src/commands/doctor.d.ts.map +1 -0
- package/dist/src/commands/doctor.js +202 -0
- package/dist/src/commands/doctor.js.map +1 -0
- package/dist/src/commands/history.d.ts +7 -0
- package/dist/src/commands/history.d.ts.map +1 -0
- package/dist/src/commands/history.js +56 -0
- package/dist/src/commands/history.js.map +1 -0
- package/dist/src/commands/init.d.ts +25 -0
- package/dist/src/commands/init.d.ts.map +1 -0
- package/dist/src/commands/init.js +543 -0
- package/dist/src/commands/init.js.map +1 -0
- package/dist/src/commands/install.d.ts +48 -0
- package/dist/src/commands/install.d.ts.map +1 -0
- package/dist/src/commands/install.js +303 -0
- package/dist/src/commands/install.js.map +1 -0
- package/dist/src/commands/logs.d.ts +15 -0
- package/dist/src/commands/logs.d.ts.map +1 -0
- package/dist/src/commands/logs.js +104 -0
- package/dist/src/commands/logs.js.map +1 -0
- package/dist/src/commands/prd-state.d.ts +12 -0
- package/dist/src/commands/prd-state.d.ts.map +1 -0
- package/dist/src/commands/prd-state.js +47 -0
- package/dist/src/commands/prd-state.js.map +1 -0
- package/dist/src/commands/prd.d.ts +24 -0
- package/dist/src/commands/prd.d.ts.map +1 -0
- package/dist/src/commands/prd.js +283 -0
- package/dist/src/commands/prd.js.map +1 -0
- package/dist/src/commands/prds.d.ts +13 -0
- package/dist/src/commands/prds.d.ts.map +1 -0
- package/dist/src/commands/prds.js +196 -0
- package/dist/src/commands/prds.js.map +1 -0
- package/dist/src/commands/prs.d.ts +14 -0
- package/dist/src/commands/prs.d.ts.map +1 -0
- package/dist/src/commands/prs.js +106 -0
- package/dist/src/commands/prs.js.map +1 -0
- package/dist/src/commands/qa.d.ts +30 -0
- package/dist/src/commands/qa.d.ts.map +1 -0
- package/dist/src/commands/qa.js +159 -0
- package/dist/src/commands/qa.js.map +1 -0
- package/dist/src/commands/retry.d.ts +9 -0
- package/dist/src/commands/retry.d.ts.map +1 -0
- package/dist/src/commands/retry.js +72 -0
- package/dist/src/commands/retry.js.map +1 -0
- package/dist/src/commands/review.d.ts +35 -0
- package/dist/src/commands/review.d.ts.map +1 -0
- package/dist/src/commands/review.js +252 -0
- package/dist/src/commands/review.js.map +1 -0
- package/dist/src/commands/run.d.ts +61 -0
- package/dist/src/commands/run.d.ts.map +1 -0
- package/dist/src/commands/run.js +364 -0
- package/dist/src/commands/run.js.map +1 -0
- package/dist/src/commands/serve.d.ts +7 -0
- package/dist/src/commands/serve.d.ts.map +1 -0
- package/dist/src/commands/serve.js +27 -0
- package/dist/src/commands/serve.js.map +1 -0
- package/dist/src/commands/slice.d.ts +26 -0
- package/dist/src/commands/slice.d.ts.map +1 -0
- package/dist/src/commands/slice.js +175 -0
- package/dist/src/commands/slice.js.map +1 -0
- package/dist/src/commands/state.d.ts +8 -0
- package/dist/src/commands/state.d.ts.map +1 -0
- package/dist/src/commands/state.js +56 -0
- package/dist/src/commands/state.js.map +1 -0
- package/dist/src/commands/status.d.ts +14 -0
- package/dist/src/commands/status.d.ts.map +1 -0
- package/dist/src/commands/status.js +147 -0
- package/dist/src/commands/status.js.map +1 -0
- package/dist/src/commands/uninstall.d.ts +25 -0
- package/dist/src/commands/uninstall.d.ts.map +1 -0
- package/dist/src/commands/uninstall.js +141 -0
- package/dist/src/commands/uninstall.js.map +1 -0
- package/dist/src/commands/update.d.ts +21 -0
- package/dist/src/commands/update.d.ts.map +1 -0
- package/dist/src/commands/update.js +87 -0
- package/dist/src/commands/update.js.map +1 -0
- package/dist/src/config.d.ts +23 -0
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/config.js +629 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/constants.d.ts +60 -0
- package/dist/src/constants.d.ts.map +1 -0
- package/dist/src/constants.js +118 -0
- package/dist/src/constants.js.map +1 -0
- package/dist/src/server/index.d.ts +23 -0
- package/dist/src/server/index.d.ts.map +1 -0
- package/dist/src/server/index.js +1642 -0
- package/dist/src/server/index.js.map +1 -0
- package/dist/src/slack/channel-manager.d.ts +32 -0
- package/dist/src/slack/channel-manager.d.ts.map +1 -0
- package/dist/src/slack/channel-manager.js +128 -0
- package/dist/src/slack/channel-manager.js.map +1 -0
- package/dist/src/slack/client.d.ts +63 -0
- package/dist/src/slack/client.d.ts.map +1 -0
- package/dist/src/slack/client.js +151 -0
- package/dist/src/slack/client.js.map +1 -0
- package/dist/src/slack/deliberation.d.ts +45 -0
- package/dist/src/slack/deliberation.d.ts.map +1 -0
- package/dist/src/slack/deliberation.js +539 -0
- package/dist/src/slack/deliberation.js.map +1 -0
- package/dist/src/slack/index.d.ts +6 -0
- package/dist/src/slack/index.d.ts.map +1 -0
- package/dist/src/slack/index.js +5 -0
- package/dist/src/slack/index.js.map +1 -0
- package/dist/src/slack/interaction-listener.d.ts +47 -0
- package/dist/src/slack/interaction-listener.d.ts.map +1 -0
- package/dist/src/slack/interaction-listener.js +216 -0
- package/dist/src/slack/interaction-listener.js.map +1 -0
- package/dist/src/storage/json-state-migrator.d.ts +24 -0
- package/dist/src/storage/json-state-migrator.d.ts.map +1 -0
- package/dist/src/storage/json-state-migrator.js +197 -0
- package/dist/src/storage/json-state-migrator.js.map +1 -0
- package/dist/src/storage/repositories/index.d.ts +25 -0
- package/dist/src/storage/repositories/index.d.ts.map +1 -0
- package/dist/src/storage/repositories/index.js +43 -0
- package/dist/src/storage/repositories/index.js.map +1 -0
- package/dist/src/storage/repositories/interfaces.d.ts +59 -0
- package/dist/src/storage/repositories/interfaces.d.ts.map +1 -0
- package/dist/src/storage/repositories/interfaces.js +6 -0
- package/dist/src/storage/repositories/interfaces.js.map +1 -0
- package/dist/src/storage/repositories/sqlite/agent-persona-repository.d.ts +27 -0
- package/dist/src/storage/repositories/sqlite/agent-persona-repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/sqlite/agent-persona-repository.js +569 -0
- package/dist/src/storage/repositories/sqlite/agent-persona-repository.js.map +1 -0
- package/dist/src/storage/repositories/sqlite/execution-history-repository.d.ts +21 -0
- package/dist/src/storage/repositories/sqlite/execution-history-repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/sqlite/execution-history-repository.js +94 -0
- package/dist/src/storage/repositories/sqlite/execution-history-repository.js.map +1 -0
- package/dist/src/storage/repositories/sqlite/prd-state-repository.d.ts +17 -0
- package/dist/src/storage/repositories/sqlite/prd-state-repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/sqlite/prd-state-repository.js +74 -0
- package/dist/src/storage/repositories/sqlite/prd-state-repository.js.map +1 -0
- package/dist/src/storage/repositories/sqlite/project-registry-repository.d.ts +17 -0
- package/dist/src/storage/repositories/sqlite/project-registry-repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/sqlite/project-registry-repository.js +43 -0
- package/dist/src/storage/repositories/sqlite/project-registry-repository.js.map +1 -0
- package/dist/src/storage/repositories/sqlite/roadmap-state-repository.d.ts +14 -0
- package/dist/src/storage/repositories/sqlite/roadmap-state-repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/sqlite/roadmap-state-repository.js +47 -0
- package/dist/src/storage/repositories/sqlite/roadmap-state-repository.js.map +1 -0
- package/dist/src/storage/repositories/sqlite/slack-discussion-repository.d.ts +20 -0
- package/dist/src/storage/repositories/sqlite/slack-discussion-repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/sqlite/slack-discussion-repository.js +88 -0
- package/dist/src/storage/repositories/sqlite/slack-discussion-repository.js.map +1 -0
- package/dist/src/storage/sqlite/client.d.ts +23 -0
- package/dist/src/storage/sqlite/client.d.ts.map +1 -0
- package/dist/src/storage/sqlite/client.js +47 -0
- package/dist/src/storage/sqlite/client.js.map +1 -0
- package/dist/src/storage/sqlite/migrations.d.ts +11 -0
- package/dist/src/storage/sqlite/migrations.d.ts.map +1 -0
- package/dist/src/storage/sqlite/migrations.js +94 -0
- package/dist/src/storage/sqlite/migrations.js.map +1 -0
- package/dist/src/templates/prd-template.d.ts +11 -0
- package/dist/src/templates/prd-template.d.ts.map +1 -0
- package/dist/src/templates/prd-template.js +166 -0
- package/dist/src/templates/prd-template.js.map +1 -0
- package/dist/src/templates/slicer-prompt.d.ts +54 -0
- package/dist/src/templates/slicer-prompt.d.ts.map +1 -0
- package/dist/src/templates/slicer-prompt.js +163 -0
- package/dist/src/templates/slicer-prompt.js.map +1 -0
- package/dist/src/types.d.ts +127 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +5 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/utils/checks.d.ts +55 -0
- package/dist/src/utils/checks.d.ts.map +1 -0
- package/dist/src/utils/checks.js +246 -0
- package/dist/src/utils/checks.js.map +1 -0
- package/dist/src/utils/config-writer.d.ts +16 -0
- package/dist/src/utils/config-writer.d.ts.map +1 -0
- package/dist/src/utils/config-writer.js +45 -0
- package/dist/src/utils/config-writer.js.map +1 -0
- package/dist/src/utils/crontab.d.ts +62 -0
- package/dist/src/utils/crontab.d.ts.map +1 -0
- package/dist/src/utils/crontab.js +168 -0
- package/dist/src/utils/crontab.js.map +1 -0
- package/dist/src/utils/execution-history.d.ts +54 -0
- package/dist/src/utils/execution-history.d.ts.map +1 -0
- package/dist/src/utils/execution-history.js +80 -0
- package/dist/src/utils/execution-history.js.map +1 -0
- package/dist/src/utils/github.d.ts +40 -0
- package/dist/src/utils/github.d.ts.map +1 -0
- package/dist/src/utils/github.js +126 -0
- package/dist/src/utils/github.js.map +1 -0
- package/dist/src/utils/notify.d.ts +63 -0
- package/dist/src/utils/notify.d.ts.map +1 -0
- package/dist/src/utils/notify.js +389 -0
- package/dist/src/utils/notify.js.map +1 -0
- package/dist/src/utils/prd-states.d.ts +16 -0
- package/dist/src/utils/prd-states.d.ts.map +1 -0
- package/dist/src/utils/prd-states.js +28 -0
- package/dist/src/utils/prd-states.js.map +1 -0
- package/dist/src/utils/registry.d.ts +45 -0
- package/dist/src/utils/registry.d.ts.map +1 -0
- package/dist/src/utils/registry.js +86 -0
- package/dist/src/utils/registry.js.map +1 -0
- package/dist/src/utils/roadmap-parser.d.ts +45 -0
- package/dist/src/utils/roadmap-parser.d.ts.map +1 -0
- package/dist/src/utils/roadmap-parser.js +136 -0
- package/dist/src/utils/roadmap-parser.js.map +1 -0
- package/dist/src/utils/roadmap-scanner.d.ts +92 -0
- package/dist/src/utils/roadmap-scanner.d.ts.map +1 -0
- package/dist/src/utils/roadmap-scanner.js +349 -0
- package/dist/src/utils/roadmap-scanner.js.map +1 -0
- package/dist/src/utils/roadmap-state.d.ts +90 -0
- package/dist/src/utils/roadmap-state.d.ts.map +1 -0
- package/dist/src/utils/roadmap-state.js +154 -0
- package/dist/src/utils/roadmap-state.js.map +1 -0
- package/dist/src/utils/script-result.d.ts +12 -0
- package/dist/src/utils/script-result.d.ts.map +1 -0
- package/dist/src/utils/script-result.js +46 -0
- package/dist/src/utils/script-result.js.map +1 -0
- package/dist/src/utils/shell.d.ts +27 -0
- package/dist/src/utils/shell.d.ts.map +1 -0
- package/dist/src/utils/shell.js +64 -0
- package/dist/src/utils/shell.js.map +1 -0
- package/dist/src/utils/status-data.d.ts +148 -0
- package/dist/src/utils/status-data.d.ts.map +1 -0
- package/dist/src/utils/status-data.js +593 -0
- package/dist/src/utils/status-data.js.map +1 -0
- package/dist/src/utils/ui.d.ts +55 -0
- package/dist/src/utils/ui.d.ts.map +1 -0
- package/dist/src/utils/ui.js +121 -0
- package/dist/src/utils/ui.js.map +1 -0
- package/dist/types.d.ts +43 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/notify.d.ts.map +1 -1
- package/dist/utils/notify.js +18 -0
- package/dist/utils/notify.js.map +1 -1
- package/dist/utils/status-data.d.ts +4 -0
- package/dist/utils/status-data.d.ts.map +1 -1
- package/dist/utils/status-data.js +13 -3
- package/dist/utils/status-data.js.map +1 -1
- package/package.json +3 -1
- package/scripts/night-watch-cron.sh +50 -2
- package/scripts/night-watch-helpers.sh +54 -2
- package/scripts/night-watch-pr-reviewer-cron.sh +79 -1
- package/scripts/night-watch-qa-cron.sh +269 -0
- package/templates/night-watch-qa.md +157 -0
- package/templates/night-watch.config.json +14 -1
- package/web/dist/assets/index-BtxQU4oX.css +1 -0
- package/web/dist/assets/index-CsNIryJz.js +473 -0
- package/web/dist/index.html +2 -2
- package/web/dist/assets/index-C64sy08d.js +0 -360
- package/web/dist/assets/index-DzoZeo_Y.css +0 -1
|
@@ -10,6 +10,8 @@ set -euo pipefail
|
|
|
10
10
|
# NW_REVIEWER_MAX_RUNTIME=3600 - Maximum runtime in seconds (1 hour)
|
|
11
11
|
# NW_PROVIDER_CMD=claude - AI provider CLI to use (claude, codex, etc.)
|
|
12
12
|
# NW_DRY_RUN=0 - Set to 1 for dry-run mode (prints diagnostics only)
|
|
13
|
+
# NW_AUTO_MERGE=0 - Set to 1 to enable auto-merge
|
|
14
|
+
# NW_AUTO_MERGE_METHOD=squash - Merge method: squash, merge, or rebase
|
|
13
15
|
|
|
14
16
|
PROJECT_DIR="${1:?Usage: $0 /path/to/project}"
|
|
15
17
|
PROJECT_NAME=$(basename "${PROJECT_DIR}")
|
|
@@ -20,6 +22,8 @@ MAX_LOG_SIZE="524288" # 512 KB
|
|
|
20
22
|
PROVIDER_CMD="${NW_PROVIDER_CMD:-claude}"
|
|
21
23
|
MIN_REVIEW_SCORE="${NW_MIN_REVIEW_SCORE:-80}"
|
|
22
24
|
BRANCH_PATTERNS_RAW="${NW_BRANCH_PATTERNS:-feat/,night-watch/}"
|
|
25
|
+
AUTO_MERGE="${NW_AUTO_MERGE:-0}"
|
|
26
|
+
AUTO_MERGE_METHOD="${NW_AUTO_MERGE_METHOD:-squash}"
|
|
23
27
|
|
|
24
28
|
# Ensure NVM / Node / Claude are on PATH
|
|
25
29
|
export NVM_DIR="${HOME}/.nvm"
|
|
@@ -156,6 +160,10 @@ if [ "${NW_DRY_RUN:-0}" = "1" ]; then
|
|
|
156
160
|
echo "Provider: ${PROVIDER_CMD}"
|
|
157
161
|
echo "Branch Patterns: ${BRANCH_PATTERNS_RAW}"
|
|
158
162
|
echo "Min Review Score: ${MIN_REVIEW_SCORE}"
|
|
163
|
+
echo "Auto-merge: ${AUTO_MERGE}"
|
|
164
|
+
if [ "${AUTO_MERGE}" = "1" ]; then
|
|
165
|
+
echo "Auto-merge Method: ${AUTO_MERGE_METHOD}"
|
|
166
|
+
fi
|
|
159
167
|
echo "Open PRs needing work:${PRS_NEEDING_WORK}"
|
|
160
168
|
echo "Default Branch: ${DEFAULT_BRANCH}"
|
|
161
169
|
echo "Review Worktree: ${REVIEW_WORKTREE_DIR}"
|
|
@@ -204,9 +212,79 @@ esac
|
|
|
204
212
|
|
|
205
213
|
cleanup_worktrees "${PROJECT_DIR}"
|
|
206
214
|
|
|
215
|
+
# ── Auto-merge eligible PRs ─────────────────────────────────────────────────────
|
|
216
|
+
# After the reviewer completes, check for PRs that are merge-ready and queue them
|
|
217
|
+
# for auto-merge if enabled. Uses gh pr merge --auto to respect GitHub branch protection.
|
|
218
|
+
AUTO_MERGED_PRS=""
|
|
219
|
+
AUTO_MERGE_FAILED_PRS=""
|
|
220
|
+
|
|
221
|
+
if [ "${AUTO_MERGE}" = "1" ] && [ ${EXIT_CODE} -eq 0 ]; then
|
|
222
|
+
log "AUTO-MERGE: Checking for merge-ready PRs..."
|
|
223
|
+
|
|
224
|
+
while IFS=$'\t' read -r pr_number pr_branch; do
|
|
225
|
+
if [ -z "${pr_number}" ] || [ -z "${pr_branch}" ]; then
|
|
226
|
+
continue
|
|
227
|
+
fi
|
|
228
|
+
|
|
229
|
+
# Only process PRs matching branch patterns
|
|
230
|
+
if ! printf '%s\n' "${pr_branch}" | grep -Eq "${BRANCH_REGEX}"; then
|
|
231
|
+
continue
|
|
232
|
+
fi
|
|
233
|
+
|
|
234
|
+
# Check CI status - must have no failures
|
|
235
|
+
FAILED_CHECKS=$(gh pr checks "${pr_number}" 2>/dev/null | grep -ci 'fail' || true)
|
|
236
|
+
if [ "${FAILED_CHECKS}" -gt 0 ]; then
|
|
237
|
+
continue
|
|
238
|
+
fi
|
|
239
|
+
|
|
240
|
+
# Check review score - must have score >= threshold
|
|
241
|
+
ALL_COMMENTS=$(
|
|
242
|
+
{
|
|
243
|
+
gh pr view "${pr_number}" --json comments --jq '.comments[].body' 2>/dev/null || true
|
|
244
|
+
if [ -n "${REPO}" ]; then
|
|
245
|
+
gh api "repos/${REPO}/issues/${pr_number}/comments" --jq '.[].body' 2>/dev/null || true
|
|
246
|
+
fi
|
|
247
|
+
} | sort -u
|
|
248
|
+
)
|
|
249
|
+
LATEST_SCORE=$(echo "${ALL_COMMENTS}" \
|
|
250
|
+
| grep -oP 'Overall Score:\*?\*?\s*(\d+)/100' \
|
|
251
|
+
| tail -1 \
|
|
252
|
+
| grep -oP '\d+(?=/100)' || echo "")
|
|
253
|
+
|
|
254
|
+
# Skip PRs without a score
|
|
255
|
+
if [ -z "${LATEST_SCORE}" ]; then
|
|
256
|
+
continue
|
|
257
|
+
fi
|
|
258
|
+
|
|
259
|
+
# Skip PRs with score below threshold
|
|
260
|
+
if [ "${LATEST_SCORE}" -lt "${MIN_REVIEW_SCORE}" ]; then
|
|
261
|
+
continue
|
|
262
|
+
fi
|
|
263
|
+
|
|
264
|
+
# PR is merge-ready - queue for auto-merge
|
|
265
|
+
log "AUTO-MERGE: PR #${pr_number} (${pr_branch}) — score ${LATEST_SCORE}/100, CI passing"
|
|
266
|
+
|
|
267
|
+
if gh pr merge "${pr_number}" --"${AUTO_MERGE_METHOD}" --auto --delete-branch 2>>"${LOG_FILE}"; then
|
|
268
|
+
log "AUTO-MERGE: Successfully queued merge for PR #${pr_number}"
|
|
269
|
+
if [ -z "${AUTO_MERGED_PRS}" ]; then
|
|
270
|
+
AUTO_MERGED_PRS="#${pr_number}"
|
|
271
|
+
else
|
|
272
|
+
AUTO_MERGED_PRS="${AUTO_MERGED_PRS},#${pr_number}"
|
|
273
|
+
fi
|
|
274
|
+
else
|
|
275
|
+
log "WARN: Auto-merge failed for PR #${pr_number}"
|
|
276
|
+
if [ -z "${AUTO_MERGE_FAILED_PRS}" ]; then
|
|
277
|
+
AUTO_MERGE_FAILED_PRS="#${pr_number}"
|
|
278
|
+
else
|
|
279
|
+
AUTO_MERGE_FAILED_PRS="${AUTO_MERGE_FAILED_PRS},#${pr_number}"
|
|
280
|
+
fi
|
|
281
|
+
fi
|
|
282
|
+
done < <(gh pr list --state open --json number,headRefName --jq '.[] | [.number, .headRefName] | @tsv' 2>/dev/null || true)
|
|
283
|
+
fi
|
|
284
|
+
|
|
207
285
|
if [ ${EXIT_CODE} -eq 0 ]; then
|
|
208
286
|
log "DONE: PR reviewer completed successfully"
|
|
209
|
-
emit_result "success_reviewed" "prs=${PRS_NEEDING_WORK_CSV}"
|
|
287
|
+
emit_result "success_reviewed" "prs=${PRS_NEEDING_WORK_CSV}|auto_merged=${AUTO_MERGED_PRS}|auto_merge_failed=${AUTO_MERGE_FAILED_PRS}"
|
|
210
288
|
elif [ ${EXIT_CODE} -eq 124 ]; then
|
|
211
289
|
log "TIMEOUT: PR reviewer killed after ${MAX_RUNTIME}s"
|
|
212
290
|
emit_result "timeout" "prs=${PRS_NEEDING_WORK_CSV}"
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Night Watch QA Cron Runner (project-agnostic)
|
|
5
|
+
# Usage: night-watch-qa-cron.sh /path/to/project
|
|
6
|
+
#
|
|
7
|
+
# NOTE: This script expects environment variables to be set by the caller.
|
|
8
|
+
# The Node.js CLI will inject config values via environment variables.
|
|
9
|
+
# Required env vars (with defaults shown):
|
|
10
|
+
# NW_QA_MAX_RUNTIME=3600 - Maximum runtime in seconds (1 hour)
|
|
11
|
+
# NW_PROVIDER_CMD=claude - AI provider CLI to use (claude, codex, etc.)
|
|
12
|
+
# NW_BRANCH_PATTERNS=feat/,night-watch/ - Comma-separated branch prefixes to match
|
|
13
|
+
# NW_QA_SKIP_LABEL=skip-qa - Label to skip QA on a PR
|
|
14
|
+
# NW_QA_ARTIFACTS=both - Artifact mode (both, tests, report)
|
|
15
|
+
# NW_QA_AUTO_INSTALL_PLAYWRIGHT=1 - Auto-install Playwright browsers
|
|
16
|
+
# NW_DRY_RUN=0 - Set to 1 for dry-run mode (prints diagnostics only)
|
|
17
|
+
|
|
18
|
+
PROJECT_DIR="${1:?Usage: $0 /path/to/project}"
|
|
19
|
+
PROJECT_NAME=$(basename "${PROJECT_DIR}")
|
|
20
|
+
LOG_DIR="${PROJECT_DIR}/logs"
|
|
21
|
+
LOG_FILE="${LOG_DIR}/night-watch-qa.log"
|
|
22
|
+
MAX_RUNTIME="${NW_QA_MAX_RUNTIME:-3600}" # 1 hour
|
|
23
|
+
MAX_LOG_SIZE="524288" # 512 KB
|
|
24
|
+
PROVIDER_CMD="${NW_PROVIDER_CMD:-claude}"
|
|
25
|
+
BRANCH_PATTERNS_RAW="${NW_BRANCH_PATTERNS:-feat/,night-watch/}"
|
|
26
|
+
SKIP_LABEL="${NW_QA_SKIP_LABEL:-skip-qa}"
|
|
27
|
+
QA_ARTIFACTS="${NW_QA_ARTIFACTS:-both}"
|
|
28
|
+
QA_AUTO_INSTALL_PLAYWRIGHT="${NW_QA_AUTO_INSTALL_PLAYWRIGHT:-1}"
|
|
29
|
+
|
|
30
|
+
# Ensure NVM / Node / Claude are on PATH
|
|
31
|
+
export NVM_DIR="${HOME}/.nvm"
|
|
32
|
+
[ -s "${NVM_DIR}/nvm.sh" ] && . "${NVM_DIR}/nvm.sh"
|
|
33
|
+
|
|
34
|
+
# NOTE: Environment variables should be set by the caller (Node.js CLI).
|
|
35
|
+
# The .env.night-watch sourcing has been removed - config is now injected via env vars.
|
|
36
|
+
|
|
37
|
+
mkdir -p "${LOG_DIR}"
|
|
38
|
+
|
|
39
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
40
|
+
# shellcheck source=night-watch-helpers.sh
|
|
41
|
+
source "${SCRIPT_DIR}/night-watch-helpers.sh"
|
|
42
|
+
PROJECT_RUNTIME_KEY=$(project_runtime_key "${PROJECT_DIR}")
|
|
43
|
+
# NOTE: Lock file path must match qaLockPath() in src/utils/status-data.ts
|
|
44
|
+
LOCK_FILE="/tmp/night-watch-qa-${PROJECT_RUNTIME_KEY}.lock"
|
|
45
|
+
|
|
46
|
+
emit_result() {
|
|
47
|
+
local status="${1:?status required}"
|
|
48
|
+
local details="${2:-}"
|
|
49
|
+
if [ -n "${details}" ]; then
|
|
50
|
+
echo "NIGHT_WATCH_RESULT:${status}|${details}"
|
|
51
|
+
else
|
|
52
|
+
echo "NIGHT_WATCH_RESULT:${status}"
|
|
53
|
+
fi
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
# Validate provider
|
|
57
|
+
if ! validate_provider "${PROVIDER_CMD}"; then
|
|
58
|
+
echo "ERROR: Unknown provider: ${PROVIDER_CMD}" >&2
|
|
59
|
+
exit 1
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
rotate_log
|
|
63
|
+
|
|
64
|
+
if ! acquire_lock "${LOCK_FILE}"; then
|
|
65
|
+
emit_result "skip_locked"
|
|
66
|
+
exit 0
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
cd "${PROJECT_DIR}"
|
|
70
|
+
|
|
71
|
+
# Convert comma-separated branch prefixes into a regex that matches branch starts.
|
|
72
|
+
BRANCH_REGEX=""
|
|
73
|
+
IFS=',' read -r -a BRANCH_PATTERNS <<< "${BRANCH_PATTERNS_RAW}"
|
|
74
|
+
for pattern in "${BRANCH_PATTERNS[@]}"; do
|
|
75
|
+
trimmed_pattern=$(printf '%s' "${pattern}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
|
|
76
|
+
if [ -n "${trimmed_pattern}" ]; then
|
|
77
|
+
BRANCH_REGEX="${BRANCH_REGEX}${BRANCH_REGEX:+|}^${trimmed_pattern}"
|
|
78
|
+
fi
|
|
79
|
+
done
|
|
80
|
+
|
|
81
|
+
if [ -z "${BRANCH_REGEX}" ]; then
|
|
82
|
+
BRANCH_REGEX='^(feat/|night-watch/)'
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
# List open PRs with their details for filtering
|
|
86
|
+
PR_JSON=$(gh pr list --state open --json number,headRefName,title,labels 2>/dev/null || echo "[]")
|
|
87
|
+
|
|
88
|
+
# Count PRs matching branch patterns
|
|
89
|
+
OPEN_PRS=$(
|
|
90
|
+
echo "${PR_JSON}" \
|
|
91
|
+
| jq -r '.[].headRefName' 2>/dev/null \
|
|
92
|
+
| { grep -E "${BRANCH_REGEX}" || true; } \
|
|
93
|
+
| wc -l \
|
|
94
|
+
| tr -d '[:space:]'
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
if [ "${OPEN_PRS}" -eq 0 ]; then
|
|
98
|
+
log "SKIP: No open PRs matching branch patterns (${BRANCH_PATTERNS_RAW})"
|
|
99
|
+
emit_result "skip_no_open_prs"
|
|
100
|
+
exit 0
|
|
101
|
+
fi
|
|
102
|
+
|
|
103
|
+
REPO=$(gh repo view --json nameWithOwner --jq '.nameWithOwner' 2>/dev/null || echo "")
|
|
104
|
+
|
|
105
|
+
# Collect PRs that need QA
|
|
106
|
+
PRS_NEEDING_QA=""
|
|
107
|
+
QA_NEEDED=0
|
|
108
|
+
|
|
109
|
+
while IFS=$'\t' read -r pr_number pr_branch pr_title pr_labels; do
|
|
110
|
+
if [ -z "${pr_number}" ] || [ -z "${pr_branch}" ]; then
|
|
111
|
+
continue
|
|
112
|
+
fi
|
|
113
|
+
|
|
114
|
+
# Filter by branch pattern
|
|
115
|
+
if ! printf '%s\n' "${pr_branch}" | grep -Eq "${BRANCH_REGEX}"; then
|
|
116
|
+
continue
|
|
117
|
+
fi
|
|
118
|
+
|
|
119
|
+
# Skip PRs with the skip label
|
|
120
|
+
if echo "${pr_labels}" | grep -q "${SKIP_LABEL}"; then
|
|
121
|
+
log "SKIP-QA: PR #${pr_number} (${pr_branch}) has '${SKIP_LABEL}' label"
|
|
122
|
+
continue
|
|
123
|
+
fi
|
|
124
|
+
|
|
125
|
+
# Skip PRs with [skip-qa] in their title
|
|
126
|
+
if echo "${pr_title}" | grep -qi '\[skip-qa\]'; then
|
|
127
|
+
log "SKIP-QA: PR #${pr_number} (${pr_branch}) has [skip-qa] in title"
|
|
128
|
+
continue
|
|
129
|
+
fi
|
|
130
|
+
|
|
131
|
+
# Skip PRs that already have a QA comment (idempotency)
|
|
132
|
+
ALL_COMMENTS=$(
|
|
133
|
+
{
|
|
134
|
+
gh pr view "${pr_number}" --json comments --jq '.comments[].body' 2>/dev/null || true
|
|
135
|
+
if [ -n "${REPO}" ]; then
|
|
136
|
+
gh api "repos/${REPO}/issues/${pr_number}/comments" --jq '.[].body' 2>/dev/null || true
|
|
137
|
+
fi
|
|
138
|
+
} | sort -u
|
|
139
|
+
)
|
|
140
|
+
if echo "${ALL_COMMENTS}" | grep -q '<!-- night-watch-qa-marker -->'; then
|
|
141
|
+
log "SKIP-QA: PR #${pr_number} (${pr_branch}) already has QA comment"
|
|
142
|
+
continue
|
|
143
|
+
fi
|
|
144
|
+
|
|
145
|
+
QA_NEEDED=1
|
|
146
|
+
PRS_NEEDING_QA="${PRS_NEEDING_QA} #${pr_number}"
|
|
147
|
+
done < <(
|
|
148
|
+
echo "${PR_JSON}" \
|
|
149
|
+
| jq -r '.[] | [.number, .headRefName, .title, ([.labels[].name] | join(","))] | @tsv' 2>/dev/null || true
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
if [ "${QA_NEEDED}" -eq 0 ]; then
|
|
153
|
+
log "SKIP: All ${OPEN_PRS} open PR(s) matching patterns already have QA comments"
|
|
154
|
+
emit_result "skip_all_qa_done"
|
|
155
|
+
exit 0
|
|
156
|
+
fi
|
|
157
|
+
|
|
158
|
+
PRS_NEEDING_QA=$(echo "${PRS_NEEDING_QA}" \
|
|
159
|
+
| sed -e 's/^[[:space:]]*//' -e 's/[[:space:]][[:space:]]*/ /g' -e 's/[[:space:]]*$//')
|
|
160
|
+
PRS_NEEDING_QA_CSV="${PRS_NEEDING_QA// /,}"
|
|
161
|
+
|
|
162
|
+
if [ -n "${NW_DEFAULT_BRANCH:-}" ]; then
|
|
163
|
+
DEFAULT_BRANCH="${NW_DEFAULT_BRANCH}"
|
|
164
|
+
else
|
|
165
|
+
DEFAULT_BRANCH=$(detect_default_branch "${PROJECT_DIR}")
|
|
166
|
+
fi
|
|
167
|
+
QA_WORKTREE_DIR="$(dirname "${PROJECT_DIR}")/${PROJECT_NAME}-nw-qa-runner"
|
|
168
|
+
|
|
169
|
+
log "START: Found PR(s) needing QA:${PRS_NEEDING_QA}"
|
|
170
|
+
|
|
171
|
+
cleanup_worktrees "${PROJECT_DIR}"
|
|
172
|
+
|
|
173
|
+
# Dry-run mode: print diagnostics and exit
|
|
174
|
+
if [ "${NW_DRY_RUN:-0}" = "1" ]; then
|
|
175
|
+
echo "=== Dry Run: QA Runner ==="
|
|
176
|
+
echo "Provider: ${PROVIDER_CMD}"
|
|
177
|
+
echo "Branch Patterns: ${BRANCH_PATTERNS_RAW}"
|
|
178
|
+
echo "Skip Label: ${SKIP_LABEL}"
|
|
179
|
+
echo "QA Artifacts: ${QA_ARTIFACTS}"
|
|
180
|
+
echo "Auto-install Playwright: ${QA_AUTO_INSTALL_PLAYWRIGHT}"
|
|
181
|
+
echo "Open PRs needing QA:${PRS_NEEDING_QA}"
|
|
182
|
+
echo "Default Branch: ${DEFAULT_BRANCH}"
|
|
183
|
+
echo "QA Worktree: ${QA_WORKTREE_DIR}"
|
|
184
|
+
echo "Timeout: ${MAX_RUNTIME}s"
|
|
185
|
+
exit 0
|
|
186
|
+
fi
|
|
187
|
+
|
|
188
|
+
EXIT_CODE=0
|
|
189
|
+
|
|
190
|
+
# Process each PR that needs QA
|
|
191
|
+
for pr_ref in ${PRS_NEEDING_QA}; do
|
|
192
|
+
pr_num="${pr_ref#\#}"
|
|
193
|
+
|
|
194
|
+
cleanup_worktrees "${PROJECT_DIR}"
|
|
195
|
+
if ! prepare_detached_worktree "${PROJECT_DIR}" "${QA_WORKTREE_DIR}" "${DEFAULT_BRANCH}" "${LOG_FILE}"; then
|
|
196
|
+
log "FAIL: Unable to create isolated QA worktree ${QA_WORKTREE_DIR} for PR #${pr_num}"
|
|
197
|
+
EXIT_CODE=1
|
|
198
|
+
break
|
|
199
|
+
fi
|
|
200
|
+
|
|
201
|
+
log "QA: Checking out PR #${pr_num} in worktree"
|
|
202
|
+
if ! (cd "${QA_WORKTREE_DIR}" && gh pr checkout "${pr_num}" >> "${LOG_FILE}" 2>&1); then
|
|
203
|
+
log "WARN: Failed to checkout PR #${pr_num}, skipping"
|
|
204
|
+
EXIT_CODE=1
|
|
205
|
+
cleanup_worktrees "${PROJECT_DIR}"
|
|
206
|
+
continue
|
|
207
|
+
fi
|
|
208
|
+
|
|
209
|
+
case "${PROVIDER_CMD}" in
|
|
210
|
+
claude)
|
|
211
|
+
if (
|
|
212
|
+
cd "${QA_WORKTREE_DIR}" && timeout "${MAX_RUNTIME}" \
|
|
213
|
+
claude -p "/night-watch-qa" \
|
|
214
|
+
--dangerously-skip-permissions \
|
|
215
|
+
>> "${LOG_FILE}" 2>&1
|
|
216
|
+
); then
|
|
217
|
+
log "QA: PR #${pr_num} — provider completed successfully"
|
|
218
|
+
else
|
|
219
|
+
local_exit=$?
|
|
220
|
+
log "QA: PR #${pr_num} — provider exited with code ${local_exit}"
|
|
221
|
+
if [ ${local_exit} -eq 124 ]; then
|
|
222
|
+
EXIT_CODE=124
|
|
223
|
+
break
|
|
224
|
+
fi
|
|
225
|
+
EXIT_CODE=${local_exit}
|
|
226
|
+
fi
|
|
227
|
+
;;
|
|
228
|
+
codex)
|
|
229
|
+
if (
|
|
230
|
+
cd "${QA_WORKTREE_DIR}" && timeout "${MAX_RUNTIME}" \
|
|
231
|
+
codex --quiet \
|
|
232
|
+
--yolo \
|
|
233
|
+
--prompt "$(cat "${QA_WORKTREE_DIR}/.claude/commands/night-watch-qa.md")" \
|
|
234
|
+
>> "${LOG_FILE}" 2>&1
|
|
235
|
+
); then
|
|
236
|
+
log "QA: PR #${pr_num} — provider completed successfully"
|
|
237
|
+
else
|
|
238
|
+
local_exit=$?
|
|
239
|
+
log "QA: PR #${pr_num} — provider exited with code ${local_exit}"
|
|
240
|
+
if [ ${local_exit} -eq 124 ]; then
|
|
241
|
+
EXIT_CODE=124
|
|
242
|
+
break
|
|
243
|
+
fi
|
|
244
|
+
EXIT_CODE=${local_exit}
|
|
245
|
+
fi
|
|
246
|
+
;;
|
|
247
|
+
*)
|
|
248
|
+
log "ERROR: Unknown provider: ${PROVIDER_CMD}"
|
|
249
|
+
exit 1
|
|
250
|
+
;;
|
|
251
|
+
esac
|
|
252
|
+
|
|
253
|
+
cleanup_worktrees "${PROJECT_DIR}"
|
|
254
|
+
done
|
|
255
|
+
|
|
256
|
+
cleanup_worktrees "${PROJECT_DIR}"
|
|
257
|
+
|
|
258
|
+
if [ ${EXIT_CODE} -eq 0 ]; then
|
|
259
|
+
log "DONE: QA runner completed successfully"
|
|
260
|
+
emit_result "success_qa" "prs=${PRS_NEEDING_QA_CSV}"
|
|
261
|
+
elif [ ${EXIT_CODE} -eq 124 ]; then
|
|
262
|
+
log "TIMEOUT: QA runner killed after ${MAX_RUNTIME}s"
|
|
263
|
+
emit_result "timeout" "prs=${PRS_NEEDING_QA_CSV}"
|
|
264
|
+
else
|
|
265
|
+
log "FAIL: QA runner exited with code ${EXIT_CODE}"
|
|
266
|
+
emit_result "failure" "prs=${PRS_NEEDING_QA_CSV}"
|
|
267
|
+
fi
|
|
268
|
+
|
|
269
|
+
exit "${EXIT_CODE}"
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
You are the Night Watch QA agent. Your job is to analyze open PRs, generate appropriate tests for the changes, run them, and report results with visual evidence.
|
|
2
|
+
|
|
3
|
+
## Context
|
|
4
|
+
|
|
5
|
+
You are running inside a worktree checked out to a PR branch. Your goal is to:
|
|
6
|
+
1. Analyze what changed in this PR compared to the base branch
|
|
7
|
+
2. Determine if the changes are UI-related, API-related, or both
|
|
8
|
+
3. Generate appropriate tests (Playwright e2e for UI, integration tests for API)
|
|
9
|
+
4. Run the tests and capture artifacts (screenshots, videos for UI)
|
|
10
|
+
5. Commit the tests and artifacts, then comment on the PR with results
|
|
11
|
+
|
|
12
|
+
## Environment Variables Available
|
|
13
|
+
- `NW_QA_ARTIFACTS` — What to capture: "screenshot", "video", or "both" (default: "both")
|
|
14
|
+
- `NW_QA_AUTO_INSTALL_PLAYWRIGHT` — "1" to auto-install Playwright if missing
|
|
15
|
+
|
|
16
|
+
## Instructions
|
|
17
|
+
|
|
18
|
+
### Step 1: Analyze the PR diff
|
|
19
|
+
|
|
20
|
+
Get the diff against the base branch:
|
|
21
|
+
```
|
|
22
|
+
git diff origin/${DEFAULT_BRANCH}...HEAD --name-only
|
|
23
|
+
git diff origin/${DEFAULT_BRANCH}...HEAD --stat
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Read the changed files to understand what the PR introduces.
|
|
27
|
+
|
|
28
|
+
### Step 2: Classify and Decide
|
|
29
|
+
|
|
30
|
+
Based on the diff, determine:
|
|
31
|
+
- **UI changes**: New/modified components, pages, layouts, styles, client-side logic
|
|
32
|
+
- **API changes**: New/modified endpoints, controllers, services, middleware, database queries
|
|
33
|
+
- **Both**: PR touches both UI and API code
|
|
34
|
+
- **No tests needed**: Trivial changes (docs, config, comments only) — in this case, post a comment saying "QA: No tests needed for this PR" and stop
|
|
35
|
+
|
|
36
|
+
### Step 3: Prepare Test Infrastructure
|
|
37
|
+
|
|
38
|
+
**For UI tests (Playwright):**
|
|
39
|
+
1. Check if Playwright is available: `npx playwright --version`
|
|
40
|
+
2. If not available and `NW_QA_AUTO_INSTALL_PLAYWRIGHT=1`:
|
|
41
|
+
- Run `npm install -D @playwright/test` (or yarn/pnpm equivalent based on lockfile)
|
|
42
|
+
- Run `npx playwright install chromium`
|
|
43
|
+
3. If not available and auto-install is disabled, skip UI tests and note in the report
|
|
44
|
+
|
|
45
|
+
**For API tests:**
|
|
46
|
+
- Use the project's existing test framework (vitest, jest, or mocha — detect from package.json)
|
|
47
|
+
- If no test framework exists, use vitest
|
|
48
|
+
|
|
49
|
+
### Step 4: Generate Tests
|
|
50
|
+
|
|
51
|
+
**UI Tests (Playwright):**
|
|
52
|
+
- Create test files in `tests/e2e/qa/` (or the project's existing e2e directory)
|
|
53
|
+
- Test the specific feature/page changed in the PR
|
|
54
|
+
- Configure Playwright for artifacts based on `NW_QA_ARTIFACTS`:
|
|
55
|
+
- `"screenshot"`: `screenshot: 'on'` only
|
|
56
|
+
- `"video"`: `video: { mode: 'on', size: { width: 1280, height: 720 } }` only
|
|
57
|
+
- `"both"`: Both screenshot and video enabled
|
|
58
|
+
- Name test files with a `qa-` prefix: `qa-<feature-name>.spec.ts`
|
|
59
|
+
- Include at minimum: navigation to the feature, interaction with key elements, visual assertions
|
|
60
|
+
|
|
61
|
+
**API Tests:**
|
|
62
|
+
- Create test files in `tests/integration/qa/` (or the project's existing test directory)
|
|
63
|
+
- Test the specific endpoints changed in the PR
|
|
64
|
+
- Include: happy path, error cases, validation checks
|
|
65
|
+
- Name test files with a `qa-` prefix: `qa-<endpoint-name>.test.ts`
|
|
66
|
+
|
|
67
|
+
### Step 5: Run Tests
|
|
68
|
+
|
|
69
|
+
**UI Tests:**
|
|
70
|
+
```bash
|
|
71
|
+
npx playwright test tests/e2e/qa/ --reporter=list
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**API Tests:**
|
|
75
|
+
```bash
|
|
76
|
+
npx vitest run tests/integration/qa/ --reporter=verbose
|
|
77
|
+
# (or equivalent for the project's test runner)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Capture the test output for the report.
|
|
81
|
+
|
|
82
|
+
### Step 6: Collect Artifacts
|
|
83
|
+
|
|
84
|
+
Move Playwright artifacts (screenshots, videos) to `qa-artifacts/` in the project root:
|
|
85
|
+
```bash
|
|
86
|
+
mkdir -p qa-artifacts
|
|
87
|
+
# Copy from playwright-report/ or test-results/ to qa-artifacts/
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Step 7: Commit and Push
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
git add tests/e2e/qa/ tests/integration/qa/ qa-artifacts/ || true
|
|
94
|
+
git add -A tests/*/qa/ qa-artifacts/ || true
|
|
95
|
+
git commit -m "test(qa): add automated QA tests for PR changes
|
|
96
|
+
|
|
97
|
+
- Generated by Night Watch QA agent
|
|
98
|
+
- <UI tests: X passing, Y failing | No UI tests>
|
|
99
|
+
- <API tests: X passing, Y failing | No API tests>
|
|
100
|
+
- Artifacts: <screenshots, videos | screenshots | videos | none>
|
|
101
|
+
|
|
102
|
+
Co-Authored-By: Claude <noreply@anthropic.com>"
|
|
103
|
+
git push origin HEAD
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Step 8: Comment on PR
|
|
107
|
+
|
|
108
|
+
Post a comment on the PR with results. Use the `<!-- night-watch-qa-marker -->` HTML comment for idempotency detection.
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
gh pr comment <PR_NUMBER> --body "<!-- night-watch-qa-marker -->
|
|
112
|
+
## Night Watch QA Report
|
|
113
|
+
|
|
114
|
+
### Changes Classification
|
|
115
|
+
- **Type**: <UI | API | UI + API>
|
|
116
|
+
- **Files changed**: <count>
|
|
117
|
+
|
|
118
|
+
### Test Results
|
|
119
|
+
|
|
120
|
+
<If UI tests>
|
|
121
|
+
#### UI Tests (Playwright)
|
|
122
|
+
- **Status**: <All passing | X of Y failing>
|
|
123
|
+
- **Tests**: <count> test(s) in <count> file(s)
|
|
124
|
+
|
|
125
|
+
<If screenshots captured>
|
|
126
|
+
#### Screenshots
|
|
127
|
+
<For each screenshot>
|
|
128
|
+

|
|
129
|
+
</For>
|
|
130
|
+
</If>
|
|
131
|
+
|
|
132
|
+
<If video captured>
|
|
133
|
+
#### Video Recording
|
|
134
|
+
Video artifact committed to \`qa-artifacts/\` — view in the PR's file changes.
|
|
135
|
+
</If>
|
|
136
|
+
</If>
|
|
137
|
+
|
|
138
|
+
<If API tests>
|
|
139
|
+
#### API Tests
|
|
140
|
+
- **Status**: <All passing | X of Y failing>
|
|
141
|
+
- **Tests**: <count> test(s) in <count> file(s)
|
|
142
|
+
</If>
|
|
143
|
+
|
|
144
|
+
<If no tests generated>
|
|
145
|
+
**QA: No tests needed for this PR** — changes are trivial (docs, config, comments).
|
|
146
|
+
</If>
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
*Night Watch QA Agent*"
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Important Rules
|
|
153
|
+
- Process each PR **once** per run. Do NOT loop or retry after pushing.
|
|
154
|
+
- Do NOT modify existing project tests — only add new files in `qa/` subdirectories.
|
|
155
|
+
- If tests fail, still commit and report — the failures are useful information.
|
|
156
|
+
- Keep test files self-contained and independent from each other.
|
|
157
|
+
- Follow the project's existing code style and conventions (check CLAUDE.md, package.json scripts, tsconfig).
|
|
@@ -13,5 +13,18 @@
|
|
|
13
13
|
"minReviewScore": 80,
|
|
14
14
|
"maxLogSize": 524288,
|
|
15
15
|
"cronSchedule": "0 0-21 * * *",
|
|
16
|
-
"reviewerSchedule": "0 0,3,6,9,12,15,18,21 * * *"
|
|
16
|
+
"reviewerSchedule": "0 0,3,6,9,12,15,18,21 * * *",
|
|
17
|
+
"autoMerge": false,
|
|
18
|
+
"autoMergeMethod": "squash",
|
|
19
|
+
"fallbackOnRateLimit": false,
|
|
20
|
+
"claudeModel": "sonnet",
|
|
21
|
+
"qa": {
|
|
22
|
+
"enabled": true,
|
|
23
|
+
"schedule": "30 1,7,13,19 * * *",
|
|
24
|
+
"maxRuntime": 3600,
|
|
25
|
+
"branchPatterns": [],
|
|
26
|
+
"artifacts": "both",
|
|
27
|
+
"skipLabel": "skip-qa",
|
|
28
|
+
"autoInstallPlaywright": true
|
|
29
|
+
}
|
|
17
30
|
}
|