diff --git a/docs/workflows.md b/docs/workflows.md index b63f7f7491..78d27a32c0 100644 --- a/docs/workflows.md +++ b/docs/workflows.md @@ -28,6 +28,75 @@ OPSX (fluid actions): > **Customization:** OPSX workflows are driven by schemas that define artifact sequences. See [Customization](customization.md) for details on creating custom schemas. +## Workflow at a Glance + +The default workflow stays fluid: exploration and verification are optional, and +you can update planning artifacts whenever implementation reveals something new. + +```mermaid +flowchart TD + Idea["Idea or problem"] --> Explore["/opsx:explore
(optional)"] + Idea --> Propose["/opsx:propose"] + Explore --> Propose + Propose --> Review{"Planning artifacts
ready?"} + Review -->|"Refine"| Update["/opsx:update"] + Update --> Review + Review -->|"Implement"| Apply["/opsx:apply"] + Apply -->|"Plan changed"| Update + Apply --> Archive["/opsx:archive"] + Apply --> Verify["/opsx:verify
(optional, custom selection)"] + Apply --> Sync["/opsx:sync
(optional before archive)"] + Verify --> Verified{"Ready to archive?"} + Verified -->|"Fix implementation"| Apply + Verified -->|"Revise plan"| Update + Verified -->|"Ready"| Sync + Verified -->|"Ready"| Archive + Sync --> Archive +``` + +The AI assistant drives the workflow, while the CLI provides deterministic +scaffolding, status, and artifact instructions: + +```mermaid +sequenceDiagram + actor Human + participant Assistant as AI assistant + participant CLI as OpenSpec CLI + participant Files as Planning and implementation files + + Human->>Assistant: /opsx:propose "change" + Assistant->>CLI: openspec new change + CLI->>Files: Scaffold change metadata + Assistant->>CLI: Request status and artifact instructions + CLI-->>Assistant: Build order, paths, and templates + Assistant->>Files: Write schema-defined planning artifacts + Assistant-->>Human: Present artifacts for review + + Human->>Assistant: /opsx:apply + Assistant->>CLI: Request apply instructions + CLI-->>Assistant: Context files and task state + Assistant->>Files: Implement tasks and update checkboxes + Assistant-->>Human: Report implementation status + + Human->>Assistant: /opsx:archive + Assistant->>CLI: Request archive inputs and artifact status + CLI-->>Assistant: Planning paths and artifact completion + Assistant->>Files: Read task state and compare delta specs + opt Delta specs exist + Assistant-->>Human: Offer to sync before archiving + alt Sync accepted + Human->>Assistant: Confirm sync + Assistant->>Files: Merge delta specs into main specs + else Sync skipped + Human->>Assistant: Archive without syncing + end + end + Assistant->>Files: Move the change into the archive + Assistant-->>Human: Report archive location and sync result + + Note over Human,CLI: CLI alternative: openspec archive change-name --yes skips confirmation prompts; it still validates, then applies any delta specs and archives +``` + ## Two Modes ### Default Quick Path (`core` profile) diff --git a/website/components/mdx.tsx b/website/components/mdx.tsx index a407f51f42..d638e730f4 100644 --- a/website/components/mdx.tsx +++ b/website/components/mdx.tsx @@ -2,6 +2,7 @@ import defaultMdxComponents from 'fumadocs-ui/mdx'; import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; import { Step, Steps } from 'fumadocs-ui/components/steps'; import { Accordion, Accordions } from 'fumadocs-ui/components/accordion'; +import { Mermaid } from '@/components/mermaid'; import type { MDXComponents } from 'mdx/types'; export function getMDXComponents(components?: MDXComponents) { @@ -13,6 +14,7 @@ export function getMDXComponents(components?: MDXComponents) { Steps, Accordion, Accordions, + Mermaid, ...components, } satisfies MDXComponents; } diff --git a/website/components/mermaid.tsx b/website/components/mermaid.tsx new file mode 100644 index 0000000000..62cc61ebb4 --- /dev/null +++ b/website/components/mermaid.tsx @@ -0,0 +1,37 @@ +import { renderMermaidSVG } from 'beautiful-mermaid'; +import { CodeBlock, Pre } from 'fumadocs-ui/components/codeblock'; + +export function Mermaid({ chart }: { chart: string }) { + try { + // beautiful-mermaid injects remote font imports; the site already provides Inter. + const svg = renderMermaidSVG(chart, { + bg: 'var(--color-fd-background)', + fg: 'var(--color-fd-foreground)', + transparent: true, + }).replace(/^\s*@import url\(['"]https:\/\/fonts\.googleapis\.com\/[^)]*\);\s*$/m, ''); + + return ( +
+
+ +
Mermaid diagram source: {chart}
+
+ ); + } catch { + return ( + +
{chart}
+
+ ); + } +} diff --git a/website/lib/source.ts b/website/lib/source.ts index a480d10a99..e40147f089 100644 --- a/website/lib/source.ts +++ b/website/lib/source.ts @@ -1,4 +1,5 @@ import { docs } from 'collections/server'; +import { renderPlaceholder } from 'fumadocs-core/mdx-plugins/remark-llms.runtime'; import { loader } from 'fumadocs-core/source'; import { icons } from 'lucide-react'; import { createElement } from 'react'; @@ -37,8 +38,17 @@ export function getPageMarkdownUrl(page: (typeof source)['$inferPage']) { export async function getLLMText(page: (typeof source)['$inferPage']) { const processed = await page.data.getText('processed'); + const markdown = await renderPlaceholder(processed, { + Mermaid({ attributes }) { + if (typeof attributes.chart !== 'string') return ''; + + return `\`\`\`mermaid +${attributes.chart} +\`\`\``; + }, + }); return `# ${page.data.title} (${page.url}) -${processed}`; +${markdown}`; } diff --git a/website/package.json b/website/package.json index 15b473faf3..6bc26c4cf6 100644 --- a/website/package.json +++ b/website/package.json @@ -12,6 +12,7 @@ }, "dependencies": { "@orama/orama": "^3.1.18", + "beautiful-mermaid": "^1.1.3", "fumadocs-core": "^16.12.1", "fumadocs-mdx": "^15.2.1", "fumadocs-ui": "^16.12.1", diff --git a/website/pnpm-lock.yaml b/website/pnpm-lock.yaml index f9766b7e0d..53a752ce50 100644 --- a/website/pnpm-lock.yaml +++ b/website/pnpm-lock.yaml @@ -16,6 +16,9 @@ importers: '@orama/orama': specifier: ^3.1.18 version: 3.1.18 + beautiful-mermaid: + specifier: ^1.1.3 + version: 1.1.3 fumadocs-core: specifier: ^16.12.1 version: 16.12.1(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.18)(lucide-react@1.27.0(react@19.2.8))(next@16.2.12(@types/node@26.1.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3) @@ -1133,6 +1136,9 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + beautiful-mermaid@1.1.3: + resolution: {integrity: sha512-TItrtrAyHp1vwFfFVYauWGrquouk/6SS21Aq3RsxindSYZODcN4xYrPZD6BiZRU+o5mKJzDPz9MUSMvELdylyg==} + boxen@7.0.0: resolution: {integrity: sha512-j//dBVuyacJbvW+tvZ9HuH03fZ46QcaKvvhZickZqtB271DxJ7SNRSNxrV/dZX0085m7hISRZWbzWlJvx/rHSg==} engines: {node: '>=14.16'} @@ -1285,6 +1291,9 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + elkjs@0.11.1: + resolution: {integrity: sha512-zxxR9k+rx5ktMwT/FwyLdPCrq7xN6e4VGGHH8hA01vVYKjTFik7nHOxBnAYtrgYUB1RpAiLvA1/U2YraWxyKKg==} + emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -1299,6 +1308,10 @@ packages: resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} + engines: {node: '>=0.12'} + esast-util-from-estree@2.0.0: resolution: {integrity: sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==} @@ -3211,6 +3224,11 @@ snapshots: baseline-browser-mapping@2.11.11: {} + beautiful-mermaid@1.1.3: + dependencies: + elkjs: 0.11.1 + entities: 7.0.1 + boxen@7.0.0: dependencies: ansi-align: 3.0.1 @@ -3341,6 +3359,8 @@ snapshots: eastasianwidth@0.2.0: {} + elkjs@0.11.1: {} + emoji-regex@8.0.0: {} emoji-regex@9.2.2: {} @@ -3352,6 +3372,8 @@ snapshots: entities@6.0.1: {} + entities@7.0.1: {} + esast-util-from-estree@2.0.0: dependencies: '@types/estree-jsx': 1.0.5 diff --git a/website/source.config.ts b/website/source.config.ts index 628513c667..b3c9a76773 100644 --- a/website/source.config.ts +++ b/website/source.config.ts @@ -1,5 +1,6 @@ import { defineConfig, defineDocs } from 'fumadocs-mdx/config'; import { metaSchema, pageSchema } from 'fumadocs-core/source/schema'; +import { remarkMdxMermaid } from 'fumadocs-core/mdx-plugins'; import { z } from 'zod'; // You can customize Zod schemas for frontmatter and `meta.json` here @@ -12,7 +13,9 @@ export const docs = defineDocs({ // page" link opens the real source rather than the generated mirror. schema: pageSchema.extend({ githubSource: z.string().optional() }), postprocess: { - includeProcessedMarkdown: true, + includeProcessedMarkdown: { + mdxAsPlaceholder: ['Mermaid'], + }, }, }, meta: { @@ -22,6 +25,6 @@ export const docs = defineDocs({ export default defineConfig({ mdxOptions: { - // MDX options + remarkPlugins: [remarkMdxMermaid], }, });