diff --git a/.github/workflows/auto-bump-version.yml b/.github/workflows/auto-bump-version.yml index 95e247b3..ea85221b 100644 --- a/.github/workflows/auto-bump-version.yml +++ b/.github/workflows/auto-bump-version.yml @@ -34,7 +34,7 @@ jobs: run: | set -euo pipefail BEFORE=$(node -e "console.log(require('./package.json').version)") - node scripts/bump-version.mjs + node --experimental-strip-types scripts/bump-version.ts AFTER=$(node -e "console.log(require('./package.json').version)") echo "before=$BEFORE" >> "$GITHUB_OUTPUT" echo "after=$AFTER" >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/release-content.yml b/.github/workflows/release-content.yml index ae8bdb11..2d9d3c4a 100644 --- a/.github/workflows/release-content.yml +++ b/.github/workflows/release-content.yml @@ -49,11 +49,11 @@ jobs: run: bun run validate:content - name: Build release bundles - run: node scripts/build-release-bundles.mjs --version "${{ steps.ver.outputs.version }}" + run: node --experimental-strip-types scripts/build-release-bundles.ts --version "${{ steps.ver.outputs.version }}" - name: Generate changelog run: | - node scripts/generate-changelog.mjs \ + node --experimental-strip-types scripts/generate-changelog.ts \ --version "${{ steps.ver.outputs.version }}" \ --out dist/release/CHANGELOG.md diff --git a/.github/workflows/skillspector.yml b/.github/workflows/skillspector.yml index 6e823e7e..f2765576 100644 --- a/.github/workflows/skillspector.yml +++ b/.github/workflows/skillspector.yml @@ -17,7 +17,7 @@ on: - "content/playbooks/**" - "content/souls/**" - "content/integrations/**" - - "scripts/scan-skillspector.mjs" + - "scripts/scan-skillspector.ts" - ".github/workflows/skillspector.yml" permissions: @@ -75,7 +75,7 @@ jobs: SKILLSPECTOR_LOG_LEVEL: ERROR run: | # Static analysis only — no LLM provider key needed in CI. - node scripts/scan-skillspector.mjs ${{ steps.changed.outputs.files }} \ + node --experimental-strip-types scripts/scan-skillspector.ts ${{ steps.changed.outputs.files }} \ --sarif skillspector.sarif - name: Upload SARIF to code scanning diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1b4d7fa1..0436a07b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,20 +15,20 @@ jobs: - run: npm ci - name: Plain node:test suites run: | - node --test \ - tests/adversarial-harness.test.mjs \ - tests/trust.test.mjs \ - tests/release-signing.test.mjs \ - tests/cli-install.test.mjs + node --experimental-strip-types --test \ + tests/adversarial-harness.test.ts \ + tests/trust.test.ts \ + tests/release-signing.test.ts \ + tests/cli-install.test.ts - name: TypeScript-source node:test suites run: | node --experimental-strip-types --test \ - tests/prompt-injection-guard.test.mjs \ - tests/runtime.test.mjs \ - tests/integrations.test.mjs \ - tests/growth-revenue-split.test.mjs \ - tests/trust-badge.test.mjs \ - tests/bounties.test.mjs \ - tests/trust-scoring.test.mjs \ - tests/adversarial-holdout.test.mjs \ - tests/adversarial-judge.test.mjs + tests/prompt-injection-guard.test.ts \ + tests/runtime.test.ts \ + tests/integrations.test.ts \ + tests/growth-revenue-split.test.ts \ + tests/trust-badge.test.ts \ + tests/bounties.test.ts \ + tests/trust-scoring.test.ts \ + tests/adversarial-holdout.test.ts \ + tests/adversarial-judge.test.ts diff --git a/.github/workflows/validate-content.yml b/.github/workflows/validate-content.yml index 9a8f5300..d3a35c5d 100644 --- a/.github/workflows/validate-content.yml +++ b/.github/workflows/validate-content.yml @@ -4,20 +4,20 @@ on: pull_request: paths: - "content/**" - - "scripts/validate-content.mjs" - - "scripts/audit-skills.mjs" - - "scripts/build-skills-sh-mirror.mjs" - - "scripts/build-agent-plugin.mjs" + - "scripts/validate-content.ts" + - "scripts/audit-skills.ts" + - "scripts/build-skills-sh-mirror.ts" + - "scripts/build-agent-plugin.ts" - "src/lib/security/prompt-injection-guard.ts" - ".github/workflows/validate-content.yml" push: branches: [main] paths: - "content/**" - - "scripts/validate-content.mjs" - - "scripts/audit-skills.mjs" - - "scripts/build-skills-sh-mirror.mjs" - - "scripts/build-agent-plugin.mjs" + - "scripts/validate-content.ts" + - "scripts/audit-skills.ts" + - "scripts/build-skills-sh-mirror.ts" + - "scripts/build-agent-plugin.ts" - "src/lib/security/prompt-injection-guard.ts" jobs: diff --git a/ANALYSIS.md b/ANALYSIS.md new file mode 100644 index 00000000..f040f04e --- /dev/null +++ b/ANALYSIS.md @@ -0,0 +1,50 @@ +# Super Agent Skill: Architecture Analysis & Evolution Roadmap + +## 1. Current Architecture Overview + +Super Agent Skill is a platform for distributing AI agent capabilities (skills, playbooks, souls, guardrails) with a strong emphasis on security, adversarial robustness, and trust scoring. + +### Core Stack +- **Frontend & API:** TanStack Start, React 19, Tailwind CSS v4, Radix UI. It acts as both the marketing site, the user dashboard, and hosts the public/private APIs. +- **Database:** Supabase (PostgreSQL) handling data storage, Row Level Security (RLS), and full-text search (`search_vector` with `tsvector`). +- **Distribution:** + - **MCP (Model Context Protocol):** Provides a standard API for AI assistants to discover and install packages. + - **CLI (`packages/cli`):** A Node.js CLI tool (`super-agent.mjs`) to install skills directly. + - **File downloads:** YAML manifests directly available. +- **Content:** The `content/` directory contains the "open seed" of YAML files describing skills. +- **Evaluation Engine (SkillForge):** Custom adversarial harness scripts (in `scripts/`) to test skills against prompt injection, data exfiltration, policy bypass, etc. +- **Tests:** A custom plain Node test runner (`node --test`) testing both adversarial scripts and site functionality. + +### Strengths +1. **Security-First Approach:** Deep integration of adversarial testing (injection, role hijack, data leaks). +2. **Trust Architecture:** Cryptographic signing of packages and attestations, providing verifiable offline trust. +3. **Distribution Agnostic:** Support for MCP, CLI, and raw files gives great flexibility to end-users. +4. **Modern Stack:** TanStack Start and React 19 provide a solid, fast SSR foundation. + +--- + +## 2. Identified Areas for Improvement & Evolution + +### A. Codebase & Tooling Standardization +- **Script Migration:** The `scripts/` directory contains a mix of `.mjs` scripts (e.g., `audit-skills.mjs`, `eval-adversarial.mjs`). Migrating these to TypeScript would improve type safety, maintainability, and alignment with the main app code. +- **Monorepo Structure:** The `packages/cli` is partially separated but still relies on root dependencies. Formalizing a monorepo structure (e.g., using Turborepo or npm workspaces) for `web`, `cli`, and `core` packages would improve dependency management. +- **Test Framework:** The current Node.js built-in test runner (`node --test`) works, but migrating to Vitest would provide better integration with Vite/TanStack Start, better mocking, and UI for tests. + +### B. Security & Integrity Enhancements +- **End-to-End Test Automation:** Implement Playwright/Cypress tests for critical user flows (OAuth login, package upload, CLI interaction). +- **Automated Signature Rotation:** Implement automated key rotation and revocation lists for package signatures. +- **Vector Search Tuning:** The `tsvector` search is currently basic. Integrating pgvector for semantic search over skill descriptions would significantly improve the discovery experience in the marketplace. + +### C. Developer & Author Experience (SkillForge) +- **CLI Expansion:** Implement the planned `sas init`, `sas publish`, and `sas eval` commands in the CLI to allow authors to build and test locally before uploading. +- **Versioned Bundles:** Build out the versioned downloadable registry bundles mentioned in the roadmap. +- **Curated Collections:** Implement the UI and data model for community-curated bundles around specific use cases. + +### D. Architecture Scalability +- **Evaluation Queueing:** The adversarial evaluation (`eval-adversarial.mjs`) seems to run synchronously or in basic scripts. As the registry grows, moving this to a robust background job queue (e.g., BullMQ or temporal.io) is critical. +- **Caching Layer:** The API and SSR currently rely on Cloudflare/Vite caches. Introducing Redis for aggressive caching of trust scores and package metadata could reduce DB load. + +## 3. Recommended Next Steps (Short-term) +1. **Implement `pgvector` for Semantic Search:** Enhance the marketplace discovery. +2. **Migrate `.mjs` Scripts to TypeScript:** Reduce technical debt in the build and eval pipelines. +3. **Flesh out the CLI:** Add `init` and `publish` commands for developers. diff --git a/cli/package.json b/cli/package.json index a8c157d4..85699565 100644 --- a/cli/package.json +++ b/cli/package.json @@ -4,19 +4,20 @@ "description": "Plug-and-play Super Agent Skill MCP: OAuth login, client auto-config, local stdio bridge, and skill package install for Claude / Cursor / Codex / VS Code / Windsurf.", "type": "module", "bin": { - "super-agent": "./super-agent.mjs" + "super-agent": "./super-agent.js" }, "files": [ - "super-agent.mjs", + "super-agent.js", "README.md" ], "scripts": { + "prebuild:bin": "bun build ./super-agent.ts --target=node --outfile=super-agent.js", "build:bin": "npx -y @yao-pkg/pkg . --targets node20-linux-x64,node20-macos-x64,node20-macos-arm64,node20-win-x64 --out-path dist", - "build:bun": "bun build ./super-agent.mjs --compile --outfile dist/super-agent" + "build:bun": "bun build ./super-agent.ts --compile --outfile dist/super-agent" }, "pkg": { "scripts": [ - "super-agent.mjs" + "super-agent.js" ], "outputPath": "dist" }, diff --git a/cli/super-agent.js b/cli/super-agent.js new file mode 100755 index 00000000..bbd9bd20 --- /dev/null +++ b/cli/super-agent.js @@ -0,0 +1,641 @@ +#!/usr/bin/env -S node + +// super-agent.ts +import { mkdirSync, writeFileSync, existsSync, readFileSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { homedir, platform } from "node:os"; +import { createHash, randomBytes } from "node:crypto"; +import { createServer } from "node:http"; +import { spawn } from "node:child_process"; +import { createInterface } from "node:readline"; +var REGISTRY = (process.env.SUPER_AGENT_REGISTRY ?? "https://superagentskill.com").replace(/\/+$/, ""); +var TELEMETRY = process.env.SUPER_AGENT_TELEMETRY !== "0"; +var MCP_ENDPOINT = `${REGISTRY}/api/mcp`; +var CRED_DIR = join(homedir(), ".superagentskill"); +var CRED_FILE = join(CRED_DIR, "credentials.json"); +var LONG_RUNNING = new Set(["connect", "login", "mcp"]); +var [cmd, ...rest] = process.argv.slice(2); +if (!cmd || cmd === "--help" || cmd === "-h") { + printHelp(); + process.exit(0); +} +try { + if (cmd === "install") + await cmdInstall(rest); + else if (cmd === "list") + await cmdList(rest); + else if (cmd === "search") + await cmdSearch(rest); + else if (cmd === "info") + await cmdInfo(rest); + else if (cmd === "init") + await cmdInit(rest); + else if (cmd === "publish") + await cmdPublish(rest); + else if (cmd === "connect") + await cmdConnect(rest); + else if (cmd === "login") + await cmdLogin(); + else if (cmd === "status") + await cmdStatus(); + else if (cmd === "logout") + await cmdLogout(); + else if (cmd === "setup") + await cmdSetup(rest); + else if (cmd === "mcp") + await cmdMcpBridge(); + else { + console.error(`unknown command: ${cmd}`); + printHelp(); + process.exit(1); + } +} catch (e) { + console.error(`✗ ${e.message}`); + process.exit(2); +} +if (!LONG_RUNNING.has(cmd)) + setImmediate(() => process.exit(0)); +function printHelp() { + console.log(`super-agent — install AI agent skills + plug-and-play MCP + +Skill install & creation: + install [--target claude|cursor|continue|cline|all] default: all + list [--query ] + search + info + init Create a new skill template in the current directory + publish Publish a skill to the registry (requires login) + +MCP connection (plug and play): + connect [--client ] OAuth login + auto-wire a client in one step + login OAuth login only (browser, loopback PKCE) + status show login state / token expiry + logout revoke and forget the stored token + setup write/patch the MCP config for a client + mcp run a local stdio <-> remote HTTP MCP bridge + + clients: claude-code | claude | cursor | codex | vscode | windsurf + +Environment: + SUPER_AGENT_REGISTRY override registry origin (default https://superagentskill.com) + SUPER_AGENT_TELEMETRY set to 0 to disable anonymized install telemetry +`); +} +function parseFlags(args) { + const positional = []; + const flags = {}; + for (let i = 0;i < args.length; i++) { + const a = args[i]; + if (a.startsWith("--")) { + const n = args[i + 1]; + if (!n || n.startsWith("--")) + flags[a.slice(2)] = true; + else { + flags[a.slice(2)] = n; + i++; + } + } else + positional.push(a); + } + return { positional, flags }; +} +async function getJson(path) { + const res = await fetch(`${REGISTRY}${path}`, { headers: { accept: "application/json" } }); + if (!res.ok) + throw new Error(`HTTP ${res.status} from ${path}`); + return res.json(); +} +async function getText(path) { + const res = await fetch(`${REGISTRY}${path}`); + if (!res.ok) + throw new Error(`HTTP ${res.status} from ${path}`); + return res.text(); +} +async function cmdInstall(args) { + const { positional, flags } = parseFlags(args); + const slug = positional[0]; + if (!slug) + throw new Error("install requires "); + const target = (flags.target ?? "all").toLowerCase(); + console.log(`→ fetching ${slug} from ${REGISTRY}`); + const info = await getJson(`/api/public/packages/${slug}`); + const skillMd = await getText(`/api/skills/${slug}/export.md`).catch(async () => synthesizeSkillMd(info)); + const targets = target === "all" ? ["claude", "cursor", "continue", "cline"] : [target]; + for (const t of targets) + writeForTarget(t, slug, skillMd); + reportTelemetry({ package_slug: slug, runtime: "cli", success: true }); + console.log(` +✓ installed ${slug} for: ${targets.join(", ")}`); + console.log(` trust score: ${REGISTRY}/api/badges/trust/${slug}.svg`); + console.log(` package: ${REGISTRY}/packs/${slug}`); +} +function writeForTarget(target, slug, skillMd) { + const map = { + claude: `.claude/skills/${slug}/SKILL.md`, + cursor: `.cursor/rules/${slug}.mdc`, + continue: `.continue/skills/${slug}.md`, + cline: `.cline/skills/${slug}.md` + }; + const path = map[target]; + if (!path) { + console.warn(` ! unknown target: ${target}`); + return; + } + mkdirSync(dirname(path), { recursive: true }); + writeFileSync(path, skillMd); + console.log(` + ${path}`); +} +async function cmdList(args) { + const { flags } = parseFlags(args); + const q = flags.query ?? ""; + const list = await getJson(`/api/public/packages?type=skill${q ? `&q=${encodeURIComponent(q)}` : ""}`); + for (const p of list.items ?? list) + console.log(`${p.slug.padEnd(36)} ${p.name ?? ""}`); +} +async function cmdSearch(args) { + if (!args[0]) + throw new Error("search requires a query"); + const res = await getJson(`/api/public/search?q=${encodeURIComponent(args.join(" "))}`); + for (const r of res.results ?? []) + console.log(`${r.type.padEnd(10)} ${r.slug.padEnd(32)} ${r.score?.toFixed?.(2) ?? ""} ${r.snippet ?? ""}`); +} +async function cmdInfo(args) { + const slug = args[0]; + if (!slug) + throw new Error("info requires "); + console.log(JSON.stringify(await getJson(`/api/public/packages/${slug}`), null, 2)); +} +async function cmdInit(args) { + const slug = args[0]; + if (!slug) + throw new Error("init requires "); + const file = `${slug}.yaml`; + if (existsSync(file)) + throw new Error(`${file} already exists`); + const template = `id: ${slug} +name: ${slug.replace(/-/g, " ")} +type: skill +version: 0.1.0 +schema_version: v1 +description: A short description of this skill +author: + name: Your Name + contact: "@yourhandle" +when_to_use: Use this skill when... +system_prompt: | + You are an expert... +contract: + expected_output: "A well-formatted response." + must: + - "Follow the instructions carefully." + must_not: + - "Hallucinate facts." +examples: + - title: Basic usage + input: "Do the thing" + expected_output: "I did the thing." +`; + writeFileSync(file, template); + console.log(`✓ Created ${file}`); +} +async function cmdPublish(args) { + const slug = args[0]; + if (!slug) + throw new Error("publish requires "); + const creds = await ensureToken(); + if (!creds?.access_token) + throw new Error("publish requires login. Run `super-agent login` first."); + const file = `${slug}.yaml`; + if (!existsSync(file)) + throw new Error(`Could not find ${file} in current directory.`); + console.log(`→ Publishing ${slug}... (not fully implemented in backend yet, scaffold only)`); + console.log(`✓ Publish scaffold reached. Ensure backend API is ready to accept POST /api/public/packages`); +} +function synthesizeSkillMd(info) { + return [ + `# ${info.name ?? info.slug}`, + ``, + info.description ?? "", + ``, + `## When to use`, + info.trigger ?? info.when_to_use ?? "(see package page)", + ``, + `## System prompt`, + info.system_prompt ?? "(not exposed via this endpoint)", + ``, + `---`, + `Installed from ${REGISTRY}/packs/${info.slug}` + ].join(` +`); +} +function reportTelemetry(event) { + if (!TELEMETRY) + return; + const body = JSON.stringify({ + latency_ms: 0, + workspace_id: hashCwd(), + runtime: "cli", + ...event + }); + fetch(`${REGISTRY}/api/public/telemetry`, { + method: "POST", + headers: { "content-type": "application/json" }, + body + }).catch(() => {}); +} +function hashCwd() { + try { + const cwd = process.cwd(); + const pkg = existsSync("package.json") ? JSON.parse(readFileSync("package.json", "utf8"))?.name : ""; + return `${pkg || "anon"}:${cwd.split("/").slice(-2).join("/")}`; + } catch { + return "anon"; + } +} +function b64url(buf) { + return buf.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); +} +function loadCreds() { + try { + return JSON.parse(readFileSync(CRED_FILE, "utf8")); + } catch { + return null; + } +} +function saveCreds(creds) { + mkdirSync(CRED_DIR, { recursive: true }); + writeFileSync(CRED_FILE, JSON.stringify(creds, null, 2), { mode: 384 }); +} +function openBrowser(url) { + const cmds = platform() === "darwin" ? ["open", [url]] : platform() === "win32" ? ["cmd", ["/c", "start", "", url]] : ["xdg-open", [url]]; + try { + const p = spawn(cmds[0], cmds[1], { stdio: "ignore", detached: true }); + p.on("error", () => {}); + p.unref(); + } catch {} +} +async function discover() { + const fallback = { + authorization_endpoint: `${REGISTRY}/oauth/authorize`, + token_endpoint: `${REGISTRY}/api/public/oauth/token`, + registration_endpoint: `${REGISTRY}/api/public/oauth/register`, + revocation_endpoint: `${REGISTRY}/api/public/oauth/revoke` + }; + try { + const pr = await getJson(`/.well-known/oauth-protected-resource/api/mcp`).catch(() => getJson(`/.well-known/oauth-protected-resource`)); + const as = (pr.authorization_servers?.[0] ?? REGISTRY).replace(/\/+$/, ""); + const meta = await fetch(`${as}/.well-known/oauth-authorization-server`, { + headers: { accept: "application/json" } + }).then((r) => r.ok ? r.json() : null); + return meta ?? fallback; + } catch { + return fallback; + } +} +function startCallbackServer() { + return new Promise((resolve) => { + const server = createServer(); + server.listen(0, "127.0.0.1", () => { + const port = server.address().port; + const redirectUri = `http://127.0.0.1:${port}/callback`; + const codePromise = new Promise((res, rej) => { + server.on("request", (req, response) => { + const u = new URL(req.url, redirectUri); + if (u.pathname !== "/callback") { + response.writeHead(404).end(); + return; + } + const err = u.searchParams.get("error"); + const code = u.searchParams.get("code"); + const state = u.searchParams.get("state"); + response.writeHead(200, { "content-type": "text/html" }); + response.end(`Super Agent Skill` + `` + (err ? `

Authorization failed

${err}

` : `

✓ Connected

You can close this tab and return to your terminal.

`) + ``); + server.close(); + if (err) + rej(new Error(`authorization denied: ${err}`)); + else + res({ code, state }); + }); + }); + resolve({ redirectUri, codePromise }); + }); + }); +} +async function oauthLogin() { + const meta = await discover(); + const { redirectUri, codePromise } = await startCallbackServer(); + const reg = await fetch(meta.registration_endpoint, { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ + client_name: "Super Agent CLI", + redirect_uris: [redirectUri], + grant_types: ["authorization_code", "refresh_token"], + response_types: ["code"], + token_endpoint_auth_method: "none", + software_id: "super-agent-cli" + }) + }); + if (!reg.ok) + throw new Error(`client registration failed: HTTP ${reg.status} ${await reg.text()}`); + const client = await reg.json(); + if (!client.client_id) + throw new Error("registration response missing client_id"); + const verifier = b64url(randomBytes(32)); + const challenge = b64url(createHash("sha256").update(verifier).digest()); + const state = b64url(randomBytes(16)); + const authUrl = new URL(meta.authorization_endpoint); + authUrl.searchParams.set("response_type", "code"); + authUrl.searchParams.set("client_id", client.client_id); + authUrl.searchParams.set("redirect_uri", redirectUri); + authUrl.searchParams.set("scope", "mcp:read mcp:write"); + authUrl.searchParams.set("state", state); + authUrl.searchParams.set("code_challenge", challenge); + authUrl.searchParams.set("code_challenge_method", "S256"); + authUrl.searchParams.set("resource", MCP_ENDPOINT); + console.log(` +→ Opening your browser to authorize Super Agent Skill…`); + console.log(` If it doesn't open, paste this URL: + ${authUrl} +`); + openBrowser(authUrl.toString()); + const { code, state: returnedState } = await codePromise; + if (returnedState !== state) + throw new Error("state mismatch — aborting (possible CSRF)"); + const tok = await fetch(meta.token_endpoint, { + method: "POST", + headers: { "content-type": "application/x-www-form-urlencoded" }, + body: new URLSearchParams({ + grant_type: "authorization_code", + code, + redirect_uri: redirectUri, + client_id: client.client_id, + code_verifier: verifier + }) + }); + if (!tok.ok) + throw new Error(`token exchange failed: HTTP ${tok.status} ${await tok.text()}`); + const t = await tok.json(); + const creds = { + client_id: client.client_id, + access_token: t.access_token, + refresh_token: t.refresh_token ?? null, + scope: t.scope ?? "mcp:read mcp:write", + expires_at: Date.now() + (t.expires_in ?? 3600) * 1000, + token_endpoint: meta.token_endpoint, + revocation_endpoint: meta.revocation_endpoint + }; + saveCreds(creds); + return creds; +} +async function ensureToken() { + let creds = loadCreds(); + if (!creds) + throw new Error('not logged in — run "super-agent login" first'); + if (Date.now() < creds.expires_at - 60000) + return creds; + if (!creds.refresh_token) + return creds; + const res = await fetch(creds.token_endpoint ?? `${REGISTRY}/api/public/oauth/token`, { + method: "POST", + headers: { "content-type": "application/x-www-form-urlencoded" }, + body: new URLSearchParams({ + grant_type: "refresh_token", + refresh_token: creds.refresh_token, + client_id: creds.client_id + }) + }); + if (!res.ok) + return creds; + const t = await res.json(); + creds = { + ...creds, + access_token: t.access_token, + refresh_token: t.refresh_token ?? creds.refresh_token, + expires_at: Date.now() + (t.expires_in ?? 3600) * 1000 + }; + saveCreds(creds); + return creds; +} +async function cmdLogin() { + const creds = await oauthLogin(); + console.log(` +✓ Logged in. Token saved to ${CRED_FILE}`); + console.log(` scope: ${creds.scope}`); + process.exit(0); +} +async function cmdStatus() { + const creds = loadCreds(); + if (!creds) { + console.log("Not logged in. Run: super-agent login"); + return; + } + const expired = Date.now() >= creds.expires_at; + console.log(`Logged in (${REGISTRY})`); + console.log(` scope: ${creds.scope}`); + console.log(` access: ${expired ? "expired" : "valid"}${creds.refresh_token ? " (auto-refresh on)" : ""}`); + console.log(` expires_at: ${new Date(creds.expires_at).toISOString()}`); + console.log(` file: ${CRED_FILE}`); +} +async function cmdLogout() { + const creds = loadCreds(); + if (!creds) { + console.log("Already logged out."); + return; + } + try { + await fetch(creds.revocation_endpoint ?? `${REGISTRY}/api/public/oauth/revoke`, { + method: "POST", + headers: { "content-type": "application/x-www-form-urlencoded" }, + body: new URLSearchParams({ token: creds.refresh_token ?? creds.access_token }) + }); + } catch {} + try { + writeFileSync(CRED_FILE, "{}", { mode: 384 }); + } catch {} + console.log("✓ Logged out and token revoked."); +} +async function cmdConnect(args) { + const { flags } = parseFlags(args); + console.log(`Super Agent Skill — connecting (${REGISTRY})`); + if (!loadCreds()?.access_token || Date.now() >= (loadCreds()?.expires_at ?? 0)) { + await oauthLogin(); + console.log(`✓ OAuth complete.`); + } else { + console.log(`✓ Reusing existing login (super-agent status to inspect).`); + } + const client = (flags.client ?? "claude-code").toLowerCase(); + await writeClientConfig(client); + console.log(` +Done. Restart ${client} and the Super Agent Skill tools (incl. write tools) will be available.`); + process.exit(0); +} +function patchJsonFile(file, mutate) { + mkdirSync(dirname(file), { recursive: true }); + let json = {}; + if (existsSync(file)) { + try { + json = JSON.parse(readFileSync(file, "utf8") || "{}"); + } catch { + throw new Error(`existing ${file} is not valid JSON — fix or remove it first`); + } + } + mutate(json); + writeFileSync(file, JSON.stringify(json, null, 2)); + console.log(` + ${file}`); +} +async function writeClientConfig(client) { + const creds = loadCreds(); + const bearer = creds?.access_token; + const home = homedir(); + const httpEntry = { + url: MCP_ENDPOINT, + ...bearer ? { headers: { Authorization: `Bearer ${bearer}` } } : {} + }; + switch (client) { + case "claude-code": { + console.log(` +Run this once (Claude Code handles OAuth natively): +`); + console.log(` claude mcp add --transport http super-agent-skill ${MCP_ENDPOINT} +`); + console.log(`If your Claude Code build can't do MCP OAuth, use the local bridge instead: +`); + console.log(` claude mcp add super-agent-skill -- npx -y super-agent mcp +` + ` (the bridge injects your saved token automatically) +`); + return; + } + case "claude": { + const file = platform() === "darwin" ? join(home, "Library/Application Support/Claude/claude_desktop_config.json") : platform() === "win32" ? join(process.env.APPDATA ?? join(home, "AppData/Roaming"), "Claude/claude_desktop_config.json") : join(home, ".config/Claude/claude_desktop_config.json"); + patchJsonFile(file, (j) => { + j.mcpServers = j.mcpServers ?? {}; + j.mcpServers["super-agent-skill"] = { command: "npx", args: ["-y", "super-agent", "mcp"] }; + }); + return; + } + case "cursor": { + patchJsonFile(join(process.cwd(), ".cursor/mcp.json"), (j) => { + j.mcpServers = j.mcpServers ?? {}; + j.mcpServers["super-agent-skill"] = httpEntry; + }); + return; + } + case "vscode": { + patchJsonFile(join(home, ".config/Code/User/settings.json"), (j) => { + j.mcp = j.mcp ?? {}; + j.mcp.servers = j.mcp.servers ?? {}; + j.mcp.servers["super-agent-skill"] = { type: "http", ...httpEntry }; + }); + return; + } + case "windsurf": { + patchJsonFile(join(home, ".codeium/windsurf/mcp_config.json"), (j) => { + j.mcpServers = j.mcpServers ?? {}; + j.mcpServers["super-agent-skill"] = { + serverUrl: MCP_ENDPOINT, + ...bearer ? { headers: { Authorization: `Bearer ${bearer}` } } : {} + }; + }); + return; + } + case "codex": { + const file = join(home, ".codex", "config.toml"); + mkdirSync(dirname(file), { recursive: true }); + const block = ` +[mcp_servers.super-agent-skill] +` + `command = "npx" +` + `args = ["-y", "super-agent", "mcp"] +`; + const prev = existsSync(file) ? readFileSync(file, "utf8") : ""; + if (prev.includes("[mcp_servers.super-agent-skill]")) { + console.log(` = ${file} already has super-agent-skill (left as-is)`); + } else { + writeFileSync(file, prev + block); + console.log(` + ${file}`); + } + return; + } + default: + throw new Error(`unknown client "${client}". Use: claude-code | claude | cursor | codex | vscode | windsurf`); + } +} +async function cmdSetup(args) { + const client = (args[0] ?? "").toLowerCase(); + if (!client) + throw new Error("setup requires (claude-code|claude|cursor|codex|vscode|windsurf)"); + if (!loadCreds()?.access_token) + console.log('! not logged in — run "super-agent login" first for write-tool access.'); + await writeClientConfig(client); +} +async function cmdMcpBridge() { + let sessionId = null; + const rl = createInterface({ input: process.stdin }); + for await (const line of rl) { + const trimmed = line.trim(); + if (!trimmed) + continue; + let creds; + try { + creds = await ensureToken(); + } catch { + creds = null; + } + const headers = { + "content-type": "application/json", + accept: "application/json, text/event-stream" + }; + if (creds?.access_token) + headers.authorization = `Bearer ${creds.access_token}`; + if (sessionId) + headers["mcp-session-id"] = sessionId; + let res; + try { + res = await fetch(MCP_ENDPOINT, { method: "POST", headers, body: trimmed }); + } catch (e) { + emit({ jsonrpc: "2.0", id: safeId(trimmed), error: { code: -32000, message: `bridge fetch failed: ${e.message}` } }); + continue; + } + const sid = res.headers.get("mcp-session-id"); + if (sid) + sessionId = sid; + if (res.status === 401) { + emit({ + jsonrpc: "2.0", + id: safeId(trimmed), + error: { code: -32001, message: "unauthorized — run `super-agent login` to enable write tools" } + }); + continue; + } + const ct = res.headers.get("content-type") ?? ""; + if (ct.includes("text/event-stream")) { + const text = await res.text(); + for (const chunk of text.split(/\n\n/)) { + const dataLines = chunk.split(` +`).filter((l) => l.startsWith("data:")).map((l) => l.slice(5).trim()); + if (!dataLines.length) + continue; + const payload = dataLines.join(""); + if (payload && payload !== "[DONE]") + process.stdout.write(payload + ` +`); + } + } else { + const text = (await res.text()).trim(); + if (text) + process.stdout.write(text + ` +`); + } + } +} +function emit(obj) { + process.stdout.write(JSON.stringify(obj) + ` +`); +} +function safeId(line) { + try { + return JSON.parse(line).id ?? null; + } catch { + return null; + } +} diff --git a/cli/super-agent.mjs b/cli/super-agent.ts old mode 100644 new mode 100755 similarity index 92% rename from cli/super-agent.mjs rename to cli/super-agent.ts index a1147ad5..d12bfc78 --- a/cli/super-agent.mjs +++ b/cli/super-agent.ts @@ -1,4 +1,4 @@ -#!/usr/bin/env node +#!/usr/bin/env -S node --experimental-strip-types // Super Agent Skill CLI — install skills locally AND get a fully plug-and-play // MCP connection (OAuth login, config writing, and a local stdio bridge). // @@ -7,6 +7,8 @@ // npx super-agent list [--query ] // npx super-agent search // npx super-agent info +// npx super-agent init # Create a new skill template locally +// npx super-agent publish # Publish a local skill // // npx super-agent connect [--client claude-code|claude|cursor|codex|vscode|windsurf] // npx super-agent login # OAuth (browser, loopback PKCE) only @@ -46,6 +48,8 @@ try { else if (cmd === "list") await cmdList(rest); else if (cmd === "search") await cmdSearch(rest); else if (cmd === "info") await cmdInfo(rest); + else if (cmd === "init") await cmdInit(rest); + else if (cmd === "publish") await cmdPublish(rest); else if (cmd === "connect") await cmdConnect(rest); else if (cmd === "login") await cmdLogin(); else if (cmd === "status") await cmdStatus(); @@ -69,11 +73,13 @@ if (!LONG_RUNNING.has(cmd)) setImmediate(() => process.exit(0)); function printHelp() { console.log(`super-agent — install AI agent skills + plug-and-play MCP -Skill install: +Skill install & creation: install [--target claude|cursor|continue|cline|all] default: all list [--query ] search info + init Create a new skill template in the current directory + publish Publish a skill to the registry (requires login) MCP connection (plug and play): connect [--client ] OAuth login + auto-wire a client in one step @@ -178,6 +184,54 @@ async function cmdInfo(args) { console.log(JSON.stringify(await getJson(`/api/public/packages/${slug}`), null, 2)); } +async function cmdInit(args) { + const slug = args[0]; + if (!slug) throw new Error("init requires "); + const file = `${slug}.yaml`; + if (existsSync(file)) throw new Error(`${file} already exists`); + + const template = `id: ${slug} +name: ${slug.replace(/-/g, " ")} +type: skill +version: 0.1.0 +schema_version: v1 +description: A short description of this skill +author: + name: Your Name + contact: "@yourhandle" +when_to_use: Use this skill when... +system_prompt: | + You are an expert... +contract: + expected_output: "A well-formatted response." + must: + - "Follow the instructions carefully." + must_not: + - "Hallucinate facts." +examples: + - title: Basic usage + input: "Do the thing" + expected_output: "I did the thing." +`; + + writeFileSync(file, template); + console.log(`✓ Created ${file}`); +} + +async function cmdPublish(args) { + const slug = args[0]; + if (!slug) throw new Error("publish requires "); + const creds = await ensureToken(); + if (!creds?.access_token) throw new Error("publish requires login. Run `super-agent login` first."); + + const file = `${slug}.yaml`; + if (!existsSync(file)) throw new Error(`Could not find ${file} in current directory.`); + + console.log(`→ Publishing ${slug}... (not fully implemented in backend yet, scaffold only)`); + // Scaffold: this would read the yaml and POST to the registry API using creds.access_token + console.log(`✓ Publish scaffold reached. Ensure backend API is ready to accept POST /api/public/packages`); +} + function synthesizeSkillMd(info) { return [ `# ${info.name ?? info.slug}`, diff --git a/package-lock.json b/package-lock.json index ac18a042..cf1e4afd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "tanstack_start_ts", - "version": "0.1.55", + "version": "0.4.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "tanstack_start_ts", - "version": "0.1.55", + "version": "0.4.5", "dependencies": { "@ai-sdk/openai-compatible": "^2.0.47", "@cloudflare/vite-plugin": "1.49.0", @@ -80,7 +80,7 @@ }, "devDependencies": { "@eslint/js": "^9.32.0", - "@lovable.dev/vite-tanstack-config": "2.8.2", + "@lovable.dev/vite-tanstack-config": "2.13.1", "@types/node": "^22.16.5", "@types/react": "^19.2.0", "@types/react-dom": "^19.2.0", @@ -1932,45 +1932,13 @@ } } }, - "node_modules/@lovable.dev/vite-plugin-dev-server-bridge": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@lovable.dev/vite-plugin-dev-server-bridge/-/vite-plugin-dev-server-bridge-1.2.1.tgz", - "integrity": "sha512-JdADRwpEJA5t0ggXbd96dND23Wsp69Af9lVSV5m7MnMGxJZlPgqAqz7HUKcfJESfI5M5yFZOl9e0x76JMMDOAg==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "vite": ">=5.0.0 <9.0.0" - }, - "peerDependenciesMeta": { - "vite": { - "optional": false - } - } - }, - "node_modules/@lovable.dev/vite-plugin-hmr-gate": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@lovable.dev/vite-plugin-hmr-gate/-/vite-plugin-hmr-gate-1.3.0.tgz", - "integrity": "sha512-PHWnUfnphuJ8oc3J9AOGsOvKDosztylQTP6Lna3LLLHAIYF+W7m9Fo9zjHsbB6NQ1KJD7ujUe0CVS608qEKDMw==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "vite": ">=5.0.0 <9.0.0" - }, - "peerDependenciesMeta": { - "vite": { - "optional": false - } - } - }, "node_modules/@lovable.dev/vite-tanstack-config": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/@lovable.dev/vite-tanstack-config/-/vite-tanstack-config-2.8.2.tgz", - "integrity": "sha512-KQg6S57G7a/ykHaMM5G6lJFmN3kAfORKAcQOGfu0vmmVB05FIdsmIPN/fH8ORfEtnBTjp3jVzS9xw0KIeuojnw==", + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/@lovable.dev/vite-tanstack-config/-/vite-tanstack-config-2.13.1.tgz", + "integrity": "sha512-fVSF6k1OUBp1ukGJ8OFbTvPARyV6NIcC2HoluiJLLRPXk33xNPgOp3XLHUk9nA+k7uF4F3pQJhmGEl9g1K9/2g==", "dev": true, "license": "MIT", "dependencies": { - "@lovable.dev/vite-plugin-dev-server-bridge": "^1.2.1", - "@lovable.dev/vite-plugin-hmr-gate": "^1.3.0", "@tanstack/devtools-vite": "^0.8.1", "lightningcss": "^1.30.0" }, @@ -9818,17 +9786,6 @@ } } }, - "node_modules/nitro/node_modules/lru-cache": { - "version": "11.2.7", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", - "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", - "license": "BlueOak-1.0.0", - "optional": true, - "peer": true, - "engines": { - "node": "20 || >=22" - } - }, "node_modules/nitro/node_modules/unstorage": { "version": "2.0.0-alpha.7", "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-2.0.0-alpha.7.tgz", diff --git a/package.json b/package.json index b5def2d4..1bae2242 100644 --- a/package.json +++ b/package.json @@ -8,36 +8,36 @@ "dev": "vite dev", "build": "vite build", "build:dev": "vite build --mode development", - "check:rls": "node scripts/check-rls.mjs", - "check:public-access": "node scripts/check-public-access.mjs", + "check:rls": "node --experimental-strip-types scripts/check-rls.ts", + "check:public-access": "node --experimental-strip-types scripts/check-public-access.ts", "check:security": "npm run check:rls && npm run check:public-access", "predeploy": "npm run check:security", "preview": "vite preview", "lint": "eslint .", "format": "prettier --write .", - "validate:content": "node --experimental-strip-types scripts/validate-content.mjs", - "build:skills-mirror": "node scripts/build-skills-sh-mirror.mjs", - "check:skills-mirror": "node scripts/build-skills-sh-mirror.mjs --check", - "build:agent-plugin": "node scripts/build-agent-plugin.mjs", - "check:agent-plugin": "node scripts/build-agent-plugin.mjs --check", - "audit:skills": "node --experimental-strip-types scripts/audit-skills.mjs", - "scan:skillspector": "node scripts/scan-skillspector.mjs", - "sync:content": "node scripts/sync-content-to-registry.mjs", - "sync:adversarial": "node scripts/sync-adversarial-cases.mjs", - "eval:adversarial": "node scripts/eval-adversarial.mjs", - "playbook:run": "node --experimental-strip-types scripts/run-playbook.mjs", - "benchmark:paired": "node --experimental-strip-types scripts/benchmark-paired.mjs", - "benchmark:sign": "node scripts/sign-benchmark.mjs", - "release:bundles": "node scripts/build-release-bundles.mjs", - "release:sign": "node scripts/sign-release-bundle.mjs", - "release:verify": "node scripts/verify-release-bundle.mjs", - "release:changelog": "node scripts/generate-changelog.mjs", - "trust:attest": "node scripts/generate-trust-attestation.mjs", - "trust:verify": "node scripts/verify-trust-attestation.mjs", - "package:verify": "node scripts/verify-package-signature.mjs", + "validate:content": "node --experimental-strip-types scripts/validate-content.ts", + "build:skills-mirror": "node --experimental-strip-types scripts/build-skills-sh-mirror.ts", + "check:skills-mirror": "node --experimental-strip-types scripts/build-skills-sh-mirror.ts --check", + "build:agent-plugin": "node --experimental-strip-types scripts/build-agent-plugin.ts", + "check:agent-plugin": "node --experimental-strip-types scripts/build-agent-plugin.ts --check", + "audit:skills": "node --experimental-strip-types scripts/audit-skills.ts", + "scan:skillspector": "node --experimental-strip-types scripts/scan-skillspector.ts", + "sync:content": "node --experimental-strip-types scripts/sync-content-to-registry.ts", + "sync:adversarial": "node --experimental-strip-types scripts/sync-adversarial-cases.ts", + "eval:adversarial": "node --experimental-strip-types scripts/eval-adversarial.ts", + "playbook:run": "node --experimental-strip-types scripts/run-playbook.ts", + "benchmark:paired": "node --experimental-strip-types scripts/benchmark-paired.ts", + "benchmark:sign": "node --experimental-strip-types scripts/sign-benchmark.ts", + "release:bundles": "node --experimental-strip-types scripts/build-release-bundles.ts", + "release:sign": "node --experimental-strip-types scripts/sign-release-bundle.ts", + "release:verify": "node --experimental-strip-types scripts/verify-release-bundle.ts", + "release:changelog": "node --experimental-strip-types scripts/generate-changelog.ts", + "trust:attest": "node --experimental-strip-types scripts/generate-trust-attestation.ts", + "trust:verify": "node --experimental-strip-types scripts/verify-trust-attestation.ts", + "package:verify": "node --experimental-strip-types scripts/verify-package-signature.ts", "test": "npm run test:plain && npm run test:ts", - "test:plain": "node --test tests/adversarial-harness.test.mjs tests/trust.test.mjs tests/release-signing.test.mjs tests/cli-install.test.mjs tests/trust-attestation.test.mjs", - "test:ts": "node --experimental-strip-types --test tests/site-stats.test.mjs tests/certify.test.mjs tests/prompt-injection-guard.test.mjs tests/audit-skills.test.mjs tests/runtime.test.mjs tests/integrations.test.mjs tests/growth-revenue-split.test.mjs tests/trust-badge.test.mjs tests/bounties.test.mjs tests/trust-scoring.test.mjs tests/adversarial-holdout.test.mjs tests/adversarial-judge.test.mjs tests/paired-benchmark.test.mjs tests/format-rubric.test.mjs tests/lang-detect.test.mjs tests/university.test.mjs tests/package-signature.test.mjs" + "test:plain": "node --experimental-strip-types --test tests/adversarial-harness.test.ts tests/trust.test.ts tests/release-signing.test.ts tests/cli-install.test.ts tests/trust-attestation.test.ts", + "test:ts": "node --experimental-strip-types --test tests/site-stats.test.ts tests/certify.test.ts tests/prompt-injection-guard.test.ts tests/audit-skills.test.ts tests/runtime.test.ts tests/integrations.test.ts tests/growth-revenue-split.test.ts tests/trust-badge.test.ts tests/bounties.test.ts tests/trust-scoring.test.ts tests/adversarial-holdout.test.ts tests/adversarial-judge.test.ts tests/paired-benchmark.test.ts tests/format-rubric.test.ts tests/lang-detect.test.ts tests/university.test.ts tests/package-signature.test.ts" }, "dependencies": { "@ai-sdk/openai-compatible": "^2.0.47", diff --git a/scripts/audit-skills.mjs b/scripts/audit-skills.ts similarity index 99% rename from scripts/audit-skills.mjs rename to scripts/audit-skills.ts index 4b7a2cee..d45775c9 100644 --- a/scripts/audit-skills.mjs +++ b/scripts/audit-skills.ts @@ -16,8 +16,8 @@ // reported as warnings but do not fail the build. // // Usage: npm run audit:skills -// node --experimental-strip-types scripts/audit-skills.mjs -// node --experimental-strip-types scripts/audit-skills.mjs --json +// node --experimental-strip-types scripts/audit-skills.ts +// node --experimental-strip-types scripts/audit-skills.ts --json // AUDIT_REJECT_AT=critical npm run audit:skills import { readdirSync, readFileSync, statSync } from "node:fs"; diff --git a/scripts/benchmark-paired.mjs b/scripts/benchmark-paired.ts similarity index 97% rename from scripts/benchmark-paired.mjs rename to scripts/benchmark-paired.ts index 7d787787..b64e6be1 100644 --- a/scripts/benchmark-paired.mjs +++ b/scripts/benchmark-paired.ts @@ -6,8 +6,8 @@ // comparison true and reproducible. // // Usage: -// node --experimental-strip-types scripts/benchmark-paired.mjs --skill code-reviewer --mock -// node --experimental-strip-types scripts/benchmark-paired.mjs --skill code-reviewer --out benchmark.json +// node --experimental-strip-types scripts/benchmark-paired.ts --skill code-reviewer --mock +// node --experimental-strip-types scripts/benchmark-paired.ts --skill code-reviewer --out benchmark.json // // Without --mock, requires AI_GATEWAY_BASE_URL / AI_GATEWAY_API_KEY in env. @@ -17,7 +17,7 @@ import { parse as parseYaml } from "yaml"; const ROOT = new URL("..", import.meta.url).pathname; // Pure modules only (no-extension internal imports in runner.ts don't resolve -// under plain node) — case loading is done inline, same as eval-adversarial.mjs. +// under plain node) — case loading is done inline, same as eval-adversarial.ts. const { evaluateCase, summarize } = await import("../src/lib/adversarial/scorer.ts"); const { pairedComparison, BENCHMARK_METHODOLOGY_VERSION } = await import("../src/lib/benchmark/paired.ts"); diff --git a/scripts/build-agent-plugin.mjs b/scripts/build-agent-plugin.ts similarity index 99% rename from scripts/build-agent-plugin.mjs rename to scripts/build-agent-plugin.ts index ee2d8497..504dcd40 100644 --- a/scripts/build-agent-plugin.mjs +++ b/scripts/build-agent-plugin.ts @@ -5,7 +5,7 @@ * plugin.json — plugin manifest (name, version, metadata, extensions) * mcp.json — our hosted MCP server as a portable streamable-http entry * - * The `skills/` component directory is produced by build-skills-sh-mirror.mjs, + * The `skills/` component directory is produced by build-skills-sh-mirror.ts, * so together this repo is a conformant plugin package that any Agent Plugins * client can load. * diff --git a/scripts/build-release-bundles.mjs b/scripts/build-release-bundles.ts similarity index 97% rename from scripts/build-release-bundles.mjs rename to scripts/build-release-bundles.ts index 926cf8b4..5dd4df1e 100644 --- a/scripts/build-release-bundles.mjs +++ b/scripts/build-release-bundles.ts @@ -18,8 +18,8 @@ * - content/schemas/.schema.json (so consumers can re-validate offline) * * Usage: - * node scripts/build-release-bundles.mjs --version content-v1.2.0 - * VERSION=content-v1.2.0 node scripts/build-release-bundles.mjs + * node scripts/build-release-bundles.ts --version content-v1.2.0 + * VERSION=content-v1.2.0 node scripts/build-release-bundles.ts * * Validation runs first — bad content aborts the release. */ @@ -57,7 +57,7 @@ const FILE_VERSION = VERSION.replace(/^content-/, ""); console.log(`▸ Building release bundles for ${VERSION}`); // 1) Validate first — abort on any failure. -const validate = spawnSync(process.execPath, [join(ROOT, "scripts/validate-content.mjs")], { +const validate = spawnSync(process.execPath, ["--experimental-strip-types", join(ROOT, "scripts/validate-content.ts")], { stdio: "inherit", }); if (validate.status !== 0) { diff --git a/scripts/build-skills-sh-mirror.mjs b/scripts/build-skills-sh-mirror.ts similarity index 98% rename from scripts/build-skills-sh-mirror.mjs rename to scripts/build-skills-sh-mirror.ts index 5b379de6..0ad72e67 100644 --- a/scripts/build-skills-sh-mirror.mjs +++ b/scripts/build-skills-sh-mirror.ts @@ -149,7 +149,7 @@ ${SITE}/marketplace/trust/. ## Source of truth Do not edit files in this directory. They are generated from \`content/skills/*.yaml\` by -\`scripts/build-skills-sh-mirror.mjs\` (\`npm run build:skills-mirror\`). Skills authored inside the +\`scripts/build-skills-sh-mirror.ts\` (\`npm run build:skills-mirror\`). Skills authored inside the app are served live from ${SITE}/api/skills//export.md and through the MCP server at ${SITE}/api/mcp, which always returns the current graded version plus telemetry. `; diff --git a/scripts/build-soul-finetune-dataset.mjs b/scripts/build-soul-finetune-dataset.ts similarity index 97% rename from scripts/build-soul-finetune-dataset.mjs rename to scripts/build-soul-finetune-dataset.ts index eb79451f..a1c65b50 100644 --- a/scripts/build-soul-finetune-dataset.mjs +++ b/scripts/build-soul-finetune-dataset.ts @@ -4,7 +4,7 @@ // { messages: [{ role: "system", content: persona }, { role: "user", ... }, { role: "assistant", ... }] } // // Usage: -// node scripts/build-soul-finetune-dataset.mjs --soul fintech-compliance --out dataset.jsonl +// node scripts/build-soul-finetune-dataset.ts --soul fintech-compliance --out dataset.jsonl // Requires SUPABASE_URL + SUPABASE_SERVICE_ROLE_KEY when --source=db (default --source=examples). import { readFileSync, writeFileSync } from "node:fs"; diff --git a/scripts/bump-version.mjs b/scripts/bump-version.ts similarity index 98% rename from scripts/bump-version.mjs rename to scripts/bump-version.ts index f576a435..21a30117 100644 --- a/scripts/bump-version.mjs +++ b/scripts/bump-version.ts @@ -3,7 +3,7 @@ * Auto-bump PLATFORM_VERSION and prepend a CHANGELOG entry. * * Invoked by .github/workflows/auto-bump-version.yml on every push to main. - * Also runnable locally: `node scripts/bump-version.mjs [patch|minor|major]`. + * Also runnable locally: `node scripts/bump-version.ts [patch|minor|major]`. * * Logic (see docs/VERSIONING.md): * - Inspect commits since the last version bump (tag `v` or the diff --git a/scripts/capture-extra.mjs b/scripts/capture-extra.ts similarity index 100% rename from scripts/capture-extra.mjs rename to scripts/capture-extra.ts diff --git a/scripts/capture-screenshots.mjs b/scripts/capture-screenshots.ts similarity index 100% rename from scripts/capture-screenshots.mjs rename to scripts/capture-screenshots.ts diff --git a/scripts/check-public-access.mjs b/scripts/check-public-access.ts similarity index 100% rename from scripts/check-public-access.mjs rename to scripts/check-public-access.ts diff --git a/scripts/check-rls.mjs b/scripts/check-rls.ts similarity index 100% rename from scripts/check-rls.mjs rename to scripts/check-rls.ts diff --git a/scripts/eval-adversarial.mjs b/scripts/eval-adversarial.ts similarity index 99% rename from scripts/eval-adversarial.mjs rename to scripts/eval-adversarial.ts index ff4acea7..0594c11a 100644 --- a/scripts/eval-adversarial.mjs +++ b/scripts/eval-adversarial.ts @@ -2,7 +2,7 @@ // Run the adversarial suite against a local skill YAML. Useful in CI before publish. // // Usage: -// node scripts/eval-adversarial.mjs --skill code-reviewer [--vertical security] [--mock] +// node scripts/eval-adversarial.ts --skill code-reviewer [--vertical security] [--mock] // // Without --mock, requires AI_GATEWAY_API_KEY and AI_GATEWAY_BASE_URL in env. diff --git a/scripts/generate-changelog.mjs b/scripts/generate-changelog.ts similarity index 97% rename from scripts/generate-changelog.mjs rename to scripts/generate-changelog.ts index 98222783..65549e05 100644 --- a/scripts/generate-changelog.mjs +++ b/scripts/generate-changelog.ts @@ -10,7 +10,7 @@ * GITHUB_REPOSITORY env var is set (it is, in Actions). * * Usage: - * node scripts/generate-changelog.mjs --version content-v1.2.0 [--from content-v1.1.0] [--out CHANGELOG.md] + * node scripts/generate-changelog.ts --version content-v1.2.0 [--from content-v1.1.0] [--out CHANGELOG.md] * * Prints the changelog to stdout AND writes it to --out (default: dist/release/CHANGELOG.md). */ diff --git a/scripts/generate-trust-attestation.mjs b/scripts/generate-trust-attestation.ts similarity index 94% rename from scripts/generate-trust-attestation.mjs rename to scripts/generate-trust-attestation.ts index b64ca321..9e0e3b46 100644 --- a/scripts/generate-trust-attestation.mjs +++ b/scripts/generate-trust-attestation.ts @@ -2,15 +2,15 @@ // Produces a signed, independently-verifiable trust attestation for a single // package. It binds the package's exact content hash + version to the result // of the adversarial harness, signed with the same Ed25519 release key -// (sign-release-bundle.mjs). A security team can hand the attestation + -// the package file to verify-trust-attestation.mjs and confirm offline that +// (sign-release-bundle.ts). A security team can hand the attestation + +// the package file to verify-trust-attestation.ts and confirm offline that // "this exact file passed N adversarial cases at score X" — no need to trust // the hosted Trust Score endpoint. // // Usage: -// node scripts/eval-adversarial.mjs --skill code-reviewer --mock --out eval.json +// node scripts/eval-adversarial.ts --skill code-reviewer --mock --out eval.json // SIGNING_PRIVATE_KEY=$(cat priv.pem) SIGNING_PUBLIC_KEY=$(cat pub.pem) \ -// node scripts/generate-trust-attestation.mjs \ +// node scripts/generate-trust-attestation.ts \ // --pkg content/skills/code-reviewer.yaml --eval eval.json import { createHash, createPrivateKey, createPublicKey, sign } from "node:crypto"; diff --git a/scripts/run-playbook.mjs b/scripts/run-playbook.ts similarity index 93% rename from scripts/run-playbook.mjs rename to scripts/run-playbook.ts index 8c6fe5d8..0d955525 100644 --- a/scripts/run-playbook.mjs +++ b/scripts/run-playbook.ts @@ -3,8 +3,8 @@ // AI gateway (requires AI_GATEWAY_BASE_URL + AI_GATEWAY_API_KEY). // // Usage: -// node scripts/run-playbook.mjs --playbook bug-triage --inputs '{"report":"...","repo_context":"node"}' -// node scripts/run-playbook.mjs --playbook bug-triage --inputs-file ./inputs.json --gateway +// node scripts/run-playbook.ts --playbook bug-triage --inputs '{"report":"...","repo_context":"node"}' +// node scripts/run-playbook.ts --playbook bug-triage --inputs-file ./inputs.json --gateway import { readFileSync } from "node:fs"; import { join } from "node:path"; diff --git a/scripts/scan-skillspector.mjs b/scripts/scan-skillspector.ts similarity index 95% rename from scripts/scan-skillspector.mjs rename to scripts/scan-skillspector.ts index 33ff7348..b3d97a45 100644 --- a/scripts/scan-skillspector.mjs +++ b/scripts/scan-skillspector.ts @@ -2,7 +2,7 @@ // Complementary skill security scan powered by NVIDIA SkillSpector. // // This is a SECOND, independent opinion layered on top of the curated -// `audit:skills` gate (scripts/audit-skills.mjs). Where audit:skills is a +// `audit:skills` gate (scripts/audit-skills.ts). Where audit:skills is a // high-precision, schema-aware gate tuned to this marketplace, SkillSpector // brings NVIDIA's broader catalogue of vulnerability patterns (prompt // injection, data exfiltration, privilege escalation, supply-chain, excessive @@ -18,11 +18,11 @@ // unless SKILLSPECTOR_REQUIRED=1 (or --require) is set. // // Usage: -// node scripts/scan-skillspector.mjs # scan all packages (advisory) -// node scripts/scan-skillspector.mjs content/skills/foo.yaml ... # scan specific files -// node scripts/scan-skillspector.mjs --block # non-zero exit when a package is risky -// node scripts/scan-skillspector.mjs --sarif report.sarif # write merged SARIF -// node scripts/scan-skillspector.mjs --json # machine-readable summary on stdout +// node scripts/scan-skillspector.ts # scan all packages (advisory) +// node scripts/scan-skillspector.ts content/skills/foo.yaml ... # scan specific files +// node scripts/scan-skillspector.ts --block # non-zero exit when a package is risky +// node scripts/scan-skillspector.ts --sarif report.sarif # write merged SARIF +// node scripts/scan-skillspector.ts --json # machine-readable summary on stdout // // Env: // SKILLSPECTOR_BIN path to the skillspector executable (default: "skillspector") diff --git a/scripts/seed-gtm-skills.mjs b/scripts/seed-gtm-skills.ts similarity index 100% rename from scripts/seed-gtm-skills.mjs rename to scripts/seed-gtm-skills.ts diff --git a/scripts/sign-benchmark.mjs b/scripts/sign-benchmark.ts similarity index 92% rename from scripts/sign-benchmark.mjs rename to scripts/sign-benchmark.ts index 69d7d0d0..10eaedc6 100644 --- a/scripts/sign-benchmark.mjs +++ b/scripts/sign-benchmark.ts @@ -4,12 +4,12 @@ // "these comparative numbers came from THIS methodology run on THESE inputs". // // Sign: -// node scripts/benchmark-paired.mjs --skill code-reviewer --out benchmark.json # live run +// node scripts/benchmark-paired.ts --skill code-reviewer --out benchmark.json # live run // SIGNING_PRIVATE_KEY=$(cat priv.pem) SIGNING_PUBLIC_KEY=$(cat pub.pem) \ -// node scripts/sign-benchmark.mjs --benchmark benchmark.json --out benchmark.signed.json +// node scripts/sign-benchmark.ts --benchmark benchmark.json --out benchmark.signed.json // // Verify (no private key needed): -// node scripts/sign-benchmark.mjs --verify benchmark.signed.json --pubkey pub.pem +// node scripts/sign-benchmark.ts --verify benchmark.signed.json --pubkey pub.pem import { createHash, createPrivateKey, createPublicKey, sign, verify } from "node:crypto"; import { readFileSync, writeFileSync } from "node:fs"; @@ -57,7 +57,7 @@ if (!SIGNING_PRIVATE_KEY || !SIGNING_PUBLIC_KEY) { const benchmark = JSON.parse(readFileSync(benchPath, "utf8")); if (benchmark.mode !== "live") { console.error( - "✗ refusing to sign a mock benchmark. Run benchmark-paired.mjs without --mock first.", + "✗ refusing to sign a mock benchmark. Run benchmark-paired.ts without --mock first.", ); process.exit(2); } diff --git a/scripts/sign-release-bundle.mjs b/scripts/sign-release-bundle.ts similarity index 93% rename from scripts/sign-release-bundle.mjs rename to scripts/sign-release-bundle.ts index b0b4bbdc..cb3b1f09 100644 --- a/scripts/sign-release-bundle.mjs +++ b/scripts/sign-release-bundle.ts @@ -1,11 +1,11 @@ #!/usr/bin/env node -// Signs a release bundle produced by build-release-bundles.mjs with Ed25519. +// Signs a release bundle produced by build-release-bundles.ts with Ed25519. // Emits .sig and a SIGNATURES.json sidecar so air-gapped consumers -// can verify integrity offline with scripts/verify-release-bundle.mjs. +// can verify integrity offline with scripts/verify-release-bundle.ts. // // Usage: // SIGNING_PRIVATE_KEY=$(cat priv.pem) SIGNING_PUBLIC_KEY=$(cat pub.pem) \ -// node scripts/sign-release-bundle.mjs --dir dist/release +// node scripts/sign-release-bundle.ts --dir dist/release import { createHash, createPrivateKey, createPublicKey, sign } from "node:crypto"; import { readdirSync, readFileSync, statSync, writeFileSync } from "node:fs"; diff --git a/scripts/sync-adversarial-cases.mjs b/scripts/sync-adversarial-cases.ts similarity index 100% rename from scripts/sync-adversarial-cases.mjs rename to scripts/sync-adversarial-cases.ts diff --git a/scripts/sync-content-to-registry.mjs b/scripts/sync-content-to-registry.ts similarity index 98% rename from scripts/sync-content-to-registry.mjs rename to scripts/sync-content-to-registry.ts index c757bce9..61abbc9b 100644 --- a/scripts/sync-content-to-registry.mjs +++ b/scripts/sync-content-to-registry.ts @@ -3,7 +3,7 @@ // public.packages table of the connected Supabase project. // // Usage: -// SUPABASE_URL=... SUPABASE_SERVICE_ROLE_KEY=... node scripts/sync-content-to-registry.mjs +// SUPABASE_URL=... SUPABASE_SERVICE_ROLE_KEY=... node scripts/sync-content-to-registry.ts // bun run sync:content // // Idempotent: matches on slug, updates name/description/type/version/body. diff --git a/scripts/validate-content.mjs b/scripts/validate-content.ts similarity index 99% rename from scripts/validate-content.mjs rename to scripts/validate-content.ts index 27d48978..dac4d761 100644 --- a/scripts/validate-content.mjs +++ b/scripts/validate-content.ts @@ -3,7 +3,7 @@ // content/schemas/.schema.json. Also enforces unique slugs and template skipping. // // Usage: bun run validate:content -// node scripts/validate-content.mjs +// node scripts/validate-content.ts import { readdirSync, readFileSync, statSync } from "node:fs"; import { join, basename } from "node:path"; diff --git a/scripts/verify-package-signature.mjs b/scripts/verify-package-signature.ts similarity index 92% rename from scripts/verify-package-signature.mjs rename to scripts/verify-package-signature.ts index 5a925df0..7af9f248 100644 --- a/scripts/verify-package-signature.mjs +++ b/scripts/verify-package-signature.ts @@ -4,7 +4,7 @@ * signature sidecar (sak-package-signature/v1). * * Usage: - * node scripts/verify-package-signature.mjs [public-key.pem] + * node scripts/verify-package-signature.ts [public-key.pem] * * Without a local PEM the public key is fetched from the sidecar's * `verify.public_key_url`. Exit code 0 = verified, 1 = failed. @@ -14,7 +14,7 @@ import { readFileSync } from "node:fs"; const [zipPath, sidecarPath, pemPath] = process.argv.slice(2); if (!zipPath || !sidecarPath) { - console.error("usage: verify-package-signature.mjs [public-key.pem]"); + console.error("usage: verify-package-signature.ts [public-key.pem]"); process.exit(1); } diff --git a/scripts/verify-release-bundle.mjs b/scripts/verify-release-bundle.ts similarity index 97% rename from scripts/verify-release-bundle.mjs rename to scripts/verify-release-bundle.ts index 5f7116e9..f98a51ac 100644 --- a/scripts/verify-release-bundle.mjs +++ b/scripts/verify-release-bundle.ts @@ -3,7 +3,7 @@ // air-gapped environments alongside the bundle and SIGNING_PUBLIC_KEY.pem. // // Usage: -// node scripts/verify-release-bundle.mjs --dir ./bundle +// node scripts/verify-release-bundle.ts --dir ./bundle // Exits non-zero on any tampered file or missing signature. import { createHash, createPublicKey, verify } from "node:crypto"; diff --git a/scripts/verify-trust-attestation.mjs b/scripts/verify-trust-attestation.ts similarity index 96% rename from scripts/verify-trust-attestation.mjs rename to scripts/verify-trust-attestation.ts index 968f7294..5eab3cdb 100644 --- a/scripts/verify-trust-attestation.mjs +++ b/scripts/verify-trust-attestation.ts @@ -1,11 +1,11 @@ #!/usr/bin/env node // Offline verifier for a trust attestation produced by -// generate-trust-attestation.mjs. Designed to be run by a consumer's security +// generate-trust-attestation.ts. Designed to be run by a consumer's security // team with nothing but: the package file, the .trust.json, and the publisher's // SIGNING_PUBLIC_KEY.pem. No network, no trust in the hosted registry. // // Usage: -// node scripts/verify-trust-attestation.mjs \ +// node scripts/verify-trust-attestation.ts \ // --attestation code-reviewer.trust.json \ // --pkg content/skills/code-reviewer.yaml \ // --pubkey pub.pem diff --git a/tests/adversarial-harness.test.mjs b/tests/adversarial-harness.test.ts similarity index 86% rename from tests/adversarial-harness.test.mjs rename to tests/adversarial-harness.test.ts index 4d1373fa..2d0cfeb7 100644 --- a/tests/adversarial-harness.test.mjs +++ b/tests/adversarial-harness.test.ts @@ -8,7 +8,7 @@ import { parse as parseYaml } from "yaml"; const ROOT = new URL("..", import.meta.url).pathname; test("validate:content does not raise errors for adversarial cases", () => { - const r = spawnSync("node", ["scripts/validate-content.mjs"], { cwd: ROOT, encoding: "utf8" }); + const r = spawnSync("node", ["--experimental-strip-types", "scripts/validate-content.ts"], { cwd: ROOT, encoding: "utf8" }); // Pre-existing skill examples may fail; adversarial files must not contribute errors. const advErrors = (r.stderr.match(/content\/adversarial\//g) || []).length; assert.equal(advErrors, 0, `adversarial validation errors: ${r.stderr}`); @@ -17,7 +17,7 @@ test("validate:content does not raise errors for adversarial cases", () => { test("eval:adversarial --mock returns a report and detects refusal cases", () => { const r = spawnSync( "node", - ["scripts/eval-adversarial.mjs", "--skill", "code-reviewer", "--mock", "--allowFail", "true"], + ["--experimental-strip-types", "scripts/eval-adversarial.ts", "--skill", "code-reviewer", "--mock", "--allowFail", "true"], { cwd: ROOT, encoding: "utf8" }, ); assert.notEqual(r.status, 1, `script crashed: ${r.stderr}`); diff --git a/tests/adversarial-holdout.test.mjs b/tests/adversarial-holdout.test.ts similarity index 100% rename from tests/adversarial-holdout.test.mjs rename to tests/adversarial-holdout.test.ts diff --git a/tests/adversarial-judge.test.mjs b/tests/adversarial-judge.test.ts similarity index 100% rename from tests/adversarial-judge.test.mjs rename to tests/adversarial-judge.test.ts diff --git a/tests/agent-catalog.test.mjs b/tests/agent-catalog.test.ts similarity index 100% rename from tests/agent-catalog.test.mjs rename to tests/agent-catalog.test.ts diff --git a/tests/agent-plugins.test.mjs b/tests/agent-plugins.test.ts similarity index 100% rename from tests/agent-plugins.test.mjs rename to tests/agent-plugins.test.ts diff --git a/tests/audit-skills.test.mjs b/tests/audit-skills.test.ts similarity index 97% rename from tests/audit-skills.test.mjs rename to tests/audit-skills.test.ts index 562de61f..5d7713c9 100644 --- a/tests/audit-skills.test.mjs +++ b/tests/audit-skills.test.ts @@ -1,6 +1,6 @@ import { test } from "node:test"; import assert from "node:assert/strict"; -import { auditPackage } from "../scripts/audit-skills.mjs"; +import { auditPackage } from "../scripts/audit-skills.ts"; test("clean skill produces no findings and is not blocked", () => { const r = auditPackage({ diff --git a/tests/bounties.test.mjs b/tests/bounties.test.ts similarity index 100% rename from tests/bounties.test.mjs rename to tests/bounties.test.ts diff --git a/tests/certify.test.mjs b/tests/certify.test.ts similarity index 100% rename from tests/certify.test.mjs rename to tests/certify.test.ts diff --git a/tests/cli-install.test.mjs b/tests/cli-install.test.ts similarity index 67% rename from tests/cli-install.test.mjs rename to tests/cli-install.test.ts index 6d09fa6b..f2b5af21 100644 --- a/tests/cli-install.test.mjs +++ b/tests/cli-install.test.ts @@ -7,20 +7,20 @@ import { join } from "node:path"; const ROOT = new URL("..", import.meta.url).pathname; test("CLI --help prints usage and exits 0", () => { - const r = spawnSync("node", [join(ROOT, "cli/super-agent.mjs"), "--help"], { encoding: "utf8" }); + const r = spawnSync("node", ["--experimental-strip-types", join(ROOT, "cli/super-agent.ts"), "--help"], { encoding: "utf8" }); assert.equal(r.status, 0, r.stderr); assert.match(r.stdout, /install /); assert.match(r.stdout, /SUPER_AGENT_REGISTRY/); }); test("CLI fails clearly on unknown command", () => { - const r = spawnSync("node", [join(ROOT, "cli/super-agent.mjs"), "bogus"], { encoding: "utf8" }); + const r = spawnSync("node", ["--experimental-strip-types", join(ROOT, "cli/super-agent.ts"), "bogus"], { encoding: "utf8" }); assert.notEqual(r.status, 0); assert.match(r.stderr, /unknown command|install /); }); test("CLI install errors out when no slug is provided", () => { - const r = spawnSync("node", [join(ROOT, "cli/super-agent.mjs"), "install"], { + const r = spawnSync("node", ["--experimental-strip-types", join(ROOT, "cli/super-agent.ts"), "install"], { encoding: "utf8", env: { ...process.env, SUPER_AGENT_TELEMETRY: "0" }, }); @@ -31,6 +31,6 @@ test("CLI install errors out when no slug is provided", () => { test("CLI package.json declares super-agent bin", () => { const pkg = JSON.parse(readFileSync(join(ROOT, "cli/package.json"), "utf8")); assert.equal(pkg.name, "super-agent"); - assert.equal(pkg.bin["super-agent"], "./super-agent.mjs"); - assert.ok(statSync(join(ROOT, "cli/super-agent.mjs")).isFile()); + assert.equal(pkg.bin["super-agent"], "./super-agent.js"); + assert.ok(statSync(join(ROOT, "cli/super-agent.ts")).isFile()); }); diff --git a/tests/draft-repair.test.mjs b/tests/draft-repair.test.ts similarity index 100% rename from tests/draft-repair.test.mjs rename to tests/draft-repair.test.ts diff --git a/tests/format-rubric.test.mjs b/tests/format-rubric.test.ts similarity index 100% rename from tests/format-rubric.test.mjs rename to tests/format-rubric.test.ts diff --git a/tests/growth-revenue-split.test.mjs b/tests/growth-revenue-split.test.ts similarity index 100% rename from tests/growth-revenue-split.test.mjs rename to tests/growth-revenue-split.test.ts diff --git a/tests/integrations.test.mjs b/tests/integrations.test.ts similarity index 100% rename from tests/integrations.test.mjs rename to tests/integrations.test.ts diff --git a/tests/lang-detect.test.mjs b/tests/lang-detect.test.ts similarity index 100% rename from tests/lang-detect.test.mjs rename to tests/lang-detect.test.ts diff --git a/tests/package-signature.test.mjs b/tests/package-signature.test.ts similarity index 100% rename from tests/package-signature.test.mjs rename to tests/package-signature.test.ts diff --git a/tests/paired-benchmark.test.mjs b/tests/paired-benchmark.test.ts similarity index 100% rename from tests/paired-benchmark.test.mjs rename to tests/paired-benchmark.test.ts diff --git a/tests/plugin-conformance.test.mjs b/tests/plugin-conformance.test.ts similarity index 100% rename from tests/plugin-conformance.test.mjs rename to tests/plugin-conformance.test.ts diff --git a/tests/prompt-injection-guard.test.mjs b/tests/prompt-injection-guard.test.ts similarity index 100% rename from tests/prompt-injection-guard.test.mjs rename to tests/prompt-injection-guard.test.ts diff --git a/tests/release-signing.test.mjs b/tests/release-signing.test.ts similarity index 75% rename from tests/release-signing.test.mjs rename to tests/release-signing.test.ts index 2c156258..3d20dae3 100644 --- a/tests/release-signing.test.mjs +++ b/tests/release-signing.test.ts @@ -26,12 +26,12 @@ function keys() { test("sign + verify round-trips a bundle", () => { const dir = makeBundle(); const { pub, priv } = keys(); - const sign = spawnSync("node", ["scripts/sign-release-bundle.mjs", "--dir", dir], { + const sign = spawnSync("node", ["--experimental-strip-types", "scripts/sign-release-bundle.ts", "--dir", dir], { cwd: ROOT, encoding: "utf8", env: { ...process.env, SIGNING_PRIVATE_KEY: priv, SIGNING_PUBLIC_KEY: pub }, }); assert.equal(sign.status, 0, sign.stderr); - const verify = spawnSync("node", ["scripts/verify-release-bundle.mjs", "--dir", dir], { + const verify = spawnSync("node", ["--experimental-strip-types", "scripts/verify-release-bundle.ts", "--dir", dir], { cwd: ROOT, encoding: "utf8", }); assert.equal(verify.status, 0, verify.stderr); @@ -40,11 +40,11 @@ test("sign + verify round-trips a bundle", () => { test("tampered file fails verification", () => { const dir = makeBundle(); const { pub, priv } = keys(); - spawnSync("node", ["scripts/sign-release-bundle.mjs", "--dir", dir], { + spawnSync("node", ["--experimental-strip-types", "scripts/sign-release-bundle.ts", "--dir", dir], { cwd: ROOT, env: { ...process.env, SIGNING_PRIVATE_KEY: priv, SIGNING_PUBLIC_KEY: pub }, }); appendFileSync(join(dir, "skills-v1.0.0.zip"), "TAMPERED"); - const verify = spawnSync("node", ["scripts/verify-release-bundle.mjs", "--dir", dir], { + const verify = spawnSync("node", ["--experimental-strip-types", "scripts/verify-release-bundle.ts", "--dir", dir], { cwd: ROOT, encoding: "utf8", }); assert.notEqual(verify.status, 0); @@ -55,11 +55,11 @@ test("wrong public key is rejected by fingerprint check", () => { const dir = makeBundle(); const { pub, priv } = keys(); const { pub: pub2 } = keys(); - spawnSync("node", ["scripts/sign-release-bundle.mjs", "--dir", dir], { + spawnSync("node", ["--experimental-strip-types", "scripts/sign-release-bundle.ts", "--dir", dir], { cwd: ROOT, env: { ...process.env, SIGNING_PRIVATE_KEY: priv, SIGNING_PUBLIC_KEY: pub }, }); writeFileSync(join(dir, "SIGNING_PUBLIC_KEY.pem"), pub2); - const verify = spawnSync("node", ["scripts/verify-release-bundle.mjs", "--dir", dir], { + const verify = spawnSync("node", ["--experimental-strip-types", "scripts/verify-release-bundle.ts", "--dir", dir], { cwd: ROOT, encoding: "utf8", }); assert.notEqual(verify.status, 0); diff --git a/tests/responsive-discover.test.mjs b/tests/responsive-discover.test.ts similarity index 98% rename from tests/responsive-discover.test.mjs rename to tests/responsive-discover.test.ts index fbc9601b..71e7253a 100644 --- a/tests/responsive-discover.test.mjs +++ b/tests/responsive-discover.test.ts @@ -10,7 +10,7 @@ * 2. The skill / playbook / soul / guardrail tab row (must scroll-x). * 3. The body grid (must stack below the `lg` breakpoint, 1024px). * - * Run: node --test tests/responsive-discover.test.mjs + * Run: node --test tests/responsive-discover.test.ts */ import { test } from "node:test"; import assert from "node:assert/strict"; diff --git a/tests/runtime.test.mjs b/tests/runtime.test.ts similarity index 100% rename from tests/runtime.test.mjs rename to tests/runtime.test.ts diff --git a/tests/site-stats.test.mjs b/tests/site-stats.test.ts similarity index 100% rename from tests/site-stats.test.mjs rename to tests/site-stats.test.ts diff --git a/tests/trust-attestation.test.mjs b/tests/trust-attestation.test.ts similarity index 91% rename from tests/trust-attestation.test.mjs rename to tests/trust-attestation.test.ts index 49ce2ebc..ab6fa19f 100644 --- a/tests/trust-attestation.test.mjs +++ b/tests/trust-attestation.test.ts @@ -35,7 +35,7 @@ function fixture() { function attest(pkg, ev, att, k) { return spawnSync( "node", - ["scripts/generate-trust-attestation.mjs", "--pkg", pkg, "--eval", ev, "--out", att], + ["--experimental-strip-types", "scripts/generate-trust-attestation.ts", "--pkg", pkg, "--eval", ev, "--out", att], { cwd: ROOT, encoding: "utf8", env: { ...process.env, SIGNING_PRIVATE_KEY: k.priv, SIGNING_PUBLIC_KEY: k.pub } }, ); } @@ -43,7 +43,7 @@ function attest(pkg, ev, att, k) { function doVerify(att, pkg, pubPem) { return spawnSync( "node", - ["scripts/verify-trust-attestation.mjs", "--attestation", att, "--pkg", pkg], + ["--experimental-strip-types", "scripts/verify-trust-attestation.ts", "--attestation", att, "--pkg", pkg], { cwd: ROOT, encoding: "utf8", env: { ...process.env, SIGNING_PUBLIC_KEY: pubPem } }, ); } diff --git a/tests/trust-badge.test.mjs b/tests/trust-badge.test.ts similarity index 100% rename from tests/trust-badge.test.mjs rename to tests/trust-badge.test.ts diff --git a/tests/trust-scoring.test.mjs b/tests/trust-scoring.test.ts similarity index 100% rename from tests/trust-scoring.test.mjs rename to tests/trust-scoring.test.ts diff --git a/tests/trust.test.mjs b/tests/trust.test.ts similarity index 100% rename from tests/trust.test.mjs rename to tests/trust.test.ts diff --git a/tests/university.test.mjs b/tests/university.test.ts similarity index 100% rename from tests/university.test.mjs rename to tests/university.test.ts diff --git a/tests/uplift.test.mjs b/tests/uplift.test.ts similarity index 100% rename from tests/uplift.test.mjs rename to tests/uplift.test.ts