agentxchain 0.8.8 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +136 -136
- package/bin/agentxchain.js +186 -5
- 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 +14 -6
- 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/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/dashboard.js +70 -0
- package/src/commands/init.js +516 -0
- 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/template-list.js +33 -0
- package/src/commands/template-set.js +279 -0
- package/src/commands/validate.js +20 -11
- package/src/commands/verify.js +71 -0
- 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 +97 -1
- 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/gate-evaluator.js +285 -0
- 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/normalized-config.js +539 -0
- package/src/lib/plugin-config-schema.js +192 -0
- package/src/lib/plugins.js +692 -0
- package/src/lib/protocol-conformance.js +291 -0
- package/src/lib/reference-conformance-adapter.js +858 -0
- package/src/lib/repo-observer.js +597 -0
- package/src/lib/repo.js +0 -31
- package/src/lib/schema.js +121 -0
- package/src/lib/schemas/turn-result.schema.json +205 -0
- 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 +137 -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
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { loadProjectContext, loadProjectState } from '../lib/config.js';
|
|
3
|
+
import { approveRunCompletion } from '../lib/governed-state.js';
|
|
4
|
+
import { deriveRecoveryDescriptor } from '../lib/blocked-state.js';
|
|
5
|
+
|
|
6
|
+
export async function approveCompletionCommand(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
|
+
if (context.config.protocol_mode !== 'governed') {
|
|
14
|
+
console.log(chalk.red('approve-completion is only available in governed mode.'));
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const { root, config } = context;
|
|
19
|
+
const state = loadProjectState(root, config);
|
|
20
|
+
|
|
21
|
+
if (!state?.pending_run_completion) {
|
|
22
|
+
console.log(chalk.yellow('No pending run completion to approve.'));
|
|
23
|
+
if (state?.status === 'completed') {
|
|
24
|
+
console.log(chalk.dim(' This run is already completed.'));
|
|
25
|
+
} else if (state?.phase) {
|
|
26
|
+
console.log(chalk.dim(` Current phase: ${state.phase}, status: ${state.status}`));
|
|
27
|
+
}
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const pc = state.pending_run_completion;
|
|
32
|
+
console.log('');
|
|
33
|
+
console.log(chalk.bold(' Approving Run Completion'));
|
|
34
|
+
console.log(chalk.dim(' ' + '─'.repeat(44)));
|
|
35
|
+
console.log(` ${chalk.dim('Phase:')} ${state.phase}`);
|
|
36
|
+
console.log(` ${chalk.dim('Gate:')} ${pc.gate}`);
|
|
37
|
+
console.log(` ${chalk.dim('Turn:')} ${pc.requested_by_turn}`);
|
|
38
|
+
console.log('');
|
|
39
|
+
|
|
40
|
+
const result = approveRunCompletion(root, config);
|
|
41
|
+
|
|
42
|
+
if (!result.ok) {
|
|
43
|
+
if (result.error_code?.startsWith('hook_') || result.error_code === 'hook_blocked') {
|
|
44
|
+
printGateHookFailure(result, 'run_completion', pc);
|
|
45
|
+
} else {
|
|
46
|
+
console.log(chalk.red(` Failed: ${result.error}`));
|
|
47
|
+
}
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
console.log(chalk.green(' \u2713 Run completed'));
|
|
52
|
+
console.log(chalk.dim(` Completed at: ${result.state.completed_at}`));
|
|
53
|
+
console.log('');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function printGateHookFailure(result, gateType, gateInfo) {
|
|
57
|
+
const recovery = deriveRecoveryDescriptor(result.state);
|
|
58
|
+
const hookName = result.hookResults?.blocker?.hook_name
|
|
59
|
+
|| result.hookResults?.results?.find((entry) => entry.hook_name)?.hook_name
|
|
60
|
+
|| '(unknown)';
|
|
61
|
+
|
|
62
|
+
console.log('');
|
|
63
|
+
console.log(chalk.yellow(` ${gateType === 'phase_transition' ? 'Phase Transition' : 'Run Completion'} Blocked By Hook`));
|
|
64
|
+
console.log(chalk.dim(' ' + '-'.repeat(44)));
|
|
65
|
+
console.log('');
|
|
66
|
+
console.log(` ${chalk.dim('Gate:')} ${gateInfo.gate}`);
|
|
67
|
+
console.log(` ${chalk.dim('Hook:')} ${hookName}`);
|
|
68
|
+
console.log(` ${chalk.dim('Error:')} ${result.error}`);
|
|
69
|
+
if (recovery) {
|
|
70
|
+
console.log(` ${chalk.dim('Reason:')} ${recovery.typed_reason}`);
|
|
71
|
+
console.log(` ${chalk.dim('Owner:')} ${recovery.owner}`);
|
|
72
|
+
console.log(` ${chalk.dim('Action:')} ${recovery.recovery_action}`);
|
|
73
|
+
if (recovery.detail) {
|
|
74
|
+
console.log(` ${chalk.dim('Detail:')} ${recovery.detail}`);
|
|
75
|
+
}
|
|
76
|
+
} else {
|
|
77
|
+
console.log(` ${chalk.dim('Action:')} Fix or reconfigure hook "${hookName}", then rerun agentxchain approve-completion`);
|
|
78
|
+
}
|
|
79
|
+
console.log('');
|
|
80
|
+
}
|