@mmerterden/multi-agent-pipeline 12.7.0 → 12.9.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/CHANGELOG.md +207 -0
- package/install/_common.mjs +48 -0
- package/install/_dev-only-files.mjs +125 -5
- package/install/claude.mjs +14 -8
- package/install/copilot.mjs +5 -8
- package/package.json +17 -2
- package/pipeline/commands/multi-agent/analysis/SKILL.md +1 -1
- package/pipeline/lib/credential-store.sh +20 -0
- package/pipeline/multi-agent-refs/_account-picker.md +1 -1
- package/pipeline/multi-agent-refs/_dev-context.md +1 -1
- package/pipeline/multi-agent-refs/_repo-picker.md +1 -1
- package/pipeline/multi-agent-refs/features/review-multi-repo.md +65 -0
- package/pipeline/multi-agent-refs/features/url-enrichment.md +93 -0
- package/pipeline/multi-agent-refs/phases/operations.md +28 -0
- package/pipeline/multi-agent-refs/phases/phase-0-init.md +3 -83
- package/pipeline/multi-agent-refs/phases/phase-2-planning.md +1 -1
- package/pipeline/multi-agent-refs/phases/phase-3-dev.md +1 -2
- package/pipeline/multi-agent-refs/phases/phase-4-review.md +50 -62
- package/pipeline/schemas/prefs.schema.json +6 -0
- package/pipeline/scripts/_smoke-root.sh +61 -0
- package/pipeline/scripts/agent-guard.py +57 -3
- package/pipeline/scripts/audit-log.sh +25 -0
- package/pipeline/scripts/smoke-cross-cli-behavior.sh +21 -7
- package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/expected/phase-1-analysis.json +0 -25
- package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/expected/phase-2-plan.json +0 -30
- package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/expected/phase-4-review.json +0 -20
- package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/expected/phase-4-triage.json +0 -15
- package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/task.json +0 -12
- package/pipeline/eval/golden-tasks/02-android-feature-compose/expected/phase-1-analysis.json +0 -29
- package/pipeline/eval/golden-tasks/02-android-feature-compose/expected/phase-2-plan.json +0 -43
- package/pipeline/eval/golden-tasks/02-android-feature-compose/expected/phase-4-review.json +0 -35
- package/pipeline/eval/golden-tasks/02-android-feature-compose/expected/phase-4-triage.json +0 -35
- package/pipeline/eval/golden-tasks/02-android-feature-compose/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/02-android-feature-compose/task.json +0 -12
- package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/expected/phase-1-analysis.json +0 -29
- package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/expected/phase-2-plan.json +0 -42
- package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/expected/phase-4-review.json +0 -20
- package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/expected/phase-4-triage.json +0 -15
- package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/task.json +0 -12
- package/pipeline/eval/golden-tasks/04-frontend-next-hydration/expected/phase-1-analysis.json +0 -29
- package/pipeline/eval/golden-tasks/04-frontend-next-hydration/expected/phase-2-plan.json +0 -40
- package/pipeline/eval/golden-tasks/04-frontend-next-hydration/expected/phase-4-review.json +0 -20
- package/pipeline/eval/golden-tasks/04-frontend-next-hydration/expected/phase-4-triage.json +0 -15
- package/pipeline/eval/golden-tasks/04-frontend-next-hydration/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/04-frontend-next-hydration/task.json +0 -12
- package/pipeline/eval/golden-tasks/05-ios-security-keychain/expected/phase-1-analysis.json +0 -29
- package/pipeline/eval/golden-tasks/05-ios-security-keychain/expected/phase-2-plan.json +0 -42
- package/pipeline/eval/golden-tasks/05-ios-security-keychain/expected/phase-4-review.json +0 -28
- package/pipeline/eval/golden-tasks/05-ios-security-keychain/expected/phase-4-triage.json +0 -27
- package/pipeline/eval/golden-tasks/05-ios-security-keychain/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/05-ios-security-keychain/task.json +0 -12
- package/pipeline/eval/golden-tasks/06-android-refactor-usecase/expected/phase-1-analysis.json +0 -29
- package/pipeline/eval/golden-tasks/06-android-refactor-usecase/expected/phase-2-plan.json +0 -41
- package/pipeline/eval/golden-tasks/06-android-refactor-usecase/expected/phase-4-review.json +0 -12
- package/pipeline/eval/golden-tasks/06-android-refactor-usecase/expected/phase-4-triage.json +0 -6
- package/pipeline/eval/golden-tasks/06-android-refactor-usecase/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/06-android-refactor-usecase/task.json +0 -12
- package/pipeline/eval/golden-tasks/07-backend-node-idempotency/expected/phase-1-analysis.json +0 -29
- package/pipeline/eval/golden-tasks/07-backend-node-idempotency/expected/phase-2-plan.json +0 -42
- package/pipeline/eval/golden-tasks/07-backend-node-idempotency/expected/phase-4-review.json +0 -28
- package/pipeline/eval/golden-tasks/07-backend-node-idempotency/expected/phase-4-triage.json +0 -27
- package/pipeline/eval/golden-tasks/07-backend-node-idempotency/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/07-backend-node-idempotency/task.json +0 -12
- package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/expected/phase-1-analysis.json +0 -25
- package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/expected/phase-2-plan.json +0 -31
- package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/expected/phase-4-review.json +0 -12
- package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/expected/phase-4-triage.json +0 -18
- package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/task.json +0 -12
- package/pipeline/eval/golden-tasks/README.md +0 -65
- package/pipeline/eval/intent-cases.json +0 -40
- package/pipeline/eval/run-metrics-fixture.json +0 -93
- package/pipeline/eval/triage/01-empty-findings/expected.json +0 -6
- package/pipeline/eval/triage/01-empty-findings/input.json +0 -5
- package/pipeline/eval/triage/01-empty-findings/notes.md +0 -7
- package/pipeline/eval/triage/02-real-blocker/expected.json +0 -15
- package/pipeline/eval/triage/02-real-blocker/input.json +0 -14
- package/pipeline/eval/triage/02-real-blocker/notes.md +0 -7
- package/pipeline/eval/triage/03-out-of-scope-defer/expected.json +0 -18
- package/pipeline/eval/triage/03-out-of-scope-defer/input.json +0 -14
- package/pipeline/eval/triage/03-out-of-scope-defer/notes.md +0 -10
- package/pipeline/eval/triage/04-false-positive-reject/expected.json +0 -18
- package/pipeline/eval/triage/04-false-positive-reject/input.json +0 -14
- package/pipeline/eval/triage/04-false-positive-reject/notes.md +0 -10
- package/pipeline/eval/triage/05-mixed-classification/expected.json +0 -43
- package/pipeline/eval/triage/05-mixed-classification/input.json +0 -38
- package/pipeline/eval/triage/05-mixed-classification/notes.md +0 -17
- package/pipeline/eval/triage/06-severity-mismatch/expected.json +0 -15
- package/pipeline/eval/triage/06-severity-mismatch/input.json +0 -14
- package/pipeline/eval/triage/06-severity-mismatch/notes.md +0 -9
- package/pipeline/eval/triage/07-duplicate-reviewers/expected.json +0 -27
- package/pipeline/eval/triage/07-duplicate-reviewers/input.json +0 -22
- package/pipeline/eval/triage/07-duplicate-reviewers/notes.md +0 -9
- package/pipeline/eval/triage/08-style-misclassified/expected.json +0 -18
- package/pipeline/eval/triage/08-style-misclassified/input.json +0 -14
- package/pipeline/eval/triage/08-style-misclassified/notes.md +0 -9
- package/pipeline/eval/triage/09-cascading-finding/expected.json +0 -23
- package/pipeline/eval/triage/09-cascading-finding/input.json +0 -22
- package/pipeline/eval/triage/09-cascading-finding/notes.md +0 -9
- package/pipeline/eval/triage/10-deferred-crossref/expected.json +0 -18
- package/pipeline/eval/triage/10-deferred-crossref/input.json +0 -14
- package/pipeline/eval/triage/10-deferred-crossref/notes.md +0 -9
- package/pipeline/eval/triage/11-vercel-token-leak-blocker/expected.json +0 -27
- package/pipeline/eval/triage/11-vercel-token-leak-blocker/input.json +0 -22
- package/pipeline/eval/triage/11-vercel-token-leak-blocker/notes.md +0 -14
- package/pipeline/eval/triage/README.md +0 -54
- package/pipeline/scripts/benchmark-phase-0.sh +0 -128
- package/pipeline/scripts/check-md-links.mjs +0 -88
- package/pipeline/scripts/eval-golden-tasks-live.mjs +0 -302
- package/pipeline/scripts/eval-golden-tasks.mjs +0 -224
- package/pipeline/scripts/eval-intent.mjs +0 -107
- package/pipeline/scripts/eval-mine-corpus.mjs +0 -211
- package/pipeline/scripts/eval-triage.mjs +0 -171
- package/pipeline/scripts/fixtures/diff-risk-android.diff +0 -40
- package/pipeline/scripts/fixtures/diff-risk-ios.diff +0 -48
- package/pipeline/scripts/fixtures/diff-risk-test-removal.diff +0 -40
- package/pipeline/scripts/fixtures/install-layout.tsv +0 -19
- package/pipeline/scripts/fixtures/pack-expected-count.txt +0 -1
- package/pipeline/scripts/fixtures/test-gap-node.diff +0 -30
- package/pipeline/scripts/fixtures/test-gap-python.diff +0 -32
- package/pipeline/scripts/lint-mcp-refs.mjs +0 -218
- package/pipeline/scripts/lint-skills.mjs +0 -154
- package/pipeline/scripts/run-smokes.mjs +0 -130
- package/pipeline/scripts/scorecard.mjs +0 -258
- package/pipeline/scripts/smoke-add-detail.sh +0 -137
- package/pipeline/scripts/smoke-agent-guard.sh +0 -74
- package/pipeline/scripts/smoke-agent-log-cost.sh +0 -262
- package/pipeline/scripts/smoke-agent-model-routing.sh +0 -87
- package/pipeline/scripts/smoke-ask-choice.sh +0 -42
- package/pipeline/scripts/smoke-autopilot-circuit-breaker.sh +0 -36
- package/pipeline/scripts/smoke-bitbucket-contract.sh +0 -255
- package/pipeline/scripts/smoke-changelog-version.sh +0 -47
- package/pipeline/scripts/smoke-channels-approval-gate.sh +0 -60
- package/pipeline/scripts/smoke-channels-flow.sh +0 -130
- package/pipeline/scripts/smoke-ci-workflows.sh +0 -88
- package/pipeline/scripts/smoke-clarify.sh +0 -148
- package/pipeline/scripts/smoke-command-inventory.sh +0 -81
- package/pipeline/scripts/smoke-commands-skills-parity.sh +0 -87
- package/pipeline/scripts/smoke-community-gates.sh +0 -75
- package/pipeline/scripts/smoke-compliance-skills.sh +0 -119
- package/pipeline/scripts/smoke-config-hygiene.sh +0 -58
- package/pipeline/scripts/smoke-context-budget.sh +0 -72
- package/pipeline/scripts/smoke-cost-budget.sh +0 -70
- package/pipeline/scripts/smoke-cost-summary.sh +0 -139
- package/pipeline/scripts/smoke-cross-phase-cohesion.sh +0 -128
- package/pipeline/scripts/smoke-description-tr.sh +0 -82
- package/pipeline/scripts/smoke-dev-critic.sh +0 -144
- package/pipeline/scripts/smoke-diff-explain.sh +0 -147
- package/pipeline/scripts/smoke-diff-risk.sh +0 -190
- package/pipeline/scripts/smoke-dynamic-skill-loading.sh +0 -160
- package/pipeline/scripts/smoke-eval-live.sh +0 -136
- package/pipeline/scripts/smoke-evidence-gate.sh +0 -93
- package/pipeline/scripts/smoke-extract-conventions.sh +0 -163
- package/pipeline/scripts/smoke-fetchers-offline.sh +0 -448
- package/pipeline/scripts/smoke-figma-dispatch.sh +0 -112
- package/pipeline/scripts/smoke-gate-hooks.sh +0 -74
- package/pipeline/scripts/smoke-gc-tmp.sh +0 -130
- package/pipeline/scripts/smoke-gc-worktrees.sh +0 -125
- package/pipeline/scripts/smoke-generate-issue.sh +0 -120
- package/pipeline/scripts/smoke-handoff-contract.sh +0 -92
- package/pipeline/scripts/smoke-identity-isolation.sh +0 -70
- package/pipeline/scripts/smoke-install-layout.sh +0 -248
- package/pipeline/scripts/smoke-intent-guard.sh +0 -86
- package/pipeline/scripts/smoke-issue-comment-template.sh +0 -86
- package/pipeline/scripts/smoke-issue-jira-triad.sh +0 -120
- package/pipeline/scripts/smoke-keychain.sh +0 -158
- package/pipeline/scripts/smoke-language-axis.sh +0 -109
- package/pipeline/scripts/smoke-learning-curve.sh +0 -61
- package/pipeline/scripts/smoke-learnings-ledger.sh +0 -86
- package/pipeline/scripts/smoke-lib-scripts.sh +0 -448
- package/pipeline/scripts/smoke-mcp-gate.sh +0 -68
- package/pipeline/scripts/smoke-md-links.sh +0 -8
- package/pipeline/scripts/smoke-md2confluence.sh +0 -126
- package/pipeline/scripts/smoke-metrics-cache-ratio.sh +0 -72
- package/pipeline/scripts/smoke-migrate-state.sh +0 -102
- package/pipeline/scripts/smoke-mode-dispatch-drift.sh +0 -161
- package/pipeline/scripts/smoke-model-fallback.sh +0 -89
- package/pipeline/scripts/smoke-multi-repo-integration.sh +0 -116
- package/pipeline/scripts/smoke-multi-repo-worktree.sh +0 -61
- package/pipeline/scripts/smoke-no-mcp-in-dev-phases.sh +0 -194
- package/pipeline/scripts/smoke-no-token-prompt.sh +0 -85
- package/pipeline/scripts/smoke-own-punctuation.sh +0 -103
- package/pipeline/scripts/smoke-pack-contents.sh +0 -140
- package/pipeline/scripts/smoke-pat-audit.sh +0 -128
- package/pipeline/scripts/smoke-per-repo-memory.sh +0 -156
- package/pipeline/scripts/smoke-phase-0-multi-repo.sh +0 -170
- package/pipeline/scripts/smoke-phase-6-multi.sh +0 -79
- package/pipeline/scripts/smoke-phase-banner.sh +0 -101
- package/pipeline/scripts/smoke-phase-tracker.sh +0 -324
- package/pipeline/scripts/smoke-phase0-bridge-contract.sh +0 -241
- package/pipeline/scripts/smoke-phase4-gates.sh +0 -45
- package/pipeline/scripts/smoke-phase4-triage.sh +0 -229
- package/pipeline/scripts/smoke-plan-approval-gate.sh +0 -71
- package/pipeline/scripts/smoke-plan-safety.sh +0 -139
- package/pipeline/scripts/smoke-plan-todos.sh +0 -196
- package/pipeline/scripts/smoke-pr-review-actions.sh +0 -152
- package/pipeline/scripts/smoke-pre-commit.sh +0 -170
- package/pipeline/scripts/smoke-pref-migration.sh +0 -226
- package/pipeline/scripts/smoke-prefs-language.sh +0 -134
- package/pipeline/scripts/smoke-progress-contract.sh +0 -127
- package/pipeline/scripts/smoke-prune-logs.sh +0 -137
- package/pipeline/scripts/smoke-purge.sh +0 -138
- package/pipeline/scripts/smoke-push-retry.sh +0 -75
- package/pipeline/scripts/smoke-repo-map.sh +0 -300
- package/pipeline/scripts/smoke-review-readiness.sh +0 -92
- package/pipeline/scripts/smoke-review-watch.sh +0 -146
- package/pipeline/scripts/smoke-routines.sh +0 -84
- package/pipeline/scripts/smoke-run-aggregator.sh +0 -216
- package/pipeline/scripts/smoke-run-metrics.sh +0 -50
- package/pipeline/scripts/smoke-search.sh +0 -187
- package/pipeline/scripts/smoke-shadow-git.sh +0 -224
- package/pipeline/scripts/smoke-skill-authoring.sh +0 -137
- package/pipeline/scripts/smoke-skill-language.sh +0 -83
- package/pipeline/scripts/smoke-skill-manifest.sh +0 -138
- package/pipeline/scripts/smoke-skill-scan.sh +0 -198
- package/pipeline/scripts/smoke-source-parity.sh +0 -85
- package/pipeline/scripts/smoke-subagent-validators.sh +0 -108
- package/pipeline/scripts/smoke-sync-parity.sh +0 -92
- package/pipeline/scripts/smoke-tasklist-ordering.sh +0 -112
- package/pipeline/scripts/smoke-telemetry.sh +0 -147
- package/pipeline/scripts/smoke-test-gap.sh +0 -183
- package/pipeline/scripts/smoke-token-budget.sh +0 -67
- package/pipeline/scripts/smoke-token-preflight.sh +0 -82
- package/pipeline/scripts/smoke-tracker-contract.sh +0 -191
- package/pipeline/scripts/smoke-tracker-tokens-invocation.sh +0 -73
- package/pipeline/scripts/smoke-triage-memory.sh +0 -174
- package/pipeline/scripts/smoke-update-check.sh +0 -135
- package/pipeline/scripts/smoke-url-enrichment.sh +0 -70
- package/pipeline/scripts/smoke-validate-analysis-doc.sh +0 -161
- package/pipeline/scripts/smoke-validator-contradiction.sh +0 -67
- package/pipeline/scripts/smoke-validator-gates.sh +0 -164
- package/pipeline/scripts/smoke-vercel-deploy-redact.sh +0 -129
- package/pipeline/scripts/smoke-verify-by-test.sh +0 -148
- package/pipeline/scripts/smoke-wiki-integration.sh +0 -122
- package/pipeline/scripts/smoke-work-summary.sh +0 -163
- package/pipeline/scripts/smoke-workflow-audit.sh +0 -101
- package/pipeline/scripts/smoke-worktree-path-convention.sh +0 -86
- package/pipeline/scripts/smoke-wrapper-preservation.sh +0 -68
- package/pipeline/scripts/smoke-write-state.sh +0 -159
- package/pipeline/scripts/sync-parity-check.sh +0 -135
- package/pipeline/scripts/test-gap-rules/android.json +0 -25
- package/pipeline/scripts/test-gap-rules/ios.json +0 -34
- package/pipeline/scripts/test-gap-rules/node.json +0 -29
- package/pipeline/scripts/test-gap-rules/python.json +0 -25
- package/pipeline/scripts/validate-schemas.mjs +0 -88
|
@@ -1,448 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
#
|
|
3
|
-
# smoke-fetchers-offline.sh - offline invocation tests for the pipeline/lib
|
|
4
|
-
# fetchers. No network, no real tokens.
|
|
5
|
-
#
|
|
6
|
-
# Asserts, per fetcher:
|
|
7
|
-
# 1. usage / arg-validation paths exit with the documented code
|
|
8
|
-
# 2. URL/host parsing succeeds for a representative URL. This is the
|
|
9
|
-
# permanent regression guard for the env-prefix bug where
|
|
10
|
-
# `VAR=x PARSED=$(python3 ...)` never exported VAR to the child, so
|
|
11
|
-
# fetch-confluence always failed with "could not determine Confluence
|
|
12
|
-
# host" before reaching the credential stage.
|
|
13
|
-
# 3. with stub credentials (fake credential-store in a temp HOME, fake curl
|
|
14
|
-
# on PATH) the failure moves past parsing to the documented
|
|
15
|
-
# credential/network exit code
|
|
16
|
-
# 4. static source checks: no bash-4-only constructs (macOS /bin/bash is
|
|
17
|
-
# 3.2) and no secret-bearing argv patterns (curl -H auth headers,
|
|
18
|
-
# --data-urlencode secrets, security add-generic-password -w on argv)
|
|
19
|
-
#
|
|
20
|
-
# Fetchers run under /bin/bash when available so bash-4-isms fail loudly on
|
|
21
|
-
# macOS instead of passing under a newer Homebrew bash.
|
|
22
|
-
#
|
|
23
|
-
# Exit codes: 0=pass, 1=failure
|
|
24
|
-
|
|
25
|
-
set -euo pipefail
|
|
26
|
-
|
|
27
|
-
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
28
|
-
LIB="$REPO_ROOT/pipeline/lib"
|
|
29
|
-
SCRIPTS="$REPO_ROOT/pipeline/scripts"
|
|
30
|
-
|
|
31
|
-
# Prefer the system bash (3.2 on macOS) so bash-4-isms regress loudly here.
|
|
32
|
-
SH="/bin/bash"
|
|
33
|
-
[ -x "$SH" ] || SH="$(command -v bash)"
|
|
34
|
-
|
|
35
|
-
PASS=0
|
|
36
|
-
FAIL=0
|
|
37
|
-
note() { echo " -> $*"; }
|
|
38
|
-
pass() { echo " ok $*"; PASS=$((PASS + 1)); }
|
|
39
|
-
fail() { echo " FAIL $*"; FAIL=$((FAIL + 1)); }
|
|
40
|
-
|
|
41
|
-
WORK=$(mktemp -d "${TMPDIR:-/tmp}/smoke-fetchers.XXXXXX")
|
|
42
|
-
trap 'rm -rf "$WORK"' EXIT
|
|
43
|
-
|
|
44
|
-
OUT="$WORK/out.log"
|
|
45
|
-
ERR="$WORK/err.log"
|
|
46
|
-
|
|
47
|
-
# expect_rc <expected-rc> <label> <cmd...>
|
|
48
|
-
# Runs cmd, captures stdout/stderr to $OUT/$ERR, asserts the exit code.
|
|
49
|
-
expect_rc() {
|
|
50
|
-
local expected="$1" label="$2"
|
|
51
|
-
shift 2
|
|
52
|
-
local rc=0
|
|
53
|
-
"$@" >"$OUT" 2>"$ERR" || rc=$?
|
|
54
|
-
if [ "$rc" -eq "$expected" ]; then
|
|
55
|
-
pass "$label (rc=$rc)"
|
|
56
|
-
else
|
|
57
|
-
fail "$label (rc=$rc, expected $expected)"
|
|
58
|
-
tail -5 "$ERR" | sed 's/^/ /'
|
|
59
|
-
fi
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
assert_in_file() {
|
|
63
|
-
local needle="$1" file="$2" label="$3"
|
|
64
|
-
if grep -q -e "$needle" "$file" 2>/dev/null; then
|
|
65
|
-
pass "$label"
|
|
66
|
-
else
|
|
67
|
-
fail "$label (pattern '$needle' not found)"
|
|
68
|
-
fi
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
assert_not_in_file() {
|
|
72
|
-
local needle="$1" file="$2" label="$3"
|
|
73
|
-
if grep -q -e "$needle" "$file" 2>/dev/null; then
|
|
74
|
-
fail "$label (pattern '$needle' present)"
|
|
75
|
-
else
|
|
76
|
-
pass "$label"
|
|
77
|
-
fi
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
# -----------------------------------------------------------------------------
|
|
81
|
-
note "0. fixtures: temp HOME with resolver + fake credential store, fake curls"
|
|
82
|
-
# -----------------------------------------------------------------------------
|
|
83
|
-
FAKE_HOME="$WORK/home"
|
|
84
|
-
mkdir -p "$FAKE_HOME/.claude/lib"
|
|
85
|
-
cp "$LIB/credential-store-resolver.sh" "$FAKE_HOME/.claude/lib/credential-store-resolver.sh"
|
|
86
|
-
|
|
87
|
-
# Fake credential store: empty by default, returns FAKE_CRED_VALUE when set.
|
|
88
|
-
cat > "$FAKE_HOME/.claude/lib/credential-store.sh" <<'FAKE'
|
|
89
|
-
#!/bin/bash
|
|
90
|
-
case "${1:-}" in
|
|
91
|
-
get)
|
|
92
|
-
if [ -n "${FAKE_CRED_VALUE:-}" ]; then printf '%s' "$FAKE_CRED_VALUE"; exit 0; fi
|
|
93
|
-
exit 1 ;;
|
|
94
|
-
platform) echo "fake" ;;
|
|
95
|
-
*) exit 3 ;;
|
|
96
|
-
esac
|
|
97
|
-
FAKE
|
|
98
|
-
chmod +x "$FAKE_HOME/.claude/lib/credential-store.sh"
|
|
99
|
-
|
|
100
|
-
# Fake curl variants, each in its own PATH-prepend dir.
|
|
101
|
-
BIN_CURL_FAIL="$WORK/bin-curl-fail"
|
|
102
|
-
mkdir -p "$BIN_CURL_FAIL"
|
|
103
|
-
cat > "$BIN_CURL_FAIL/curl" <<'FAKE'
|
|
104
|
-
#!/bin/bash
|
|
105
|
-
echo "curl: (7) offline stub - connection refused" >&2
|
|
106
|
-
exit 7
|
|
107
|
-
FAKE
|
|
108
|
-
chmod +x "$BIN_CURL_FAIL/curl"
|
|
109
|
-
|
|
110
|
-
# Swagger meta stub: honours --output, emits the --write-out triple with a
|
|
111
|
-
# content type that matches neither json nor yaml, forcing the URL-suffix
|
|
112
|
-
# lowercase path (the former bash-4 ${URL,,} site).
|
|
113
|
-
BIN_CURL_META="$WORK/bin-curl-meta"
|
|
114
|
-
mkdir -p "$BIN_CURL_META"
|
|
115
|
-
cat > "$BIN_CURL_META/curl" <<'FAKE'
|
|
116
|
-
#!/bin/bash
|
|
117
|
-
out=""
|
|
118
|
-
prev=""
|
|
119
|
-
for a in "$@"; do
|
|
120
|
-
if [ "$prev" = "--output" ]; then out="$a"; fi
|
|
121
|
-
prev="$a"
|
|
122
|
-
done
|
|
123
|
-
if [ -n "$out" ]; then printf '{}' > "$out"; fi
|
|
124
|
-
printf '200\napplication/octet-stream\n2\n'
|
|
125
|
-
exit 0
|
|
126
|
-
FAKE
|
|
127
|
-
chmod +x "$BIN_CURL_META/curl"
|
|
128
|
-
|
|
129
|
-
# 401 stub: honours -o, prints the HTTP code the way curl -w '%{http_code}' does.
|
|
130
|
-
BIN_CURL_401="$WORK/bin-curl-401"
|
|
131
|
-
mkdir -p "$BIN_CURL_401"
|
|
132
|
-
cat > "$BIN_CURL_401/curl" <<'FAKE'
|
|
133
|
-
#!/bin/bash
|
|
134
|
-
out=""
|
|
135
|
-
prev=""
|
|
136
|
-
for a in "$@"; do
|
|
137
|
-
if [ "$prev" = "-o" ] || [ "$prev" = "--output" ]; then out="$a"; fi
|
|
138
|
-
prev="$a"
|
|
139
|
-
done
|
|
140
|
-
if [ -n "$out" ]; then printf '{}' > "$out"; fi
|
|
141
|
-
printf '401'
|
|
142
|
-
exit 0
|
|
143
|
-
FAKE
|
|
144
|
-
chmod +x "$BIN_CURL_401/curl"
|
|
145
|
-
|
|
146
|
-
# Fake security + uname (forces the macOS branch of credential-store.sh) so
|
|
147
|
-
# the no-secret-on-argv property of `security -i` writes can be asserted on
|
|
148
|
-
# any platform without touching a real keychain.
|
|
149
|
-
BIN_DARWIN="$WORK/bin-darwin"
|
|
150
|
-
mkdir -p "$BIN_DARWIN"
|
|
151
|
-
cat > "$BIN_DARWIN/uname" <<'FAKE'
|
|
152
|
-
#!/bin/bash
|
|
153
|
-
echo "Darwin"
|
|
154
|
-
FAKE
|
|
155
|
-
cat > "$BIN_DARWIN/security" <<'FAKE'
|
|
156
|
-
#!/bin/bash
|
|
157
|
-
printf '%s\n' "$*" >> "$SMOKE_CAPTURE_DIR/security-argv.log"
|
|
158
|
-
if [ "${1:-}" = "-i" ]; then
|
|
159
|
-
cat >> "$SMOKE_CAPTURE_DIR/security-stdin.log"
|
|
160
|
-
fi
|
|
161
|
-
exit 0
|
|
162
|
-
FAKE
|
|
163
|
-
chmod +x "$BIN_DARWIN/uname" "$BIN_DARWIN/security"
|
|
164
|
-
|
|
165
|
-
# -----------------------------------------------------------------------------
|
|
166
|
-
note "1. fetch-confluence.sh"
|
|
167
|
-
# -----------------------------------------------------------------------------
|
|
168
|
-
expect_rc 4 "no args exits 4 (usage)" \
|
|
169
|
-
"$SH" "$LIB/fetch-confluence.sh"
|
|
170
|
-
expect_rc 4 "--help exits 4 (usage)" \
|
|
171
|
-
"$SH" "$LIB/fetch-confluence.sh" --help
|
|
172
|
-
|
|
173
|
-
# Regression guard for the env-prefix parse bug: a plain page URL must get
|
|
174
|
-
# past host/pageId resolution and fail at the credential stage (exit 2),
|
|
175
|
-
# never with the parse error.
|
|
176
|
-
expect_rc 2 "URL parse reaches credential stage (viewpage.action?pageId)" \
|
|
177
|
-
env HOME="$FAKE_HOME" USER=smoke \
|
|
178
|
-
"$SH" "$LIB/fetch-confluence.sh" \
|
|
179
|
-
"https://confluence.example.com/pages/viewpage.action?pageId=12345"
|
|
180
|
-
assert_in_file "missing-token" "$ERR" "blocked JSON names missing-token"
|
|
181
|
-
assert_not_in_file "could not determine Confluence host" "$ERR" \
|
|
182
|
-
"no host-parse error (env-prefix regression)"
|
|
183
|
-
|
|
184
|
-
expect_rc 2 "URL parse reaches credential stage (cloud /wiki/spaces form)" \
|
|
185
|
-
env HOME="$FAKE_HOME" USER=smoke \
|
|
186
|
-
"$SH" "$LIB/fetch-confluence.sh" \
|
|
187
|
-
"https://tenant.atlassian.net/wiki/spaces/ENG/pages/99887/Some+Page"
|
|
188
|
-
assert_in_file "missing-token" "$ERR" "cloud form also blocked on token only"
|
|
189
|
-
|
|
190
|
-
# With a stub token and a failing curl, the failure must be the documented
|
|
191
|
-
# network error (3), proving parsing and auth wiring are past.
|
|
192
|
-
expect_rc 3 "stub token + offline curl exits 3 (network stage)" \
|
|
193
|
-
env HOME="$FAKE_HOME" USER=smoke FAKE_CRED_VALUE=fake-token \
|
|
194
|
-
PATH="$BIN_CURL_FAIL:$PATH" \
|
|
195
|
-
"$SH" "$LIB/fetch-confluence.sh" \
|
|
196
|
-
"https://confluence.example.com/pages/viewpage.action?pageId=12345"
|
|
197
|
-
assert_in_file "Confluence GET failed" "$ERR" "network-stage error message"
|
|
198
|
-
|
|
199
|
-
# -----------------------------------------------------------------------------
|
|
200
|
-
note "2. fetch-crashlytics.sh"
|
|
201
|
-
# -----------------------------------------------------------------------------
|
|
202
|
-
expect_rc 4 "no args exits 4 (usage)" \
|
|
203
|
-
"$SH" "$LIB/fetch-crashlytics.sh"
|
|
204
|
-
expect_rc 4 "--help exits 4 (usage)" \
|
|
205
|
-
"$SH" "$LIB/fetch-crashlytics.sh" --help
|
|
206
|
-
|
|
207
|
-
CRASH_URL="https://console.firebase.google.com/project/my-proj/crashlytics/app/ios:com.example.app/issues/abc123"
|
|
208
|
-
expect_rc 2 "URL parse reaches credential stage" \
|
|
209
|
-
env HOME="$FAKE_HOME" USER=smoke \
|
|
210
|
-
"$SH" "$LIB/fetch-crashlytics.sh" "$CRASH_URL"
|
|
211
|
-
assert_in_file "missing-token" "$ERR" "blocked JSON names missing-token"
|
|
212
|
-
|
|
213
|
-
# Stub SA JSON whose project_id mismatches the URL: proves parse + credential
|
|
214
|
-
# read + base64 decode + JSON validation all pass, fully offline (exit 5).
|
|
215
|
-
MISMATCH_SA_B64=$(printf '{"project_id":"other-proj"}' | base64 | tr -d '\n')
|
|
216
|
-
expect_rc 5 "stub SA JSON reaches project-mismatch check (exit 5)" \
|
|
217
|
-
env HOME="$FAKE_HOME" USER=smoke FAKE_CRED_VALUE="$MISMATCH_SA_B64" \
|
|
218
|
-
"$SH" "$LIB/fetch-crashlytics.sh" "$CRASH_URL"
|
|
219
|
-
assert_in_file "project_id" "$ERR" "mismatch error mentions project_id"
|
|
220
|
-
|
|
221
|
-
# The decoded SA JSON must never touch a predictable /tmp path.
|
|
222
|
-
assert_not_in_file "/tmp/.fc-sa" "$LIB/fetch-crashlytics.sh" \
|
|
223
|
-
"predictable /tmp SA path removed from source"
|
|
224
|
-
|
|
225
|
-
# -----------------------------------------------------------------------------
|
|
226
|
-
note "3. fetch-swagger.sh"
|
|
227
|
-
# -----------------------------------------------------------------------------
|
|
228
|
-
expect_rc 4 "no args exits 4 (usage)" \
|
|
229
|
-
"$SH" "$LIB/fetch-swagger.sh"
|
|
230
|
-
|
|
231
|
-
expect_rc 2 "offline curl exits 2 (network stage)" \
|
|
232
|
-
env PATH="$BIN_CURL_FAIL:$PATH" \
|
|
233
|
-
"$SH" "$LIB/fetch-swagger.sh" "https://spec.example.com/openapi.json"
|
|
234
|
-
|
|
235
|
-
# Uppercase .YAML suffix with a non-matching content type exercises the
|
|
236
|
-
# lowercase-URL branch (formerly bash-4 ${URL,,}, dies on /bin/bash 3.2).
|
|
237
|
-
expect_rc 0 "uppercase .YAML suffix detected via lowercase branch" \
|
|
238
|
-
env PATH="$BIN_CURL_META:$PATH" \
|
|
239
|
-
"$SH" "$LIB/fetch-swagger.sh" "https://spec.example.com/openapi.YAML"
|
|
240
|
-
assert_in_file '"format": "yaml"' "$OUT" "output reports yaml format"
|
|
241
|
-
|
|
242
|
-
# -----------------------------------------------------------------------------
|
|
243
|
-
note "4. fetch-fortify.sh"
|
|
244
|
-
# -----------------------------------------------------------------------------
|
|
245
|
-
expect_rc 6 "no host configured exits 6" \
|
|
246
|
-
env HOME="$FAKE_HOME" USER=smoke \
|
|
247
|
-
"$SH" "$LIB/fetch-fortify.sh" --version-id 123
|
|
248
|
-
expect_rc 4 "host override + no version id exits 4 (usage)" \
|
|
249
|
-
env HOME="$FAKE_HOME" USER=smoke FORTIFY_HOST_OVERRIDE=ssc.example.com \
|
|
250
|
-
"$SH" "$LIB/fetch-fortify.sh"
|
|
251
|
-
|
|
252
|
-
FORTIFY_URL="https://ssc.example.com/ssc/html/ssc/version/123/fix/ABCD-1"
|
|
253
|
-
expect_rc 2 "URL parse reaches credential stage" \
|
|
254
|
-
env HOME="$FAKE_HOME" USER=smoke FORTIFY_HOST_OVERRIDE=ssc.example.com \
|
|
255
|
-
"$SH" "$LIB/fetch-fortify.sh" "$FORTIFY_URL"
|
|
256
|
-
assert_in_file "missing-token" "$ERR" "blocked JSON names missing-token"
|
|
257
|
-
|
|
258
|
-
expect_rc 0 "stub token + offline curl degrades to empty result (exit 0)" \
|
|
259
|
-
env HOME="$FAKE_HOME" USER=smoke FORTIFY_HOST_OVERRIDE=ssc.example.com \
|
|
260
|
-
FAKE_CRED_VALUE=fake-token PATH="$BIN_CURL_FAIL:$PATH" \
|
|
261
|
-
"$SH" "$LIB/fetch-fortify.sh" "$FORTIFY_URL"
|
|
262
|
-
assert_in_file '"gateOutcome"' "$OUT" "normalized JSON still emitted"
|
|
263
|
-
|
|
264
|
-
# -----------------------------------------------------------------------------
|
|
265
|
-
note "4b. fetch-graylog.sh"
|
|
266
|
-
# -----------------------------------------------------------------------------
|
|
267
|
-
expect_rc 6 "no host configured exits 6" \
|
|
268
|
-
env HOME="$FAKE_HOME" USER=smoke \
|
|
269
|
-
"$SH" "$LIB/fetch-graylog.sh" --trx T123
|
|
270
|
-
expect_rc 4 "host override + no ids exits 4 (usage)" \
|
|
271
|
-
env HOME="$FAKE_HOME" USER=smoke GRAYLOG_HOST_OVERRIDE=logs.example.com \
|
|
272
|
-
"$SH" "$LIB/fetch-graylog.sh"
|
|
273
|
-
|
|
274
|
-
expect_rc 2 "host override + id reaches credential stage" \
|
|
275
|
-
env HOME="$FAKE_HOME" USER=smoke GRAYLOG_HOST_OVERRIDE=logs.example.com \
|
|
276
|
-
"$SH" "$LIB/fetch-graylog.sh" --trx T123 --conv C456
|
|
277
|
-
assert_in_file "missing-token" "$ERR" "blocked JSON names missing-token"
|
|
278
|
-
|
|
279
|
-
# Non-blocking contract: with a stub token and a failing curl, the fetch must
|
|
280
|
-
# degrade to an empty normalized result (messages present) and exit 0 so a log
|
|
281
|
-
# fetch never blocks a run.
|
|
282
|
-
expect_rc 0 "stub token + offline curl degrades to empty result (exit 0)" \
|
|
283
|
-
env HOME="$FAKE_HOME" USER=smoke GRAYLOG_HOST_OVERRIDE=logs.example.com \
|
|
284
|
-
FAKE_CRED_VALUE=fake-token PATH="$BIN_CURL_FAIL:$PATH" \
|
|
285
|
-
"$SH" "$LIB/fetch-graylog.sh" --trx T123
|
|
286
|
-
assert_in_file '"messages"' "$OUT" "normalized JSON still emitted"
|
|
287
|
-
|
|
288
|
-
# -----------------------------------------------------------------------------
|
|
289
|
-
note "5. figma-screenshot.sh"
|
|
290
|
-
# -----------------------------------------------------------------------------
|
|
291
|
-
expect_rc 1 "no args exits 1 (usage)" \
|
|
292
|
-
"$SH" "$LIB/figma-screenshot.sh"
|
|
293
|
-
expect_rc 0 "--help exits 0" \
|
|
294
|
-
"$SH" "$LIB/figma-screenshot.sh" --help
|
|
295
|
-
|
|
296
|
-
expect_rc 3 "no PAT resolvable exits 3 (Tier 3 required)" \
|
|
297
|
-
env -u FIGMA_PAT HOME="$FAKE_HOME" USER=smoke \
|
|
298
|
-
"$SH" "$LIB/figma-screenshot.sh" --file-key abc123 --node-id 1-2 \
|
|
299
|
-
--output-dir "$WORK/shots-no-pat"
|
|
300
|
-
|
|
301
|
-
expect_rc 2 "stub PAT + 401 curl exits 2 (auth stage, past parsing)" \
|
|
302
|
-
env HOME="$FAKE_HOME" USER=smoke FIGMA_PAT=fake-pat \
|
|
303
|
-
PATH="$BIN_CURL_401:$PATH" \
|
|
304
|
-
"$SH" "$LIB/figma-screenshot.sh" --file-key abc123 --node-id 1-2 \
|
|
305
|
-
--output-dir "$WORK/shots-401"
|
|
306
|
-
|
|
307
|
-
# -----------------------------------------------------------------------------
|
|
308
|
-
note "6. figma-mcp-refresh.sh"
|
|
309
|
-
# -----------------------------------------------------------------------------
|
|
310
|
-
expect_rc 2 "missing prefs exits 2" \
|
|
311
|
-
env HOME="$FAKE_HOME" USER=smoke \
|
|
312
|
-
"$SH" "$LIB/figma-mcp-refresh.sh"
|
|
313
|
-
|
|
314
|
-
if command -v jq >/dev/null 2>&1; then
|
|
315
|
-
PREFS_HOME="$WORK/home-prefs"
|
|
316
|
-
mkdir -p "$PREFS_HOME/.claude"
|
|
317
|
-
printf '{"global":{"keychainMapping":{"figma_mcp":"Smoke_Figma_MCP"}}}\n' \
|
|
318
|
-
> "$PREFS_HOME/.claude/multi-agent-preferences.json"
|
|
319
|
-
export SMOKE_CAPTURE_DIR="$WORK"
|
|
320
|
-
expect_rc 2 "mapped key + empty keychain exits 2 (no refresh token)" \
|
|
321
|
-
env HOME="$PREFS_HOME" USER=smoke SMOKE_CAPTURE_DIR="$WORK" \
|
|
322
|
-
PATH="$BIN_DARWIN:$PATH" \
|
|
323
|
-
"$SH" "$LIB/figma-mcp-refresh.sh"
|
|
324
|
-
else
|
|
325
|
-
note " (jq not installed - skipping mapped-key case)"
|
|
326
|
-
fi
|
|
327
|
-
|
|
328
|
-
# -----------------------------------------------------------------------------
|
|
329
|
-
note "7. credential-store.sh + keychain-save.sh"
|
|
330
|
-
# -----------------------------------------------------------------------------
|
|
331
|
-
expect_rc 0 "platform subcommand works" \
|
|
332
|
-
"$SH" "$LIB/credential-store.sh" platform
|
|
333
|
-
expect_rc 3 "get without key exits 3 (usage)" \
|
|
334
|
-
"$SH" "$LIB/credential-store.sh" get
|
|
335
|
-
expect_rc 1 "keychain-save without service name exits 1 (usage)" \
|
|
336
|
-
"$SH" "$SCRIPTS/keychain-save.sh"
|
|
337
|
-
|
|
338
|
-
# set <key> - : the secret must travel via stdin to `security -i`, never on
|
|
339
|
-
# the security argv. Fake uname forces the macOS branch; fake security logs
|
|
340
|
-
# argv and stdin separately.
|
|
341
|
-
rm -f "$WORK/security-argv.log" "$WORK/security-stdin.log"
|
|
342
|
-
SECRET_VALUE="topsecret-smoke-value-123"
|
|
343
|
-
rc=0
|
|
344
|
-
printf '%s' "$SECRET_VALUE" \
|
|
345
|
-
| env PATH="$BIN_DARWIN:$PATH" KEYCHAIN_DELEGATE=0 SMOKE_CAPTURE_DIR="$WORK" \
|
|
346
|
-
"$SH" "$LIB/credential-store.sh" set smoke-test-key - >"$OUT" 2>"$ERR" || rc=$?
|
|
347
|
-
if [ "$rc" -eq 0 ]; then
|
|
348
|
-
pass "set <key> - via stdin succeeds (rc=0)"
|
|
349
|
-
else
|
|
350
|
-
fail "set <key> - via stdin failed (rc=$rc)"
|
|
351
|
-
tail -5 "$ERR" | sed 's/^/ /'
|
|
352
|
-
fi
|
|
353
|
-
assert_in_file "^-i" "$WORK/security-argv.log" "security invoked in -i stdin mode"
|
|
354
|
-
assert_not_in_file "$SECRET_VALUE" "$WORK/security-argv.log" "secret absent from security argv"
|
|
355
|
-
assert_in_file "$SECRET_VALUE" "$WORK/security-stdin.log" "secret delivered via stdin"
|
|
356
|
-
|
|
357
|
-
# -----------------------------------------------------------------------------
|
|
358
|
-
note "7b. fetch-figma-annotations.sh (Tier-2 annotation ingestion)"
|
|
359
|
-
# -----------------------------------------------------------------------------
|
|
360
|
-
expect_rc 1 "no args exits 1 (usage)" \
|
|
361
|
-
"$SH" "$LIB/fetch-figma-annotations.sh"
|
|
362
|
-
|
|
363
|
-
# Missing token -> blocked JSON + exit 2 (no network).
|
|
364
|
-
expect_rc 2 "missing token exits 2 with blocked JSON" \
|
|
365
|
-
env HOME="$FAKE_HOME" USER=smoke \
|
|
366
|
-
"$SH" "$LIB/fetch-figma-annotations.sh" --file-key ABC --node-id "1:23"
|
|
367
|
-
assert_in_file "missing-token" "$OUT" "blocked JSON names missing-token"
|
|
368
|
-
|
|
369
|
-
# Fake curl returns a /nodes doc carrying a TR:/EN: annotation; with a stub
|
|
370
|
-
# token the parser must reach + parse it (regression guard for the parser).
|
|
371
|
-
BIN_CURL_ANNOT="$WORK/bin-curl-annot"
|
|
372
|
-
mkdir -p "$BIN_CURL_ANNOT"
|
|
373
|
-
cat > "$BIN_CURL_ANNOT/curl" <<'FAKE'
|
|
374
|
-
#!/bin/bash
|
|
375
|
-
out=""; prev=""
|
|
376
|
-
for a in "$@"; do
|
|
377
|
-
[ "$prev" = "-o" ] && out="$a"
|
|
378
|
-
prev="$a"
|
|
379
|
-
done
|
|
380
|
-
if [ -n "$out" ]; then
|
|
381
|
-
cat > "$out" <<'JSON'
|
|
382
|
-
{"nodes":{"1:23":{"document":{"id":"1:23","name":"Title","type":"TEXT","characters":"Giriniz","annotations":[{"label":"TR: Ucusunuzu secin\nEN: Select your flight"}]}}}}
|
|
383
|
-
JSON
|
|
384
|
-
fi
|
|
385
|
-
printf '200'
|
|
386
|
-
exit 0
|
|
387
|
-
FAKE
|
|
388
|
-
chmod +x "$BIN_CURL_ANNOT/curl"
|
|
389
|
-
|
|
390
|
-
expect_rc 0 "stub token + annotation doc parses (exit 0)" \
|
|
391
|
-
env HOME="$FAKE_HOME" USER=smoke FIGMA_PAT=fake-token \
|
|
392
|
-
PATH="$BIN_CURL_ANNOT:$PATH" \
|
|
393
|
-
"$SH" "$LIB/fetch-figma-annotations.sh" --file-key ABC --node-id "1:23" --lang-prefixes TR,EN
|
|
394
|
-
assert_in_file "\"mode\": \"prefixed\"" "$OUT" "annotation parsed as prefixed"
|
|
395
|
-
assert_in_file "Select your flight" "$OUT" "EN copy extracted from annotation"
|
|
396
|
-
|
|
397
|
-
# -----------------------------------------------------------------------------
|
|
398
|
-
note "8. static source checks (bash-3.2 compat + no secrets on argv)"
|
|
399
|
-
# -----------------------------------------------------------------------------
|
|
400
|
-
OWNED_FILES=(
|
|
401
|
-
"$LIB/fetch-confluence.sh"
|
|
402
|
-
"$LIB/fetch-crashlytics.sh"
|
|
403
|
-
"$LIB/fetch-swagger.sh"
|
|
404
|
-
"$LIB/fetch-fortify.sh"
|
|
405
|
-
"$LIB/fetch-graylog.sh"
|
|
406
|
-
"$LIB/fetch-figma-annotations.sh"
|
|
407
|
-
"$LIB/figma-mcp-refresh.sh"
|
|
408
|
-
"$LIB/figma-screenshot.sh"
|
|
409
|
-
"$LIB/credential-store.sh"
|
|
410
|
-
"$SCRIPTS/keychain-save.sh"
|
|
411
|
-
)
|
|
412
|
-
|
|
413
|
-
if grep -nE '\$\{[A-Za-z_0-9]+(,,|\^\^)|mapfile |readarray |declare -A' "${OWNED_FILES[@]}"; then
|
|
414
|
-
fail "bash-4-only construct found (must run on /bin/bash 3.2)"
|
|
415
|
-
else
|
|
416
|
-
pass "no bash-4-only constructs"
|
|
417
|
-
fi
|
|
418
|
-
|
|
419
|
-
if grep -nE -- '-H "(Authorization|X-Figma-Token)' "${OWNED_FILES[@]}"; then
|
|
420
|
-
fail "auth header passed to curl via argv (-H)"
|
|
421
|
-
else
|
|
422
|
-
pass "no auth headers on curl argv"
|
|
423
|
-
fi
|
|
424
|
-
|
|
425
|
-
if grep -nE -- '--data-urlencode "(refresh_token|client_secret)' "${OWNED_FILES[@]}"; then
|
|
426
|
-
fail "OAuth secret passed to curl via argv (--data-urlencode)"
|
|
427
|
-
else
|
|
428
|
-
pass "no OAuth secrets on curl argv"
|
|
429
|
-
fi
|
|
430
|
-
|
|
431
|
-
if grep -nE 'add-generic-password[^|]*-w "\$' "${OWNED_FILES[@]}"; then
|
|
432
|
-
fail "keychain secret passed to security via argv (-w)"
|
|
433
|
-
else
|
|
434
|
-
pass "no keychain secrets on security argv"
|
|
435
|
-
fi
|
|
436
|
-
|
|
437
|
-
# shellcheck disable=SC2016 # literal $HOME is the pattern being hunted
|
|
438
|
-
if grep -nE '\$HOME/\.claude/lib/credential-store\.sh|~/\.claude/lib/credential-store\.sh' "${OWNED_FILES[@]}"; then
|
|
439
|
-
fail "hardcoded credential-store path (must go through the resolver)"
|
|
440
|
-
else
|
|
441
|
-
pass "credential-store access routed through the resolver"
|
|
442
|
-
fi
|
|
443
|
-
|
|
444
|
-
# -----------------------------------------------------------------------------
|
|
445
|
-
echo ""
|
|
446
|
-
echo "smoke-fetchers-offline: $PASS passed, $FAIL failed"
|
|
447
|
-
[ "$FAIL" -eq 0 ] || exit 1
|
|
448
|
-
exit 0
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# smoke-figma-dispatch.sh - Phase 3 component dispatch contract (plugin-based).
|
|
3
|
-
#
|
|
4
|
-
# Verifies that multi-agent Phase 3 dispatches component tasks to the
|
|
5
|
-
# ai-<platform>-engineering-toolkit MARKETPLACE PLUGIN (create-component,
|
|
6
|
-
# fallback create-ui-component) instead of a bundled figma-to-component skill.
|
|
7
|
-
#
|
|
8
|
-
# Runs offline - scans docs only, no skill invocation.
|
|
9
|
-
|
|
10
|
-
set -euo pipefail
|
|
11
|
-
|
|
12
|
-
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
13
|
-
ROOT="$(cd "$HERE/../.." && pwd)"
|
|
14
|
-
PHASE0="$ROOT/pipeline/multi-agent-refs/phases/phase-0-init.md"
|
|
15
|
-
PHASE3="$ROOT/pipeline/multi-agent-refs/phases/phase-3-dev.md"
|
|
16
|
-
DISPATCH="$ROOT/pipeline/multi-agent-refs/component-dispatch.md"
|
|
17
|
-
|
|
18
|
-
PASS=0
|
|
19
|
-
FAIL=0
|
|
20
|
-
pass() { PASS=$((PASS+1)); echo " ✓ $1"; }
|
|
21
|
-
fail() { FAIL=$((FAIL+1)); echo " ✗ $1"; }
|
|
22
|
-
|
|
23
|
-
[ -f "$PHASE0" ] || { echo "error: $PHASE0 missing" >&2; exit 1; }
|
|
24
|
-
[ -f "$PHASE3" ] || { echo "error: $PHASE3 missing" >&2; exit 1; }
|
|
25
|
-
[ -f "$DISPATCH" ] || { echo "error: $DISPATCH missing" >&2; exit 1; }
|
|
26
|
-
|
|
27
|
-
echo "→ 1. no bundled figma orchestrator remains in the tree"
|
|
28
|
-
if [ -e "$ROOT/pipeline/skills/figma-ios" ] || [ -e "$ROOT/pipeline/skills/figma-android" ] || [ -e "$ROOT/pipeline/skills/figma-common" ]; then
|
|
29
|
-
fail "bundled pipeline/skills/figma-* tree still present (should be plugin-only)"
|
|
30
|
-
else
|
|
31
|
-
pass "no pipeline/skills/figma-* tree (component skills live in the plugin)"
|
|
32
|
-
fi
|
|
33
|
-
|
|
34
|
-
echo ""
|
|
35
|
-
echo "→ 2. Phase 0 routes component tasks to the plugin component skill"
|
|
36
|
-
if grep -Fq "create-component" "$PHASE0"; then
|
|
37
|
-
pass "Phase 0 references the plugin create-component skill"
|
|
38
|
-
else
|
|
39
|
-
fail "Phase 0 does NOT reference create-component"
|
|
40
|
-
fi
|
|
41
|
-
if grep -Fq "figma-to-swiftui" "$PHASE0" || grep -Fq "skills/figma-ios/figma-to-component" "$PHASE0"; then
|
|
42
|
-
fail "Phase 0 still references a legacy/bundled figma orchestrator path"
|
|
43
|
-
else
|
|
44
|
-
pass "Phase 0 has no legacy/bundled figma orchestrator reference"
|
|
45
|
-
fi
|
|
46
|
-
|
|
47
|
-
echo ""
|
|
48
|
-
echo "→ 3. dispatch guards taskType and resolves the plugin (dual-name)"
|
|
49
|
-
if grep -Fq 'taskType === "component"' "$DISPATCH"; then
|
|
50
|
-
pass "dispatch mentions taskType guard"
|
|
51
|
-
else
|
|
52
|
-
fail "dispatch missing taskType guard"
|
|
53
|
-
fi
|
|
54
|
-
if grep -Fq "create-component" "$DISPATCH" && grep -Fq "create-ui-component" "$DISPATCH"; then
|
|
55
|
-
pass "dispatch documents dual-name resolution (create-component / create-ui-component)"
|
|
56
|
-
else
|
|
57
|
-
fail "dispatch missing dual-name plugin resolution"
|
|
58
|
-
fi
|
|
59
|
-
|
|
60
|
-
echo ""
|
|
61
|
-
echo "→ 4. dispatch layer owns subphase state + phase-tracker"
|
|
62
|
-
if grep -Fq 'subphases[]' "$DISPATCH" && grep -Fq "phase-tracker" "$DISPATCH"; then
|
|
63
|
-
pass "dispatch documents state.phases[3].subphases[] + phase-tracker"
|
|
64
|
-
else
|
|
65
|
-
fail "dispatch missing subphases[]/phase-tracker documentation"
|
|
66
|
-
fi
|
|
67
|
-
|
|
68
|
-
echo ""
|
|
69
|
-
echo "→ 5. phase docs carry the progress-contract adoption marker"
|
|
70
|
-
if grep -Fq "<!-- progress-contract: applied -->" "$PHASE3"; then
|
|
71
|
-
pass "Phase 3 carries the adoption marker"
|
|
72
|
-
else
|
|
73
|
-
fail "Phase 3 missing the adoption marker"
|
|
74
|
-
fi
|
|
75
|
-
if grep -Fq "<!-- progress-contract: applied -->" "$PHASE0"; then
|
|
76
|
-
pass "Phase 0 carries the adoption marker"
|
|
77
|
-
else
|
|
78
|
-
fail "Phase 0 missing the adoption marker"
|
|
79
|
-
fi
|
|
80
|
-
|
|
81
|
-
echo ""
|
|
82
|
-
echo "→ 6. Phase 3 emits the plugin dispatch progress line"
|
|
83
|
-
if grep -qE '→ dispatching create-component' "$PHASE3"; then
|
|
84
|
-
pass "Phase 3 documents '→ dispatching create-component <name>' emission"
|
|
85
|
-
else
|
|
86
|
-
fail "Phase 3 missing dispatch progress line"
|
|
87
|
-
fi
|
|
88
|
-
|
|
89
|
-
echo ""
|
|
90
|
-
echo "→ 7. dispatch has failure + retry semantics"
|
|
91
|
-
if grep -Fq "retryCount" "$DISPATCH"; then
|
|
92
|
-
pass "dispatch documents retryCount"
|
|
93
|
-
else
|
|
94
|
-
fail "dispatch missing retryCount documentation"
|
|
95
|
-
fi
|
|
96
|
-
|
|
97
|
-
echo ""
|
|
98
|
-
echo "→ 8. dispatch covers multi-repo + intentional cross-CLI divergence"
|
|
99
|
-
if grep -Fq 'state.projects[]' "$DISPATCH"; then
|
|
100
|
-
pass "dispatch documents multi-repo report"
|
|
101
|
-
else
|
|
102
|
-
fail "dispatch missing multi-repo report paragraph"
|
|
103
|
-
fi
|
|
104
|
-
if grep -Fiq "Copilot" "$DISPATCH"; then
|
|
105
|
-
pass "dispatch documents the Copilot cross-CLI divergence"
|
|
106
|
-
else
|
|
107
|
-
fail "dispatch missing cross-CLI divergence note"
|
|
108
|
-
fi
|
|
109
|
-
|
|
110
|
-
echo ""
|
|
111
|
-
echo "══ figma-dispatch smoke: $PASS passed, $FAIL failed ══"
|
|
112
|
-
[ "$FAIL" -eq 0 ]
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# smoke-gate-hooks.sh - the recommended Claude Code hooks template must be
|
|
3
|
-
# valid JSON, wire the secret scan as a PreToolUse hook on git commit, and point
|
|
4
|
-
# at the installed pre-commit-check.sh. This is the one gate that is OS-enforced
|
|
5
|
-
# (a non-zero exit blocks the commit); the template is what makes it real for
|
|
6
|
-
# end users instead of only the maintainer's local settings.
|
|
7
|
-
#
|
|
8
|
-
# Exit 0 = all pass, 1 = any failure.
|
|
9
|
-
|
|
10
|
-
set -uo pipefail
|
|
11
|
-
|
|
12
|
-
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
13
|
-
TMPL="$ROOT/install/templates/claude-hooks.json"
|
|
14
|
-
PASS=0; FAIL=0
|
|
15
|
-
pass() { PASS=$((PASS+1)); echo " ✓ $1"; }
|
|
16
|
-
fail() { FAIL=$((FAIL+1)); echo " ✗ $1"; }
|
|
17
|
-
command -v node >/dev/null 2>&1 || { echo "error: node required" >&2; exit 127; }
|
|
18
|
-
|
|
19
|
-
[ -f "$TMPL" ] || { echo "FAIL: hooks template missing at $TMPL" >&2; exit 1; }
|
|
20
|
-
|
|
21
|
-
# 1. valid JSON
|
|
22
|
-
if node -e 'JSON.parse(require("fs").readFileSync(process.argv[1],"utf8"))' "$TMPL" 2>/dev/null; then
|
|
23
|
-
pass "hooks template is valid JSON"
|
|
24
|
-
else
|
|
25
|
-
fail "hooks template is not valid JSON"
|
|
26
|
-
fi
|
|
27
|
-
|
|
28
|
-
# 2. wires a PreToolUse hook on git commit -> pre-commit-check.sh
|
|
29
|
-
node -e '
|
|
30
|
-
const t = JSON.parse(require("fs").readFileSync(process.argv[1],"utf8"));
|
|
31
|
-
const pre = t.hooks && t.hooks.PreToolUse;
|
|
32
|
-
if (!Array.isArray(pre) || pre.length === 0) { console.error("no PreToolUse"); process.exit(1); }
|
|
33
|
-
const onCommit = pre.find(h => /git commit/.test(h.matcher || ""));
|
|
34
|
-
if (!onCommit) { console.error("no git-commit matcher"); process.exit(1); }
|
|
35
|
-
const cmds = (onCommit.hooks || []).map(h => h.command || "").join(" ");
|
|
36
|
-
if (!/pre-commit-check\.sh/.test(cmds)) { console.error("does not call pre-commit-check.sh"); process.exit(1); }
|
|
37
|
-
' "$TMPL" 2>/dev/null && pass "template wires secret scan as PreToolUse on git commit" || fail "template does not wire the secret-scan hook"
|
|
38
|
-
|
|
39
|
-
# 3. the referenced script exists in the source tree
|
|
40
|
-
if [ -f "$ROOT/pipeline/scripts/pre-commit-check.sh" ]; then
|
|
41
|
-
pass "pre-commit-check.sh exists to back the hook"
|
|
42
|
-
else
|
|
43
|
-
fail "pre-commit-check.sh missing"
|
|
44
|
-
fi
|
|
45
|
-
|
|
46
|
-
# 3b. template also wires agent-guard.sh on git commit AND git push
|
|
47
|
-
node -e '
|
|
48
|
-
const t = JSON.parse(require("fs").readFileSync(process.argv[1],"utf8"));
|
|
49
|
-
const pre = (t.hooks && t.hooks.PreToolUse) || [];
|
|
50
|
-
const commit = pre.find(h => /git commit/.test(h.matcher || ""));
|
|
51
|
-
const push = pre.find(h => /git push/.test(h.matcher || ""));
|
|
52
|
-
const cmds = e => (e && e.hooks || []).map(h => h.command || "").join(" ");
|
|
53
|
-
if (!/agent-guard\.sh/.test(cmds(commit))) { console.error("guard not on git commit"); process.exit(1); }
|
|
54
|
-
if (!/agent-guard\.sh/.test(cmds(push))) { console.error("guard not on git push"); process.exit(1); }
|
|
55
|
-
' "$TMPL" 2>/dev/null && pass "template wires agent-guard on git commit + git push" || fail "template does not wire agent-guard on both matchers"
|
|
56
|
-
|
|
57
|
-
# 3c. the guard scripts exist in the source tree
|
|
58
|
-
if [ -f "$ROOT/pipeline/scripts/agent-guard.sh" ] && [ -f "$ROOT/pipeline/scripts/agent-guard.py" ]; then
|
|
59
|
-
pass "agent-guard.sh + agent-guard.py exist to back the hook"
|
|
60
|
-
else
|
|
61
|
-
fail "agent-guard scripts missing"
|
|
62
|
-
fi
|
|
63
|
-
|
|
64
|
-
# 4. picker-contract documents the hook template + the phase-enforced caveat
|
|
65
|
-
PC="$ROOT/pipeline/multi-agent-refs/picker-contract.md"
|
|
66
|
-
if grep -q "claude-hooks.json" "$PC" && grep -q "phase-enforced" "$PC"; then
|
|
67
|
-
pass "picker-contract references the template + the phase-enforced gates caveat"
|
|
68
|
-
else
|
|
69
|
-
fail "picker-contract does not document the hooks template honestly"
|
|
70
|
-
fi
|
|
71
|
-
|
|
72
|
-
echo ""
|
|
73
|
-
echo "══ gate-hooks smoke: $PASS passed, $FAIL failed ══"
|
|
74
|
-
[ "$FAIL" -eq 0 ]
|