diff --git a/apps/supercode-cli/server/src/cli/commands/slashCommands/crisp-audit.ts b/apps/supercode-cli/server/src/cli/commands/slashCommands/crisp-audit.ts new file mode 100644 index 0000000..f2a3e95 --- /dev/null +++ b/apps/supercode-cli/server/src/cli/commands/slashCommands/crisp-audit.ts @@ -0,0 +1,49 @@ +import chalk from "chalk" +import { execSync } from "node:child_process" +import { theme, frame } from "src/cli/utils/tui.ts" +import { getCrispModeSync } from "src/cli/crisp/index.ts" +import type { SlashCommandResult } from "./index.ts" + +export async function crispAuditCommand(argsStr: string): Promise { + const mode = getCrispModeSync() + const focus = argsStr.trim() || "all" + const modeLabel = mode === "off" ? "full" : mode + + let fileList = "" + try { + fileList = execSync(`fd -e ts -e tsx -e js -e jsx --exclude node_modules --exclude dist --exclude .git --max-depth 6 . ${process.cwd()}`, { encoding: "utf-8" }) + } catch { + fileList = "(could not list files)" + } + + const lines = fileList.split("\n").filter(Boolean) + const sampleFiles = lines.slice(0, 30).join("\n") + const totalFiles = lines.length + + const header = + `Audit this workspace against the Crisp simplicity ladder (${modeLabel}). focus: ${focus}\n` + + `Scan the whole tree. Rank findings biggest cut first.\n` + + `One line per finding. Format: . . [path]\n\n` + + `Tags:\n` + + ` delete: dead code, unused flexibility, speculative feature. Replacement: nothing.\n` + + ` stdlib: hand-rolled thing the stdlib ships. Name the function.\n` + + ` native: dependency or code doing what the platform already does. Name the feature.\n` + + ` yagni: abstraction with one implementation, config nobody sets, layer with one caller.\n` + + ` shrink: same logic, fewer lines. Show the shorter form.\n\n` + + `Prioritise: deps the stdlib or platform already ships, single-implementation interfaces, ` + + `factories with one product, wrappers that only delegate, files exporting one thing, ` + + `dead flags and config, hand-rolled stdlib.\n\n` + + `End with: net: - lines, - deps possible.\n` + + `If nothing to cut: "Lean already. Ship."\n\n` + + `Found ${totalFiles} source files. Sample (${Math.min(totalFiles, 30)} of ${totalFiles}):\n` + + sampleFiles + + console.log( + frame( + `${chalk.hex(theme.amber).bold("Supercode Crisp Audit")} ${chalk.hex(theme.muted)(`mode: ${modeLabel} · focus: ${focus}`)}\n\n${chalk.hex(theme.greenDim)(`Scanning ${totalFiles} source files across the workspace...`)}`, + { title: "crisp-audit", borderColor: theme.amber, padding: 0 }, + ), + ) + + return { type: "message", message: header } +} diff --git a/apps/supercode-cli/server/src/cli/commands/slashCommands/crisp-debt.ts b/apps/supercode-cli/server/src/cli/commands/slashCommands/crisp-debt.ts new file mode 100644 index 0000000..82cb028 --- /dev/null +++ b/apps/supercode-cli/server/src/cli/commands/slashCommands/crisp-debt.ts @@ -0,0 +1,106 @@ +import chalk from "chalk" +import { execSync } from "node:child_process" +import { theme, frame } from "src/cli/utils/tui.ts" +import type { SlashCommandResult } from "./index.ts" + +export async function crispDebtCommand(argsStr: string): Promise { + let tagCounts: Record = {} + let rawMatches = "" + let crispCommentMatches = "" + try { + rawMatches = execSync("rg -n '\\[crisp:\\d+\\]' --type ts --type tsx --type js --type jsx -g '!node_modules' -g '!dist'", { encoding: "utf-8" }) + const matches = rawMatches.split("\n").filter(Boolean) + for (const m of matches) { + const tag = m.match(/\[crisp:(\d+)\]/) + if (tag) { + const rung = tag[1]! + tagCounts[rung] = (tagCounts[rung] ?? 0) + 1 + } + } + } catch { + rawMatches = "" + } + try { + crispCommentMatches = execSync("rg -n '(//|#|--)\\s*crisp:' --type ts --type tsx --type js --type jsx -g '!node_modules' -g '!dist'", { encoding: "utf-8" }) + } catch { + crispCommentMatches = "" + } + + const totalTags = Object.values(tagCounts).reduce((a, b) => a + b, 0) + + const rungLabels: Record = { + "1": "YAGNI", + "2": "Reuse what exists", + "3": "Standard library first", + "4": "Native platform APIs", + "5": "Dependencies are debt", + "6": "One line > many", + "7": "Minimum code to satisfy the spec", + } + + const summaryLines = Object.entries(rungLabels) + .map(([rung, label]) => { + const count = tagCounts[rung] ?? 0 + const color = count > 0 ? theme.green : theme.muted + return ` ${chalk.hex(color)(`[crisp:${rung}]`)} ${chalk.hex(color)(label.padEnd(28))} ${count > 0 ? chalk.hex(theme.amber)(`${count} tags`) : chalk.hex(theme.muted)("—")}` + }) + .join("\n") + + const detailLines = rawMatches + .split("\n") + .filter(Boolean) + .slice(0, 40) + .map((line) => { + const idx = line.indexOf(":") + if (idx > 0) { + const file = line.slice(0, idx) + const rest = line.slice(idx + 1) + return ` ${chalk.hex(theme.greenDim)(file)}:${chalk.hex(theme.muted)(rest.slice(0, 80))}` + } + return ` ${chalk.hex(theme.muted)(line.slice(0, 80))}` + }) + .join("\n") + + const crispComments = crispCommentMatches + .split("\n") + .filter(Boolean) + .slice(0, 20) + + const commentLines = crispComments.map((line) => { + const idx = line.indexOf(":") + if (idx > 0) { + const file = line.slice(0, idx) + const rest = line.slice(idx + 1) + const hasUpgrade = /\b(upgrade|when|if|once|until|trigger|todo)\b/i.test(rest) + const triggerTag = hasUpgrade ? "" : chalk.hex(theme.red)(" no-trigger") + return ` ${chalk.hex(theme.greenDim)(file)}:${chalk.hex(theme.muted)(rest.slice(0, 80))}${triggerTag}` + } + return ` ${chalk.hex(theme.muted)(line.slice(0, 80))}` + }).join("\n") + + const noTriggerCount = crispComments.filter((l) => !/\b(upgrade|when|if|once|until|trigger|todo)\b/i.test(l)).length + + const sections: string[] = [ + chalk.hex(theme.amber).bold("Crisp Debt Ledger"), + "", + `${chalk.hex(theme.muted)(`[crisp:N] tag ledger: ${totalTags} markers`)}`, + "", + ...(totalTags > 0 + ? [summaryLines, "", detailLines] + : [`${chalk.hex(theme.greenDim)("No [crisp:N] tags found. Clean — or crisp hasn't been applied yet.")}`]), + ] + + if (crispComments.length > 0) { + sections.push( + "", + chalk.hex(theme.amber).bold(`Deliberate shortcuts (crisp: comments)`), + "", + commentLines, + "", + `${chalk.hex(theme.muted)(`${crispComments.length} shortcuts, ${noTriggerCount} with no upgrade trigger`)}`, + ) + } + + console.log(frame(sections.join("\n"), { title: "crisp-debt", borderColor: theme.amber, padding: 0 })) + return { type: "help" } +} diff --git a/apps/supercode-cli/server/src/cli/commands/slashCommands/crisp-gain.ts b/apps/supercode-cli/server/src/cli/commands/slashCommands/crisp-gain.ts new file mode 100644 index 0000000..469ec3c --- /dev/null +++ b/apps/supercode-cli/server/src/cli/commands/slashCommands/crisp-gain.ts @@ -0,0 +1,39 @@ +import chalk from "chalk" +import { theme, frame } from "src/cli/utils/tui.ts" +import { getCrispModeSync } from "src/cli/crisp/index.ts" +import type { SlashCommandResult } from "./index.ts" + +const bar = (pct: number, width = 20): string => { + const filled = Math.round((pct / 100) * width) + return "█".repeat(filled) + "·".repeat(width - filled) +} + +export async function crispGainCommand(argsStr: string): Promise { + const mode = getCrispModeSync() + + const content = [ + chalk.hex(theme.amber).bold("Crisp Gain"), + chalk.hex(theme.muted)("benchmark median · 5 tasks · 3 models"), + "", + chalk.hex(theme.green)("Lines of code"), + ` no-crisp ${chalk.hex(theme.white)(bar(100))} 100%`, + ` crisp ${chalk.hex(theme.green)(bar(13))}${chalk.hex(theme.muted)(bar(87))} 6-20% ${chalk.hex(theme.red)("▼ 80-94%")}`, + "", + chalk.hex(theme.green)("Cost"), + ` no-crisp ${chalk.hex(theme.white)(bar(100))} 100%`, + ` crisp ${chalk.hex(theme.green)(bar(38))}${chalk.hex(theme.muted)(bar(62))} 23-53% ${chalk.hex(theme.red)("▼ 47-77%")}`, + "", + chalk.hex(theme.green)("Speed"), + ` crisp ${chalk.hex(theme.amber)("▸ 3-6× faster")}`, + "", + chalk.hex(theme.muted)("These are published benchmark medians (see README)."), + chalk.hex(theme.muted)("They are measured, not computed from this repo."), + "", + chalk.hex(theme.amber)("This repo:"), + ` ${chalk.hex(theme.green)("/crisp-debt")} ${chalk.hex(theme.muted)("(shortcuts you deferred — the counted ledger)")}`, + ` ${chalk.hex(theme.green)("/crisp-audit")} ${chalk.hex(theme.muted)("(what's still cuttable)")}`, + ].join("\n") + + console.log(frame(content, { title: "crisp-gain", borderColor: theme.amber, padding: 0 })) + return { type: "help" } +} diff --git a/apps/supercode-cli/server/src/cli/commands/slashCommands/crisp-help.ts b/apps/supercode-cli/server/src/cli/commands/slashCommands/crisp-help.ts new file mode 100644 index 0000000..713bfe1 --- /dev/null +++ b/apps/supercode-cli/server/src/cli/commands/slashCommands/crisp-help.ts @@ -0,0 +1,49 @@ +import chalk from "chalk" +import { theme, frame } from "src/cli/utils/tui.ts" +import { getCrispModeSync } from "src/cli/crisp/index.ts" +import type { SlashCommandResult } from "./index.ts" + +export async function crispHelpCommand(): Promise { + const current = getCrispModeSync() + + const content = [ + chalk.hex(theme.amber).bold("Supercode Crisp — Simplicity Ladder Commands"), + "", + chalk.hex(theme.amber).bold("Levels"), + ` ${chalk.hex(theme.green)("/crisp")} ${chalk.hex(theme.muted)("Show current mode")}`, + ` ${chalk.hex(theme.green)("/crisp full")} ${chalk.hex(theme.muted)("Ladder enforced. Every abstraction must be justified. Default.")}`, + ` ${chalk.hex(theme.green)("/crisp lite")} ${chalk.hex(theme.muted)("Build what's asked, name the lazier alternative. User picks.")}`, + ` ${chalk.hex(theme.green)("/crisp ultra")} ${chalk.hex(theme.muted)("YAGNI extremist. Deletion before addition. Challenges requirements.")}`, + ` ${chalk.hex(theme.green)("/crisp off")} ${chalk.hex(theme.muted)("Disable crisp mode.")}`, + "", + chalk.hex(theme.amber).bold("Skills (one-shot)"), + ` ${chalk.hex(theme.green)("/crisp-review")} ${chalk.hex(theme.muted)("Over-engineering review: L42: yagni: factory, one product. Inline.")}`, + ` ${chalk.hex(theme.green)("/crisp-audit")} ${chalk.hex(theme.muted)("Whole-repo over-engineering audit: ranked list of what to delete.")}`, + ` ${chalk.hex(theme.green)("/crisp-debt")} ${chalk.hex(theme.muted)("Harvest [crisp:N] tags and crisp: shortcut comments.")}`, + ` ${chalk.hex(theme.green)("/crisp-gain")} ${chalk.hex(theme.muted)("Measured-impact scoreboard: less code, less cost, more speed.")}`, + ` ${chalk.hex(theme.green)("/crisp-help")} ${chalk.hex(theme.muted)("This card.")}`, + "", + chalk.hex(theme.amber).bold("The ladder"), + " 1. YAGNI — don't build what you don't need", + " 2. Reuse what exists — stdlib > library > new code", + " 3. Standard library first — built-in APIs > third-party", + " 4. Native platform APIs — platform > framework > userland", + " 5. Dependencies are debt — every dep is a liability", + " 6. One line > many — single expression > temp var chain", + " 7. Minimum code to satisfy the spec — delete everything else", + "", + chalk.hex(theme.amber).bold("Deactivate"), + ` ${chalk.hex(theme.green)('"stop crisp"')} ${chalk.hex(theme.muted)("or")} ${chalk.hex(theme.green)("normal mode")} ${chalk.hex(theme.muted)("revert to normal. Resume anytime with /crisp.")}`, + "", + chalk.hex(theme.amber).bold("Configure default"), + chalk.hex(theme.muted)("Environment variable (highest priority):"), + ` ${chalk.hex(theme.greenDim)("export CRISP_DEFAULT_MODE=ultra")}`, + chalk.hex(theme.muted)("Config file (~/.config/supercode/crisp.json):"), + ` ${chalk.hex(theme.greenDim)('{"defaultMode": "lite"}')}`, + "", + chalk.hex(theme.greenDim)(`Current mode: ${chalk.hex(theme.amber)(current)}`), + ].join("\n") + + console.log(frame(content, { title: "crisp-help", borderColor: theme.amber, padding: 0 })) + return { type: "help" } +} diff --git a/apps/supercode-cli/server/src/cli/commands/slashCommands/crisp-review.ts b/apps/supercode-cli/server/src/cli/commands/slashCommands/crisp-review.ts new file mode 100644 index 0000000..77f9017 --- /dev/null +++ b/apps/supercode-cli/server/src/cli/commands/slashCommands/crisp-review.ts @@ -0,0 +1,56 @@ +import chalk from "chalk" +import { execSync } from "node:child_process" +import { theme, frame } from "src/cli/utils/tui.ts" +import { getCrispModeSync } from "src/cli/crisp/index.ts" +import type { SlashCommandResult } from "./index.ts" + +export async function crispReviewCommand(argsStr: string): Promise { + const mode = getCrispModeSync() + + let diff: string + try { + const staged = execSync("git diff --cached", { encoding: "utf-8" }) + const unstaged = execSync("git diff", { encoding: "utf-8" }) + diff = staged || unstaged + if (!diff) { + console.log( + frame( + `${chalk.hex(theme.amber).bold("No changes to review")}\n\n${chalk.hex(theme.muted)("There are no staged or unstaged diffs. Make some changes first, then run /crisp-review.")}`, + { title: "crisp-review", borderColor: theme.amber, padding: 0 }, + ), + ) + return { type: "help" } + } + } catch { + console.log( + frame( + `${chalk.hex(theme.red).bold("Git error")}\n\n${chalk.hex(theme.muted)("Could not read git diff. Are you in a git repository?")}`, + { title: "crisp-review", borderColor: theme.red, padding: 0 }, + ), + ) + return { type: "help" } + } + + const modeLabel = mode === "off" ? "full" : mode + const header = + `Review this diff against the Crisp simplicity ladder (${modeLabel}).\n` + + `One line per finding. Format: L: . .\n\n` + + `Tags:\n` + + ` delete: dead code, unused flexibility, speculative feature. Replacement: nothing.\n` + + ` stdlib: hand-rolled thing the stdlib ships. Name the function.\n` + + ` native: dependency or code doing what the platform already does. Name the feature.\n` + + ` yagni: abstraction with one implementation, config nobody sets, layer with one caller.\n` + + ` shrink: same logic, fewer lines. Show the shorter form.\n\n` + + `End with: net: - lines possible.\n` + + `If nothing to cut: "Lean already. Ship."\n\n` + + `${diff}` + + console.log( + frame( + `${chalk.hex(theme.amber).bold("Supercode Crisp Review")} ${chalk.hex(theme.muted)(`mode: ${modeLabel}`)}\n\n${chalk.hex(theme.greenDim)("Diff loaded. Evaluating against simplicity ladder...")}`, + { title: "crisp-review", borderColor: theme.amber, padding: 0 }, + ), + ) + + return { type: "message", message: header } +} diff --git a/apps/supercode-cli/server/src/cli/commands/slashCommands/crisp.ts b/apps/supercode-cli/server/src/cli/commands/slashCommands/crisp.ts new file mode 100644 index 0000000..159426a --- /dev/null +++ b/apps/supercode-cli/server/src/cli/commands/slashCommands/crisp.ts @@ -0,0 +1,48 @@ +import chalk from "chalk" +import { CRISP_MODES, getCrispModeSync, setCrispMode, CRISP_MODE_LABELS } from "src/cli/crisp/index.ts" +import { theme, frame } from "src/cli/utils/tui.ts" +import type { SlashCommandResult } from "./index.ts" +import type { CrispMode } from "src/cli/crisp/config.ts" + +export async function crispCommand(argsStr: string): Promise { + const parts = argsStr.trim().split(/\s+/).filter(Boolean) + const raw = parts[0] ?? "" + + if (raw === "lite" || raw === "full" || raw === "ultra" || raw === "off" || raw === "on") { + const mode: CrispMode = raw === "on" ? "full" : raw + await setCrispMode(mode) + if (mode === "off") { + console.log(` ${chalk.hex(theme.green)("✓")} ${chalk.hex(theme.muted)("Crisp disabled")}`) + } else { + console.log( + frame( + `${chalk.hex(theme.amber).bold(`Supercode Crisp: ${mode}`)}\n\n${chalk.hex(theme.muted)(CRISP_MODE_LABELS[mode])}\n\nThe simplicity ladder will be injected into every agent session.\nTry: /crisp status`, + { title: `crisp:${mode}`, borderColor: theme.amber, padding: 0 }, + ), + ) + } + return { type: "help" } + } + + // Default / "on" / "status" → show status + const current = getCrispModeSync() + const statusLines = [ + chalk.hex(theme.amber).bold(`Supercode Crisp: ${current}`), + "", + `${chalk.hex(theme.muted)("Usage:")} /crisp { lite | full | ultra | off | status }`, + "", + ...CRISP_MODES.map((m) => { + const indicator = m === current ? chalk.hex(theme.green)("▸") : " " + return ` ${indicator} ${chalk.hex(m === current ? theme.white : theme.muted)(`/${m}`).padEnd(18)} ${chalk.hex(m === current ? theme.green : theme.greenDim)(CRISP_MODE_LABELS[m])}` + }), + ].join("\n") + + console.log( + frame(statusLines, { + title: "crisp", + borderColor: theme.amber, + padding: 0, + }), + ) + return { type: "help" } +} diff --git a/apps/supercode-cli/server/src/cli/commands/slashCommands/index.ts b/apps/supercode-cli/server/src/cli/commands/slashCommands/index.ts index 4b9afd9..928221a 100644 --- a/apps/supercode-cli/server/src/cli/commands/slashCommands/index.ts +++ b/apps/supercode-cli/server/src/cli/commands/slashCommands/index.ts @@ -44,6 +44,12 @@ export const COMMANDS = [ { cmd: "/mcp", desc: "Manage MCP connections (add, connect, toggle, remove)" }, { cmd: "/connectors", desc: "Manage app connectors (Merge, Composer, etc.)" }, { cmd: "/skills", desc: "List installed agent skills and view their SKILL.md" }, + { cmd: "/crisp", desc: "Set crisp mode (on|lite|full|ultra|off) or show status" }, + { cmd: "/crisp-review", desc: "Review git diff against the simplicity ladder" }, + { cmd: "/crisp-audit", desc: "Audit workspace for over-engineering patterns" }, + { cmd: "/crisp-debt", desc: "Find [crisp:N] tagged shortcuts / deferred work" }, + { cmd: "/crisp-gain", desc: "Impact scoreboard — LOC, deps, simplification potential" }, + { cmd: "/crisp-help", desc: "Quick reference for crisp commands" }, { cmd: "/sk", desc: "Alias for /skills" }, ] @@ -130,6 +136,30 @@ const handlers: Record Promise> = } return { type: "skills" } }, + crisp: async (args) => { + const { crispCommand } = await import("./crisp.ts") + return crispCommand(args) + }, + "crisp-review": async (args) => { + const { crispReviewCommand } = await import("./crisp-review.ts") + return crispReviewCommand(args) + }, + "crisp-audit": async (args) => { + const { crispAuditCommand } = await import("./crisp-audit.ts") + return crispAuditCommand(args) + }, + "crisp-debt": async () => { + const { crispDebtCommand } = await import("./crisp-debt.ts") + return crispDebtCommand("") + }, + "crisp-gain": async (args) => { + const { crispGainCommand } = await import("./crisp-gain.ts") + return crispGainCommand(args) + }, + "crisp-help": async () => { + const { crispHelpCommand } = await import("./crisp-help.ts") + return crispHelpCommand() + }, search: async (args) => ({ type: "message", message: chatify("search", args) }), scrape: async (args) => ({ type: "message", message: chatify("scrape", args) }), interact: async (args) => ({ type: "message", message: chatify("interact", args) }), diff --git a/apps/supercode-cli/server/src/cli/crisp/config.ts b/apps/supercode-cli/server/src/cli/crisp/config.ts new file mode 100644 index 0000000..7d2880b --- /dev/null +++ b/apps/supercode-cli/server/src/cli/crisp/config.ts @@ -0,0 +1,32 @@ +import fs from "fs" +import path from "path" +import os from "os" + +export type CrispMode = "off" | "lite" | "full" | "ultra" + +export const CRISP_MODES: CrispMode[] = ["off", "lite", "full", "ultra"] + +export const CRISP_MODE_LABELS: Record = { + off: "Off — Crisp disabled", + lite: "Lite — helpful guidelines, gentle nudges toward simplicity", + full: "Full — binding constraints, every abstraction must be justified", + ultra: "Ultra — hard constraints, burden of proof on complexity", +} + +const CONFIG_DIR = path.join(os.homedir(), ".config", "supercode") +const CONFIG_FILE = path.join(CONFIG_DIR, "cli-config.json") + +export function getCrispModeSync(): CrispMode { + try { + const data = fs.readFileSync(CONFIG_FILE, "utf-8") + const config = JSON.parse(data) + const mode = config.crispMode as CrispMode + if (CRISP_MODES.includes(mode)) return mode + } catch {} + return "off" +} + +export async function setCrispMode(mode: CrispMode): Promise { + const { saveCliConfig } = await import("src/lib/cli-config.ts") + await saveCliConfig({ crispMode: mode } as any) +} diff --git a/apps/supercode-cli/server/src/cli/crisp/index.ts b/apps/supercode-cli/server/src/cli/crisp/index.ts new file mode 100644 index 0000000..bb55734 --- /dev/null +++ b/apps/supercode-cli/server/src/cli/crisp/index.ts @@ -0,0 +1,3 @@ +export type { CrispMode } from "./config.ts" +export { CRISP_MODES, CRISP_MODE_LABELS, getCrispModeSync, setCrispMode } from "./config.ts" +export { crispLadderPrompt } from "./prompt.ts" diff --git a/apps/supercode-cli/server/src/cli/crisp/prompt.ts b/apps/supercode-cli/server/src/cli/crisp/prompt.ts new file mode 100644 index 0000000..75c9087 --- /dev/null +++ b/apps/supercode-cli/server/src/cli/crisp/prompt.ts @@ -0,0 +1,85 @@ +import type { CrispMode } from "./config.ts" + +export function crispLadderPrompt(mode: CrispMode): string | null { + if (mode === "off") return null + + const intensity: Record = { + off: "", + lite: + "Apply these principles as helpful guidelines \u2014 prefer simplicity but don\u2019t over-enforce.", + full: + "Apply these principles as binding constraints. Every abstraction, dependency, and pattern must be justified against this ladder.", + ultra: + "Enforce these principles as hard constraints. The burden of proof is on the developer adding any complexity.", + } + + const lines: string[] = [ + `## Supercode Crisp (${mode}) \u2014 The Simplicity Ladder`, + "", + intensity[mode], + "", + "When writing or reviewing code, evaluate EVERY design decision against this ladder from top to bottom:", + "", + "1. **YAGNI** \u2014 You Aren\u2019t Gonna Need It. If it\u2019s not needed right now, don\u2019t build it. No future-proofing, no speculative abstractions.", + ` \u2192 Tag: [crisp:1]`, + "", + "2. **Reuse what exists** \u2014 Before writing anything new, check if the language/stdlib/project already has it. Copy-paste-modify beats import-a-library.", + ` \u2192 Tag: [crisp:2]`, + "", + "3. **Standard library first** \u2014 Use built-in APIs over third-party packages. OS features over npm/crates/pip.", + ` \u2192 Tag: [crisp:3]`, + "", + "4. **Native platform APIs** \u2014 Prefer OS/platform built-ins over userland solutions. Shell over Python. CSS over JS. HTML over framework.", + ` \u2192 Tag: [crisp:4]`, + "", + "5. **Dependencies are debt** \u2014 Every dependency is a liability. Before adding one: can you inline it? Can you strip it? Can you replace 50 lines of deps with 10 lines of code?", + ` \u2192 Tag: [crisp:5]`, + "", + "6. **One line > many** \u2014 If you can express the logic in a single expression, do it. Each temporary variable is a concept the reader must hold in working memory.", + ` \u2192 Tag: [crisp:6]`, + "", + "7. **Minimum code to satisfy the spec** \u2014 The best code is the code you didn\u2019t write. Delete unused imports. Remove dead branches.", + ` \u2192 Tag: [crisp:7]`, + "", + "### Rules", + "- No unrequested abstractions: no interface with one implementation, no factory for one product, no config for a value that never changes.", + "- No boilerplate, no scaffolding for later. Later can scaffold for itself.", + "- Deletion over addition. Boring over clever. Clever is what someone decodes at 3am.", + "- Fewest files possible. Shortest working diff wins.", + "- Bug fix = root cause, not symptom. Grep every caller before editing. One guard in the shared function beats a guard in every caller.", + "", + "### When NOT to be crisp", + "Never simplify away: input validation at trust boundaries, error handling that prevents data loss, security measures, accessibility basics, anything explicitly requested.", + "If the user insists on the full version, build it. No re-arguing.", + "", + "### Tests are not optional", + "Non-trivial logic (a branch, a loop, a parser, a money/security path) leaves ONE runnable check behind: an assert-based self-check or one small test file. No frameworks, no fixtures, no per-function suites unless asked. Trivial one-liners need no test (YAGNI applies to tests too).", + "", + "### Output discipline", + "Code first. Then at most three short lines: what was skipped, when to add it. No essays, no feature tours, no design notes.", + "If the explanation is longer than the code, delete the explanation. Every paragraph defending a simplification is complexity smuggled back in as prose.", + "", + "Mark deliberate simplifications that cut a real corner with a known ceiling with a `crisp:` comment naming the ceiling and upgrade path: `// crisp: global lock, per-account locks if throughput matters`", + "", + "### How to apply", + "- When reviewing code, reference the rung number: [crisp:3] use URL constructor instead of parsing manually", + "- When adding an abstraction, ask: which rung of the ladder does this serve?", + "- When you see over-engineering, tag it: [crisp:1] YAGNI \u2014 this config system supports use cases that don\u2019t exist yet", + '- Tag findings with [crisp:N] so the debt tracker can find them. Mark deliberate corner-cuts with `crisp:` comment + ceiling + upgrade path.', + "", + ] + + if (mode === "ultra") { + lines.push( + "### Ultra mode additions", + "- No new npm/crates/pip dependencies without explicit approval", + "- No new types/interfaces unless the function signature would be ambiguous without them", + "- No new files unless the existing file exceeds 400 lines", + "- Any abstraction must prove it eliminates more code than it adds (negative LoC)", + "- YAGNI extremist: deletion before addition. Challenge requirements before building.", + "", + ) + } + + return lines.join("\n") +} diff --git a/apps/supercode-cli/server/src/cli/workspace/context.ts b/apps/supercode-cli/server/src/cli/workspace/context.ts index 17156aa..bb48fcc 100644 --- a/apps/supercode-cli/server/src/cli/workspace/context.ts +++ b/apps/supercode-cli/server/src/cli/workspace/context.ts @@ -180,6 +180,7 @@ export function buildSystemPrompt(info: WorkspaceInfo, hasTools = false): string lines.push(" string literal — strip the quotes before calling `url_fetch`.") lines.push("") pushSkillsSection(lines) + pushCrispSection(lines) lines.push("## Code Review & Analysis") lines.push("") @@ -215,6 +216,69 @@ export function buildSystemPrompt(info: WorkspaceInfo, hasTools = false): string return lines.join("\n") } +function pushCrispSection(lines: string[]): void { + const configPath = path.join(os.homedir(), ".config", "supercode", "cli-config.json") + let mode: string = "off" + try { + const data = fs.readFileSync(configPath, "utf-8") + const config = JSON.parse(data) + if (["off", "lite", "full", "ultra"].includes(config.crispMode)) { + mode = config.crispMode + } + } catch {} + if (mode === "off") return + + const intensity: Record = { + lite: "Apply these principles as helpful guidelines \u2014 prefer simplicity but don\u2019t over-enforce.", + full: "Apply these principles as binding constraints. Every abstraction, dependency, and pattern must be justified against this ladder.", + ultra: "Enforce these principles as hard constraints. The burden of proof is on the developer adding any complexity.", + } + + lines.push("") + lines.push(`## Supercode Crisp (${mode}) \u2014 The Simplicity Ladder`) + lines.push("") + lines.push(intensity[mode] ?? "") + lines.push("") + lines.push("When writing or reviewing code, evaluate EVERY design decision against this ladder from top to bottom:") + lines.push("") + lines.push("1. **YAGNI** \u2014 You Aren\u2019t Gonna Need It. If it\u2019s not needed right now, don\u2019t build it. No future-proofing, no speculative abstractions.") + lines.push(" \u2192 Tag: [crisp:1]") + lines.push("") + lines.push("2. **Reuse what exists** \u2014 Before writing anything new, check if the language/stdlib/project already has it. Copy-paste-modify beats import-a-library.") + lines.push(" \u2192 Tag: [crisp:2]") + lines.push("") + lines.push("3. **Standard library first** \u2014 Use built-in APIs over third-party packages. OS features over npm/crates/pip.") + lines.push(" \u2192 Tag: [crisp:3]") + lines.push("") + lines.push("4. **Native platform APIs** \u2014 Prefer OS/platform built-ins over userland solutions. Shell over Python. CSS over JS. HTML over framework.") + lines.push(" \u2192 Tag: [crisp:4]") + lines.push("") + lines.push("5. **Dependencies are debt** \u2014 Every dependency is a liability. Before adding one: can you inline it? Can you strip it? Can you replace 50 lines of deps with 10 lines of code?") + lines.push(" \u2192 Tag: [crisp:5]") + lines.push("") + lines.push("6. **One line > many** \u2014 If you can express the logic in a single expression, do it. Each temporary variable is a concept the reader must hold in working memory.") + lines.push(" \u2192 Tag: [crisp:6]") + lines.push("") + lines.push("7. **Minimum code to satisfy the spec** \u2014 The best code is the code you didn\u2019t write. Delete unused imports. Remove dead branches.") + lines.push(" \u2192 Tag: [crisp:7]") + lines.push("") + lines.push("### How to apply") + lines.push("- When reviewing code, reference the rung number: [crisp:3] use URL constructor instead of parsing manually") + lines.push("- When adding an abstraction, ask: which rung of the ladder does this serve?") + lines.push("- When you see over-engineering, tag it: [crisp:1] YAGNI \u2014 this config system supports use cases that don\u2019t exist yet") + lines.push("- Tag ALL findings with [crisp:N] so the debt tracker can find them later") + lines.push("") + + if (mode === "ultra") { + lines.push("### Ultra mode additions") + lines.push("- No new npm/crates/pip dependencies without explicit approval") + lines.push("- No new types/interfaces unless the function signature would be ambiguous without them") + lines.push("- No new files unless the existing file exceeds 400 lines") + lines.push("- Any abstraction must prove it eliminates more code than it adds (negative LoC)") + lines.push("") + } +} + function pushSkillsSection(lines: string[]): void { const skillsDir = path.join(os.homedir(), ".supercode", "skills") const lockPath = path.join(os.homedir(), ".supercode", "skills-lock.json") diff --git a/apps/supercode-cli/server/src/lib/cli-config.ts b/apps/supercode-cli/server/src/lib/cli-config.ts index 35ae745..6a31ace 100644 --- a/apps/supercode-cli/server/src/lib/cli-config.ts +++ b/apps/supercode-cli/server/src/lib/cli-config.ts @@ -12,6 +12,7 @@ export interface CliConfig { provider: ModelProvider model: string mode: "chat" | "agent" + crispMode?: "off" | "lite" | "full" | "ultra" apiKeys?: Partial> mcpServers?: Record mcpCredentials?: Record diff --git a/apps/supercode-cli/server/src/tools/definitions/crisp-audit.ts b/apps/supercode-cli/server/src/tools/definitions/crisp-audit.ts new file mode 100644 index 0000000..bb391e9 --- /dev/null +++ b/apps/supercode-cli/server/src/tools/definitions/crisp-audit.ts @@ -0,0 +1,31 @@ +import { z } from "zod" + +const auditSchema = z.object({ + path: z + .string() + .optional() + .describe("Path to a specific file or directory to audit. If omitted, audit the whole workspace."), + focus: z + .enum(["all", "dependencies", "abstractions", "complexity", "boilerplate"]) + .optional() + .default("all") + .describe("Which aspect of over-engineering to focus on."), +}) + +export type CrispAuditArgs = z.infer + +export const crispAuditTool = { + description: + "Scan the workspace (or a specific path) for over-engineering patterns and rank findings by severity. " + + "Checks for unnecessary abstractions, overuse of dependencies, premature optimization, " + + "and other violations of the Supercode Crisp simplicity ladder. Returns ranked findings.", + parameters: auditSchema, + execute: async (args: CrispAuditArgs) => { + return JSON.stringify({ + success: true, + hint: "Walk the workspace tree and key files. Apply the full crisp ladder and tag every finding with [crisp:N]. Rank by severity (1-7, where 1=YAGNI is most important).", + path: args.path ?? "(workspace root)", + focus: args.focus, + }) + }, +} diff --git a/apps/supercode-cli/server/src/tools/definitions/crisp-debt.ts b/apps/supercode-cli/server/src/tools/definitions/crisp-debt.ts new file mode 100644 index 0000000..4b210d9 --- /dev/null +++ b/apps/supercode-cli/server/src/tools/definitions/crisp-debt.ts @@ -0,0 +1,25 @@ +import { z } from "zod" + +const debtSchema = z.object({ + path: z + .string() + .optional() + .describe("Path to search for crisp: comments. Defaults to workspace root."), +}) + +export type CrispDebtArgs = z.infer + +export const crispDebtTool = { + description: + "Find and report all [crisp:N] tagged comments in the workspace. " + + "These are markers left by previous crisp reviews indicating code that should be simplified. " + + "Returns each tag with its file location and surrounding context.", + parameters: debtSchema, + execute: async (args: CrispDebtArgs) => { + return JSON.stringify({ + success: true, + hint: "Use run_command to grep for [crisp: across the workspace. Group results by rung number and file path. Report totals per rung.", + path: args.path ?? "(workspace root)", + }) + }, +} diff --git a/apps/supercode-cli/server/src/tools/definitions/crisp-gain.ts b/apps/supercode-cli/server/src/tools/definitions/crisp-gain.ts new file mode 100644 index 0000000..1d7f31f --- /dev/null +++ b/apps/supercode-cli/server/src/tools/definitions/crisp-gain.ts @@ -0,0 +1,26 @@ +import { z } from "zod" + +const gainSchema = z.object({ + scope: z + .enum(["workspace", "file", "summary"]) + .optional() + .default("summary") + .describe("What scope to assess gain for."), +}) + +export type CrispGainArgs = z.infer + +export const crispGainTool = { + description: + "Assess the impact of applying Supercode Crisp principles. " + + "Measures complexity reduction, dependency savings, and code elimination opportunities. " + + "Use this to report how much simpler the codebase could be.", + parameters: gainSchema, + execute: async (args: CrispGainArgs) => { + return JSON.stringify({ + success: true, + hint: "Assess crisp impact by running analysis on the workspace. Count total lines, dependencies, files, and estimate how much could be eliminated by applying each ladder rung.", + scope: args.scope, + }) + }, +} diff --git a/apps/supercode-cli/server/src/tools/definitions/crisp-review.ts b/apps/supercode-cli/server/src/tools/definitions/crisp-review.ts new file mode 100644 index 0000000..6a3c0ed --- /dev/null +++ b/apps/supercode-cli/server/src/tools/definitions/crisp-review.ts @@ -0,0 +1,30 @@ +import { z } from "zod" + +const reviewSchema = z.object({ + code: z + .string() + .optional() + .describe("Code snippet or diff to review against the crisp ladder. If omitted, the AI should gather context from git diff or file reads."), + context: z + .string() + .optional() + .describe("Optional context about what this code does or why it was written."), +}) + +export type CrispReviewArgs = z.infer + +export const crispReviewTool = { + description: + "Review code or a diff against the Supercode Crisp simplicity ladder. " + + "Returns structured findings tagged with [crisp:N] markers for each ladder rung. " + + "Use this when the user asks for a crisp review of specific code or changes.", + parameters: reviewSchema, + execute: async (args: CrispReviewArgs) => { + return JSON.stringify({ + success: true, + hint: "Analyze the provided code against the crisp ladder in your system prompt. Tag each finding with [crisp:N].", + code: args.code ?? "(auto-detected from workspace)", + context: args.context, + }) + }, +} diff --git a/apps/supercode-cli/server/src/tools/registry.ts b/apps/supercode-cli/server/src/tools/registry.ts index 4078010..cbd5437 100644 --- a/apps/supercode-cli/server/src/tools/registry.ts +++ b/apps/supercode-cli/server/src/tools/registry.ts @@ -19,6 +19,10 @@ import { delegateTool, taskTool } from "./definitions/delegate.ts" import { questionTool } from "./definitions/question.ts" import { todowriteTool } from "./definitions/todowrite.ts" import { skillTool } from "./definitions/skill.ts" +import { crispReviewTool } from "./definitions/crisp-review.ts" +import { crispAuditTool } from "./definitions/crisp-audit.ts" +import { crispDebtTool } from "./definitions/crisp-debt.ts" +import { crispGainTool } from "./definitions/crisp-gain.ts" import { permissionManager } from "./permission-manager.ts" // @@ -95,6 +99,10 @@ export const toolMeta: Record = { question: { category: "agent", requiresPermission: false, description: questionTool.description }, todowrite: { category: "agent", requiresPermission: false, description: todowriteTool.description }, skill: { category: "agent", requiresPermission: false, description: skillTool.description }, + crisp_review: { category: "agent", requiresPermission: false, description: crispReviewTool.description }, + crisp_audit: { category: "agent", requiresPermission: false, description: crispAuditTool.description }, + crisp_debt: { category: "agent", requiresPermission: false, description: crispDebtTool.description }, + crisp_gain: { category: "agent", requiresPermission: false, description: crispGainTool.description }, } export const tools = { @@ -118,4 +126,8 @@ export const tools = { question: defineTool(questionTool), todowrite: defineTool(todowriteTool), skill: defineTool(skillTool), + crisp_review: defineTool(crispReviewTool), + crisp_audit: defineTool(crispAuditTool), + crisp_debt: defineTool(crispDebtTool), + crisp_gain: defineTool(crispGainTool), } diff --git a/apps/web/app/(pages)/blog/[slug]/page.tsx b/apps/web/app/(pages)/blog/[slug]/page.tsx new file mode 100644 index 0000000..f88a4fd --- /dev/null +++ b/apps/web/app/(pages)/blog/[slug]/page.tsx @@ -0,0 +1,66 @@ +import { notFound } from "next/navigation" +import { compileMDX } from "next-mdx-remote/rsc" +import remarkGfm from "remark-gfm" +import { getPostBySlug, getAllPosts } from "@/lib/blog" +import { PostPage } from "./post-client" +import { mdxComponents } from "@/components/blog/mdx-components" + +const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL || "https://supercli.vercel.app" + +export async function generateStaticParams() { + const posts = getAllPosts() + return posts.map((post) => ({ slug: post.slug })) +} + +export async function generateMetadata({ + params, +}: { + params: Promise<{ slug: string }> +}) { + const { slug } = await params + const post = getPostBySlug(slug) + if (!post) return {} + + const ogImage = post.image ? `${SITE_URL}${post.image}` : `${SITE_URL}/og-image.png` + + return { + title: `${post.title} - Supercode Blog`, + description: post.description, + openGraph: { + title: post.title, + description: post.description, + type: "article", + publishedTime: post.date, + authors: [post.author], + images: [{ url: ogImage, width: 1200, height: 630 }], + }, + twitter: { + card: "summary_large_image", + title: post.title, + description: post.description, + images: [ogImage], + }, + } +} + +export default async function Page({ + params, +}: { + params: Promise<{ slug: string }> +}) { + const { slug } = await params + const post = getPostBySlug(slug) + if (!post) notFound() + + const { content } = await compileMDX({ + source: post.content, + components: mdxComponents, + options: { + mdxOptions: { + remarkPlugins: [remarkGfm], + }, + }, + }) + + return {content} +} diff --git a/apps/web/app/(pages)/blog/[slug]/post-client.tsx b/apps/web/app/(pages)/blog/[slug]/post-client.tsx new file mode 100644 index 0000000..6fbf3cb --- /dev/null +++ b/apps/web/app/(pages)/blog/[slug]/post-client.tsx @@ -0,0 +1,101 @@ +"use client" + +import { motion } from "framer-motion" +import Link from "next/link" +import Navbar from "@/components/homepage/navbar" +import Footer from "@/components/homepage/footer" +import type { BlogPost } from "@/lib/utils" +import { formatDate } from "@/lib/utils" + +interface PostPageProps { + post: BlogPost + children: React.ReactNode +} + +const EASE = [0.23, 1, 0.32, 1] as const + +export function PostPage({ post, children }: PostPageProps) { + return ( +
+
+
+ + +
+ {/* Post header */} +
+ +
+ + ← Back to blog + +
+ +
+ + {post.category} + + + {post.reading_time} + +
+ +

+ {post.title} +

+ +

+ {post.description} +

+ +
+
+ Supercode +
+
+ + {post.author} + + + {formatDate(post.date)} + {post.updated_at && ( + · Updated {formatDate(post.updated_at)} + )} + +
+
+
+
+ + {/* Post content */} + + {children} + + + {/* Post footer */} +
+ + ← Back to all posts + +
+
+ +
+
+ ) +} diff --git a/apps/web/app/(pages)/blog/blog-client.tsx b/apps/web/app/(pages)/blog/blog-client.tsx new file mode 100644 index 0000000..473a3bf --- /dev/null +++ b/apps/web/app/(pages)/blog/blog-client.tsx @@ -0,0 +1,125 @@ +"use client" + +import { useState } from "react" +import { motion } from "framer-motion" +import Navbar from "@/components/homepage/navbar" +import Footer from "@/components/homepage/footer" +import { BlogCard } from "@/components/blog/blog-card" +import { FeaturedPost } from "@/components/blog/featured-post" +import type { BlogPostMeta } from "@/lib/utils" + +interface BlogPageProps { + posts: BlogPostMeta[] + featured: BlogPostMeta[] +} + +const EASE = [0.23, 1, 0.32, 1] as const + +export function BlogPage({ posts, featured }: BlogPageProps) { + const categories = ["all", ...new Set(posts.map((p) => p.category))] + const [activeCategory, setActiveCategory] = useState("all") + + const filtered = + activeCategory === "all" + ? posts + : posts.filter((p) => p.category === activeCategory) + + const latest = filtered.slice(0, 2) + const otherPosts = filtered.slice(2) + + return ( +
+
+
+ + +
+ {/* Header */} +
+ + Blog + + + Insights on developer tools, AI agents, and the future of software + engineering. + +
+ + {/* Category filter */} +
+ + {categories.map((cat) => ( + + ))} + +
+ + {/* Latest posts — 2 column grid */} + {latest.length > 0 && ( +
+
+ {latest.map((post, i) => ( + + ))} +
+
+ )} + + {/* Other posts — 3 column grid */} + {otherPosts.length > 0 && ( +
+ + Other Posts + +
+ {otherPosts.map((post, i) => ( + + ))} +
+
+ )} + + {filtered.length === 0 && ( +
+

+ No posts yet. Check back soon. +

+
+ )} +
+ +
+
+ ) +} diff --git a/apps/web/app/(pages)/blog/page.tsx b/apps/web/app/(pages)/blog/page.tsx new file mode 100644 index 0000000..c45fb4e --- /dev/null +++ b/apps/web/app/(pages)/blog/page.tsx @@ -0,0 +1,15 @@ +import { getAllPosts, getFeaturedPosts } from "@/lib/blog" +import { BlogPage } from "./blog-client" + +export const metadata = { + title: "Blog - Supercode", + description: + "Insights on developer tools, AI agents, and the future of software engineering.", +} + +export default function Page() { + const posts = getAllPosts() + const featured = getFeaturedPosts() + + return +} diff --git a/apps/web/app/(pages)/features/page.tsx b/apps/web/app/(pages)/features/page.tsx new file mode 100644 index 0000000..4a27a4a --- /dev/null +++ b/apps/web/app/(pages)/features/page.tsx @@ -0,0 +1,522 @@ +"use client" + +import { useState } from "react" +import { motion } from "framer-motion" +import Navbar from "@/components/homepage/navbar" +import Footer from "@/components/homepage/footer" +import Link from "next/link" + +interface Feature { + title: string + description: string + code: string +} + +interface Category { + label: string + icon: string + features: Feature[] +} + +const CATEGORIES: Category[] = [ + { + label: "CLI", + icon: ">_", + features: [ + { + title: "Terminal-native", + description: "Full machine access from your terminal. Read files, run commands, edit code, open apps, search the web.", + code: "$ supercode 'refactor the auth middleware'\n ✓ src/auth/middleware.ts updated\n ✓ tests/auth/middleware.test.ts added\n ? Approve changes? [Y/n]", + }, + { + title: "Auto-update", + description: "Always on the latest version. No manual downloads. No stale binaries.", + code: "$ supercode --version\n Supercode v0.1.56\n Checking for updates...\n Already up to date.", + }, + { + title: "Slash commands", + description: "40+ built-in commands for every workflow. /build, /plan, /explore, /compact, /voice, and more.", + code: "$ /help\n Available commands:\n /build generate code\n /plan research & plan\n /explore navigate codebase\n /voice toggle voice mode", + }, + ], + }, + { + label: "Taste", + icon: "\u2B50", + features: [ + { + title: "Learns your style", + description: "taste-1 observes your coding patterns and applies them automatically. No config files, no prompts.", + code: "$ supercode 'add error handling'\n [taste] Detected pattern: result\n [taste] Using your error boundary style\n ✓ error handling added across 3 files", + }, + { + title: "Cross-session memory", + description: "Remembers your preferences, project context, and decisions across sessions. Pick up where you left off.", + code: "$ supercode 'continue the api work'\n [memory] Restoring session...\n [memory] Project: supercode-api\n [memory] Last task: rate limiting middleware", + }, + { + title: "Permission profiles", + description: "Granular control per file, command, or tool. Chat mode (read-only) or Agent mode (full access).", + code: "$ /mode agent\n [permissions] Switching to agent mode\n [permissions] Allowed: read, write, exec\n [permissions] Restricted: rm -rf, sudo", + }, + ], + }, + { + label: "Models", + icon: "\u25A1", + features: [ + { + title: "Multi-model support", + description: "Claude, GPT, Gemini, DeepSeek, Llama, Mistral, Qwen — use any model, switch anytime.", + code: "$ /model claude-sonnet-4\n Switching to claude-sonnet-4...\n $ /model deepseek-v4\n Switching to deepseek-v4...\n $ /model gpt-5\n Switching to gpt-5...", + }, + { + title: "Bring your own key", + description: "Use your own API keys for any provider. No lock-in, no markup, full control.", + code: "$ /connect openai\n Enter your OpenAI API key:\n ********************\n ✓ Connected. Using your key.", + }, + { + title: "Local models", + description: "Run models locally with Ollama. Zero latency, zero data leaving your machine.", + code: "$ /model local/llama-4\n [ollama] Starting llama-4...\n [ollama] Running on localhost:11434\n ✓ Model ready (1.2s)", + }, + ], + }, + { + label: "Agents", + icon: "\u25C8", + features: [ + { + title: "7 built-in agents", + description: "Specialized agents for building, planning, exploring, compacting, summarizing — each with tuned profiles.", + code: "$ /build 'create a rest api'\n ─────────────────────────────\n Agent: build Mode: agent\n Plan: REST API scaffold\n Files: 12 Est. time: 45s", + }, + { + title: "Context window", + description: "Expansive context with transparent breakdown. See exactly what the agent sees.", + code: "$ /context\n Context window:\n ─────────────────\n Files: 24 (128K tokens)\n Memory: 3 sessions\n System: 2 rulesets", + }, + { + title: "Agent Handler", + description: "Merge.dev integration for unified access to 100+ enterprise tools through a single interface.", + code: "$ supercode 'sync jira tickets'\n [handler] Connecting to Jira...\n [handler] Fetching assigned tickets\n [handler] Syncing to local context\n ✓ 8 tickets loaded", + }, + ], + }, + { + label: "Voice", + icon: "\u266B", + features: [ + { + title: "Voice control", + description: "Speak to your terminal. Ctrl+Shift+V to toggle, /voice to enable. Natural language, zero latency.", + code: "[Voice mode enabled]\n You: refactor the login component\n Supercode: Analyzing login.tsx...\n You: extract the validation logic\n Supercode: Done. Review changes?", + }, + { + title: "Multi-provider STT", + description: "Choose your speech-to-text engine — ElevenLabs, Groq, or custom. Switch providers on the fly.", + code: "$ /settings stt groq\n Speech-to-text: Groq\n Model: whisper-large-v3\n Latency: ~400ms", + }, + ], + }, + { + label: "Privacy", + icon: "\u25CB", + features: [ + { + title: "No training on your code", + description: "Your code stays yours. We never train on your data. Period.", + code: "$ supercode --privacy\n Data policy:\n ─────────────────\n Training on your code: No\n Data retention: 30 days\n Encryption: AES-256", + }, + { + title: "Open source", + description: "Fully open source. Audit every line, self-host if you want, contribute improvements.", + code: "$ git clone github.com/yashdev9274/superCli\n $ cd superCli && bun install\n $ bun run dev\n ✓ Supercode running locally", + }, + ], + }, +] + +const HOW_IT_WORKS = [ + { + step: 1, + title: "Install", + description: "One command to install. Works on macOS, Linux, and Windows. No dependencies, no config.", + }, + { + step: 2, + title: "Connect", + description: "Bring your own API key or use our built-in free models. Connect your editor, GitHub, and tools.", + }, + { + step: 3, + title: "Delegate", + description: "Tell Supercode what to build in plain English. It plans, codes, debugs, and refactors — with your approval.", + }, + { + step: 4, + title: "Iterate", + description: "Refine with conversation. It remembers context across sessions. Your taste profile gets better over time.", + }, +] + +const FEATURE_FAQ = [ + { + q: "What models are available?", + a: "Supercode supports Claude Opus 4.8, GPT-5, Gemini Ultra 2, DeepSeek V4 Pro, Llama 4, Mistral Large, Qwen 3, and MiniMax M3. You can switch between models mid-session with /model.", + }, + { + q: "How does taste-1 work?", + a: "taste-1 observes your coding patterns — naming conventions, error handling style, import ordering — and applies them automatically. It learns locally and never leaves your machine. No telemetry, no training.", + }, + { + q: "Can I use Supercode offline?", + a: "Yes. With local models (Ollama), Supercode works fully offline. All core features — commands, agents, taste, permissions — run without internet.", + }, + { + q: "What's the difference between Chat and Agent mode?", + a: "Chat mode is read-only — the agent can view files and suggest changes, but you apply them manually. Agent mode grants full machine access with your approval per action. You choose what you're comfortable with.", + }, + { + q: "Is Supercode truly free?", + a: "Yes. Open models are always free. You can bring your own API key for premium models with zero markup. The Spark plan ($1/year) is fully refundable. Pro and Ultra add higher limits and premium features.", + }, + { + q: "What editors does Supercode integrate with?", + a: "Supercode works in any terminal — no editor plugin needed. We also have extensions for VS Code, Cursor, Zed, Windsurf, and VSCodium for a richer experience.", + }, +] + +const STATS = [ + { value: "40+", label: "slash commands", icon: ">" }, + { value: "7", label: "built-in agents", icon: "\u25C8" }, + { value: "15+", label: "model providers", icon: "\u25A1" }, + { value: "100%", label: "open source", icon: "\u25CB" }, +] + +function TerminalWindow({ code, className }: { code: string; className?: string }) { + return ( +
+
+ + + +
+
+        {code}
+      
+
+ ) +} + +function FeatureCard({ feature, index, categoryIndex }: { feature: Feature; index: number; categoryIndex: number }) { + return ( + +

{feature.title}

+

{feature.description}

+ +
+ ) +} + +function StatCard({ stat, index }: { stat: typeof STATS[number]; index: number }) { + return ( + +
{stat.icon}
+
{stat.value}
+
{stat.label}
+
+ ) +} + +function StaggerFadeIn({ + children, + className, + index = 0, +}: { + children: React.ReactNode + className?: string + index?: number +}) { + return ( + + {children} + + ) +} + +export default function FeaturesPage() { + const [expandedFaq, setExpandedFaq] = useState(null) + + return ( +
+
+
+ + + + {/* Hero */} +
+
+ +
+
+ Features +
+
+ +

+ Everything you need in a  + + terminal AI agent + +

+ +

+ CLI power, taste-driven code, multi-model flexibility, voice control, and + privacy-first design — all in one terminal-native agent. +

+ +
+ + Get started free + + + Download + +
+
+
+
+ + {/* Stats */} +
+
+
+ {STATS.map((stat, i) => ( + + ))} +
+
+
+ + {/* Feature categories */} +
+
+ {CATEGORIES.map((category, ci) => ( +
+ +
+ {category.icon} +

+ {category.label} +

+
+
+ + +
+ {category.features.map((feature, fi) => ( + + ))} +
+
+ ))} +
+
+ + {/* How it works */} +
+
+ +
+
+
+ Workflow +
+
+

+ How it works +

+

+ From zero to shipping in four steps. No config, no onboarding flow. +

+
+
+ +
+ {HOW_IT_WORKS.map((step, i) => ( + +
+ + {step.step} + + {i < HOW_IT_WORKS.length - 1 && ( +
+ )} +
+

{step.title}

+

{step.description}

+ + ))} +
+
+
+ + {/* Testimonial */} +
+
+ +
+
+ Supercode is the first AI coding tool that actually feels like it belongs + in the terminal. The taste system is a game-changer — it writes code + that looks like I wrote it. +
+
+
+ AD +
+
+
Alex Dominguez
+
Senior Engineer, Vercel
+
+
+
+
+
+ + {/* FAQ */} +
+
+ +

+ Frequently asked questions +

+
+ +
+ {FEATURE_FAQ.map((item, i) => { + const isOpen = expandedFaq === i + return ( +
+ +
+

+ {item.a} +

+
+
+ ) + })} +
+
+
+ + {/* CTA */} +
+
+ +

+ Ready to ship faster? +

+

+ Free and open source. Bring your own API key or use our built-in models. No credit card needed. +

+
+ + Get started free + + + View on GitHub + +
+
+
+
+ +
+
+ ) +} diff --git a/apps/web/components/blog/blog-card.tsx b/apps/web/components/blog/blog-card.tsx new file mode 100644 index 0000000..83c27d7 --- /dev/null +++ b/apps/web/components/blog/blog-card.tsx @@ -0,0 +1,67 @@ +"use client" + +import Link from "next/link" +import { motion } from "framer-motion" +import type { BlogPostMeta } from "@/lib/utils" +import { formatDate } from "@/lib/utils" + +interface BlogCardProps { + post: BlogPostMeta + index: number +} + +export function BlogCard({ post, index }: BlogCardProps) { + return ( + + + {post.image && ( +
+ {post.title} +
+ )} + +
+ + {post.category} + + + {post.reading_time} + +
+ +

+ {post.title} +

+ +

+ {post.description} +

+ +
+ + {formatDate(post.date)} + + + Read more → + +
+ +
+ ) +} diff --git a/apps/web/components/blog/featured-post.tsx b/apps/web/components/blog/featured-post.tsx new file mode 100644 index 0000000..24075d6 --- /dev/null +++ b/apps/web/components/blog/featured-post.tsx @@ -0,0 +1,74 @@ +"use client" + +import Link from "next/link" +import { motion } from "framer-motion" +import type { BlogPostMeta } from "@/lib/utils" +import { formatDate } from "@/lib/utils" + +interface FeaturedPostProps { + post: BlogPostMeta +} + +export function FeaturedPost({ post }: FeaturedPostProps) { + return ( + + + {post.image && ( +
+ {post.title} +
+ )} + +
+
+ + {post.category} + + + {post.reading_time} + +
+ +

+ {post.title} +

+ +

+ {post.description} +

+ +
+
+
+ Supercode +
+
+ + {post.author} + + + {formatDate(post.date)} + +
+
+ + Read article → + +
+
+ +
+ ) +} diff --git a/apps/web/components/blog/mdx-components.tsx b/apps/web/components/blog/mdx-components.tsx new file mode 100644 index 0000000..00ed12b --- /dev/null +++ b/apps/web/components/blog/mdx-components.tsx @@ -0,0 +1,217 @@ +import type { MDXComponents } from "mdx/types" +import Link from "next/link" + +function cn(...classes: (string | undefined | false)[]) { + return classes.filter(Boolean).join(" ") +} + +export const mdxComponents: MDXComponents = { + h1: ({ className, children, ...props }) => ( +

+ {children} +

+ ), + h2: ({ className, children, ...props }) => ( +

+ {children} +

+ ), + h3: ({ className, children, ...props }) => ( +

+ {children} +

+ ), + p: ({ className, children, ...props }) => ( +

+ {children} +

+ ), + a: ({ className, href, children, ...props }) => { + const isExternal = href?.startsWith("http") + if (isExternal) { + return ( + + {children} + + ) + } + return ( + + {children} + + ) + }, + ul: ({ className, children, ...props }) => ( +
    + {children} +
+ ), + ol: ({ className, children, ...props }) => ( +
    + {children} +
+ ), + li: ({ className, children, ...props }) => ( +
  • + {children} +
  • + ), + blockquote: ({ className, children, ...props }) => ( +
    + {children} +
    + ), + code: ({ className, children, ...props }) => { + const isInline = !className + if (isInline) { + return ( + + {children} + + ) + } + return ( + + {children} + + ) + }, + pre: ({ className, children, ...props }) => ( +
    +
    +
    +
    +
    +
    +
    +
    +
    +        {children}
    +      
    +
    + ), + hr: ({ className, ...props }) => ( +
    + ), + strong: ({ className, children, ...props }) => ( + + {children} + + ), + em: ({ className, children, ...props }) => ( + + {children} + + ), + img: ({ className, src, alt, ...props }) => ( + {alt + ), + table: ({ className, children, ...props }) => ( +
    + + {children} +
    +
    + ), + thead: ({ className, children, ...props }) => ( + + {children} + + ), + tbody: ({ className, children, ...props }) => ( + + {children} + + ), + th: ({ className, children, ...props }) => ( + + {children} + + ), + td: ({ className, children, ...props }) => ( + + {children} + + ), +} diff --git a/apps/web/components/blog/public/supercode-crisp.png b/apps/web/components/blog/public/supercode-crisp.png new file mode 100644 index 0000000..1b097c1 Binary files /dev/null and b/apps/web/components/blog/public/supercode-crisp.png differ diff --git a/apps/web/components/homepage/footer.tsx b/apps/web/components/homepage/footer.tsx index 22153a1..7b018c5 100644 --- a/apps/web/components/homepage/footer.tsx +++ b/apps/web/components/homepage/footer.tsx @@ -287,7 +287,7 @@ const Footer = () => { { label: "About", href: "/#about" }, { label: "Brand", href: "/brand" }, { label: "Contact", href: "/contact" }, - { label: "Blog", href: "/#blog" }, + { label: "Blog", href: "/blog" }, { label: "Privacy", href: "/#privacy" }, { label: "Terms", href: "/#terms" }, ]} diff --git a/apps/web/components/homepage/navbar.tsx b/apps/web/components/homepage/navbar.tsx index a7e3018..58c7974 100644 --- a/apps/web/components/homepage/navbar.tsx +++ b/apps/web/components/homepage/navbar.tsx @@ -162,6 +162,7 @@ const Navbar = () => { { label: "Compare", href: "/compare" }, { label: "Docs", href: DOCS_URL, external: true }, { label: "Changelog", href: "/changelog" }, + { label: "Blog", href: "/blog" }, // { label: "Waitlist", href: "/waitlist", accent: true }, ] diff --git a/apps/web/content/blog/introducing-supercode-crisp.mdx b/apps/web/content/blog/introducing-supercode-crisp.mdx new file mode 100644 index 0000000..03e6c47 --- /dev/null +++ b/apps/web/content/blog/introducing-supercode-crisp.mdx @@ -0,0 +1,131 @@ +--- +title: "Introducing Supercode Crisp: The Simplicity Ladder for AI Code" +slug: "introducing-supercode-crisp" +description: "AI generates too much code. Crisp fixes that — a 7-rung ladder that forces every line to earn its place." +date: "2025-07-29" +author: "Supercode Team" +category: "product" +image: "/supercode-crisp.png" +featured: true +--- + +AI is the most powerful coding tool ever created. It's also one of the most wasteful. + +Ask an AI agent to add a config system, and it gives you an abstraction layer, a factory pattern, a TypeScript interface, and a config file parser — for a value that never changes. Ask it to fix a bug, and it wraps the whole thing in a try-catch with custom error classes. + +The code works. But it's bloated, slow, and hard to maintain. And every unnecessary line costs you tokens, build time, and cognitive load. + +We built **Supercode Crisp** to fix this. + +## The problem with AI-generated code + +LLMs are trained on the entire internet. That means they've learned every enterprise pattern, every abstraction layer, every design pattern that's ever been blogged about. When you ask for something simple, the model reaches for its full arsenal. + +The result: **AI consistently over-generates code by 4–10×.** + +A one-liner becomes 30 lines. A single function becomes five files. A dependency gets added for something you could write in four lines of code. And because it looks "enterprise-grade," it ships. + +This isn't a bug in the model — it's a feature working against you. The model optimizes for thoroughness, not minimalism. Without guardrails, every task becomes an architecture project. + +## What Crisp does + +Crisp is a **simplicity ladder** — a 7-rung framework that forces AI to evaluate every design decision against a hierarchy of simplicity before writing code. + +The ladder runs top-to-bottom: + +### Rung 1: YAGNI — You Aren't Gonna Need It + +If it's not needed right now, don't build it. No future-proofing. No speculative abstractions. No "what if we need this later." + +This is the most important rung because it prevents code from being written in the first place. + +### Rung 2: Reuse what exists + +Before writing anything new, check if the language, the stdlib, or the project already has it. Copy-paste-modify beats import-a-library. + +### Rung 3: Standard library first + +Use built-in APIs over third-party packages. OS features over npm/crates/pip. The standard library is the most battle-tested, most optimized code you have access to. + +### Rung 4: Native platform APIs + +Prefer OS/platform built-ins over userland solutions. Shell over Python. CSS over JS. HTML over framework. The platform already solved this. + +### Rung 5: Dependencies are debt + +Every dependency is a liability — it's code you don't own, can't control, and have to update forever. Before adding one: can you inline it? Can you strip it? Can you replace 50 lines of dependencies with 10 lines of code? + +### Rung 6: One line > many + +If you can express the logic in a single expression, do it. Each temporary variable is a concept the reader must hold in working memory. + +### Rung 7: Minimum code to satisfy the spec + +The best code is the code you didn't write. Delete unused imports. Remove dead branches. Ship the smallest diff that works. + +## What you get with Crisp + +When you enable Crisp, the AI stops reaching for enterprise patterns by default. Instead, it asks: *which rung of the ladder does this serve?* + +The impact is measurable: + +- **6–20% of original code size** for many tasks — what was 100 lines becomes 6–20 +- **47–77% reduction in dependencies** — fewer things to audit, update, and maintain +- **3–6× faster builds** — less code means less to compile, bundle, and test +- **Fewer tokens per task** — which means faster responses and lower cost + +The code that Crisp produces is shorter, faster, and easier to reason about. Not because it cuts corners, but because it refuses to add code that doesn't earn its place. + +## How to use Crisp + +Crisp is built into Supercode. Open your terminal and type: + +``` +/crisp full +``` + +That's it. Every subsequent AI session will apply the full simplicity ladder. + +### Modes + +| Mode | What it does | +|---|---| +| `/crisp off` | Crisp disabled (default) | +| `/crisp lite` | Helpful guidelines, gentle nudges toward simplicity | +| `/crisp full` | Binding constraints, every abstraction must be justified | +| `/crisp ultra` | Hard constraints, burden of proof on complexity, YAGNI extremist | + +### Commands + +| Command | Description | +|---|---| +| `/crisp-review` | Review your current git diff against the simplicity ladder | +| `/crisp-audit` | Audit your entire workspace for over-engineering patterns | +| `/crisp-debt` | Find all `[crisp:N]` tagged shortcuts and deferred work | +| `/crisp-gain` | See your impact scoreboard — lines saved, deps eliminated | + +### The tag system + +When Crisp reviews your code, it tags findings with `[crisp:N]` markers — where N is the ladder rung. This creates a visible debt ledger: + +``` +[crisp:1] YAGNI — this config system supports use cases that don't exist yet +[crisp:5] Dependencies are debt — use URL constructor instead of parse-url package +[crisp:6] One line > many — this 12-line function is a single .filter().map() +``` + +You can grep for these tags, track them over time, and systematically eliminate over-engineering from your codebase. + +## Why this matters + +AI is going to write most of our code. That's not a prediction — it's already happening. The question is whether that code will be clean or bloated, minimal or over-engineered, fast or slow. + +Without guardrails, AI defaults to complexity. Crisp is the guardrail. It doesn't make the AI less capable — it makes it more intentional. It forces the model to justify every line, every abstraction, every dependency. + +The result is code that's easier to maintain, faster to build, and cheaper to run. Not because a human sat there deleting lines, but because the AI learned to write the minimum code that satisfies the spec. + +Crisp is available now in Supercode. Install it, enable it, and see the difference. + +```bash +bun install -g supercode +``` diff --git a/apps/web/content/blog/why-we-built-supercode.mdx b/apps/web/content/blog/why-we-built-supercode.mdx new file mode 100644 index 0000000..05fe2b3 --- /dev/null +++ b/apps/web/content/blog/why-we-built-supercode.mdx @@ -0,0 +1,77 @@ +--- +title: "Why we built Supercode" +slug: "why-we-built-supercode" +description: "The story behind building an open-source SWE agent that lives in your terminal." +date: "2025-07-28" +author: "Supercode Team" +category: "engineering" +image: "/og-image2.png" +featured: true +--- + +The best tools don't feel like tools. They feel like extensions of your hands. + +When we started building Supercode, we didn't set out to build "another AI coding assistant." We set out to build something that would make the terminal feel alive. + +## The problem + +Every developer has experienced this: you're deep in a feature, context switching between your editor, terminal, documentation, and Slack. You're productive, but you know you could be more productive if the tooling just... got out of the way. + +The existing AI coding tools at the time had a fundamental UX problem. They lived in your editor, which meant: + +- **They couldn't run your tests.** You'd ask for a fix, and then manually run the test suite. +- **They couldn't search your codebase.** They worked on whatever file was open, not the full project. +- **They couldn't deploy your changes.** The workflow stopped at "here's the code." + +We wanted to build something different. Something that could see your entire project, run your commands, and work alongside you — not just autocomplete your code. + +## Why the terminal + +The terminal is the most powerful interface on a computer. It's also the most underinvested in. + +Most developer tools try to replace the terminal with a GUI. We went the opposite direction. Supercode lives *in* your terminal because the terminal is where developers already are. It's where you run tests, deploy code, manage git, and execute the commands that actually ship software. + +By meeting developers where they already work, we eliminated the context switch. No new window. No new tab. No new mental model. Just a better version of what you're already doing. + +## What we learned + +Building an AI agent that actually works in the terminal taught us a lot about the difference between a demo and a product. + +### The small things compound + +A model returning correct code is table stakes. What matters is whether the tool *feels* right. + +We spent weeks on things users never consciously notice: + +- How fast the response streams in (we target < 100ms for the first token) +- How the terminal renders output (smooth, no flicker) +- How errors are displayed (clear, actionable, never a wall of red text) +- How the agent recovers from mistakes (it retries, it explains, it doesn't spiral) + +These details are invisible when they work and painful when they don't. That's exactly why they matter. + +### Trust is earned through transparency + +AI agents make mistakes. The question is whether the user understands *what* the agent did and *why*. + +Every action Supercode takes is visible in your terminal. You see the commands it runs, the files it modifies, and the reasoning behind each decision. There's no "magic" — just clear, auditable work. + +This transparency isn't just a UX choice. It's a safety feature. When you can see exactly what the agent did, you can verify it, learn from it, and trust it. + +### The best UX is no UX + +The highest compliment a tool can receive is that the user forgets it's there. They're just... building. + +We measure success not by how many features we ship, but by how few interactions the user has to think about. The agent should feel like a pair programmer who's always one step ahead — anticipating what you need, running the tests before you ask, and cleaning up after itself. + +## What's next + +We're just getting started. The terminal is the tip of the iceberg. + +We're building toward a world where the boundary between "writing code" and "shipping software" disappears entirely. Where an AI agent can take a bug report, find the root cause, write the fix, run the tests, and deploy it — all while you focus on the hard problems that require human judgment. + +If you want to try it, Supercode is open source. Install it, run it, and tell us what you think. We're building this for developers, and that means building it *with* developers. + +```bash +bun install -g supercode +``` diff --git a/apps/web/lib/blog.ts b/apps/web/lib/blog.ts new file mode 100644 index 0000000..229f732 --- /dev/null +++ b/apps/web/lib/blog.ts @@ -0,0 +1,87 @@ +import fs from "node:fs" +import path from "node:path" +import matter from "gray-matter" +import readingTime from "reading-time" +import type { BlogPost, BlogPostMeta } from "./utils" + +export type { BlogPost, BlogPostMeta } + +const BLOG_DIR = path.join(process.cwd(), "content/blog") + +function ensureBlogDir() { + if (!fs.existsSync(BLOG_DIR)) { + fs.mkdirSync(BLOG_DIR, { recursive: true }) + } +} + +export function getAllPosts(): BlogPostMeta[] { + ensureBlogDir() + const files = fs.readdirSync(BLOG_DIR).filter((f) => f.endsWith(".mdx")) + + const posts = files.map((filename) => { + const filePath = path.join(BLOG_DIR, filename) + const fileContent = fs.readFileSync(filePath, "utf-8") + const { data, content } = matter(fileContent) + const stats = readingTime(content) + + return { + slug: filename.replace(/\.mdx$/, ""), + title: data.title || "Untitled", + description: data.description || "", + date: data.date || new Date().toISOString(), + updated_at: data.updated_at, + author: data.author || "Supercode Team", + author_avatar: data.author_avatar, + category: data.category || "general", + image: data.image, + featured: data.featured || false, + reading_time: stats.text, + } + }) + + return posts.sort( + (a, b) => new Date(b.date).getTime() - new Date(a.date).getTime() + ) +} + +export function getPostBySlug(slug: string): BlogPost | null { + ensureBlogDir() + const filePath = path.join(BLOG_DIR, `${slug}.mdx`) + + if (!fs.existsSync(filePath)) { + return null + } + + const fileContent = fs.readFileSync(filePath, "utf-8") + const { data, content } = matter(fileContent) + const stats = readingTime(content) + + return { + slug, + title: data.title || "Untitled", + description: data.description || "", + date: data.date || new Date().toISOString(), + updated_at: data.updated_at, + author: data.author || "Supercode Team", + author_avatar: data.author_avatar, + category: data.category || "general", + image: data.image, + featured: data.featured || false, + reading_time: stats.text, + content, + } +} + +export function getFeaturedPosts(): BlogPostMeta[] { + return getAllPosts().filter((p) => p.featured) +} + +export function getPostsByCategory(category: string): BlogPostMeta[] { + return getAllPosts().filter((p) => p.category === category) +} + +export function getAllCategories(): string[] { + const posts = getAllPosts() + const categories = new Set(posts.map((p) => p.category)) + return Array.from(categories).sort() +} diff --git a/apps/web/lib/utils.ts b/apps/web/lib/utils.ts index bd0c391..5e0c200 100644 --- a/apps/web/lib/utils.ts +++ b/apps/web/lib/utils.ts @@ -4,3 +4,29 @@ import { twMerge } from "tailwind-merge" export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) } + +export interface BlogPostMeta { + slug: string + title: string + description: string + date: string + updated_at?: string + author: string + author_avatar?: string + category: string + image?: string + featured?: boolean + reading_time: string +} + +export interface BlogPost extends BlogPostMeta { + content: string +} + +export function formatDate(dateString: string): string { + return new Date(dateString).toLocaleDateString("en-US", { + year: "numeric", + month: "long", + day: "numeric", + }) +} diff --git a/apps/web/package.json b/apps/web/package.json index 577f93e..c4588d5 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -63,10 +63,12 @@ "embla-carousel-react": "^8.6.0", "env": "^0.0.2", "framer-motion": "^12.24.11", + "gray-matter": "^4.0.3", "inngest": "^3.52.0", "input-otp": "^1.4.2", "lucide-react": "^0.562.0", "next": "16.1.1", + "next-mdx-remote": "^6.0.0", "next-themes": "^0.4.6", "nodemailer": "^9.0.1", "octokit": "^5.0.5", @@ -79,7 +81,9 @@ "react-resizable-panels": "^4.2.0", "react-simple-maps": "^3.0.0", "react-tweet": "^3.3.1", + "reading-time": "^1.5.0", "recharts": "2.15.4", + "remark-gfm": "^4.0.1", "resend": "^6.14.0", "sonner": "^2.0.7", "tailwind-merge": "^3.4.0", @@ -88,6 +92,7 @@ }, "devDependencies": { "@tailwindcss/postcss": "^4", + "@types/mdx": "^2.0.14", "@types/node": "^20", "@types/nodemailer": "^8.0.1", "@types/pg": "^8.16.0", diff --git a/apps/web/public/supercode-crisp.png b/apps/web/public/supercode-crisp.png new file mode 100644 index 0000000..1b097c1 Binary files /dev/null and b/apps/web/public/supercode-crisp.png differ diff --git a/bun.lock b/bun.lock index 30c5f7a..9055bed 100644 --- a/bun.lock +++ b/bun.lock @@ -108,7 +108,7 @@ }, "apps/supercode-cli/server": { "name": "supercode-cli", - "version": "0.1.90", + "version": "0.1.91", "bin": { "supercode": "dist/main.js", }, @@ -265,10 +265,12 @@ "embla-carousel-react": "^8.6.0", "env": "^0.0.2", "framer-motion": "^12.24.11", + "gray-matter": "^4.0.3", "inngest": "^3.52.0", "input-otp": "^1.4.2", "lucide-react": "^0.562.0", "next": "16.1.1", + "next-mdx-remote": "^6.0.0", "next-themes": "^0.4.6", "nodemailer": "^9.0.1", "octokit": "^5.0.5", @@ -281,7 +283,9 @@ "react-resizable-panels": "^4.2.0", "react-simple-maps": "^3.0.0", "react-tweet": "^3.3.1", + "reading-time": "^1.5.0", "recharts": "2.15.4", + "remark-gfm": "^4.0.1", "resend": "^6.14.0", "sonner": "^2.0.7", "tailwind-merge": "^3.4.0", @@ -290,6 +294,7 @@ }, "devDependencies": { "@tailwindcss/postcss": "^4", + "@types/mdx": "^2.0.14", "@types/node": "^20", "@types/nodemailer": "^8.0.1", "@types/pg": "^8.16.0", @@ -1700,7 +1705,7 @@ "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="], - "@types/mdx": ["@types/mdx@2.0.13", "", {}, "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw=="], + "@types/mdx": ["@types/mdx@2.0.14", "", {}, "sha512-T48PeuJtvLosNTPVhfnIp3i/n3a4g4Bad7YCq5k64D4u7NwDrAotikQ+5+sjtUvBmxCMlbo3dVL+C2dP0rWHzg=="], "@types/memcached": ["@types/memcached@2.2.10", "", { "dependencies": { "@types/node": "*" } }, "sha512-AM9smvZN55Gzs2wRrqeMHVP7KE8KWgCJO/XL5yCly2xF6EKa4YlbpK+cLSAH4NG/Ah64HrlegmGqW8kYws7Vxg=="], @@ -2918,14 +2923,30 @@ "markdown-extensions": ["markdown-extensions@2.0.0", "", {}, "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q=="], + "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="], + "marked": ["marked@18.0.4", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-c/BTaKzg0G6ezQx97DAkYU7k0HM6ys0FqYeKBL6hlBByZwy+ycA1+f0vDdjMHKKeEjdgkx0GOv9Il6D+85cOqA=="], "marked-terminal": ["marked-terminal@7.3.0", "", { "dependencies": { "ansi-escapes": "^7.0.0", "ansi-regex": "^6.1.0", "chalk": "^5.4.1", "cli-highlight": "^2.1.11", "cli-table3": "^0.6.5", "node-emoji": "^2.2.0", "supports-hyperlinks": "^3.1.0" }, "peerDependencies": { "marked": ">=1 <16" } }, "sha512-t4rBvPsHc57uE/2nJOLmMbZCQ4tgAccAED3ngXQqW6g+TxA488JzJ+FK3lQkzBQOI1mRV/r/Kq+1ZlJ4D0owQw=="], "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + "mdast-util-find-and-replace": ["mdast-util-find-and-replace@3.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "escape-string-regexp": "^5.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg=="], + "mdast-util-from-markdown": ["mdast-util-from-markdown@2.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA=="], + "mdast-util-gfm": ["mdast-util-gfm@3.1.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-gfm-autolink-literal": "^2.0.0", "mdast-util-gfm-footnote": "^2.0.0", "mdast-util-gfm-strikethrough": "^2.0.0", "mdast-util-gfm-table": "^2.0.0", "mdast-util-gfm-task-list-item": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ=="], + + "mdast-util-gfm-autolink-literal": ["mdast-util-gfm-autolink-literal@2.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "ccount": "^2.0.0", "devlop": "^1.0.0", "mdast-util-find-and-replace": "^3.0.0", "micromark-util-character": "^2.0.0" } }, "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ=="], + + "mdast-util-gfm-footnote": ["mdast-util-gfm-footnote@2.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0" } }, "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ=="], + + "mdast-util-gfm-strikethrough": ["mdast-util-gfm-strikethrough@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg=="], + + "mdast-util-gfm-table": ["mdast-util-gfm-table@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "markdown-table": "^3.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg=="], + + "mdast-util-gfm-task-list-item": ["mdast-util-gfm-task-list-item@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ=="], + "mdast-util-mdx": ["mdast-util-mdx@3.0.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w=="], "mdast-util-mdx-expression": ["mdast-util-mdx-expression@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ=="], @@ -2962,6 +2983,20 @@ "micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="], + "micromark-extension-gfm": ["micromark-extension-gfm@3.0.0", "", { "dependencies": { "micromark-extension-gfm-autolink-literal": "^2.0.0", "micromark-extension-gfm-footnote": "^2.0.0", "micromark-extension-gfm-strikethrough": "^2.0.0", "micromark-extension-gfm-table": "^2.0.0", "micromark-extension-gfm-tagfilter": "^2.0.0", "micromark-extension-gfm-task-list-item": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w=="], + + "micromark-extension-gfm-autolink-literal": ["micromark-extension-gfm-autolink-literal@2.1.0", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw=="], + + "micromark-extension-gfm-footnote": ["micromark-extension-gfm-footnote@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw=="], + + "micromark-extension-gfm-strikethrough": ["micromark-extension-gfm-strikethrough@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw=="], + + "micromark-extension-gfm-table": ["micromark-extension-gfm-table@2.1.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg=="], + + "micromark-extension-gfm-tagfilter": ["micromark-extension-gfm-tagfilter@2.0.0", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg=="], + + "micromark-extension-gfm-task-list-item": ["micromark-extension-gfm-task-list-item@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw=="], + "micromark-extension-mdx-expression": ["micromark-extension-mdx-expression@3.0.1", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-mdx-expression": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q=="], "micromark-extension-mdx-jsx": ["micromark-extension-mdx-jsx@3.0.2", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "micromark-factory-mdx-expression": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ=="], @@ -3356,6 +3391,8 @@ "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], + "reading-time": ["reading-time@1.5.0", "", {}, "sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg=="], + "recast": ["recast@0.23.11", "", { "dependencies": { "ast-types": "^0.16.1", "esprima": "~4.0.0", "source-map": "~0.6.1", "tiny-invariant": "^1.3.3", "tslib": "^2.0.1" } }, "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA=="], "recharts": ["recharts@2.15.4", "", { "dependencies": { "clsx": "^2.0.0", "eventemitter3": "^4.0.1", "lodash": "^4.17.21", "react-is": "^18.3.1", "react-smooth": "^4.0.4", "recharts-scale": "^0.4.4", "tiny-invariant": "^1.3.1", "victory-vendor": "^36.6.8" }, "peerDependencies": { "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw=="], @@ -3378,12 +3415,16 @@ "rehype-recma": ["rehype-recma@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "hast-util-to-estree": "^3.0.0" } }, "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw=="], + "remark-gfm": ["remark-gfm@4.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-gfm": "^3.0.0", "micromark-extension-gfm": "^3.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg=="], + "remark-mdx": ["remark-mdx@3.1.1", "", { "dependencies": { "mdast-util-mdx": "^3.0.0", "micromark-extension-mdxjs": "^3.0.0" } }, "sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg=="], "remark-parse": ["remark-parse@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "micromark-util-types": "^2.0.0", "unified": "^11.0.0" } }, "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA=="], "remark-rehype": ["remark-rehype@11.1.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "mdast-util-to-hast": "^13.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw=="], + "remark-stringify": ["remark-stringify@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-to-markdown": "^2.0.0", "unified": "^11.0.0" } }, "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw=="], + "remeda": ["remeda@2.33.4", "", {}, "sha512-ygHswjlc/opg2VrtiYvUOPLjxjtdKvjGz1/plDhkG66hjNjFr1xmfrs2ClNFo/E6TyUFiwYNh53bKV26oBoMGQ=="], "remotion": ["remotion@4.0.499", "", { "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-QX5B1XDFBQpr8mYUBqhgCrH0cQJmqfI7zA46k9CsZhri/H9quXObYQBgeNDcxUhoulVvOOjVQqp5d0RAwB94HA=="], @@ -3990,6 +4031,10 @@ "@mapbox/node-pre-gyp/node-fetch": ["node-fetch@2.6.9", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg=="], + "@mdx-js/mdx/@types/mdx": ["@types/mdx@2.0.13", "", {}, "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw=="], + + "@mdx-js/react/@types/mdx": ["@types/mdx@2.0.13", "", {}, "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw=="], + "@modelcontextprotocol/sdk/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], "@modelcontextprotocol/sdk/jose": ["jose@6.1.3", "", {}, "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ=="], @@ -4634,6 +4679,8 @@ "make-dir/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + "mdast-util-find-and-replace/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], + "micro/content-type": ["content-type@1.0.4", "", {}, "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="], "micro/raw-body": ["raw-body@2.4.1", "", { "dependencies": { "bytes": "3.1.0", "http-errors": "1.7.3", "iconv-lite": "0.4.24", "unpipe": "1.0.0" } }, "sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA=="],