mirror of
https://github.com/obra/superpowers.git
synced 2026-06-28 13:39:05 +08:00
Compare commits
1 Commits
codex/pri-
...
codex/pri-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3567c68388 |
@@ -206,22 +206,14 @@ const helperInjection = '<script>\n' + helperScript + '\n</script>';
|
|||||||
// ========== Helper Functions ==========
|
// ========== Helper Functions ==========
|
||||||
|
|
||||||
function readSuperpowersVersion() {
|
function readSuperpowersVersion() {
|
||||||
const root = path.join(__dirname, '../../..');
|
try {
|
||||||
const manifests = [
|
const packageJson = JSON.parse(
|
||||||
path.join(root, 'package.json'),
|
fs.readFileSync(path.join(__dirname, '../../..', 'package.json'), 'utf-8')
|
||||||
path.join(root, '.codex-plugin/plugin.json')
|
);
|
||||||
];
|
return String(packageJson.version || 'unknown');
|
||||||
|
} catch (e) {
|
||||||
for (const manifest of manifests) {
|
return 'unknown';
|
||||||
try {
|
|
||||||
const data = JSON.parse(fs.readFileSync(manifest, 'utf-8'));
|
|
||||||
if (data.version) return String(data.version);
|
|
||||||
} catch (e) {
|
|
||||||
// Packaged Codex plugins omit package.json; try the next manifest.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'unknown';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function isTruthyEnv(value) {
|
function isTruthyEnv(value) {
|
||||||
|
|||||||
@@ -4,8 +4,7 @@
|
|||||||
# through the controller's context.
|
# through the controller's context.
|
||||||
#
|
#
|
||||||
# Usage: task-brief PLAN_FILE TASK_NUMBER [OUTFILE]
|
# Usage: task-brief PLAN_FILE TASK_NUMBER [OUTFILE]
|
||||||
# Default OUTFILE: <git-dir>/sdd/task-<N>-brief.md — unique per repo
|
# Default OUTFILE: <git-dir>/sdd/task-<N>.<unique>/task-<N>-brief.md.
|
||||||
# instance, so concurrent sessions cannot collide.
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
if [ $# -lt 2 ] || [ $# -gt 3 ]; then
|
if [ $# -lt 2 ] || [ $# -gt 3 ]; then
|
||||||
@@ -23,7 +22,8 @@ else
|
|||||||
dir=$(git rev-parse --git-path sdd)
|
dir=$(git rev-parse --git-path sdd)
|
||||||
mkdir -p "$dir"
|
mkdir -p "$dir"
|
||||||
dir=$(cd "$dir" && pwd)
|
dir=$(cd "$dir" && pwd)
|
||||||
out="$dir/task-${n}-brief.md"
|
brief_dir=$(mktemp -d "$dir/task-${n}.XXXXXX")
|
||||||
|
out="$brief_dir/task-${n}-brief.md"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
awk -v n="$n" '
|
awk -v n="$n" '
|
||||||
|
|||||||
@@ -26,9 +26,9 @@ function sleep(ms) {
|
|||||||
return new Promise(resolve => setTimeout(resolve, ms));
|
return new Promise(resolve => setTimeout(resolve, ms));
|
||||||
}
|
}
|
||||||
|
|
||||||
function startServer({ port, dir, env = {}, serverPath = SERVER_PATH }) {
|
function startServer({ port, dir, env = {} }) {
|
||||||
cleanup(dir);
|
cleanup(dir);
|
||||||
return spawn('node', [serverPath], {
|
return spawn('node', [SERVER_PATH], {
|
||||||
env: {
|
env: {
|
||||||
...process.env,
|
...process.env,
|
||||||
BRAINSTORM_PORT: String(port),
|
BRAINSTORM_PORT: String(port),
|
||||||
@@ -74,21 +74,6 @@ function writeFragment(dir) {
|
|||||||
fs.writeFileSync(path.join(contentDir, 'screen.html'), '<h2>Pick a layout</h2>');
|
fs.writeFileSync(path.join(contentDir, 'screen.html'), '<h2>Pick a layout</h2>');
|
||||||
}
|
}
|
||||||
|
|
||||||
function createPackagedServerFixture(version) {
|
|
||||||
const root = fs.mkdtempSync(path.join('/tmp', 'superpowers-packaged-server-'));
|
|
||||||
const scriptDir = path.join(root, 'skills/brainstorming/scripts');
|
|
||||||
fs.cpSync(path.join(REPO_ROOT, 'skills/brainstorming/scripts'), scriptDir, { recursive: true });
|
|
||||||
fs.mkdirSync(path.join(root, '.codex-plugin'), { recursive: true });
|
|
||||||
fs.writeFileSync(
|
|
||||||
path.join(root, '.codex-plugin/plugin.json'),
|
|
||||||
JSON.stringify({ name: 'superpowers', version }, null, 2)
|
|
||||||
);
|
|
||||||
return {
|
|
||||||
root,
|
|
||||||
serverPath: path.join(scriptDir, 'server.cjs')
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async function withServer(options, fn) {
|
async function withServer(options, fn) {
|
||||||
const server = startServer(options);
|
const server = startServer(options);
|
||||||
try {
|
try {
|
||||||
@@ -119,13 +104,13 @@ async function test(name, fn) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function assertBrandedWithLogo(html, version = PACKAGE_VERSION) {
|
function assertBrandedWithLogo(html) {
|
||||||
assert(
|
assert(
|
||||||
html.includes(`Superpowers v${version}`),
|
html.includes(`Superpowers v${PACKAGE_VERSION}`),
|
||||||
'branding text should include dynamic package version'
|
'branding text should include dynamic package version'
|
||||||
);
|
);
|
||||||
assert(
|
assert(
|
||||||
!html.includes(`Superpowers v${version} by`),
|
!html.includes(`Superpowers v${PACKAGE_VERSION} by`),
|
||||||
'branding text should not include "by" when the logo is visible'
|
'branding text should not include "by" when the logo is visible'
|
||||||
);
|
);
|
||||||
assert(
|
assert(
|
||||||
@@ -154,15 +139,15 @@ function assertBrandedWithLogo(html, version = PACKAGE_VERSION) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function assertBrandedFallbackText(html, version = PACKAGE_VERSION) {
|
function assertBrandedFallbackText(html) {
|
||||||
assert(
|
assert(
|
||||||
html.includes(`Prime Radiant Superpowers v${version}`),
|
html.includes(`Prime Radiant Superpowers v${PACKAGE_VERSION}`),
|
||||||
'disabled telemetry should keep plain text Prime Radiant/Superpowers branding'
|
'disabled telemetry should keep plain text Prime Radiant/Superpowers branding'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function assertTelemetryImage(html, version = PACKAGE_VERSION) {
|
function assertTelemetryImage(html) {
|
||||||
const expectedUrl = `${ASSET_URL}?v=${encodeURIComponent(version)}`;
|
const expectedUrl = `${ASSET_URL}?v=${encodeURIComponent(PACKAGE_VERSION)}`;
|
||||||
assert(html.includes(`src="${expectedUrl}"`), 'remote image should use the dedicated main-domain asset with only v=');
|
assert(html.includes(`src="${expectedUrl}"`), 'remote image should use the dedicated main-domain asset with only v=');
|
||||||
assert(!html.includes('event='), 'remote image URL must not include event=');
|
assert(!html.includes('event='), 'remote image URL must not include event=');
|
||||||
assert(!html.includes('surface='), 'remote image URL must not include surface=');
|
assert(!html.includes('surface='), 'remote image URL must not include surface=');
|
||||||
@@ -270,26 +255,6 @@ async function main() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
await test('packaged Codex plugin reads version from .codex-plugin manifest', async () => {
|
|
||||||
const port = 3457;
|
|
||||||
const dir = '/tmp/brainstorm-branding-packaged-codex';
|
|
||||||
const packagedVersion = '7.8.9';
|
|
||||||
const fixture = createPackagedServerFixture(packagedVersion);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await withServer({ port, dir, serverPath: fixture.serverPath }, async () => {
|
|
||||||
writeFragment(dir);
|
|
||||||
await sleep(300);
|
|
||||||
const html = await fetchHtml(port);
|
|
||||||
assertBrandedWithLogo(html, packagedVersion);
|
|
||||||
assertTelemetryImage(html, packagedVersion);
|
|
||||||
assert(!html.includes('Superpowers vunknown'), 'packaged plugin should not fall back to unknown version');
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
cleanup(fixture.root);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
await test('SUPERPOWERS_DISABLE_TELEMETRY=true omits remote image but keeps local branding', async () => {
|
await test('SUPERPOWERS_DISABLE_TELEMETRY=true omits remote image but keeps local branding', async () => {
|
||||||
const port = 3453;
|
const port = 3453;
|
||||||
const dir = '/tmp/brainstorm-branding-disabled';
|
const dir = '/tmp/brainstorm-branding-disabled';
|
||||||
|
|||||||
117
tests/claude-code/test-task-brief.sh
Executable file
117
tests/claude-code/test-task-brief.sh
Executable file
@@ -0,0 +1,117 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||||
|
TASK_BRIEF="$REPO_ROOT/skills/subagent-driven-development/scripts/task-brief"
|
||||||
|
|
||||||
|
FAILURES=0
|
||||||
|
TEST_ROOT=""
|
||||||
|
|
||||||
|
pass() {
|
||||||
|
echo " [PASS] $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
fail() {
|
||||||
|
echo " [FAIL] $1"
|
||||||
|
FAILURES=$((FAILURES + 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
if [[ -n "$TEST_ROOT" && -d "$TEST_ROOT" ]]; then
|
||||||
|
rm -rf "$TEST_ROOT"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
extract_written_path() {
|
||||||
|
local output="$1"
|
||||||
|
printf '%s\n' "$output" | sed -n 's/^wrote \(.*\): [0-9][0-9]* lines$/\1/p'
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_not_equals() {
|
||||||
|
local actual="$1"
|
||||||
|
local expected="$2"
|
||||||
|
local description="$3"
|
||||||
|
|
||||||
|
if [[ "$actual" != "$expected" ]]; then
|
||||||
|
pass "$description"
|
||||||
|
else
|
||||||
|
fail "$description"
|
||||||
|
echo " both were: $actual"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_file_contains() {
|
||||||
|
local path="$1"
|
||||||
|
local needle="$2"
|
||||||
|
local description="$3"
|
||||||
|
|
||||||
|
if grep -Fq -- "$needle" "$path"; then
|
||||||
|
pass "$description"
|
||||||
|
else
|
||||||
|
fail "$description"
|
||||||
|
echo " expected $path to contain: $needle"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
echo "=== Test: task-brief output paths ==="
|
||||||
|
|
||||||
|
TEST_ROOT="$(mktemp -d)"
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
local repo="$TEST_ROOT/repo"
|
||||||
|
local plan="$repo/plan.md"
|
||||||
|
local output_one
|
||||||
|
local output_two
|
||||||
|
local path_one
|
||||||
|
local path_two
|
||||||
|
|
||||||
|
git init -q -b main "$repo"
|
||||||
|
|
||||||
|
cat > "$plan" <<'EOF'
|
||||||
|
# Implementation Plan
|
||||||
|
|
||||||
|
## Task 1: First thing
|
||||||
|
|
||||||
|
Do the first thing.
|
||||||
|
|
||||||
|
## Task 2: Second thing
|
||||||
|
|
||||||
|
Do the second thing.
|
||||||
|
EOF
|
||||||
|
|
||||||
|
output_one="$(cd "$repo" && "$TASK_BRIEF" "$plan" 1)"
|
||||||
|
output_two="$(cd "$repo" && "$TASK_BRIEF" "$plan" 1)"
|
||||||
|
path_one="$(extract_written_path "$output_one")"
|
||||||
|
path_two="$(extract_written_path "$output_two")"
|
||||||
|
|
||||||
|
assert_not_equals "$path_one" "$path_two" "Default task brief paths are unique per invocation"
|
||||||
|
assert_file_contains "$path_one" "## Task 1: First thing" "First default brief contains the requested task"
|
||||||
|
assert_file_contains "$path_two" "## Task 1: First thing" "Second default brief contains the requested task"
|
||||||
|
|
||||||
|
if [[ "$path_one" == "$repo/.git/sdd/"* ]]; then
|
||||||
|
pass "First default brief stays under the repo git metadata directory"
|
||||||
|
else
|
||||||
|
fail "First default brief stays under the repo git metadata directory"
|
||||||
|
echo " actual: $path_one"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$path_two" == "$repo/.git/sdd/"* ]]; then
|
||||||
|
pass "Second default brief stays under the repo git metadata directory"
|
||||||
|
else
|
||||||
|
fail "Second default brief stays under the repo git metadata directory"
|
||||||
|
echo " actual: $path_two"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $FAILURES -ne 0 ]]; then
|
||||||
|
echo ""
|
||||||
|
echo "FAILED: $FAILURES assertion(s) failed."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "PASS"
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
Reference in New Issue
Block a user