agentxchain 0.8.7 → 2.1.1
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/README.md +123 -154
- package/bin/agentxchain.js +240 -8
- package/dashboard/app.js +305 -0
- package/dashboard/components/blocked.js +145 -0
- package/dashboard/components/cross-repo.js +126 -0
- package/dashboard/components/gate.js +311 -0
- package/dashboard/components/hooks.js +177 -0
- package/dashboard/components/initiative.js +147 -0
- package/dashboard/components/ledger.js +165 -0
- package/dashboard/components/timeline.js +222 -0
- package/dashboard/index.html +352 -0
- package/package.json +16 -7
- package/scripts/agentxchain-autonudge.applescript +32 -5
- package/scripts/live-api-proxy-preflight-smoke.sh +531 -0
- package/scripts/publish-from-tag.sh +88 -0
- package/scripts/release-postflight.sh +231 -0
- package/scripts/release-preflight.sh +167 -0
- package/scripts/run-autonudge.sh +1 -1
- package/src/adapters/claude-code.js +7 -14
- package/src/adapters/cursor-local.js +17 -16
- package/src/commands/accept-turn.js +160 -0
- package/src/commands/approve-completion.js +80 -0
- package/src/commands/approve-transition.js +85 -0
- package/src/commands/branch.js +2 -2
- package/src/commands/claim.js +84 -9
- package/src/commands/config.js +16 -0
- package/src/commands/dashboard.js +70 -0
- package/src/commands/doctor.js +9 -1
- package/src/commands/init.js +540 -5
- package/src/commands/migrate.js +348 -0
- package/src/commands/multi.js +549 -0
- package/src/commands/plugin.js +157 -0
- package/src/commands/reject-turn.js +204 -0
- package/src/commands/resume.js +389 -0
- package/src/commands/status.js +196 -3
- package/src/commands/step.js +947 -0
- package/src/commands/stop.js +65 -33
- package/src/commands/template-list.js +33 -0
- package/src/commands/template-set.js +279 -0
- package/src/commands/update.js +24 -3
- package/src/commands/validate.js +20 -11
- package/src/commands/verify.js +71 -0
- package/src/commands/watch.js +112 -25
- package/src/lib/adapters/api-proxy-adapter.js +1076 -0
- package/src/lib/adapters/local-cli-adapter.js +337 -0
- package/src/lib/adapters/manual-adapter.js +169 -0
- package/src/lib/blocked-state.js +94 -0
- package/src/lib/config.js +143 -12
- package/src/lib/context-compressor.js +121 -0
- package/src/lib/context-section-parser.js +220 -0
- package/src/lib/coordinator-acceptance.js +428 -0
- package/src/lib/coordinator-config.js +461 -0
- package/src/lib/coordinator-dispatch.js +276 -0
- package/src/lib/coordinator-gates.js +487 -0
- package/src/lib/coordinator-hooks.js +239 -0
- package/src/lib/coordinator-recovery.js +523 -0
- package/src/lib/coordinator-state.js +365 -0
- package/src/lib/cross-repo-context.js +247 -0
- package/src/lib/dashboard/bridge-server.js +284 -0
- package/src/lib/dashboard/file-watcher.js +93 -0
- package/src/lib/dashboard/state-reader.js +96 -0
- package/src/lib/dispatch-bundle.js +568 -0
- package/src/lib/dispatch-manifest.js +252 -0
- package/src/lib/filter-agents.js +12 -0
- package/src/lib/gate-evaluator.js +285 -0
- package/src/lib/generate-vscode.js +158 -68
- package/src/lib/governed-state.js +2139 -0
- package/src/lib/governed-templates.js +145 -0
- package/src/lib/hook-runner.js +788 -0
- package/src/lib/next-owner.js +61 -6
- package/src/lib/normalized-config.js +539 -0
- package/src/lib/notify.js +14 -12
- package/src/lib/plugin-config-schema.js +192 -0
- package/src/lib/plugins.js +692 -0
- package/src/lib/prompt-core.js +108 -0
- package/src/lib/protocol-conformance.js +291 -0
- package/src/lib/reference-conformance-adapter.js +717 -0
- package/src/lib/repo-observer.js +597 -0
- package/src/lib/repo.js +0 -31
- package/src/lib/safe-write.js +44 -0
- package/src/lib/schema.js +189 -0
- package/src/lib/schemas/turn-result.schema.json +205 -0
- package/src/lib/seed-prompt-polling.js +15 -73
- package/src/lib/seed-prompt.js +17 -63
- package/src/lib/token-budget.js +206 -0
- package/src/lib/token-counter.js +27 -0
- package/src/lib/turn-paths.js +67 -0
- package/src/lib/turn-result-validator.js +496 -0
- package/src/lib/validation.js +167 -19
- package/src/lib/verify-command.js +72 -0
- package/src/templates/governed/api-service.json +31 -0
- package/src/templates/governed/cli-tool.json +30 -0
- package/src/templates/governed/generic.json +10 -0
- package/src/templates/governed/web-app.json +30 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Release postflight — run this after publish succeeds.
|
|
3
|
+
# Verifies: release tag exists, npm registry serves the version, metadata is present,
|
|
4
|
+
# and the published package can execute its CLI entrypoint.
|
|
5
|
+
# Usage: bash scripts/release-postflight.sh --target-version <semver> [--tag vX.Y.Z]
|
|
6
|
+
set -uo pipefail
|
|
7
|
+
|
|
8
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
9
|
+
CLI_DIR="${SCRIPT_DIR}/.."
|
|
10
|
+
cd "$CLI_DIR"
|
|
11
|
+
|
|
12
|
+
TARGET_VERSION=""
|
|
13
|
+
TAG=""
|
|
14
|
+
RETRY_ATTEMPTS="${RELEASE_POSTFLIGHT_RETRY_ATTEMPTS:-6}"
|
|
15
|
+
RETRY_DELAY_SECONDS="${RELEASE_POSTFLIGHT_RETRY_DELAY_SECONDS:-10}"
|
|
16
|
+
|
|
17
|
+
usage() {
|
|
18
|
+
echo "Usage: bash scripts/release-postflight.sh --target-version <semver> [--tag vX.Y.Z]" >&2
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
while [[ $# -gt 0 ]]; do
|
|
22
|
+
case "$1" in
|
|
23
|
+
--target-version)
|
|
24
|
+
if [[ -z "${2:-}" ]]; then
|
|
25
|
+
echo "Error: --target-version requires a semver argument" >&2
|
|
26
|
+
usage
|
|
27
|
+
exit 1
|
|
28
|
+
fi
|
|
29
|
+
if ! [[ "$2" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
30
|
+
echo "Invalid semver: $2" >&2
|
|
31
|
+
usage
|
|
32
|
+
exit 1
|
|
33
|
+
fi
|
|
34
|
+
TARGET_VERSION="$2"
|
|
35
|
+
shift 2
|
|
36
|
+
;;
|
|
37
|
+
--tag)
|
|
38
|
+
if [[ -z "${2:-}" ]]; then
|
|
39
|
+
echo "Error: --tag requires a git tag argument" >&2
|
|
40
|
+
usage
|
|
41
|
+
exit 1
|
|
42
|
+
fi
|
|
43
|
+
TAG="$2"
|
|
44
|
+
shift 2
|
|
45
|
+
;;
|
|
46
|
+
*)
|
|
47
|
+
usage
|
|
48
|
+
exit 1
|
|
49
|
+
;;
|
|
50
|
+
esac
|
|
51
|
+
done
|
|
52
|
+
|
|
53
|
+
if [[ -z "$TARGET_VERSION" ]]; then
|
|
54
|
+
echo "Error: --target-version is required" >&2
|
|
55
|
+
usage
|
|
56
|
+
exit 1
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
if [[ -z "$TAG" ]]; then
|
|
60
|
+
TAG="v${TARGET_VERSION}"
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
if ! [[ "$RETRY_ATTEMPTS" =~ ^[0-9]+$ ]] || [[ "$RETRY_ATTEMPTS" -lt 1 ]]; then
|
|
64
|
+
echo "Error: RELEASE_POSTFLIGHT_RETRY_ATTEMPTS must be a positive integer" >&2
|
|
65
|
+
exit 1
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
if ! [[ "$RETRY_DELAY_SECONDS" =~ ^[0-9]+$ ]]; then
|
|
69
|
+
echo "Error: RELEASE_POSTFLIGHT_RETRY_DELAY_SECONDS must be a non-negative integer" >&2
|
|
70
|
+
exit 1
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
PASS=0
|
|
74
|
+
FAIL=0
|
|
75
|
+
TARBALL_URL=""
|
|
76
|
+
REGISTRY_CHECKSUM=""
|
|
77
|
+
PACKAGE_NAME="$(node -e "console.log(JSON.parse(require('fs').readFileSync('package.json', 'utf8')).name)")"
|
|
78
|
+
|
|
79
|
+
pass() { PASS=$((PASS + 1)); echo " PASS: $1"; }
|
|
80
|
+
fail() { FAIL=$((FAIL + 1)); echo " FAIL: $1"; }
|
|
81
|
+
|
|
82
|
+
run_and_capture() {
|
|
83
|
+
local __var_name="$1"
|
|
84
|
+
shift
|
|
85
|
+
|
|
86
|
+
local captured_output
|
|
87
|
+
local status
|
|
88
|
+
captured_output="$("$@" 2>&1)"
|
|
89
|
+
status=$?
|
|
90
|
+
|
|
91
|
+
printf -v "$__var_name" '%s' "$captured_output"
|
|
92
|
+
return "$status"
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
trim_last_line() {
|
|
96
|
+
printf '%s\n' "$1" | awk 'NF { line=$0 } END { gsub(/^[[:space:]]+|[[:space:]]+$/, "", line); print line }'
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
run_with_retry() {
|
|
100
|
+
local __output_var="$1"
|
|
101
|
+
local description="$2"
|
|
102
|
+
local success_mode="$3"
|
|
103
|
+
local expected_value="$4"
|
|
104
|
+
shift 4
|
|
105
|
+
|
|
106
|
+
local output=""
|
|
107
|
+
local status=0
|
|
108
|
+
local value=""
|
|
109
|
+
local attempt=1
|
|
110
|
+
|
|
111
|
+
while [[ "$attempt" -le "$RETRY_ATTEMPTS" ]]; do
|
|
112
|
+
if run_and_capture output "$@"; then
|
|
113
|
+
status=0
|
|
114
|
+
else
|
|
115
|
+
status=$?
|
|
116
|
+
fi
|
|
117
|
+
|
|
118
|
+
value="$(trim_last_line "$output")"
|
|
119
|
+
|
|
120
|
+
case "$success_mode" in
|
|
121
|
+
equals)
|
|
122
|
+
if [[ "$status" -eq 0 && "$value" == "$expected_value" ]]; then
|
|
123
|
+
printf -v "$__output_var" '%s' "$output"
|
|
124
|
+
return 0
|
|
125
|
+
fi
|
|
126
|
+
;;
|
|
127
|
+
nonempty)
|
|
128
|
+
if [[ "$status" -eq 0 && -n "$value" ]]; then
|
|
129
|
+
printf -v "$__output_var" '%s' "$output"
|
|
130
|
+
return 0
|
|
131
|
+
fi
|
|
132
|
+
;;
|
|
133
|
+
*)
|
|
134
|
+
echo "Error: unsupported retry success mode '${success_mode}'" >&2
|
|
135
|
+
exit 1
|
|
136
|
+
;;
|
|
137
|
+
esac
|
|
138
|
+
|
|
139
|
+
if [[ "$attempt" -lt "$RETRY_ATTEMPTS" ]]; then
|
|
140
|
+
echo " INFO: ${description} not ready (attempt ${attempt}/${RETRY_ATTEMPTS}); retrying in ${RETRY_DELAY_SECONDS}s..."
|
|
141
|
+
sleep "$RETRY_DELAY_SECONDS"
|
|
142
|
+
fi
|
|
143
|
+
attempt=$((attempt + 1))
|
|
144
|
+
done
|
|
145
|
+
|
|
146
|
+
printf -v "$__output_var" '%s' "$output"
|
|
147
|
+
return 1
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
echo "AgentXchain v${TARGET_VERSION} Release Postflight"
|
|
151
|
+
echo "====================================="
|
|
152
|
+
echo "Checks release truth after publish: tag, registry visibility, metadata, and install smoke."
|
|
153
|
+
echo ""
|
|
154
|
+
|
|
155
|
+
echo "[1/5] Git tag"
|
|
156
|
+
if git rev-parse -q --verify "refs/tags/${TAG}" >/dev/null 2>&1; then
|
|
157
|
+
pass "Git tag ${TAG} exists locally"
|
|
158
|
+
else
|
|
159
|
+
fail "Git tag ${TAG} is missing locally"
|
|
160
|
+
fi
|
|
161
|
+
|
|
162
|
+
echo "[2/5] Registry version"
|
|
163
|
+
if run_with_retry VERSION_OUTPUT "registry version" equals "${TARGET_VERSION}" npm view "${PACKAGE_NAME}@${TARGET_VERSION}" version; then
|
|
164
|
+
PUBLISHED_VERSION="$(trim_last_line "$VERSION_OUTPUT")"
|
|
165
|
+
if [[ "$PUBLISHED_VERSION" == "$TARGET_VERSION" ]]; then
|
|
166
|
+
pass "npm registry serves ${PACKAGE_NAME}@${TARGET_VERSION}"
|
|
167
|
+
else
|
|
168
|
+
fail "npm registry returned '${PUBLISHED_VERSION}', expected '${TARGET_VERSION}'"
|
|
169
|
+
fi
|
|
170
|
+
else
|
|
171
|
+
fail "npm registry does not serve ${PACKAGE_NAME}@${TARGET_VERSION}"
|
|
172
|
+
printf '%s\n' "$VERSION_OUTPUT" | tail -20
|
|
173
|
+
fi
|
|
174
|
+
|
|
175
|
+
echo "[3/5] Registry tarball metadata"
|
|
176
|
+
if run_with_retry TARBALL_OUTPUT "registry tarball metadata" nonempty "" npm view "${PACKAGE_NAME}@${TARGET_VERSION}" dist.tarball; then
|
|
177
|
+
TARBALL_URL="$(trim_last_line "$TARBALL_OUTPUT")"
|
|
178
|
+
if [[ -n "$TARBALL_URL" ]]; then
|
|
179
|
+
pass "registry exposes dist.tarball metadata"
|
|
180
|
+
else
|
|
181
|
+
fail "registry returned empty dist.tarball metadata"
|
|
182
|
+
fi
|
|
183
|
+
else
|
|
184
|
+
fail "registry did not return dist.tarball metadata"
|
|
185
|
+
printf '%s\n' "$TARBALL_OUTPUT" | tail -20
|
|
186
|
+
fi
|
|
187
|
+
|
|
188
|
+
echo "[4/5] Registry checksum metadata"
|
|
189
|
+
if run_with_retry INTEGRITY_OUTPUT "registry checksum metadata" nonempty "" npm view "${PACKAGE_NAME}@${TARGET_VERSION}" dist.integrity; then
|
|
190
|
+
REGISTRY_CHECKSUM="$(trim_last_line "$INTEGRITY_OUTPUT")"
|
|
191
|
+
fi
|
|
192
|
+
if [[ -z "$REGISTRY_CHECKSUM" ]]; then
|
|
193
|
+
if run_with_retry SHASUM_OUTPUT "registry shasum metadata" nonempty "" npm view "${PACKAGE_NAME}@${TARGET_VERSION}" dist.shasum; then
|
|
194
|
+
REGISTRY_CHECKSUM="$(trim_last_line "$SHASUM_OUTPUT")"
|
|
195
|
+
fi
|
|
196
|
+
fi
|
|
197
|
+
if [[ -n "$REGISTRY_CHECKSUM" ]]; then
|
|
198
|
+
pass "registry exposes checksum metadata"
|
|
199
|
+
else
|
|
200
|
+
fail "registry did not return checksum metadata"
|
|
201
|
+
fi
|
|
202
|
+
|
|
203
|
+
echo "[5/5] Install smoke"
|
|
204
|
+
if run_with_retry EXEC_OUTPUT "install smoke" nonempty "" npm exec --yes --package "${PACKAGE_NAME}@${TARGET_VERSION}" -- agentxchain --version; then
|
|
205
|
+
EXEC_VERSION="$(trim_last_line "$EXEC_OUTPUT")"
|
|
206
|
+
if [[ "$EXEC_VERSION" == "$TARGET_VERSION" ]]; then
|
|
207
|
+
pass "published CLI executes and reports ${TARGET_VERSION}"
|
|
208
|
+
else
|
|
209
|
+
fail "published CLI reported '${EXEC_VERSION}', expected '${TARGET_VERSION}'"
|
|
210
|
+
fi
|
|
211
|
+
else
|
|
212
|
+
fail "published CLI install smoke failed"
|
|
213
|
+
printf '%s\n' "$EXEC_OUTPUT" | tail -20
|
|
214
|
+
fi
|
|
215
|
+
|
|
216
|
+
echo ""
|
|
217
|
+
echo "====================================="
|
|
218
|
+
echo "Results: ${PASS} passed, ${FAIL} failed"
|
|
219
|
+
if [[ -n "$TARBALL_URL" ]]; then
|
|
220
|
+
echo "Tarball: ${TARBALL_URL}"
|
|
221
|
+
fi
|
|
222
|
+
if [[ -n "$REGISTRY_CHECKSUM" ]]; then
|
|
223
|
+
echo "Checksum: ${REGISTRY_CHECKSUM}"
|
|
224
|
+
fi
|
|
225
|
+
if [ "$FAIL" -gt 0 ]; then
|
|
226
|
+
echo "POSTFLIGHT FAILED — do not mark the release complete."
|
|
227
|
+
exit 1
|
|
228
|
+
fi
|
|
229
|
+
|
|
230
|
+
echo "POSTFLIGHT PASSED — registry truth matches the release tag."
|
|
231
|
+
exit 0
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Release preflight — run this before cutting a release.
|
|
3
|
+
# Verifies: clean tree, deps, tests, CHANGELOG entry, pack dry-run.
|
|
4
|
+
# Usage: bash scripts/release-preflight.sh [--strict] [--target-version <semver>]
|
|
5
|
+
set -uo pipefail
|
|
6
|
+
|
|
7
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
8
|
+
CLI_DIR="${SCRIPT_DIR}/.."
|
|
9
|
+
cd "$CLI_DIR"
|
|
10
|
+
|
|
11
|
+
STRICT_MODE=0
|
|
12
|
+
TARGET_VERSION="2.0.0"
|
|
13
|
+
|
|
14
|
+
usage() {
|
|
15
|
+
echo "Usage: bash scripts/release-preflight.sh [--strict] [--target-version <semver>]" >&2
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
while [[ $# -gt 0 ]]; do
|
|
19
|
+
case "$1" in
|
|
20
|
+
--strict)
|
|
21
|
+
STRICT_MODE=1
|
|
22
|
+
shift
|
|
23
|
+
;;
|
|
24
|
+
--target-version)
|
|
25
|
+
if [[ -z "${2:-}" ]]; then
|
|
26
|
+
echo "Error: --target-version requires a semver argument" >&2
|
|
27
|
+
usage
|
|
28
|
+
exit 1
|
|
29
|
+
fi
|
|
30
|
+
if ! [[ "$2" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
31
|
+
echo "Invalid semver: $2" >&2
|
|
32
|
+
usage
|
|
33
|
+
exit 1
|
|
34
|
+
fi
|
|
35
|
+
TARGET_VERSION="$2"
|
|
36
|
+
shift 2
|
|
37
|
+
;;
|
|
38
|
+
*)
|
|
39
|
+
usage
|
|
40
|
+
exit 1
|
|
41
|
+
;;
|
|
42
|
+
esac
|
|
43
|
+
done
|
|
44
|
+
|
|
45
|
+
PASS=0
|
|
46
|
+
FAIL=0
|
|
47
|
+
WARN=0
|
|
48
|
+
|
|
49
|
+
pass() { PASS=$((PASS + 1)); echo " PASS: $1"; }
|
|
50
|
+
fail() { FAIL=$((FAIL + 1)); echo " FAIL: $1"; }
|
|
51
|
+
warn() { WARN=$((WARN + 1)); echo " WARN: $1"; }
|
|
52
|
+
|
|
53
|
+
run_and_capture() {
|
|
54
|
+
local __var_name="$1"
|
|
55
|
+
shift
|
|
56
|
+
|
|
57
|
+
local output
|
|
58
|
+
local status
|
|
59
|
+
output="$("$@" 2>&1)"
|
|
60
|
+
status=$?
|
|
61
|
+
|
|
62
|
+
printf -v "$__var_name" '%s' "$output"
|
|
63
|
+
return "$status"
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
echo "AgentXchain v${TARGET_VERSION} Release Preflight"
|
|
67
|
+
echo "====================================="
|
|
68
|
+
if [[ "$TARGET_VERSION" == "1.0.0" ]]; then
|
|
69
|
+
echo "Local checks only. Human-gated release items remain in .planning/V1_RELEASE_CHECKLIST.md."
|
|
70
|
+
else
|
|
71
|
+
echo "Local checks only. Human-gated release items remain in .planning/V1_RELEASE_CHECKLIST.md (v1.0) or .planning/V1_1_RELEASE_CHECKLIST.md (v1.1+)."
|
|
72
|
+
fi
|
|
73
|
+
if [[ "$STRICT_MODE" -eq 1 ]]; then
|
|
74
|
+
echo "Mode: STRICT (dirty tree and non-${TARGET_VERSION} package version are hard failures)"
|
|
75
|
+
else
|
|
76
|
+
echo "Mode: DEFAULT (dirty tree and pre-bump package version are warnings)"
|
|
77
|
+
fi
|
|
78
|
+
echo ""
|
|
79
|
+
|
|
80
|
+
# 1. Clean working tree
|
|
81
|
+
echo "[1/6] Git status"
|
|
82
|
+
if git diff --quiet HEAD 2>/dev/null && [ -z "$(git ls-files --others --exclude-standard 2>/dev/null)" ]; then
|
|
83
|
+
pass "Working tree is clean"
|
|
84
|
+
else
|
|
85
|
+
if [[ "$STRICT_MODE" -eq 1 ]]; then
|
|
86
|
+
fail "Working tree is not clean"
|
|
87
|
+
else
|
|
88
|
+
warn "Uncommitted or untracked files present"
|
|
89
|
+
fi
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
# 2. Dependencies
|
|
93
|
+
echo "[2/6] Dependencies"
|
|
94
|
+
if run_and_capture NPM_CI_OUTPUT npm ci --ignore-scripts; then
|
|
95
|
+
pass "npm ci succeeded"
|
|
96
|
+
else
|
|
97
|
+
fail "npm ci failed"
|
|
98
|
+
printf '%s\n' "$NPM_CI_OUTPUT" | tail -20
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
# 3. Tests
|
|
102
|
+
echo "[3/6] Test suite"
|
|
103
|
+
if run_and_capture TEST_OUTPUT npm test; then
|
|
104
|
+
TEST_STATUS=0
|
|
105
|
+
else
|
|
106
|
+
TEST_STATUS=$?
|
|
107
|
+
fi
|
|
108
|
+
TEST_PASS="$(printf '%s\n' "$TEST_OUTPUT" | awk '/^# pass / { print $3 }')"
|
|
109
|
+
TEST_FAIL="$(printf '%s\n' "$TEST_OUTPUT" | awk '/^# fail / { print $3 }')"
|
|
110
|
+
if [ "$TEST_STATUS" -eq 0 ] && [ "${TEST_FAIL:-0}" = "0" ]; then
|
|
111
|
+
pass "${TEST_PASS} tests passed, 0 failures"
|
|
112
|
+
else
|
|
113
|
+
fail "npm test failed"
|
|
114
|
+
printf '%s\n' "$TEST_OUTPUT" | tail -20
|
|
115
|
+
fi
|
|
116
|
+
|
|
117
|
+
# 4. CHANGELOG has target version
|
|
118
|
+
echo "[4/6] CHANGELOG"
|
|
119
|
+
if grep -Fxq "## ${TARGET_VERSION}" CHANGELOG.md 2>/dev/null; then
|
|
120
|
+
pass "CHANGELOG.md contains ${TARGET_VERSION} entry"
|
|
121
|
+
else
|
|
122
|
+
fail "CHANGELOG.md missing ${TARGET_VERSION} entry"
|
|
123
|
+
fi
|
|
124
|
+
|
|
125
|
+
# 5. Package version
|
|
126
|
+
echo "[5/6] Package version"
|
|
127
|
+
PKG_VERSION=$(node -e "console.log(JSON.parse(require('fs').readFileSync('package.json','utf8')).version)")
|
|
128
|
+
echo " Current version: ${PKG_VERSION}"
|
|
129
|
+
if [ "$PKG_VERSION" = "${TARGET_VERSION}" ]; then
|
|
130
|
+
pass "package.json is at ${TARGET_VERSION}"
|
|
131
|
+
else
|
|
132
|
+
if [[ "$STRICT_MODE" -eq 1 ]]; then
|
|
133
|
+
fail "package.json is at ${PKG_VERSION}, expected ${TARGET_VERSION}"
|
|
134
|
+
else
|
|
135
|
+
warn "package.json is at ${PKG_VERSION}, not yet bumped to ${TARGET_VERSION}"
|
|
136
|
+
fi
|
|
137
|
+
fi
|
|
138
|
+
|
|
139
|
+
# 6. Pack dry-run
|
|
140
|
+
echo "[6/6] npm pack --dry-run"
|
|
141
|
+
if run_and_capture PACK_OUTPUT npm pack --dry-run; then
|
|
142
|
+
pass "npm pack --dry-run succeeded"
|
|
143
|
+
PACK_SIZE_LINE="$(printf '%s\n' "$PACK_OUTPUT" | awk '/total files:/ { print; found=1 } END { if (!found) exit 1 }')"
|
|
144
|
+
if [ -n "${PACK_SIZE_LINE:-}" ]; then
|
|
145
|
+
echo " ${PACK_SIZE_LINE}"
|
|
146
|
+
else
|
|
147
|
+
printf '%s\n' "$PACK_OUTPUT" | tail -5
|
|
148
|
+
fi
|
|
149
|
+
else
|
|
150
|
+
fail "npm pack --dry-run failed"
|
|
151
|
+
printf '%s\n' "$PACK_OUTPUT" | tail -20
|
|
152
|
+
fi
|
|
153
|
+
|
|
154
|
+
# Summary
|
|
155
|
+
echo ""
|
|
156
|
+
echo "====================================="
|
|
157
|
+
echo "Results: ${PASS} passed, ${FAIL} failed, ${WARN} warnings"
|
|
158
|
+
if [ "$FAIL" -gt 0 ]; then
|
|
159
|
+
echo "PREFLIGHT FAILED — fix failures before release."
|
|
160
|
+
exit 1
|
|
161
|
+
elif [ "$WARN" -gt 0 ]; then
|
|
162
|
+
echo "PREFLIGHT PASSED WITH WARNINGS — resolve warnings before release day."
|
|
163
|
+
exit 0
|
|
164
|
+
else
|
|
165
|
+
echo "PREFLIGHT PASSED — ready for release."
|
|
166
|
+
exit 0
|
|
167
|
+
fi
|
package/scripts/run-autonudge.sh
CHANGED
|
@@ -104,7 +104,7 @@ echo "Mode: $( [[ "${AUTO_SEND}" == "true" ]] && echo "auto-send" || echo "
|
|
|
104
104
|
echo "Interval: ${INTERVAL_SECONDS}s"
|
|
105
105
|
echo ""
|
|
106
106
|
echo "Requirements:"
|
|
107
|
-
echo "-
|
|
107
|
+
echo "- Watch must be running (e.g. 'agentxchain supervise --autonudge' starts it in the same supervisor, or run 'agentxchain watch' in another terminal)."
|
|
108
108
|
echo "- Grant Accessibility permission to Terminal and Cursor."
|
|
109
109
|
echo ""
|
|
110
110
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { spawn } from 'child_process';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import { generateSeedPrompt } from '../lib/seed-prompt.js';
|
|
4
|
-
import { writeFileSync } from 'fs';
|
|
5
4
|
import { join } from 'path';
|
|
5
|
+
import { safeWriteJson } from '../lib/safe-write.js';
|
|
6
|
+
import { filterAgents } from '../lib/filter-agents.js';
|
|
6
7
|
|
|
7
8
|
export async function launchClaudeCodeAgents(config, root, opts) {
|
|
8
9
|
const agents = filterAgents(config, opts.agent);
|
|
@@ -28,22 +29,14 @@ export async function launchClaudeCodeAgents(config, root, opts) {
|
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
if (launched.length > 0) {
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
safeWriteJson(join(root, '.agentxchain-session.json'), {
|
|
33
|
+
launched,
|
|
34
|
+
started_at: new Date().toISOString(),
|
|
35
|
+
ide: 'claude-code'
|
|
36
|
+
});
|
|
33
37
|
console.log('');
|
|
34
38
|
console.log(chalk.dim(` Session saved to .agentxchain-session.json`));
|
|
35
39
|
}
|
|
36
40
|
|
|
37
41
|
return launched;
|
|
38
42
|
}
|
|
39
|
-
|
|
40
|
-
function filterAgents(config, specificId) {
|
|
41
|
-
if (specificId) {
|
|
42
|
-
if (!config.agents[specificId]) {
|
|
43
|
-
console.log(chalk.red(` Agent "${specificId}" not found in agentxchain.json`));
|
|
44
|
-
process.exit(1);
|
|
45
|
-
}
|
|
46
|
-
return { [specificId]: config.agents[specificId] };
|
|
47
|
-
}
|
|
48
|
-
return config.agents;
|
|
49
|
-
}
|
|
@@ -4,6 +4,7 @@ import { join } from 'path';
|
|
|
4
4
|
import chalk from 'chalk';
|
|
5
5
|
import inquirer from 'inquirer';
|
|
6
6
|
import { generatePollingPrompt } from '../lib/seed-prompt-polling.js';
|
|
7
|
+
import { filterAgents } from '../lib/filter-agents.js';
|
|
7
8
|
|
|
8
9
|
export async function launchCursorLocal(config, root, opts) {
|
|
9
10
|
const agents = filterAgents(config, opts.agent);
|
|
@@ -50,13 +51,22 @@ export async function launchCursorLocal(config, root, opts) {
|
|
|
50
51
|
console.log(chalk.cyan(` ─── Agent ${i + 1}/${total}: ${chalk.bold(id)} — ${agent.name} ───`));
|
|
51
52
|
console.log('');
|
|
52
53
|
|
|
53
|
-
copyToClipboard(prompt);
|
|
54
|
-
|
|
54
|
+
const copied = copyToClipboard(prompt);
|
|
55
|
+
if (copied) {
|
|
56
|
+
console.log(chalk.green(' ✓ Prompt copied to clipboard.'));
|
|
57
|
+
} else {
|
|
58
|
+
console.log(chalk.yellow(' ! Clipboard copy failed. Use the saved prompt file manually.'));
|
|
59
|
+
}
|
|
55
60
|
console.log(chalk.dim(` Saved to: .agentxchain-prompts/${id}.prompt.md`));
|
|
56
61
|
|
|
57
62
|
// Open a separate Cursor window using the symlinked path
|
|
58
|
-
openCursorWindow(agentWorkspace);
|
|
59
|
-
|
|
63
|
+
const opened = openCursorWindow(agentWorkspace);
|
|
64
|
+
if (opened) {
|
|
65
|
+
console.log(chalk.dim(` Cursor window opened for ${id}.`));
|
|
66
|
+
} else {
|
|
67
|
+
console.log(chalk.yellow(` Could not open Cursor window automatically for ${id}.`));
|
|
68
|
+
console.log(chalk.dim(` Open manually: cursor --new-window "${agentWorkspace}"`));
|
|
69
|
+
}
|
|
60
70
|
|
|
61
71
|
console.log('');
|
|
62
72
|
console.log(` ${chalk.bold('In the new Cursor window:')}`);
|
|
@@ -108,17 +118,6 @@ export async function launchCursorLocal(config, root, opts) {
|
|
|
108
118
|
console.log('');
|
|
109
119
|
}
|
|
110
120
|
|
|
111
|
-
function filterAgents(config, specificId) {
|
|
112
|
-
if (specificId) {
|
|
113
|
-
if (!config.agents[specificId]) {
|
|
114
|
-
console.log(chalk.red(` Agent "${specificId}" not found in agentxchain.json`));
|
|
115
|
-
process.exit(1);
|
|
116
|
-
}
|
|
117
|
-
return { [specificId]: config.agents[specificId] };
|
|
118
|
-
}
|
|
119
|
-
return config.agents;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
121
|
function copyToClipboard(text) {
|
|
123
122
|
try {
|
|
124
123
|
if (process.platform === 'darwin') {
|
|
@@ -137,10 +136,12 @@ function openCursorWindow(folderPath) {
|
|
|
137
136
|
try {
|
|
138
137
|
if (process.platform === 'darwin') {
|
|
139
138
|
execSync(`open -na "Cursor" --args "${folderPath}"`, { stdio: 'ignore' });
|
|
140
|
-
return;
|
|
139
|
+
return true;
|
|
141
140
|
}
|
|
142
141
|
execSync(`cursor --new-window "${folderPath}"`, { stdio: 'ignore' });
|
|
142
|
+
return true;
|
|
143
143
|
} catch {}
|
|
144
|
+
return false;
|
|
144
145
|
}
|
|
145
146
|
|
|
146
147
|
function isPmLike(agentId, agentDef) {
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { loadProjectContext } from '../lib/config.js';
|
|
3
|
+
import { acceptGovernedTurn } from '../lib/governed-state.js';
|
|
4
|
+
import { deriveRecoveryDescriptor } from '../lib/blocked-state.js';
|
|
5
|
+
|
|
6
|
+
export async function acceptTurnCommand(opts = {}) {
|
|
7
|
+
const context = loadProjectContext();
|
|
8
|
+
if (!context) {
|
|
9
|
+
console.log(chalk.red('No agentxchain.json found. Run `agentxchain init` first.'));
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const { root, config } = context;
|
|
14
|
+
|
|
15
|
+
if (config.protocol_mode !== 'governed') {
|
|
16
|
+
console.log(chalk.red('The accept-turn command is only available for governed projects.'));
|
|
17
|
+
console.log(chalk.dim('Legacy projects use: agentxchain release'));
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const result = acceptGovernedTurn(root, config, {
|
|
22
|
+
turnId: opts.turn,
|
|
23
|
+
resolutionMode: opts.resolution || 'standard',
|
|
24
|
+
});
|
|
25
|
+
if (!result.ok) {
|
|
26
|
+
if (result.error_code?.startsWith('hook_') || result.error_code === 'hook_blocked') {
|
|
27
|
+
const recovery = deriveRecoveryDescriptor(result.state);
|
|
28
|
+
const activeTurn = result.state?.current_turn;
|
|
29
|
+
const hookName = result.hookResults?.blocker?.hook_name
|
|
30
|
+
|| result.hookResults?.results?.find((entry) => entry.hook_name)?.hook_name
|
|
31
|
+
|| '(unknown)';
|
|
32
|
+
|
|
33
|
+
console.log('');
|
|
34
|
+
console.log(chalk.yellow(` ${result.accepted ? 'Turn Accepted, Hook Failure Detected' : 'Turn Acceptance Blocked By Hook'}`));
|
|
35
|
+
console.log(chalk.dim(' ' + '─'.repeat(44)));
|
|
36
|
+
console.log('');
|
|
37
|
+
console.log(` ${chalk.dim('Turn:')} ${result.accepted?.turn_id || activeTurn?.turn_id || opts.turn || '(unknown)'}`);
|
|
38
|
+
console.log(` ${chalk.dim('Role:')} ${result.accepted?.role || activeTurn?.assigned_role || '(unknown)'}`);
|
|
39
|
+
if (result.accepted?.status) {
|
|
40
|
+
console.log(` ${chalk.dim('Status:')} ${result.accepted.status}`);
|
|
41
|
+
}
|
|
42
|
+
console.log(` ${chalk.dim('Hook:')} ${hookName}`);
|
|
43
|
+
console.log(` ${chalk.dim('Error:')} ${result.error}`);
|
|
44
|
+
if (recovery) {
|
|
45
|
+
console.log(` ${chalk.dim('Reason:')} ${recovery.typed_reason}`);
|
|
46
|
+
console.log(` ${chalk.dim('Owner:')} ${recovery.owner}`);
|
|
47
|
+
console.log(` ${chalk.dim('Action:')} ${recovery.recovery_action}`);
|
|
48
|
+
if (recovery.detail) {
|
|
49
|
+
console.log(` ${chalk.dim('Detail:')} ${recovery.detail}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
console.log('');
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (result.error_code === 'conflict' && result.conflict) {
|
|
57
|
+
console.log('');
|
|
58
|
+
console.log(chalk.yellow(' Acceptance Conflict Detected'));
|
|
59
|
+
console.log(chalk.dim(' ' + '─'.repeat(44)));
|
|
60
|
+
console.log('');
|
|
61
|
+
console.log(` ${chalk.dim('Turn:')} ${result.conflict.conflicting_turn.turn_id}`);
|
|
62
|
+
console.log(` ${chalk.dim('Role:')} ${result.conflict.conflicting_turn.role}`);
|
|
63
|
+
console.log(` ${chalk.dim('Files:')} ${result.conflict.conflicting_files.join(', ') || '(none)'}`);
|
|
64
|
+
console.log(` ${chalk.dim('Overlap:')} ${(result.conflict.overlap_ratio * 100).toFixed(0)}%`);
|
|
65
|
+
console.log(` ${chalk.dim('Suggest:')} ${result.conflict.suggested_resolution}`);
|
|
66
|
+
if (result.state?.status === 'blocked') {
|
|
67
|
+
const recovery = deriveRecoveryDescriptor(result.state);
|
|
68
|
+
if (recovery) {
|
|
69
|
+
console.log(` ${chalk.dim('Action:')} ${recovery.recovery_action}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
console.log('');
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (result.error_code === 'lock_timeout') {
|
|
77
|
+
console.log('');
|
|
78
|
+
console.log(chalk.yellow(' Acceptance Lock Held'));
|
|
79
|
+
console.log(chalk.dim(' ' + '─'.repeat(44)));
|
|
80
|
+
console.log('');
|
|
81
|
+
console.log(` ${chalk.dim('Reason:')} ${result.error}`);
|
|
82
|
+
console.log(` ${chalk.dim('Action:')} Wait for the other acceptance to complete, then retry.`);
|
|
83
|
+
console.log(` ${chalk.dim('Note:')} Stale locks are auto-reclaimed after 30 seconds.`);
|
|
84
|
+
console.log('');
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (result.error_code === 'protocol_error') {
|
|
89
|
+
console.log(chalk.red(result.error || 'Protocol error.'));
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (!result.validation) {
|
|
94
|
+
console.log(chalk.red(result.error || 'Failed to accept turn.'));
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const errorClass = result.validation?.error_class || 'unknown';
|
|
99
|
+
const stage = result.validation?.stage || 'unknown';
|
|
100
|
+
|
|
101
|
+
console.log('');
|
|
102
|
+
console.log(chalk.red(` Validation failed at stage ${stage}`));
|
|
103
|
+
console.log(chalk.dim(' ' + '─'.repeat(44)));
|
|
104
|
+
console.log('');
|
|
105
|
+
console.log(` ${chalk.dim('Reason:')} ${errorClass}`);
|
|
106
|
+
console.log(` ${chalk.dim('Owner:')} human`);
|
|
107
|
+
console.log(` ${chalk.dim('Action:')} Fix staged result and rerun agentxchain accept-turn, or reject with agentxchain reject-turn --reason "..."`);
|
|
108
|
+
console.log(` ${chalk.dim('Turn:')} retained`);
|
|
109
|
+
if (result.validation?.errors?.length) {
|
|
110
|
+
for (const err of result.validation.errors) {
|
|
111
|
+
console.log(` ${chalk.dim('Detail:')} ${err}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
console.log('');
|
|
115
|
+
console.log(chalk.dim('Inspect the staged result with: agentxchain validate --mode turn'));
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const accepted = result.accepted;
|
|
120
|
+
const turnId = accepted?.turn_id || result.state?.last_completed_turn_id || '(unknown)';
|
|
121
|
+
|
|
122
|
+
console.log('');
|
|
123
|
+
console.log(chalk.green(' Turn Accepted'));
|
|
124
|
+
console.log(chalk.dim(' ' + '─'.repeat(44)));
|
|
125
|
+
console.log('');
|
|
126
|
+
console.log(` ${chalk.dim('Turn:')} ${turnId}`);
|
|
127
|
+
console.log(` ${chalk.dim('Role:')} ${accepted?.role || '(unknown)'}`);
|
|
128
|
+
console.log(` ${chalk.dim('Status:')} ${accepted?.status || 'completed'}`);
|
|
129
|
+
console.log(` ${chalk.dim('Summary:')} ${accepted?.summary || '(none)'}`);
|
|
130
|
+
if (accepted?.proposed_next_role) {
|
|
131
|
+
console.log(` ${chalk.dim('Proposed:')} ${accepted.proposed_next_role}`);
|
|
132
|
+
}
|
|
133
|
+
if (result.state?.accepted_integration_ref) {
|
|
134
|
+
console.log(` ${chalk.dim('Accepted:')} ${result.state.accepted_integration_ref}`);
|
|
135
|
+
}
|
|
136
|
+
if (accepted?.cost?.usd != null) {
|
|
137
|
+
console.log(` ${chalk.dim('Cost:')} $${formatUsd(accepted.cost.usd)}`);
|
|
138
|
+
}
|
|
139
|
+
console.log('');
|
|
140
|
+
|
|
141
|
+
const recovery = deriveRecoveryDescriptor(result.state);
|
|
142
|
+
if (recovery) {
|
|
143
|
+
console.log(` ${chalk.dim('Reason:')} ${recovery.typed_reason}`);
|
|
144
|
+
console.log(` ${chalk.dim('Owner:')} ${recovery.owner}`);
|
|
145
|
+
console.log(` ${chalk.dim('Action:')} ${recovery.recovery_action}`);
|
|
146
|
+
console.log(` ${chalk.dim('Turn:')} ${recovery.turn_retained ? 'retained' : 'cleared'}`);
|
|
147
|
+
if (recovery.detail) {
|
|
148
|
+
console.log(` ${chalk.dim('Detail:')} ${recovery.detail}`);
|
|
149
|
+
}
|
|
150
|
+
} else if (accepted?.proposed_next_role && accepted.proposed_next_role !== 'human') {
|
|
151
|
+
console.log(chalk.dim(` Next: agentxchain resume --role ${accepted.proposed_next_role}`));
|
|
152
|
+
} else {
|
|
153
|
+
console.log(chalk.dim(' Next: review state, then run agentxchain resume when ready.'));
|
|
154
|
+
}
|
|
155
|
+
console.log('');
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function formatUsd(value) {
|
|
159
|
+
return typeof value === 'number' && !Number.isNaN(value) ? value.toFixed(2) : '0.00';
|
|
160
|
+
}
|