@jonit-dev/night-watch-cli 1.7.79 → 1.7.81
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/commands/analytics.d.ts +14 -0
- package/dist/commands/analytics.d.ts.map +1 -0
- package/dist/commands/analytics.js +74 -0
- package/dist/commands/analytics.js.map +1 -0
- package/dist/scripts/night-watch-audit-cron.sh +14 -10
- package/dist/scripts/night-watch-cron.sh +86 -7
- package/dist/scripts/night-watch-helpers.sh +85 -0
- package/dist/scripts/night-watch-pr-reviewer-cron.sh +6 -7
- package/dist/scripts/night-watch-qa-cron.sh +6 -7
- package/dist/scripts/night-watch-slicer-cron.sh +3 -4
- package/dist/web/assets/index-BjhCFjZi.js +381 -0
- package/package.json +1 -1
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analytics command - runs the Amplitude analytics job
|
|
3
|
+
*/
|
|
4
|
+
import { Command } from 'commander';
|
|
5
|
+
export interface IAnalyticsOptions {
|
|
6
|
+
dryRun: boolean;
|
|
7
|
+
timeout?: string;
|
|
8
|
+
provider?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Register the analytics command with the program
|
|
12
|
+
*/
|
|
13
|
+
export declare function analyticsCommand(program: Command): void;
|
|
14
|
+
//# sourceMappingURL=analytics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../../src/commands/analytics.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAcpC,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAyEvD"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analytics command - runs the Amplitude analytics job
|
|
3
|
+
*/
|
|
4
|
+
import { createSpinner, createTable, header, info, loadConfig, resolveJobProvider, runAnalytics, } from '@night-watch/core';
|
|
5
|
+
import { maybeApplyCronSchedulingDelay } from './shared/env-builder.js';
|
|
6
|
+
/**
|
|
7
|
+
* Register the analytics command with the program
|
|
8
|
+
*/
|
|
9
|
+
export function analyticsCommand(program) {
|
|
10
|
+
program
|
|
11
|
+
.command('analytics')
|
|
12
|
+
.description('Run Amplitude analytics job now')
|
|
13
|
+
.option('--dry-run', 'Show what would be executed without running')
|
|
14
|
+
.option('--timeout <seconds>', 'Override max runtime in seconds')
|
|
15
|
+
.option('--provider <string>', 'AI provider to use (claude or codex)')
|
|
16
|
+
.action(async (options) => {
|
|
17
|
+
const projectDir = process.cwd();
|
|
18
|
+
let config = loadConfig(projectDir);
|
|
19
|
+
if (options.timeout) {
|
|
20
|
+
const timeout = parseInt(options.timeout, 10);
|
|
21
|
+
if (!isNaN(timeout)) {
|
|
22
|
+
config = { ...config, analytics: { ...config.analytics, maxRuntime: timeout } };
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (options.provider) {
|
|
26
|
+
config = {
|
|
27
|
+
...config,
|
|
28
|
+
_cliProviderOverride: options.provider,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
if (!config.analytics.enabled && !options.dryRun) {
|
|
32
|
+
info('Analytics is disabled in config; skipping run.');
|
|
33
|
+
process.exit(0);
|
|
34
|
+
}
|
|
35
|
+
// Validate Amplitude keys
|
|
36
|
+
const apiKey = config.providerEnv?.AMPLITUDE_API_KEY;
|
|
37
|
+
const secretKey = config.providerEnv?.AMPLITUDE_SECRET_KEY;
|
|
38
|
+
if (!apiKey || !secretKey) {
|
|
39
|
+
info('AMPLITUDE_API_KEY and AMPLITUDE_SECRET_KEY must be set in providerEnv to run analytics.');
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
if (options.dryRun) {
|
|
43
|
+
header('Dry Run: Analytics Job');
|
|
44
|
+
const analyticsProvider = resolveJobProvider(config, 'analytics');
|
|
45
|
+
header('Configuration');
|
|
46
|
+
const configTable = createTable({ head: ['Setting', 'Value'] });
|
|
47
|
+
configTable.push(['Provider', analyticsProvider]);
|
|
48
|
+
configTable.push(['Max Runtime', `${config.analytics.maxRuntime}s`]);
|
|
49
|
+
configTable.push(['Lookback Days', String(config.analytics.lookbackDays)]);
|
|
50
|
+
configTable.push(['Target Column', config.analytics.targetColumn]);
|
|
51
|
+
configTable.push(['Amplitude API Key', apiKey ? '***' + apiKey.slice(-4) : 'not set']);
|
|
52
|
+
console.log(configTable.toString());
|
|
53
|
+
console.log();
|
|
54
|
+
process.exit(0);
|
|
55
|
+
}
|
|
56
|
+
const spinner = createSpinner('Running analytics job...');
|
|
57
|
+
spinner.start();
|
|
58
|
+
try {
|
|
59
|
+
await maybeApplyCronSchedulingDelay(config, 'analytics', projectDir);
|
|
60
|
+
const result = await runAnalytics(config, projectDir);
|
|
61
|
+
if (result.issuesCreated > 0) {
|
|
62
|
+
spinner.succeed(`Analytics complete — ${result.summary}`);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
spinner.succeed('Analytics complete — no actionable insights found');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
spinner.fail(`Analytics failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=analytics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analytics.js","sourceRoot":"","sources":["../../src/commands/analytics.ts"],"names":[],"mappings":"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"}
|
|
@@ -23,21 +23,13 @@ SCRIPT_TYPE="audit"
|
|
|
23
23
|
PROVIDER_LABEL="${NW_PROVIDER_LABEL:-}"
|
|
24
24
|
SCRIPT_START_TIME=$(date +%s)
|
|
25
25
|
|
|
26
|
-
# Ensure NVM / Node / Claude are on PATH
|
|
27
|
-
export NVM_DIR="${HOME}/.nvm"
|
|
28
|
-
[ -s "${NVM_DIR}/nvm.sh" ] && . "${NVM_DIR}/nvm.sh"
|
|
29
|
-
|
|
30
26
|
mkdir -p "${LOG_DIR}"
|
|
31
27
|
|
|
32
28
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
33
29
|
# shellcheck source=night-watch-helpers.sh
|
|
34
30
|
source "${SCRIPT_DIR}/night-watch-helpers.sh"
|
|
35
|
-
PROJECT_RUNTIME_KEY=$(project_runtime_key "${PROJECT_DIR}")
|
|
36
|
-
# NOTE: Lock file path must match auditLockPath() in src/utils/status-data.ts
|
|
37
|
-
LOCK_FILE="/tmp/night-watch-audit-${PROJECT_RUNTIME_KEY}.lock"
|
|
38
|
-
AUDIT_PROMPT_TEMPLATE="${SCRIPT_DIR}/../templates/audit.md"
|
|
39
|
-
PROVIDER_MODEL_DISPLAY=$(resolve_provider_model_display "${PROVIDER_CMD}" "${PROVIDER_LABEL}")
|
|
40
31
|
|
|
32
|
+
# emit_result helper - must be defined before use
|
|
41
33
|
emit_result() {
|
|
42
34
|
local status="${1:?status required}"
|
|
43
35
|
local details="${2:-}"
|
|
@@ -48,13 +40,25 @@ emit_result() {
|
|
|
48
40
|
fi
|
|
49
41
|
}
|
|
50
42
|
|
|
51
|
-
# Validate provider
|
|
43
|
+
# Validate provider name first (must be claude or codex)
|
|
52
44
|
if ! validate_provider "${PROVIDER_CMD}"; then
|
|
53
45
|
echo "ERROR: Unknown provider: ${PROVIDER_CMD}" >&2
|
|
54
46
|
emit_result "failure" "reason=unknown_provider"
|
|
55
47
|
exit 1
|
|
56
48
|
fi
|
|
57
49
|
|
|
50
|
+
# Ensure provider CLI is on PATH (nvm, fnm, volta, common bin dirs)
|
|
51
|
+
if ! ensure_provider_on_path "${PROVIDER_CMD}"; then
|
|
52
|
+
echo "ERROR: Provider '${PROVIDER_CMD}' not found in PATH or common installation locations" >&2
|
|
53
|
+
emit_result "failure" "reason=provider_not_found"
|
|
54
|
+
exit 1
|
|
55
|
+
fi
|
|
56
|
+
PROJECT_RUNTIME_KEY=$(project_runtime_key "${PROJECT_DIR}")
|
|
57
|
+
# NOTE: Lock file path must match auditLockPath() in src/utils/status-data.ts
|
|
58
|
+
LOCK_FILE="/tmp/night-watch-audit-${PROJECT_RUNTIME_KEY}.lock"
|
|
59
|
+
AUDIT_PROMPT_TEMPLATE="${SCRIPT_DIR}/../templates/audit.md"
|
|
60
|
+
PROVIDER_MODEL_DISPLAY=$(resolve_provider_model_display "${PROVIDER_CMD}" "${PROVIDER_LABEL}")
|
|
61
|
+
|
|
58
62
|
# Global gate: if queue is enabled and we can't acquire the global lock,
|
|
59
63
|
# enqueue the job and exit. The dispatcher will run it later.
|
|
60
64
|
if [ "${NW_QUEUE_ENABLED:-0}" = "1" ]; then
|
|
@@ -34,19 +34,18 @@ EFFECTIVE_PROVIDER_LABEL="${PROVIDER_LABEL}"
|
|
|
34
34
|
BRANCH_PREFIX="${NW_BRANCH_PREFIX:-night-watch}"
|
|
35
35
|
SCRIPT_START_TIME=$(date +%s)
|
|
36
36
|
|
|
37
|
-
# Ensure NVM / Node / Claude are on PATH
|
|
38
|
-
export NVM_DIR="${HOME}/.nvm"
|
|
39
|
-
[ -s "${NVM_DIR}/nvm.sh" ] && . "${NVM_DIR}/nvm.sh"
|
|
40
|
-
|
|
41
|
-
# NOTE: Environment variables should be set by the caller (Node.js CLI).
|
|
42
|
-
# The .env.night-watch sourcing has been removed - config is now injected via env vars.
|
|
43
|
-
|
|
44
37
|
mkdir -p "${LOG_DIR}"
|
|
45
38
|
|
|
46
39
|
# Load shared helpers
|
|
47
40
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
48
41
|
# shellcheck source=night-watch-helpers.sh
|
|
49
42
|
source "${SCRIPT_DIR}/night-watch-helpers.sh"
|
|
43
|
+
|
|
44
|
+
# Ensure provider CLI is on PATH (nvm, fnm, volta, common bin dirs)
|
|
45
|
+
if ! ensure_provider_on_path "${PROVIDER_CMD}"; then
|
|
46
|
+
echo "ERROR: Provider '${PROVIDER_CMD}' not found in PATH or common installation locations" >&2
|
|
47
|
+
exit 127
|
|
48
|
+
fi
|
|
50
49
|
PROJECT_RUNTIME_KEY=$(project_runtime_key "${PROJECT_DIR}")
|
|
51
50
|
# NOTE: Lock file path must match executorLockPath() in src/utils/status-data.ts
|
|
52
51
|
LOCK_FILE="/tmp/night-watch-${PROJECT_RUNTIME_KEY}.lock"
|
|
@@ -602,6 +601,74 @@ while [ "${ATTEMPT}" -lt "${MAX_RETRIES}" ]; do
|
|
|
602
601
|
BACKOFF_MIN=$(( BACKOFF / 60 ))
|
|
603
602
|
log "RATE-LIMITED: Attempt ${ATTEMPT}/${MAX_RETRIES}, retrying in ${BACKOFF_MIN}m"
|
|
604
603
|
sleep "${BACKOFF}"
|
|
604
|
+
elif check_context_exhausted "${LOG_FILE}" "${LOG_LINE_BEFORE}"; then
|
|
605
|
+
# Context window exhausted — checkpoint progress and resume in a fresh session
|
|
606
|
+
ATTEMPT=$((ATTEMPT + 1))
|
|
607
|
+
if [ "${ATTEMPT}" -ge "${MAX_RETRIES}" ]; then
|
|
608
|
+
log "CONTEXT-EXHAUSTED: All ${MAX_RETRIES} resume attempts exhausted for ${ELIGIBLE_PRD}"
|
|
609
|
+
break
|
|
610
|
+
fi
|
|
611
|
+
log "CONTEXT-EXHAUSTED: Session ${ATTEMPT_NUM} hit context limit — checkpointing and resuming (${ATTEMPT}/${MAX_RETRIES})"
|
|
612
|
+
checkpoint_timeout_progress "${WORKTREE_DIR}" "${BRANCH_NAME}" "${ELIGIBLE_PRD}"
|
|
613
|
+
git -C "${WORKTREE_DIR}" push origin "${BRANCH_NAME}" --force-with-lease >> "${LOG_FILE}" 2>&1 || true
|
|
614
|
+
# Switch prompt to "continue" mode for the next attempt (fresh context)
|
|
615
|
+
if [ -n "${ISSUE_NUMBER}" ]; then
|
|
616
|
+
PROMPT="Continue implementing PRD (GitHub issue #${ISSUE_NUMBER}: ${ISSUE_TITLE_RAW}).
|
|
617
|
+
|
|
618
|
+
The previous session ran out of context window. Progress has been committed on branch ${BRANCH_NAME}.
|
|
619
|
+
|
|
620
|
+
## Your task
|
|
621
|
+
1. Review the current state: check git log, existing code changes, and any task list
|
|
622
|
+
2. Compare against the original PRD requirements (issue #${ISSUE_NUMBER}) to identify what is already done vs remaining
|
|
623
|
+
3. Continue implementing the remaining phases/tasks
|
|
624
|
+
4. Do NOT redo work that is already completed and committed
|
|
625
|
+
|
|
626
|
+
## Setup
|
|
627
|
+
- You are already inside an isolated worktree at: ${WORKTREE_DIR}
|
|
628
|
+
- Current branch is already checked out: ${BRANCH_NAME}
|
|
629
|
+
- Do NOT run git checkout/switch in ${PROJECT_DIR}
|
|
630
|
+
- Do NOT create or remove worktrees; the cron script manages that
|
|
631
|
+
|
|
632
|
+
## Implementation — PRD Executor Workflow
|
|
633
|
+
Read ${EXECUTOR_PROMPT_REF} and follow the FULL execution pipeline for remaining phases only.
|
|
634
|
+
Follow all CLAUDE.md conventions (if present).
|
|
635
|
+
|
|
636
|
+
## Finalize
|
|
637
|
+
- Commit all changes, push, and open a PR:
|
|
638
|
+
git push -u origin ${BRANCH_NAME}
|
|
639
|
+
gh pr create --title \"feat: <short title>\" --body \"Closes #${ISSUE_NUMBER}
|
|
640
|
+
|
|
641
|
+
<summary>\"
|
|
642
|
+
- Do NOT process any other issues — only issue #${ISSUE_NUMBER}"
|
|
643
|
+
else
|
|
644
|
+
PROMPT="Continue implementing the PRD at ${PRD_DIR_REL}/${ELIGIBLE_PRD}
|
|
645
|
+
|
|
646
|
+
The previous session ran out of context window. Progress has been committed on branch ${BRANCH_NAME}.
|
|
647
|
+
|
|
648
|
+
## Your task
|
|
649
|
+
1. Review the current state: check git log, existing code changes, and any task list
|
|
650
|
+
2. Compare against the original PRD to identify what is already done vs remaining
|
|
651
|
+
3. Continue implementing the remaining phases/tasks
|
|
652
|
+
4. Do NOT redo work that is already completed and committed
|
|
653
|
+
|
|
654
|
+
## Setup
|
|
655
|
+
- You are already inside an isolated worktree at: ${WORKTREE_DIR}
|
|
656
|
+
- Current branch is already checked out: ${BRANCH_NAME}
|
|
657
|
+
- Do NOT run git checkout/switch in ${PROJECT_DIR}
|
|
658
|
+
- Do NOT create or remove worktrees; the cron script manages that
|
|
659
|
+
|
|
660
|
+
## Implementation — PRD Executor Workflow
|
|
661
|
+
Read ${EXECUTOR_PROMPT_REF} and follow the FULL execution pipeline for remaining phases only.
|
|
662
|
+
Follow all CLAUDE.md conventions (if present).
|
|
663
|
+
|
|
664
|
+
## Finalize
|
|
665
|
+
- Commit all changes, push, and open a PR:
|
|
666
|
+
git push -u origin ${BRANCH_NAME}
|
|
667
|
+
gh pr create --title \"feat: <short title>\" --body \"<summary referencing PRD>\"
|
|
668
|
+
- Do NOT move the PRD to done/ — the cron script handles that
|
|
669
|
+
- Do NOT process any other PRDs — only ${ELIGIBLE_PRD}"
|
|
670
|
+
fi
|
|
671
|
+
# No backoff — context exhaustion is not rate-limiting
|
|
605
672
|
else
|
|
606
673
|
# Non-retryable failure
|
|
607
674
|
break
|
|
@@ -738,6 +805,18 @@ elif [ "${DOUBLE_RATE_LIMITED}" = "1" ]; then
|
|
|
738
805
|
fi
|
|
739
806
|
night_watch_history record "${PROJECT_DIR}" "${ELIGIBLE_PRD}" rate_limited --exit-code "${EXIT_CODE}" 2>/dev/null || true
|
|
740
807
|
emit_result "rate_limited" "prd=${ELIGIBLE_PRD}|branch=${BRANCH_NAME}|reason=double_rate_limit"
|
|
808
|
+
elif check_context_exhausted "${LOG_FILE}" "${LOG_LINE_BEFORE}"; then
|
|
809
|
+
# All resume attempts for context exhaustion were used up
|
|
810
|
+
log "FAIL: Context window exhausted after ${MAX_RETRIES} resume attempts for ${ELIGIBLE_PRD}"
|
|
811
|
+
checkpoint_timeout_progress "${WORKTREE_DIR}" "${BRANCH_NAME}" "${ELIGIBLE_PRD}"
|
|
812
|
+
git -C "${WORKTREE_DIR}" push origin "${BRANCH_NAME}" --force-with-lease >> "${LOG_FILE}" 2>&1 || true
|
|
813
|
+
if [ -n "${ISSUE_NUMBER}" ]; then
|
|
814
|
+
"${NW_CLI}" board move-issue "${ISSUE_NUMBER}" --column "Ready" 2>>"${LOG_FILE}" || true
|
|
815
|
+
"${NW_CLI}" board comment "${ISSUE_NUMBER}" \
|
|
816
|
+
--body "Context window exhausted after ${MAX_RETRIES} resume attempts (${TOTAL_ELAPSED}s total, via ${EFFECTIVE_PROVIDER_LABEL}). Progress checkpointed on branch \`${BRANCH_NAME}\`. Will resume on next run." 2>>"${LOG_FILE}" || true
|
|
817
|
+
fi
|
|
818
|
+
night_watch_history record "${PROJECT_DIR}" "${ELIGIBLE_PRD}" context_exhausted --exit-code "${EXIT_CODE}" 2>/dev/null || true
|
|
819
|
+
emit_result "failure" "prd=${ELIGIBLE_PRD}|branch=${BRANCH_NAME}|reason=context_exhausted|exit_code=${EXIT_CODE}"
|
|
741
820
|
else
|
|
742
821
|
PROVIDER_ERROR_DETAIL=$(latest_failure_detail "${LOG_FILE}" "${LOG_LINE_BEFORE}")
|
|
743
822
|
log "FAIL: Night watch exited with code ${EXIT_CODE} while processing ${ELIGIBLE_PRD}"
|
|
@@ -2,6 +2,78 @@
|
|
|
2
2
|
# Night Watch helper functions — shared by cron scripts.
|
|
3
3
|
# Source this file, don't execute it directly.
|
|
4
4
|
|
|
5
|
+
# ── Provider PATH resolution ─────────────────────────────────────────────────
|
|
6
|
+
|
|
7
|
+
# Ensure AI provider CLI (claude, codex, etc.) and Node.js tooling are
|
|
8
|
+
# discoverable on PATH. Sources common Node version managers and probes
|
|
9
|
+
# well-known bin directories so the script works regardless of how the
|
|
10
|
+
# provider was installed (nvm, fnm, volta, npm global, Homebrew, etc.).
|
|
11
|
+
# Returns 0 if the provider is found, 1 otherwise.
|
|
12
|
+
ensure_provider_on_path() {
|
|
13
|
+
local provider="${1:-claude}"
|
|
14
|
+
|
|
15
|
+
# Already available — nothing to do
|
|
16
|
+
if command -v "${provider}" >/dev/null 2>&1; then
|
|
17
|
+
return 0
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
# ── Node version managers ──────────────────────────────────────────────
|
|
21
|
+
# nvm
|
|
22
|
+
export NVM_DIR="${NVM_DIR:-${HOME}/.nvm}"
|
|
23
|
+
if [ -s "${NVM_DIR}/nvm.sh" ]; then
|
|
24
|
+
# shellcheck source=/dev/null
|
|
25
|
+
. "${NVM_DIR}/nvm.sh"
|
|
26
|
+
if command -v "${provider}" >/dev/null 2>&1; then return 0; fi
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
# fnm (Fast Node Manager)
|
|
30
|
+
if command -v fnm >/dev/null 2>&1; then
|
|
31
|
+
eval "$(fnm env 2>/dev/null)" || true
|
|
32
|
+
elif [ -x "${HOME}/.local/share/fnm/fnm" ]; then
|
|
33
|
+
export PATH="${HOME}/.local/share/fnm:${PATH}"
|
|
34
|
+
eval "$(fnm env 2>/dev/null)" || true
|
|
35
|
+
fi
|
|
36
|
+
if command -v "${provider}" >/dev/null 2>&1; then return 0; fi
|
|
37
|
+
|
|
38
|
+
# volta
|
|
39
|
+
if [ -d "${HOME}/.volta/bin" ]; then
|
|
40
|
+
export PATH="${HOME}/.volta/bin:${PATH}"
|
|
41
|
+
if command -v "${provider}" >/dev/null 2>&1; then return 0; fi
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
# mise / asdf
|
|
45
|
+
if [ -d "${HOME}/.local/share/mise/shims" ]; then
|
|
46
|
+
export PATH="${HOME}/.local/share/mise/shims:${PATH}"
|
|
47
|
+
if command -v "${provider}" >/dev/null 2>&1; then return 0; fi
|
|
48
|
+
fi
|
|
49
|
+
if [ -d "${HOME}/.asdf/shims" ]; then
|
|
50
|
+
export PATH="${HOME}/.asdf/shims:${PATH}"
|
|
51
|
+
if command -v "${provider}" >/dev/null 2>&1; then return 0; fi
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
# ── Well-known bin directories ─────────────────────────────────────────
|
|
55
|
+
local candidate_dirs=(
|
|
56
|
+
"${HOME}/.npm-global/bin"
|
|
57
|
+
"${HOME}/.local/bin"
|
|
58
|
+
"${HOME}/.claude/bin"
|
|
59
|
+
"/usr/local/bin"
|
|
60
|
+
"${HOME}/.yarn/bin"
|
|
61
|
+
"${HOME}/.bun/bin"
|
|
62
|
+
"${HOME}/.local/share/pnpm"
|
|
63
|
+
"/home/linuxbrew/.linuxbrew/bin"
|
|
64
|
+
"/opt/homebrew/bin"
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
for dir in "${candidate_dirs[@]}"; do
|
|
68
|
+
if [ -x "${dir}/${provider}" ]; then
|
|
69
|
+
export PATH="${dir}:${PATH}"
|
|
70
|
+
return 0
|
|
71
|
+
fi
|
|
72
|
+
done
|
|
73
|
+
|
|
74
|
+
return 1
|
|
75
|
+
}
|
|
76
|
+
|
|
5
77
|
# ── Provider validation ───────────────────────────────────────────────────────
|
|
6
78
|
|
|
7
79
|
# Validates that the provider command is supported.
|
|
@@ -626,6 +698,19 @@ check_rate_limited() {
|
|
|
626
698
|
fi
|
|
627
699
|
}
|
|
628
700
|
|
|
701
|
+
# Detect context window exhaustion from Claude API logs.
|
|
702
|
+
# Usage: check_context_exhausted <log_file> [start_line]
|
|
703
|
+
# Returns 0 if context exhausted, 1 otherwise.
|
|
704
|
+
check_context_exhausted() {
|
|
705
|
+
local log_file="${1:?log_file required}"
|
|
706
|
+
local start_line="${2:-0}"
|
|
707
|
+
if [ "${start_line}" -gt 0 ] 2>/dev/null; then
|
|
708
|
+
tail -n "+$((start_line + 1))" "${log_file}" 2>/dev/null | grep -qi "context window"
|
|
709
|
+
else
|
|
710
|
+
tail -20 "${log_file}" 2>/dev/null | grep -qi "context window"
|
|
711
|
+
fi
|
|
712
|
+
}
|
|
713
|
+
|
|
629
714
|
# Resolve URL host from a URL-like string.
|
|
630
715
|
# Example: "https://api.z.ai/api/anthropic" -> "api.z.ai"
|
|
631
716
|
extract_url_host() {
|
|
@@ -54,18 +54,17 @@ if [ "${REVIEWER_RETRY_DELAY}" -gt 300 ]; then
|
|
|
54
54
|
REVIEWER_RETRY_DELAY="300"
|
|
55
55
|
fi
|
|
56
56
|
|
|
57
|
-
# Ensure NVM / Node / Claude are on PATH
|
|
58
|
-
export NVM_DIR="${HOME}/.nvm"
|
|
59
|
-
[ -s "${NVM_DIR}/nvm.sh" ] && . "${NVM_DIR}/nvm.sh"
|
|
60
|
-
|
|
61
|
-
# NOTE: Environment variables should be set by the caller (Node.js CLI).
|
|
62
|
-
# The .env.night-watch sourcing has been removed - config is now injected via env vars.
|
|
63
|
-
|
|
64
57
|
mkdir -p "${LOG_DIR}"
|
|
65
58
|
|
|
66
59
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
67
60
|
# shellcheck source=night-watch-helpers.sh
|
|
68
61
|
source "${SCRIPT_DIR}/night-watch-helpers.sh"
|
|
62
|
+
|
|
63
|
+
# Ensure provider CLI is on PATH (nvm, fnm, volta, common bin dirs)
|
|
64
|
+
if ! ensure_provider_on_path "${PROVIDER_CMD}"; then
|
|
65
|
+
echo "ERROR: Provider '${PROVIDER_CMD}' not found in PATH or common installation locations" >&2
|
|
66
|
+
exit 127
|
|
67
|
+
fi
|
|
69
68
|
PROJECT_RUNTIME_KEY=$(project_runtime_key "${PROJECT_DIR}")
|
|
70
69
|
PROVIDER_MODEL_DISPLAY=$(resolve_provider_model_display "${PROVIDER_CMD}" "${PROVIDER_LABEL}")
|
|
71
70
|
GLOBAL_LOCK_FILE="/tmp/night-watch-pr-reviewer-${PROJECT_RUNTIME_KEY}.lock"
|
|
@@ -29,18 +29,17 @@ QA_ARTIFACTS="${NW_QA_ARTIFACTS:-both}"
|
|
|
29
29
|
QA_AUTO_INSTALL_PLAYWRIGHT="${NW_QA_AUTO_INSTALL_PLAYWRIGHT:-1}"
|
|
30
30
|
SCRIPT_START_TIME=$(date +%s)
|
|
31
31
|
|
|
32
|
-
# Ensure NVM / Node / Claude are on PATH
|
|
33
|
-
export NVM_DIR="${HOME}/.nvm"
|
|
34
|
-
[ -s "${NVM_DIR}/nvm.sh" ] && . "${NVM_DIR}/nvm.sh"
|
|
35
|
-
|
|
36
|
-
# NOTE: Environment variables should be set by the caller (Node.js CLI).
|
|
37
|
-
# The .env.night-watch sourcing has been removed - config is now injected via env vars.
|
|
38
|
-
|
|
39
32
|
mkdir -p "${LOG_DIR}"
|
|
40
33
|
|
|
41
34
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
42
35
|
# shellcheck source=night-watch-helpers.sh
|
|
43
36
|
source "${SCRIPT_DIR}/night-watch-helpers.sh"
|
|
37
|
+
|
|
38
|
+
# Ensure provider CLI is on PATH (nvm, fnm, volta, common bin dirs)
|
|
39
|
+
if ! ensure_provider_on_path "${PROVIDER_CMD}"; then
|
|
40
|
+
echo "ERROR: Provider '${PROVIDER_CMD}' not found in PATH or common installation locations" >&2
|
|
41
|
+
exit 127
|
|
42
|
+
fi
|
|
44
43
|
PROJECT_RUNTIME_KEY=$(project_runtime_key "${PROJECT_DIR}")
|
|
45
44
|
# NOTE: Lock file path must match qaLockPath() in src/utils/status-data.ts
|
|
46
45
|
LOCK_FILE="/tmp/night-watch-qa-${PROJECT_RUNTIME_KEY}.lock"
|
|
@@ -25,15 +25,14 @@ PROVIDER_CMD="${NW_PROVIDER_CMD:-claude}"
|
|
|
25
25
|
PROVIDER_LABEL="${NW_PROVIDER_LABEL:-}"
|
|
26
26
|
SCRIPT_START_TIME=$(date +%s)
|
|
27
27
|
|
|
28
|
-
# Ensure NVM / Node / Night Watch CLI are on PATH
|
|
29
|
-
export NVM_DIR="${HOME}/.nvm"
|
|
30
|
-
[ -s "${NVM_DIR}/nvm.sh" ] && . "${NVM_DIR}/nvm.sh"
|
|
31
|
-
|
|
32
28
|
mkdir -p "${LOG_DIR}"
|
|
33
29
|
|
|
34
30
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
35
31
|
# shellcheck source=night-watch-helpers.sh
|
|
36
32
|
source "${SCRIPT_DIR}/night-watch-helpers.sh"
|
|
33
|
+
|
|
34
|
+
# Ensure provider CLI is on PATH (nvm, fnm, volta, common bin dirs)
|
|
35
|
+
ensure_provider_on_path "${PROVIDER_CMD}" || true
|
|
37
36
|
PROJECT_RUNTIME_KEY=$(project_runtime_key "${PROJECT_DIR}")
|
|
38
37
|
LOCK_FILE="/tmp/night-watch-slicer-${PROJECT_RUNTIME_KEY}.lock"
|
|
39
38
|
PROVIDER_MODEL_DISPLAY=$(resolve_provider_model_display "${PROVIDER_CMD}" "${PROVIDER_LABEL}")
|