Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 24 additions & 4 deletions astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,23 @@ import starlight from '@astrojs/starlight';
import sitemap from '@astrojs/sitemap';

const gaId = process.env.GOOGLE_ANALYTICS_ID;
const site = 'https://repl.yllibed.org';
const ogImage = new URL('/og-image.png', site).href;
const ogImageAlt = 'Repl Toolkit: one graph, every surface. A terminal runs the same app as a CLI, a REPL and an MCP server.';

// https://astro.build/config
export default defineConfig({
site: 'https://repl.yllibed.org',
site,
integrations: [
sitemap(),
starlight({
title: 'Repl Toolkit',
description: 'A .NET framework for building composable command surfaces — CLI, REPL, remote sessions, and MCP tools from one command graph.',
logo: {
light: './src/assets/logo-light.svg',
dark: './src/assets/logo-dark.svg',
alt: '',
},
social: [
{ icon: 'github', label: 'GitHub', href: 'https://github.com/yllibed/repl' },
],
Expand Down Expand Up @@ -59,6 +67,7 @@ export default defineConfig({
{ label: 'Pipelines & Integration', slug: 'reference/pipelining' },
{ label: 'Exit Codes & Signals', slug: 'reference/exit-codes' },
{ label: 'Terminal Integration', slug: 'reference/terminal-integration' },
{ label: 'Remote Protocol', slug: 'reference/remote-protocol' },
{ label: 'Architecture', slug: 'reference/architecture' },
],
},
Expand All @@ -81,13 +90,24 @@ export default defineConfig({
],
},
],
customCss: ['./src/styles/custom.css'],
customCss: [
// Self-hosted font files; src/styles/custom.css assigns the families.
'@fontsource-variable/jetbrains-mono/wght.css',
'@fontsource/ibm-plex-sans/400.css',
'@fontsource/ibm-plex-sans/400-italic.css',
'@fontsource/ibm-plex-sans/600.css',
'./src/styles/custom.css',
],
head: [
{ tag: 'meta', attrs: { property: 'og:image', content: '/og-image.png' } },
{ tag: 'meta', attrs: { property: 'og:image', content: ogImage } },
{ tag: 'meta', attrs: { property: 'og:image:width', content: '1200' } },
{ tag: 'meta', attrs: { property: 'og:image:height', content: '630' } },
{ tag: 'meta', attrs: { property: 'og:image:alt', content: ogImageAlt } },
{ tag: 'meta', attrs: { property: 'og:type', content: 'website' } },
{ tag: 'meta', attrs: { property: 'og:locale', content: 'en_US' } },
{ tag: 'meta', attrs: { name: 'twitter:card', content: 'summary_large_image' } },
{ tag: 'meta', attrs: { name: 'twitter:image', content: '/og-image.png' } },
{ tag: 'meta', attrs: { name: 'twitter:image', content: ogImage } },
{ tag: 'meta', attrs: { name: 'twitter:image:alt', content: ogImageAlt } },
...(gaId ? [
{
tag: 'script',
Expand Down
20 changes: 20 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"docs:api": "node ./scripts/build-api-docs.mjs",
"docs:api:mdx": "node ./scripts/generate-api-mdx.mjs",
"docs:validate": "node ./scripts/validate-docs.mjs",
"og:image": "node ./scripts/build-og-image.mjs",
"prebuild": "npm run docs:api",
"build": "astro build",
"preview": "astro preview",
Expand All @@ -18,6 +19,8 @@
"dependencies": {
"@astrojs/sitemap": "^3.7.2",
"@astrojs/starlight": "^0.38.4",
"@fontsource-variable/jetbrains-mono": "^5.3.0",
"@fontsource/ibm-plex-sans": "^5.3.0",
"astro": "^6.1.9",
"js-yaml": "^4.1.1",
"sharp": "^0.34.5"
Expand Down
19 changes: 18 additions & 1 deletion public/favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 4 additions & 3 deletions public/llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,13 @@
- [Dependency Injection](https://repl.yllibed.org/reference/dependency-injection/): Handler-parameter injection, service lifetimes (Scoped = one instance per Run* call or MCP connection), SessionScopeBehavior.CallerOwned, and integration with IHostBuilder.
- [Advanced Input & Startup](https://repl.yllibed.org/reference/advanced-input-startup/): Opt-in hooks — UseArgumentPreprocessor for legacy syntax, UseInvocationHook and native ReplInvocationRequest values, UseInputValidator with services, ReplStartup preparation before DI.
- [Interactivity](https://repl.yllibed.org/reference/interactivity/): IReplInteractionChannel API (AskTextAsync, AskSecretAsync, AskConfirmationAsync, AskChoiceAsync), --answer:* prefill, cancellation, prompt answers in tests.
- [MCP In Depth](https://repl.yllibed.org/reference/mcp-concepts/): Full MCP concepts — tools, resources, prompts, MCP Apps, tool annotations (ReadOnly, Destructive, Idempotent, OpenWorld), interaction tiers, client roots, hidden options.
- [MCP — Advanced](https://repl.yllibed.org/reference/mcp-advanced/): MCP SDK 2.2, protocol revisions 2025-11-25 and 2026-07-28, stateless HTTP hosting with ConfigureMcpServerOptions, operator log events 2001-2009, sampling, elicitation, roots.
- [MCP In Depth](https://repl.yllibed.org/reference/mcp-concepts/): Full MCP concepts — tools, resources, prompts, MCP Apps, tool annotations (ReadOnly, Destructive, Idempotent, OpenWorld), interaction tiers, hidden options.
- [MCP — Advanced](https://repl.yllibed.org/reference/mcp-advanced/): MCP SDK 2.2, protocol revisions 2025-11-25 and 2026-07-28, stateless HTTP hosting with ConfigureMcpServerOptions, operator log events 2001-2009, and legacy features new applications should not adopt: roots and sampling are deprecated by revision 2026-07-28, and server-initiated elicitation is replaced by multi round-trip requests, which Repl 0.12 does not use yet.
- [Customization & Output](https://repl.yllibed.org/reference/customization/): Output formats (text, JSON, XML, YAML, Markdown), feedback on stderr for machine-readable output, ANSI detection, render width, IReplOutputTransformer, Spectre renderables.
- [Pipelines & Integration](https://repl.yllibed.org/reference/pipelining/): 12-stage execution pipeline, middleware, CLI scripting with pipes/jq, ASP.NET Core integration, IHostedService, process signals and graceful shutdown.
- [Exit Codes & Signals](https://repl.yllibed.org/reference/exit-codes/): Outcome kinds and default codes (0 success, 2 usage/binding refusal, 1 handler failure, 130/143 on Ctrl+C or SIGTERM), ReplOptions.ExitCodes and Resolver, Results.CompletedWithFailures, IReplModuleExitCodes, ReplProcessSignalHarness.
- [Terminal Integration](https://repl.yllibed.org/reference/terminal-integration/): ANSI capability levels, NO_COLOR, shell autocomplete installation, response files, terminal detection.
- [Remote Protocol](https://repl.yllibed.org/reference/remote-protocol/): The experimental JSON-RPC 2.0 profile behind Repl.Remote — Content-Length framing, rpc.discover/cli.describe/cli.execute, terminal.* methods and notifications, errors, cancellation and retries, security limits.
- [Architecture](https://repl.yllibed.org/reference/architecture/): Internal design, execution model, extension points, and how the packages compose.

## Reference
Expand All @@ -60,7 +61,7 @@
- `app.UseMcpServer()` — let the app expose all commands as MCP tools (one line, no handler changes); Repl.Mcp is the component used to build the MCP server
- `app.Run(args)` — dispatch: CLI if args present, REPL if interactive, MCP serve if args == ["mcp","serve"]
- `Results.Success(message, details)` / `Results.Error(code, message)` / `Results.CompletedWithFailures(report)` / `Results.Exit(code)` — typed command results; the exit code comes from ReplOptions.ExitCodes, never from a returned int
- `IReplInteractionChannel` — ask users for input; fulfilled by terminal in interactive mode, --answer:* in CI, MCP elicitation in agents; feedback goes to stderr when output is machine-readable
- `IReplInteractionChannel` — ask users for input; fulfilled by terminal in interactive mode, --answer:* in CI, tool-argument prefill in agents; feedback goes to stderr when output is machine-readable
- `Repl.Testing` — in-memory harness for full multi-step interaction testing with typed assertions


Expand Down
Binary file added public/og-image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions scripts/build-og-image.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Renders the social card (og:image, twitter:image) from its SVG source.
// Run `npm run og:image` after changing src/assets/og-image.svg, and commit the PNG.
import { fileURLToPath } from 'node:url';
import sharp from 'sharp';

const source = fileURLToPath(new URL('../src/assets/og-image.svg', import.meta.url));
const target = fileURLToPath(new URL('../public/og-image.png', import.meta.url));

const info = await sharp(source, { density: 144 })
.resize(1200, 630)
.png({ compressionLevel: 9 })
.toFile(target);

console.log(`Wrote ${target} (${info.width}x${info.height}, ${info.size} bytes).`);
Binary file removed src/assets/houston.webp
Binary file not shown.
8 changes: 8 additions & 0 deletions src/assets/logo-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions src/assets/logo-light.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
47 changes: 47 additions & 0 deletions src/assets/og-image.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 36 additions & 0 deletions src/components/Figure.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
/**
* Draws an inline SVG diagram: the <svg> element with its accessible label, a scroll box for narrow
* screens, and a caption. The slot holds the drawing. See ./diagrams/README.md for how to draw one.
*/
interface Props {
/** The viewBox width. The diagram renders between 85 % and 120 % of it, so labels stay legible. */
viewWidth: number;
viewHeight: number;
/** What connects to what, for screen readers. */
label: string;
/** What the diagram shows, displayed under it. */
caption: string;
}

const { viewWidth, viewHeight, label, caption } = Astro.props;
const sizes = {
'--rt-min-width': `${Math.round(viewWidth * 0.85)}px`,
'--rt-max-width': `${Math.round(viewWidth * 1.2)}px`,
};
// Captions differ between the diagrams of a page, so they make a stable id.
const captionId = `rt-fig-${caption
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.slice(0, 48)}`;
---

<figure class="rt-figure" style={sizes}>
{/* Focusable so keyboard users can scroll the diagram when it is wider than the screen. */}
<div class="rt-scroll" tabindex="0" role="group" aria-labelledby={captionId}>
<svg viewBox={`0 0 ${viewWidth} ${viewHeight}`} role="img" aria-label={label}>
<slot />
</svg>
</div>
<figcaption id={captionId}>{caption}</figcaption>
</figure>
47 changes: 47 additions & 0 deletions src/components/diagrams/AgentDiscovery.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
import Figure from '../Figure.astro';
import { textWidth } from './metrics';

/* What an agent runs at each step, and what comes back, in the order the page's examples follow. */
const steps = [
{ title: 'Discover', command: 'myapp --help --json', returns: 'top-level contexts and commands' },
{ title: 'Drill in', command: 'myapp contacts --help --json', returns: 'the commands of one context' },
{ title: 'Describe', command: 'myapp contacts add --help --json', returns: 'parameters, types, constraints' },
{ title: 'Call', command: 'myapp contacts add ... --json', returns: 'the result as JSON' },
];
const minWidth = 560;
const stepX = 26;
const row = { x: 52, height: 64, gap: 14 };
const titleX = row.x + 20;
const commandX = titleX + Math.max(...steps.map((step) => textWidth(step.title, 'tag'))) + 32;
const textEnd = Math.max(...steps.map((step) => Math.max(textWidth(step.command, 'code'), textWidth(`returns ${step.returns}`, 'note'))));
const viewWidth = Math.max(minWidth, commandX + textEnd + 30);
const top = 10;
const rowY = (index: number) => top + index * (row.height + row.gap);
const centerY = (index: number) => rowY(index) + row.height / 2;
const viewHeight = rowY(steps.length - 1) + row.height + 10;
const label = `How an agent uses a Repl app, in ${steps.length} steps: ${steps
.map((step, index) => `${index + 1}, ${step.title}: run ${step.command}, which returns ${step.returns}`)
.join('; ')}.`;
---

<Figure
viewWidth={viewWidth}
viewHeight={viewHeight}
label={label}
caption="The commands an agent runs, from knowing only the executable's name to a call that returns data."
>
<path class="rt-thread" d={`M${stepX} ${centerY(0)}V${centerY(steps.length - 1)}`}></path>
{
steps.map((step, index) => (
<g>
<rect class="rt-cloth" x={row.x} y={rowY(index)} width={viewWidth - row.x - 10} height={row.height} rx="2" />
<circle class="rt-step" cx={stepX} cy={centerY(index)} r="11" />
<text class="rt-stepn" x={stepX} y={centerY(index) + 4} text-anchor="middle">{index + 1}</text>
<text class="rt-tag" x={titleX} y={centerY(index) + 5}>{step.title}</text>
<text class="rt-code" x={commandX} y={centerY(index) - 4}>{step.command}</text>
<text class="rt-note" x={commandX} y={centerY(index) + 16}>returns {step.returns}</text>
</g>
))
}
</Figure>
Loading
Loading