diff --git a/apps/logicsrc-web/package.json b/apps/logicsrc-web/package.json index 3512a17..68fe012 100644 --- a/apps/logicsrc-web/package.json +++ b/apps/logicsrc-web/package.json @@ -14,6 +14,7 @@ "dependencies": { "@profullstack/autoblog": "github:profullstack/autoblog#75e54af", "@supabase/supabase-js": "^2.105.4", + "marked": "^18.0.5", "next": "16.2.6", "react": "19.2.0", "react-dom": "19.2.0" diff --git a/apps/logicsrc-web/src/app/[[...slug]]/page.tsx b/apps/logicsrc-web/src/app/[[...slug]]/page.tsx index 1a2168c..326b4d7 100644 --- a/apps/logicsrc-web/src/app/[[...slug]]/page.tsx +++ b/apps/logicsrc-web/src/app/[[...slug]]/page.tsx @@ -8,11 +8,9 @@ import { HomeInteractivity } from "@/components/home-interactivity"; // scrolled to the matching section. We preserve those URLs (they are canonical // in sitemap.xml) by rendering the same page for each known route and 404ing // anything else. +// /about and /docs are now real routes (app/about, app/docs); the rest still +// render the homepage SPA scrolled to their section. const ROUTE_META: Record = { - docs: { - title: "Docs · LogicSRC", - description: "LogicSRC specification guides, schemas, and CLI conventions for human–AI agent coordination.", - }, openspec: { title: "LogicSRC vs OpenSpec.dev · LogicSRC", description: "How LogicSRC's coordination standard compares with OpenSpec.dev, including MCP and agent support.", @@ -25,10 +23,6 @@ const ROUTE_META: Record = { title: "Hire Us · LogicSRC", description: "Implementation help for LogicSRC, AgentSwarm, and Credential Sharing at $250/week for accepted work, paid via CoinPay.", }, - about: { - title: "About · LogicSRC", - description: "LogicSRC is the Profullstack, Inc. open-specification project for human and AI agent coordination.", - }, terms: { title: "Terms · LogicSRC", description: "LogicSRC terms of use." }, privacy: { title: "Privacy · LogicSRC", description: "LogicSRC privacy notes." }, "agent-swarm": { diff --git a/apps/logicsrc-web/src/app/about/page.tsx b/apps/logicsrc-web/src/app/about/page.tsx new file mode 100644 index 0000000..b610fa3 --- /dev/null +++ b/apps/logicsrc-web/src/app/about/page.tsx @@ -0,0 +1,94 @@ +import type { ReactNode } from "react"; +import type { Metadata } from "next"; +import { SiteShell } from "@/components/site-shell"; + +export const metadata: Metadata = { + title: "About · LogicSRC", + description: + "LogicSRC is the Profullstack, Inc. open-specification project for coordination between humans and AI agents — schemas, primitives, and conventions that products implement without owning the standard.", + alternates: { canonical: "/about" }, +}; + +export default function AboutPage(): ReactNode { + return ( + +
+
+

About LogicSRC

+

+ LogicSRC is the open-specification project from{" "} + + Profullstack, Inc. + {" "} + for coordination between humans, AI agents, plugins, payment systems, + and hosted products. +

+
+ +
+

What it is

+

+ LogicSRC defines a shared language — open JSON-Schema contracts and + conventions — so that independent tools, agents, and services can + coordinate without any one vendor owning the standard. Products + implement LogicSRC; they don't depend on a proprietary platform + to interoperate. +

+ +

The standards surface

+
    +
  • + Identity — DIDs, OAuth accounts, profiles, and + organization membership. +
  • +
  • + Coordination — boards, tasks, and the workflow + primitives agents and humans share. +
  • +
  • + Agents — agent profiles, capabilities, runs, + logs, permissions, and audit trails. +
  • +
  • + Value — payment, invoicing, and metering hooks. +
  • +
  • + Events — a common event envelope for policy, + audit, and webhooks. +
  • +
+ +

Reference implementation

+

+ CommandBoard.run is the first hosted product built + on LogicSRC — a modern BBS where humans and AI agents coordinate work + through boards, tasks, DID identity, OAuth, CLI, TUI, plugins, + reputation, audit logs, and payments. It demonstrates the primitives + across PWA, CLI, TUI, API, plugins, CoinPay, and uGig. +

+ +

How it's built

+

+ The spec, schemas, SDKs, CLI, TUI, and reference plugins are + developed in the open at{" "} + + github.com/profullstack/logicsrc + + . See the docs for the data model and conventions, + or the OpenSpec.dev comparison for how LogicSRC + differs from repo-local planning specs. +

+ +

Work with us

+

+ Profullstack implements LogicSRC-based systems at $250/week for + accepted work, paid via CoinPay. See Hire Us. +

+
+
+
+ ); +} diff --git a/apps/logicsrc-web/src/app/docs/[slug]/page.tsx b/apps/logicsrc-web/src/app/docs/[slug]/page.tsx new file mode 100644 index 0000000..be2fbf7 --- /dev/null +++ b/apps/logicsrc-web/src/app/docs/[slug]/page.tsx @@ -0,0 +1,58 @@ +import Link from "next/link"; +import { notFound } from "next/navigation"; +import type { ReactNode } from "react"; +import type { Metadata } from "next"; +import { marked } from "marked"; +import { DOC_SLUGS, docExcerpt, docTitle, readDoc } from "@/lib/docs"; +import { SiteShell } from "@/components/site-shell"; + +// Statically generate one page per curated doc at build time. +export function generateStaticParams(): Array<{ slug: string }> { + return DOC_SLUGS.map((slug) => ({ slug })); +} + +export const dynamicParams = false; + +export async function generateMetadata({ + params, +}: { + params: Promise<{ slug: string }>; +}): Promise { + const { slug } = await params; + const md = readDoc(slug); + if (!md) return { title: "Not found · LogicSRC" }; + return { + title: `${docTitle(md, slug)} · LogicSRC Docs`, + description: docExcerpt(md) || undefined, + alternates: { canonical: `/docs/${slug}` }, + }; +} + +export default async function DocPage({ + params, +}: { + params: Promise<{ slug: string }>; +}): Promise { + const { slug } = await params; + const md = readDoc(slug); + if (!md) notFound(); + + const html = await marked.parse(md); + + return ( + +
+

+ + ← Docs + +

+
+
+
+ ); +} diff --git a/apps/logicsrc-web/src/app/docs/page.tsx b/apps/logicsrc-web/src/app/docs/page.tsx new file mode 100644 index 0000000..ffc04ac --- /dev/null +++ b/apps/logicsrc-web/src/app/docs/page.tsx @@ -0,0 +1,53 @@ +import Link from "next/link"; +import type { ReactNode } from "react"; +import type { Metadata } from "next"; +import { listDocs } from "@/lib/docs"; +import { SiteShell } from "@/components/site-shell"; + +export const metadata: Metadata = { + title: "Docs · LogicSRC", + description: + "LogicSRC specification guides — data model, CLI/TUI conventions, config, permission scopes, plugins, credential sharing, and the OpenSpec.dev comparison.", + alternates: { canonical: "/docs" }, +}; + +export default function DocsIndex(): ReactNode { + const docs = listDocs(); + return ( + +
+
+

Docs

+

+ Specification guides and conventions for the LogicSRC coordination + standard. Source lives in the{" "} + + profullstack/logicsrc + {" "} + repository. +

+
+
    + {docs.map((doc) => ( +
  • + +

    + {doc.title} +

    + + {doc.excerpt ? ( +

    {doc.excerpt}

    + ) : null} +
  • + ))} +
+
+
+ ); +} diff --git a/apps/logicsrc-web/src/app/sitemap.ts b/apps/logicsrc-web/src/app/sitemap.ts index 9701a55..2c9d461 100644 --- a/apps/logicsrc-web/src/app/sitemap.ts +++ b/apps/logicsrc-web/src/app/sitemap.ts @@ -1,5 +1,6 @@ import type { MetadataRoute } from "next"; import { publicClient } from "@/lib/supabase"; +import { DOC_SLUGS } from "@/lib/docs"; export const dynamic = "force-dynamic"; @@ -35,6 +36,12 @@ export default async function sitemap(): Promise { priority: route.priority, })); + const docEntries: MetadataRoute.Sitemap = DOC_SLUGS.map((slug) => ({ + url: `${base}/docs/${slug}`, + changeFrequency: "monthly", + priority: 0.6, + })); + let postEntries: MetadataRoute.Sitemap = []; try { const supabase = publicClient(); @@ -54,5 +61,5 @@ export default async function sitemap(): Promise { postEntries = []; } - return [...staticEntries, ...postEntries]; + return [...staticEntries, ...docEntries, ...postEntries]; } diff --git a/apps/logicsrc-web/src/lib/docs.ts b/apps/logicsrc-web/src/lib/docs.ts new file mode 100644 index 0000000..edf5dd6 --- /dev/null +++ b/apps/logicsrc-web/src/lib/docs.ts @@ -0,0 +1,62 @@ +import { readFileSync } from "node:fs"; +import { resolve } from "node:path"; + +// Repo-root docs/ (read at build time during static generation, so there is +// no runtime filesystem dependency in the deployed image). +const DOCS_DIR = resolve(process.cwd(), "../../docs"); + +// Curated, public-facing reference docs. Internal notes (roadmap, positioning, +// arcade) are intentionally excluded. +export const DOC_SLUGS = [ + "openspec-comparison", + "data-model", + "cli", + "tui", + "config", + "permissions", + "plugins", + "credential-sharing", + "agent-screening", +] as const; + +export type DocSlug = (typeof DOC_SLUGS)[number]; + +export function isDocSlug(slug: string): slug is DocSlug { + return (DOC_SLUGS as readonly string[]).includes(slug); +} + +export function readDoc(slug: string): string | null { + if (!isDocSlug(slug)) return null; + try { + return readFileSync(resolve(DOCS_DIR, `${slug}.md`), "utf8"); + } catch { + return null; + } +} + +export function docTitle(markdown: string, slug: string): string { + const h1 = markdown.split("\n").find((line) => line.startsWith("# ")); + return h1 ? h1.replace(/^#\s+/, "").trim() : slug; +} + +export function docExcerpt(markdown: string): string { + for (const raw of markdown.split("\n")) { + const line = raw.trim(); + if (line && !line.startsWith("#") && !line.startsWith("```") && !line.startsWith(">")) { + return line.replace(/[*_`#>[\]()]/g, "").trim().slice(0, 160); + } + } + return ""; +} + +export type DocSummary = { slug: DocSlug; title: string; excerpt: string }; + +export function listDocs(): DocSummary[] { + const out: DocSummary[] = []; + for (const slug of DOC_SLUGS) { + const md = readDoc(slug); + if (!md) continue; + out.push({ slug, title: docTitle(md, slug), excerpt: docExcerpt(md) }); + } + return out; +} diff --git a/package-lock.json b/package-lock.json index ce6e231..81d6fda 100644 --- a/package-lock.json +++ b/package-lock.json @@ -128,6 +128,7 @@ "dependencies": { "@profullstack/autoblog": "github:profullstack/autoblog#75e54af", "@supabase/supabase-js": "^2.105.4", + "marked": "^18.0.5", "next": "16.2.6", "react": "19.2.0", "react-dom": "19.2.0" @@ -3853,6 +3854,18 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/marked": { + "version": "18.0.5", + "resolved": "https://registry.npmjs.org/marked/-/marked-18.0.5.tgz", + "integrity": "sha512-S6GcvALHg6K4ohtu4E7x0a1AqhAjp6cV8KhLSyN9qVapnzJkusVBxZRcIU9AeYsbe6P1hKDusSbEOzGyyuce6w==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",