bigpowers 2.88.16 → 2.88.18
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/.gemini/extensions/bigpowers/gemini-extension.json +1 -1
- package/.mcp.json +2 -2
- package/.pi/package.json +1 -1
- package/CHANGELOG.md +18 -0
- package/bigpowers-mcp/build/lib/git-context.js +52 -36
- package/bigpowers-mcp/build/lib/git-context.js.map +1 -1
- package/bigpowers-mcp/src/lib/git-context.ts +9 -5
- package/dashboard/src/web/client.html +15 -8
- package/dashboard/src/web/server.js +2 -1
- package/package.json +1 -1
- package/scripts/bp-read-agents.sh +7 -5
- package/scripts/lib/__pycache__/link_utils.cpython-310.pyc +0 -0
- package/scripts/lib/__pycache__/srp_engine_links.cpython-310.pyc +0 -0
- package/scripts/lib/__pycache__/srp_engine_okf.cpython-310.pyc +0 -0
- package/scripts/lib/golden-suite-gates.sh +6 -0
- package/scripts/lib/migrate-version-common.sh +15 -5
- package/scripts/lib/migrate-version-plan.sh +7 -7
- package/scripts/lib/migrate-version-run.sh +3 -0
- package/scripts/lib/srp-engine.py +1 -60
- package/scripts/lib/srp_engine_okf.py +65 -0
- package/scripts/lib/story-verify-memo.sh +56 -0
- package/scripts/lib/test-assertions.sh +6 -0
- package/scripts/mcp-smoke.ts +48 -43
- package/scripts/run-story-verify.sh +3 -50
- package/scripts/test-golden-gate-runner.sh +18 -18
- package/scripts/test-project-runtime.sh +10 -19
- package/scripts/validate-docs-base-path.sh +15 -22
- package/scripts/validate-mcp-package.sh +18 -20
- package/scripts/validate-omp-extension.sh +23 -30
- package/tests/security/agents-import.cjs +36 -0
- package/tests/security/dashboard-bind.cjs +31 -0
- package/tests/security/dashboard-render.cjs +60 -0
- package/tests/security/git-context.cjs +44 -0
- package/tests/security/migration.cjs +76 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# story: BUG-2026-10-30-compliance-akita-masked
|
|
3
|
+
# Memoized verify execution for run-story-verify.sh. The caller defines
|
|
4
|
+
# run_with_timeout and may set MEMO_DIR (one per gate_all run).
|
|
5
|
+
|
|
6
|
+
if [ -n "${STORY_VERIFY_MEMO_LOADED:-}" ]; then return 0; fi
|
|
7
|
+
STORY_VERIFY_MEMO_LOADED=1
|
|
8
|
+
|
|
9
|
+
# Many stories share one verify (e61s02…e76s02 all run the same installer
|
|
10
|
+
# test); same command at the same commit gives the same result, so run it once
|
|
11
|
+
# per gate_all and reuse the verdict for every story that declares it.
|
|
12
|
+
# Sets RUN_RC, RUN_OUT, and RUN_REUSED (1 when nothing new ran).
|
|
13
|
+
run_memoized() {
|
|
14
|
+
local cmd="$1" key
|
|
15
|
+
key=$(printf '%s' "$cmd" | cksum | tr -d ' ')
|
|
16
|
+
if [ -n "${MEMO_DIR:-}" ] && [ -f "$MEMO_DIR/$key.rc" ]; then
|
|
17
|
+
RUN_RC=$(cat "$MEMO_DIR/$key.rc")
|
|
18
|
+
RUN_OUT=$(cat "$MEMO_DIR/$key.out")
|
|
19
|
+
return 0
|
|
20
|
+
fi
|
|
21
|
+
RUN_REUSED=0
|
|
22
|
+
RUN_OUT=$(run_with_timeout "$cmd") && RUN_RC=0 || RUN_RC=$?
|
|
23
|
+
if [ -n "${MEMO_DIR:-}" ]; then
|
|
24
|
+
printf '%s' "$RUN_RC" > "$MEMO_DIR/$key.rc"
|
|
25
|
+
printf '%s' "$RUN_OUT" > "$MEMO_DIR/$key.out"
|
|
26
|
+
fi
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
# A plain `a && b && c` chain shares no shell state between segments when it
|
|
30
|
+
# has no quoting, expansion, grouping, pipes, `;`, cd, export or assignment —
|
|
31
|
+
# so each segment can be cached on its own (test-adapter-render.sh prefixes ten
|
|
32
|
+
# different story verifies). Anything else is cached as one whole command.
|
|
33
|
+
is_plain_and_chain() {
|
|
34
|
+
case "$1" in
|
|
35
|
+
*\'*|*\"*|*\$*|*\`*|*\(*|*\)*|*\{*|*\}*|*\|*|*\;*|*=*|*\<*) return 1 ;;
|
|
36
|
+
esac
|
|
37
|
+
printf '%s' "$1" | grep -qE '(^|&&)[[:space:]]*(cd|export|source|\.)[[:space:]]' && return 1
|
|
38
|
+
return 0
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
run_memoized_chain() {
|
|
42
|
+
local cmd="$1" seg
|
|
43
|
+
RUN_REUSED=1
|
|
44
|
+
if ! is_plain_and_chain "$cmd"; then
|
|
45
|
+
run_memoized "$cmd"
|
|
46
|
+
return
|
|
47
|
+
fi
|
|
48
|
+
local rest="$cmd"
|
|
49
|
+
while :; do
|
|
50
|
+
seg=${rest%%&&*}
|
|
51
|
+
run_memoized "$(printf '%s' "$seg" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
|
|
52
|
+
[ "$RUN_RC" -eq 0 ] || return
|
|
53
|
+
[ "$seg" = "$rest" ] && return
|
|
54
|
+
rest=${rest#*&&}
|
|
55
|
+
done
|
|
56
|
+
}
|
|
@@ -23,3 +23,9 @@ ta_fail() { echo " FAIL $1"; TA_FAIL=$((TA_FAIL + 1)); }
|
|
|
23
23
|
# Strip ANSI color codes — used by scripts that grep the colored output of a
|
|
24
24
|
# gate/skill they're testing, where the escape codes would break the match.
|
|
25
25
|
ta_strip_ansi() { sed -E 's/\x1b\[[0-9;]*m//g'; }
|
|
26
|
+
|
|
27
|
+
# Validator style for validate-*.sh gates: "FAIL: msg" / "ok : msg", counting
|
|
28
|
+
# into the caller's ERRORS so its summary can report "N error(s)".
|
|
29
|
+
ta_err() { echo "FAIL: $*"; ERRORS=$((ERRORS + 1)); }
|
|
30
|
+
|
|
31
|
+
ta_ok() { echo "ok : $*"; }
|
package/scripts/mcp-smoke.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
// story: e32s06
|
|
2
|
-
//
|
|
3
|
-
// Verifies Node resolves @modelcontextprotocol/sdk from the root
|
|
4
|
-
// and the server reaches its startup log line without
|
|
2
|
+
// Spawns bigpowers-mcp from the shipped .mcp.json the way each host expands it
|
|
3
|
+
// (#123, #168). Verifies Node resolves @modelcontextprotocol/sdk from the root
|
|
4
|
+
// package graph and the server reaches its startup log line without
|
|
5
|
+
// module-not-found, whether or not the host injects OMP_PLUGIN_ROOT.
|
|
5
6
|
|
|
6
7
|
import { readFileSync } from "node:fs";
|
|
7
8
|
import { spawn } from "node:child_process";
|
|
8
|
-
import { dirname, join } from "node:path";
|
|
9
|
+
import { dirname, join, resolve } from "node:path";
|
|
9
10
|
import { fileURLToPath } from "node:url";
|
|
10
11
|
|
|
11
12
|
const repoRoot = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
@@ -15,53 +16,57 @@ const manifest = JSON.parse(readFileSync(join(repoRoot, ".mcp.json"), "utf8")) a
|
|
|
15
16
|
const server = manifest.mcpServers?.["bigpowers-mcp"];
|
|
16
17
|
if (!server) throw new Error("bigpowers-mcp entry missing from .mcp.json");
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
// POSIX ${VAR} / ${VAR:-default}; an unset ${VAR} stays literal (omp and Claude Code both do this).
|
|
20
|
+
function expand(value: string, env: Record<string, string | undefined>): string {
|
|
21
|
+
return value.replace(/\$\{([^}:]+)(?::-([^}]*))?\}/g, (whole, name: string, fallback?: string) => {
|
|
22
|
+
const v = env[name];
|
|
23
|
+
if (v !== undefined && (fallback === undefined || v !== "")) return v;
|
|
24
|
+
return fallback ?? whole;
|
|
24
25
|
});
|
|
26
|
+
}
|
|
25
27
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
},
|
|
28
|
+
// omp injects OMP_PLUGIN_ROOT and honours cwd; Claude Code project scope sets
|
|
29
|
+
// neither and spawns from the project root.
|
|
30
|
+
const { OMP_PLUGIN_ROOT: _ambient, ...baseEnv } = process.env;
|
|
31
|
+
const hosts = [
|
|
32
|
+
{ name: "omp", env: { ...baseEnv, OMP_PLUGIN_ROOT: repoRoot }, honoursCwd: true },
|
|
33
|
+
{ name: "claude-code", env: baseEnv, honoursCwd: false },
|
|
34
|
+
];
|
|
31
35
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
function startServer(entry: string, cwd: string, env: NodeJS.ProcessEnv): Promise<{ ok: boolean; detail: string }> {
|
|
37
|
+
return new Promise((done) => {
|
|
38
|
+
const child = spawn("node", [entry], { cwd, stdio: ["ignore", "pipe", "pipe"], env });
|
|
39
|
+
|
|
40
|
+
let stderr = "";
|
|
41
|
+
const timer = setTimeout(() => {
|
|
36
42
|
child.kill("SIGTERM");
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
});
|
|
43
|
+
done({ ok: false, detail: `timeout waiting for startup (stderr: ${stderr.trim()})` });
|
|
44
|
+
}, 5000);
|
|
40
45
|
|
|
41
|
-
|
|
42
|
-
|
|
46
|
+
child.stderr.on("data", (chunk: Buffer) => {
|
|
47
|
+
stderr += chunk.toString();
|
|
48
|
+
if (stderr.includes("bigpowers-mcp started")) {
|
|
49
|
+
clearTimeout(timer);
|
|
50
|
+
child.kill("SIGTERM");
|
|
51
|
+
done({ ok: true, detail: "bigpowers-mcp started" });
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
child.on("exit", (code, signal) => {
|
|
56
|
+
if (signal === "SIGTERM" && stderr.includes("bigpowers-mcp started")) return;
|
|
43
57
|
clearTimeout(timer);
|
|
44
|
-
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
if (signal === "SIGTERM" && stderr.includes("bigpowers-mcp started")) return;
|
|
48
|
-
clearTimeout(timer);
|
|
49
|
-
resolve({
|
|
50
|
-
ok: false,
|
|
51
|
-
detail: `exit code=${code ?? "null"} signal=${signal ?? "null"} stderr=${stderr.trim()}`,
|
|
58
|
+
done({ ok: false, detail: `exit code=${code ?? "null"} signal=${signal ?? "null"} stderr=${stderr.trim()}` });
|
|
52
59
|
});
|
|
53
60
|
});
|
|
54
|
-
}
|
|
61
|
+
}
|
|
55
62
|
|
|
56
|
-
|
|
57
|
-
|
|
63
|
+
const results = [];
|
|
64
|
+
for (const host of hosts) {
|
|
65
|
+
const cwd = host.honoursCwd && server.cwd ? resolve(repoRoot, expand(server.cwd, host.env)) : repoRoot;
|
|
66
|
+
const entry = resolve(cwd, expand(server.args?.[0] ?? "", host.env));
|
|
67
|
+
const started = await startServer(entry, cwd, host.env);
|
|
68
|
+
if (!started.ok) throw new Error(`[${host.name}] ${entry}: ${started.detail}`);
|
|
69
|
+
results.push({ host: host.name, entry, startup: started.detail });
|
|
58
70
|
}
|
|
59
71
|
|
|
60
|
-
console.log(
|
|
61
|
-
JSON.stringify({
|
|
62
|
-
pluginRoot: repoRoot,
|
|
63
|
-
entry,
|
|
64
|
-
ompPluginRootInManifest: server.cwd?.includes("OMP_PLUGIN_ROOT") ?? false,
|
|
65
|
-
startup: started.detail,
|
|
66
|
-
}),
|
|
67
|
-
);
|
|
72
|
+
console.log(JSON.stringify({ pluginRoot: repoRoot, results }));
|
|
@@ -20,6 +20,8 @@ cd "$REPO_ROOT"
|
|
|
20
20
|
source "$REPO_ROOT/scripts/lib/fail-open-detect.sh"
|
|
21
21
|
# shellcheck source=lib/python-env.sh
|
|
22
22
|
source "$REPO_ROOT/scripts/lib/python-env.sh"
|
|
23
|
+
# shellcheck source=lib/story-verify-memo.sh
|
|
24
|
+
source "$REPO_ROOT/scripts/lib/story-verify-memo.sh"
|
|
23
25
|
resolve_python || exit 1
|
|
24
26
|
|
|
25
27
|
VERIFY_TIMEOUT_SECONDS=120
|
|
@@ -58,55 +60,6 @@ reject_non_executable() {
|
|
|
58
60
|
FAIL=$((FAIL + 1))
|
|
59
61
|
}
|
|
60
62
|
|
|
61
|
-
# Many stories share one verify (e61s02…e76s02 all run the same installer
|
|
62
|
-
# test); same command at the same commit gives the same result, so run it once
|
|
63
|
-
# per gate_all and reuse the verdict for every story that declares it.
|
|
64
|
-
# Sets RUN_RC, RUN_OUT, and RUN_REUSED (1 when nothing new ran).
|
|
65
|
-
run_memoized() {
|
|
66
|
-
local cmd="$1" key
|
|
67
|
-
key=$(printf '%s' "$cmd" | cksum | tr -d ' ')
|
|
68
|
-
if [ -n "${MEMO_DIR:-}" ] && [ -f "$MEMO_DIR/$key.rc" ]; then
|
|
69
|
-
RUN_RC=$(cat "$MEMO_DIR/$key.rc")
|
|
70
|
-
RUN_OUT=$(cat "$MEMO_DIR/$key.out")
|
|
71
|
-
return 0
|
|
72
|
-
fi
|
|
73
|
-
RUN_REUSED=0
|
|
74
|
-
RUN_OUT=$(run_with_timeout "$cmd") && RUN_RC=0 || RUN_RC=$?
|
|
75
|
-
if [ -n "${MEMO_DIR:-}" ]; then
|
|
76
|
-
printf '%s' "$RUN_RC" > "$MEMO_DIR/$key.rc"
|
|
77
|
-
printf '%s' "$RUN_OUT" > "$MEMO_DIR/$key.out"
|
|
78
|
-
fi
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
# A plain `a && b && c` chain shares no shell state between segments when it
|
|
82
|
-
# has no quoting, expansion, grouping, pipes, `;`, cd, export or assignment —
|
|
83
|
-
# so each segment can be cached on its own (test-adapter-render.sh prefixes ten
|
|
84
|
-
# different story verifies). Anything else is cached as one whole command.
|
|
85
|
-
is_plain_and_chain() {
|
|
86
|
-
case "$1" in
|
|
87
|
-
*\'*|*\"*|*\$*|*\`*|*\(*|*\)*|*\{*|*\}*|*\|*|*\;*|*=*|*\<*) return 1 ;;
|
|
88
|
-
esac
|
|
89
|
-
printf '%s' "$1" | grep -qE '(^|&&)[[:space:]]*(cd|export|source|\.)[[:space:]]' && return 1
|
|
90
|
-
return 0
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
run_verify_cmd() {
|
|
94
|
-
local cmd="$1" seg
|
|
95
|
-
RUN_REUSED=1
|
|
96
|
-
if ! is_plain_and_chain "$cmd"; then
|
|
97
|
-
run_memoized "$cmd"
|
|
98
|
-
return
|
|
99
|
-
fi
|
|
100
|
-
local rest="$cmd"
|
|
101
|
-
while :; do
|
|
102
|
-
seg=${rest%%&&*}
|
|
103
|
-
run_memoized "$(printf '%s' "$seg" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
|
|
104
|
-
[ "$RUN_RC" -eq 0 ] || return
|
|
105
|
-
[ "$seg" = "$rest" ] && return
|
|
106
|
-
rest=${rest#*&&}
|
|
107
|
-
done
|
|
108
|
-
}
|
|
109
|
-
|
|
110
63
|
run_story_verify() {
|
|
111
64
|
local sid="$1" src="$2" cmd="$3"
|
|
112
65
|
|
|
@@ -119,7 +72,7 @@ run_story_verify() {
|
|
|
119
72
|
return 1
|
|
120
73
|
fi
|
|
121
74
|
|
|
122
|
-
|
|
75
|
+
run_memoized_chain "$cmd"
|
|
123
76
|
local note=""
|
|
124
77
|
[ "$RUN_REUSED" -eq 1 ] && note=" (shared result)"
|
|
125
78
|
if [ "$RUN_RC" -eq 0 ]; then
|
|
@@ -16,13 +16,13 @@ source "$(dirname "${BASH_SOURCE[0]}")/lib/skill-common.sh"
|
|
|
16
16
|
resolve_repo_root
|
|
17
17
|
cd "$REPO_ROOT"
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
source scripts/lib/test-assertions.sh
|
|
20
|
+
TA_PASS=0
|
|
21
|
+
TA_FAIL=0
|
|
22
22
|
|
|
23
23
|
# Run the runner in a subshell with a fixture gate list; prints its stdout and
|
|
24
24
|
# the final counters as "COUNTS pass fail skip".
|
|
25
|
-
|
|
25
|
+
ggr_run_fixture() {
|
|
26
26
|
(
|
|
27
27
|
source scripts/lib/golden-suite-gates.sh
|
|
28
28
|
GOLDEN_GATES=(
|
|
@@ -35,25 +35,25 @@ run_fixture() {
|
|
|
35
35
|
)
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
out=$(GOLDEN_SKIP_GATES=""
|
|
38
|
+
out=$(GOLDEN_SKIP_GATES="" ggr_run_fixture)
|
|
39
39
|
echo "$out" | grep -q 'marker-line-from-failing-gate' \
|
|
40
|
-
&&
|
|
41
|
-
||
|
|
40
|
+
&& ta_pass "(a) failing gate output is printed" \
|
|
41
|
+
|| ta_fail "(a) failing gate output is missing"
|
|
42
42
|
echo "$out" | grep -q '^COUNTS 2 1 0$' \
|
|
43
|
-
&&
|
|
44
|
-
||
|
|
43
|
+
&& ta_pass "(d) no skip list runs every gate" \
|
|
44
|
+
|| ta_fail "(d) default run counts wrong: $(echo "$out" | grep COUNTS)"
|
|
45
45
|
|
|
46
|
-
out=$(GOLDEN_SKIP_GATES="covered-gate" GOLDEN_SKIP_REASON="runs in its own CI job"
|
|
46
|
+
out=$(GOLDEN_SKIP_GATES="covered-gate" GOLDEN_SKIP_REASON="runs in its own CI job" ggr_run_fixture)
|
|
47
47
|
echo "$out" | grep -q '^\[covered-gate\] .*SKIP.*runs in its own CI job' \
|
|
48
|
-
&&
|
|
49
|
-
||
|
|
48
|
+
&& ta_pass "(b) skipped gate reported with reason" \
|
|
49
|
+
|| ta_fail "(b) skipped gate not reported with reason"
|
|
50
50
|
echo "$out" | grep -q '^COUNTS 1 1 1$' \
|
|
51
|
-
&&
|
|
52
|
-
||
|
|
51
|
+
&& ta_pass "(b) skipped gate counted as skip, not pass" \
|
|
52
|
+
|| ta_fail "(b) skip counts wrong: $(echo "$out" | grep COUNTS)"
|
|
53
53
|
|
|
54
|
-
out=$(GOLDEN_SKIP_GATES="no-such-gate"
|
|
54
|
+
out=$(GOLDEN_SKIP_GATES="no-such-gate" ggr_run_fixture)
|
|
55
55
|
echo "$out" | grep -q 'no-such-gate' && echo "$out" | grep -q '^COUNTS 2 2 0$' \
|
|
56
|
-
&&
|
|
57
|
-
||
|
|
56
|
+
&& ta_pass "(c) unknown skip name fails the run" \
|
|
57
|
+
|| ta_fail "(c) unknown skip name accepted: $(echo "$out" | grep COUNTS)"
|
|
58
58
|
|
|
59
|
-
[ "$
|
|
59
|
+
[ "$TA_FAIL" -eq 0 ] && echo "OK: golden gate runner" || { echo "$TA_FAIL check(s) failed"; exit 1; }
|
|
@@ -6,27 +6,18 @@ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
|
6
6
|
# shellcheck disable=SC1091
|
|
7
7
|
source "$ROOT/scripts/lib/project-runtime.sh"
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
source "$ROOT/scripts/lib/test-assertions.sh"
|
|
10
|
+
TA_PASS=0
|
|
11
|
+
TA_FAIL=0
|
|
11
12
|
TMPDIR_ROOT="$(mktemp -d)"
|
|
12
13
|
trap 'rm -rf "$TMPDIR_ROOT"' EXIT
|
|
13
14
|
|
|
14
|
-
pass() {
|
|
15
|
-
echo " PASS $1"
|
|
16
|
-
PASS=$((PASS + 1))
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
fail() {
|
|
20
|
-
echo " FAIL $1" >&2
|
|
21
|
-
FAIL=$((FAIL + 1))
|
|
22
|
-
}
|
|
23
|
-
|
|
24
15
|
assert_eq() {
|
|
25
16
|
local name="$1" expected="$2" actual="$3"
|
|
26
17
|
if [[ "$actual" == "$expected" ]]; then
|
|
27
|
-
|
|
18
|
+
ta_pass "$name"
|
|
28
19
|
else
|
|
29
|
-
|
|
20
|
+
ta_fail "$name — expected '$expected', got '$actual'"
|
|
30
21
|
fi
|
|
31
22
|
}
|
|
32
23
|
|
|
@@ -35,11 +26,11 @@ assert_fails_with() {
|
|
|
35
26
|
shift 2
|
|
36
27
|
local output
|
|
37
28
|
if output="$("$@" 2>&1)"; then
|
|
38
|
-
|
|
29
|
+
ta_fail "$name — command unexpectedly succeeded"
|
|
39
30
|
elif [[ "$output" == *"$expected"* ]]; then
|
|
40
|
-
|
|
31
|
+
ta_pass "$name"
|
|
41
32
|
else
|
|
42
|
-
|
|
33
|
+
ta_fail "$name — expected error containing '$expected', got '$output'"
|
|
43
34
|
fi
|
|
44
35
|
}
|
|
45
36
|
|
|
@@ -87,5 +78,5 @@ assert_fails_with "empty specs directory is rejected" \
|
|
|
87
78
|
"specs_dir must not be empty" bp_specs_dir "$EMPTY_PROJECT"
|
|
88
79
|
|
|
89
80
|
echo "---"
|
|
90
|
-
echo "test-project-runtime: $
|
|
91
|
-
[[ "$
|
|
81
|
+
echo "test-project-runtime: $TA_PASS passed, $TA_FAIL failed"
|
|
82
|
+
[[ "$TA_FAIL" -eq 0 ]]
|
|
@@ -9,29 +9,22 @@ WEBSITE="$REPO_ROOT/website"
|
|
|
9
9
|
SITE_BASE="/bigpowers"
|
|
10
10
|
ERRORS=0
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
echo "FAIL: $*"
|
|
14
|
-
ERRORS=$((ERRORS + 1))
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
pass() {
|
|
18
|
-
echo "ok : $*"
|
|
19
|
-
}
|
|
12
|
+
source "$REPO_ROOT/scripts/lib/test-assertions.sh"
|
|
20
13
|
|
|
21
14
|
ensure_website_deps() {
|
|
22
15
|
if [[ -d "$WEBSITE/node_modules/astro" ]]; then
|
|
23
16
|
return 0
|
|
24
17
|
fi
|
|
25
18
|
if ! command -v npm >/dev/null 2>&1; then
|
|
26
|
-
|
|
19
|
+
ta_err "npm unavailable and website/node_modules missing — cannot run build check"
|
|
27
20
|
return 1
|
|
28
21
|
fi
|
|
29
22
|
echo "info: installing website deps (golden-suite only installs repo root npm ci)"
|
|
30
23
|
if ! (cd "$WEBSITE" && npm ci >/dev/null 2>&1); then
|
|
31
|
-
|
|
24
|
+
ta_err "website npm ci failed — cannot run build check"
|
|
32
25
|
return 1
|
|
33
26
|
fi
|
|
34
|
-
|
|
27
|
+
ta_ok "website deps installed for build check"
|
|
35
28
|
}
|
|
36
29
|
|
|
37
30
|
INDEX_MDX="$WEBSITE/src/content/docs/index.mdx"
|
|
@@ -39,27 +32,27 @@ LLMS_TXT="$WEBSITE/public/llms.txt"
|
|
|
39
32
|
|
|
40
33
|
echo "--- [docs] generated landing hero link ---"
|
|
41
34
|
if [[ ! -f "$INDEX_MDX" ]]; then
|
|
42
|
-
|
|
35
|
+
ta_err "missing $INDEX_MDX — run cd website && npm run generate"
|
|
43
36
|
elif grep -q "link: ${SITE_BASE}/skills/" "$INDEX_MDX"; then
|
|
44
|
-
|
|
37
|
+
ta_ok "hero Browse skills link includes SITE_BASE"
|
|
45
38
|
else
|
|
46
|
-
|
|
39
|
+
ta_err "hero link missing ${SITE_BASE}/skills/ prefix in index.mdx"
|
|
47
40
|
fi
|
|
48
41
|
|
|
49
42
|
echo "--- [docs] no root-absolute skill links in landing page ---"
|
|
50
43
|
if [[ -f "$INDEX_MDX" ]] && grep -E '\]\(/skills/' "$INDEX_MDX" >/dev/null 2>&1; then
|
|
51
|
-
|
|
44
|
+
ta_err "index.mdx still contains root-absolute /skills/ links"
|
|
52
45
|
else
|
|
53
|
-
|
|
46
|
+
ta_ok "index.mdx skill links are base-prefixed or absent"
|
|
54
47
|
fi
|
|
55
48
|
|
|
56
49
|
echo "--- [docs] llms.txt URL shape ---"
|
|
57
50
|
if [[ ! -f "$LLMS_TXT" ]]; then
|
|
58
|
-
|
|
51
|
+
ta_err "missing $LLMS_TXT — run cd website && npm run generate"
|
|
59
52
|
elif grep -q '/bigpowers/bigpowers/' "$LLMS_TXT"; then
|
|
60
|
-
|
|
53
|
+
ta_err "llms.txt double-prefixes SITE_BASE in URLs"
|
|
61
54
|
else
|
|
62
|
-
|
|
55
|
+
ta_ok "llms.txt has no /bigpowers/bigpowers/ double prefix"
|
|
63
56
|
fi
|
|
64
57
|
|
|
65
58
|
echo "--- [docs] production build hero href ---"
|
|
@@ -69,12 +62,12 @@ elif ensure_website_deps; then
|
|
|
69
62
|
BUILD_ERR=$(mktemp)
|
|
70
63
|
if (cd "$WEBSITE" && npm run build >/dev/null 2>"$BUILD_ERR"); then
|
|
71
64
|
if grep -q 'href="/bigpowers/skills/"' "$WEBSITE/dist/index.html" 2>/dev/null; then
|
|
72
|
-
|
|
65
|
+
ta_ok "dist/index.html hero href includes /bigpowers/skills/"
|
|
73
66
|
else
|
|
74
|
-
|
|
67
|
+
ta_err "dist/index.html missing href=\"/bigpowers/skills/\" for Browse skills"
|
|
75
68
|
fi
|
|
76
69
|
else
|
|
77
|
-
|
|
70
|
+
ta_err "website build failed — $(head -3 "$BUILD_ERR" 2>/dev/null | tr '\n' ' ')"
|
|
78
71
|
fi
|
|
79
72
|
rm -f "$BUILD_ERR"
|
|
80
73
|
fi
|
|
@@ -9,59 +9,57 @@ cd "$REPO_ROOT"
|
|
|
9
9
|
|
|
10
10
|
ERRORS=0
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
echo "FAIL: $*"
|
|
14
|
-
ERRORS=$((ERRORS + 1))
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
pass() {
|
|
18
|
-
echo "ok : $*"
|
|
19
|
-
}
|
|
12
|
+
source "$REPO_ROOT/scripts/lib/test-assertions.sh"
|
|
20
13
|
|
|
21
14
|
MCP_JSON="$REPO_ROOT/.mcp.json"
|
|
22
15
|
echo "--- [MCP] shipped .mcp.json OMP compatibility ---"
|
|
23
16
|
if [[ ! -f "$MCP_JSON" ]]; then
|
|
24
|
-
|
|
17
|
+
ta_err ".mcp.json missing at repo root"
|
|
25
18
|
else
|
|
26
19
|
if grep -q 'workspaceFolder' "$MCP_JSON" 2>/dev/null; then
|
|
27
|
-
|
|
20
|
+
ta_err ".mcp.json uses workspaceFolder — not valid in OMP env expansion (#123)"
|
|
28
21
|
else
|
|
29
|
-
|
|
22
|
+
ta_ok ".mcp.json does not reference workspaceFolder"
|
|
30
23
|
fi
|
|
31
24
|
if grep -q 'OMP_PLUGIN_ROOT' "$MCP_JSON" 2>/dev/null; then
|
|
32
|
-
|
|
25
|
+
ta_ok ".mcp.json references OMP_PLUGIN_ROOT for plugin installs"
|
|
26
|
+
else
|
|
27
|
+
ta_err ".mcp.json must reference OMP_PLUGIN_ROOT for OMP plugin cwd/args"
|
|
28
|
+
fi
|
|
29
|
+
if grep -q '\${OMP_PLUGIN_ROOT}' "$MCP_JSON" 2>/dev/null; then
|
|
30
|
+
ta_err ".mcp.json uses \${OMP_PLUGIN_ROOT} without a :- default — hosts that do not set it spawn a literal path (#168)"
|
|
33
31
|
else
|
|
34
|
-
|
|
32
|
+
ta_ok ".mcp.json OMP_PLUGIN_ROOT references carry a :- default"
|
|
35
33
|
fi
|
|
36
34
|
fi
|
|
37
35
|
|
|
38
36
|
echo "--- [MCP] root runtime dependencies ---"
|
|
39
37
|
for dep in "@modelcontextprotocol/sdk" "unified" "remark-parse" "zod"; do
|
|
40
38
|
if node -e "const p=require('./package.json'); process.exit(p.dependencies?.['$dep'] ? 0 : 1)"; then
|
|
41
|
-
|
|
39
|
+
ta_ok "root dependencies include $dep"
|
|
42
40
|
else
|
|
43
|
-
|
|
41
|
+
ta_err "root dependencies missing $dep (bigpowers-mcp runtime)"
|
|
44
42
|
fi
|
|
45
43
|
done
|
|
46
44
|
|
|
47
45
|
echo "--- [MCP] built server entry ---"
|
|
48
46
|
if [[ -f "$REPO_ROOT/bigpowers-mcp/build/index.js" ]]; then
|
|
49
|
-
|
|
47
|
+
ta_ok "bigpowers-mcp/build/index.js exists"
|
|
50
48
|
else
|
|
51
|
-
|
|
49
|
+
ta_err "bigpowers-mcp/build/index.js missing — run cd bigpowers-mcp && npm run build"
|
|
52
50
|
fi
|
|
53
51
|
|
|
54
52
|
echo "--- [MCP] runtime load smoke (scripts/mcp-smoke.ts) ---"
|
|
55
53
|
if ! command -v node >/dev/null 2>&1; then
|
|
56
54
|
echo "warn: node unavailable — skipping runtime load smoke"
|
|
57
55
|
elif [[ ! -f "$REPO_ROOT/scripts/mcp-smoke.ts" ]]; then
|
|
58
|
-
|
|
56
|
+
ta_err "scripts/mcp-smoke.ts not found"
|
|
59
57
|
else
|
|
60
58
|
SMOKE_ERR=$(mktemp)
|
|
61
59
|
if node "$REPO_ROOT/scripts/mcp-smoke.ts" >/dev/null 2>"$SMOKE_ERR"; then
|
|
62
|
-
|
|
60
|
+
ta_ok "MCP server loads from simulated OMP plugin root"
|
|
63
61
|
else
|
|
64
|
-
|
|
62
|
+
ta_err "MCP smoke failed — $(head -3 "$SMOKE_ERR" 2>/dev/null | tr '\n' ' ')"
|
|
65
63
|
fi
|
|
66
64
|
rm -f "$SMOKE_ERR"
|
|
67
65
|
fi
|