Knowledge base + grouped, reordered sidebar
Adds an in-app Knowledge base (route /help): 15 searchable, expandable how-to articles with step-by-step guides and examples (concepts, A-to-Z setup, the review inbox, the handoff + memory, the libraries, analytics, governance, troubleshooting), rendered as markdown. Reorganizes the sidebar into UX-ordered groups with section labels — Get started · Work (Board/Team/Cartable/Reviews) · Organization (Structure/Org chart/Members) · AI & libraries (AI seats/Skills/Agent profiles/Product profiles) · Insights (Performance/Analytics) · Help (Knowledge base). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,7 @@ import { AnalyticsPage } from '@/pages/AnalyticsPage'
|
|||||||
import { BoardPage } from '@/pages/BoardPage'
|
import { BoardPage } from '@/pages/BoardPage'
|
||||||
import { CartablePage } from '@/pages/CartablePage'
|
import { CartablePage } from '@/pages/CartablePage'
|
||||||
import { GetStartedPage } from '@/pages/GetStartedPage'
|
import { GetStartedPage } from '@/pages/GetStartedPage'
|
||||||
|
import { KnowledgeBasePage } from '@/pages/KnowledgeBasePage'
|
||||||
import { LoginPage } from '@/pages/LoginPage'
|
import { LoginPage } from '@/pages/LoginPage'
|
||||||
import { MembersPage } from '@/pages/MembersPage'
|
import { MembersPage } from '@/pages/MembersPage'
|
||||||
import { OrgChartPage } from '@/pages/OrgChartPage'
|
import { OrgChartPage } from '@/pages/OrgChartPage'
|
||||||
@@ -38,6 +39,7 @@ export default function App() {
|
|||||||
<Route path="/agent-profiles" element={token ? <AgentProfilesPage /> : <Navigate to="/login" replace />} />
|
<Route path="/agent-profiles" element={token ? <AgentProfilesPage /> : <Navigate to="/login" replace />} />
|
||||||
<Route path="/product-profiles" element={token ? <ProductProfilesPage /> : <Navigate to="/login" replace />} />
|
<Route path="/product-profiles" element={token ? <ProductProfilesPage /> : <Navigate to="/login" replace />} />
|
||||||
<Route path="/performance" element={token ? <PerformancePage /> : <Navigate to="/login" replace />} />
|
<Route path="/performance" element={token ? <PerformancePage /> : <Navigate to="/login" replace />} />
|
||||||
|
<Route path="/help" element={token ? <KnowledgeBasePage /> : <Navigate to="/login" replace />} />
|
||||||
<Route path="*" element={<Navigate to="/" replace />} />
|
<Route path="*" element={<Navigate to="/" replace />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
<Toaster richColors position="top-right" />
|
<Toaster richColors position="top-right" />
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import type { ReactNode } from 'react'
|
|||||||
import { Link, useLocation } from 'react-router'
|
import { Link, useLocation } from 'react-router'
|
||||||
import {
|
import {
|
||||||
BookMarked,
|
BookMarked,
|
||||||
|
BookOpen,
|
||||||
BookUser,
|
BookUser,
|
||||||
Bot,
|
Bot,
|
||||||
Boxes,
|
Boxes,
|
||||||
@@ -45,21 +46,32 @@ export function AppShell({ children }: { children: ReactNode }) {
|
|||||||
|
|
||||||
<Separator className="bg-sidebar-border" />
|
<Separator className="bg-sidebar-border" />
|
||||||
|
|
||||||
<nav className="flex flex-1 flex-col gap-1 p-3">
|
<nav className="flex flex-1 flex-col gap-0.5 overflow-y-auto p-3">
|
||||||
<NavItem icon={Rocket} label="Get started" to="/start" />
|
<NavItem icon={Rocket} label="Get started" to="/start" />
|
||||||
|
|
||||||
|
<NavSection label="Work" />
|
||||||
<NavItem icon={LayoutDashboard} label="Board" to="/" />
|
<NavItem icon={LayoutDashboard} label="Board" to="/" />
|
||||||
<NavItem icon={Sparkles} label="Team" to="/team" />
|
<NavItem icon={Sparkles} label="Team" to="/team" />
|
||||||
<NavItem icon={Inbox} label="Cartable" to="/cartable" />
|
<NavItem icon={Inbox} label="Cartable" to="/cartable" />
|
||||||
<NavItem icon={ShieldCheck} label="Review inbox" to="/reviews" />
|
<NavItem icon={ShieldCheck} label="Review inbox" to="/reviews" />
|
||||||
<NavItem icon={Bot} label="AI seats" to="/seats" />
|
|
||||||
<NavItem icon={BookUser} label="Agent profiles" to="/agent-profiles" />
|
<NavSection label="Organization" />
|
||||||
<NavItem icon={BookMarked} label="Skills" to="/skills" />
|
|
||||||
<NavItem icon={Package} label="Product profiles" to="/product-profiles" />
|
|
||||||
<NavItem icon={Network} label="Org chart" to="/org" />
|
|
||||||
<NavItem icon={Boxes} label="Structure" to="/structure" />
|
<NavItem icon={Boxes} label="Structure" to="/structure" />
|
||||||
|
<NavItem icon={Network} label="Org chart" to="/org" />
|
||||||
<NavItem icon={Users} label="Members" to="/members" />
|
<NavItem icon={Users} label="Members" to="/members" />
|
||||||
|
|
||||||
|
<NavSection label="AI & libraries" />
|
||||||
|
<NavItem icon={Bot} label="AI seats" to="/seats" />
|
||||||
|
<NavItem icon={BookMarked} label="Skills" to="/skills" />
|
||||||
|
<NavItem icon={BookUser} label="Agent profiles" to="/agent-profiles" />
|
||||||
|
<NavItem icon={Package} label="Product profiles" to="/product-profiles" />
|
||||||
|
|
||||||
|
<NavSection label="Insights" />
|
||||||
<NavItem icon={Gauge} label="Performance" to="/performance" />
|
<NavItem icon={Gauge} label="Performance" to="/performance" />
|
||||||
<NavItem icon={ChartColumn} label="Analytics" to="/analytics" />
|
<NavItem icon={ChartColumn} label="Analytics" to="/analytics" />
|
||||||
|
|
||||||
|
<NavSection label="Help" />
|
||||||
|
<NavItem icon={BookOpen} label="Knowledge base" to="/help" />
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<Separator className="bg-sidebar-border" />
|
<Separator className="bg-sidebar-border" />
|
||||||
@@ -83,6 +95,14 @@ export function AppShell({ children }: { children: ReactNode }) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function NavSection({ label }: { label: string }) {
|
||||||
|
return (
|
||||||
|
<div className="px-3 pb-1 pt-4 text-[10px] font-semibold uppercase tracking-wider text-sidebar-foreground/45">
|
||||||
|
{label}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function NavItem({
|
function NavItem({
|
||||||
icon: Icon,
|
icon: Icon,
|
||||||
label,
|
label,
|
||||||
|
|||||||
@@ -0,0 +1,312 @@
|
|||||||
|
export interface KbArticle {
|
||||||
|
id: string
|
||||||
|
category: string
|
||||||
|
title: string
|
||||||
|
summary: string
|
||||||
|
keywords: string
|
||||||
|
body: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const KB_CATEGORIES = [
|
||||||
|
'Getting started',
|
||||||
|
'Concepts',
|
||||||
|
'Build your team',
|
||||||
|
'Run & review',
|
||||||
|
'Libraries',
|
||||||
|
'Insights & governance',
|
||||||
|
'Troubleshooting',
|
||||||
|
] as const
|
||||||
|
|
||||||
|
// Authored as Markdown. Examples use 4-space indented code blocks (no backticks) so they render
|
||||||
|
// cleanly. Keep articles practical: what it is, steps, and an example.
|
||||||
|
export const KB_ARTICLES: KbArticle[] = [
|
||||||
|
{
|
||||||
|
id: 'what-is-teamup',
|
||||||
|
category: 'Concepts',
|
||||||
|
title: 'What is TeamUp?',
|
||||||
|
summary: 'A live org chart that does work — humans and governed AI agents on one board.',
|
||||||
|
keywords: 'overview intro what is teamup agents',
|
||||||
|
body: `## What is TeamUp?
|
||||||
|
|
||||||
|
TeamUp.AI is a **live org chart that does work**. You model your organization — divisions, products, teams, and the role-seats inside them — and any open seat can be filled by a **governed AI agent** that does that role's work: writing specs, breaking down stories, implementing, drafting test plans, reviewing.
|
||||||
|
|
||||||
|
Humans and AI work side by side on one board, and **every AI action is a proposal a human reviews** before it takes effect.
|
||||||
|
|
||||||
|
### Why it's different
|
||||||
|
- It has a concept of a **team** and of **role coverage** — not just a single assistant in one editor.
|
||||||
|
- Output is **governed**: an action waits in a review queue unless you allow it to run.
|
||||||
|
- It measures **human edit distance** — how little you change an agent's output before approving. Low and falling means the AI is trusted.
|
||||||
|
|
||||||
|
### The loop in one line
|
||||||
|
Model the org → give the product an identity → staff seats with agents → assign work → review the action, result, and log → approve → the result lands, memory compounds, and the next role picks up automatically.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'orientation',
|
||||||
|
category: 'Concepts',
|
||||||
|
title: '5-minute orientation: the key terms',
|
||||||
|
summary: 'Seats, agents, skills, autonomy, the action gate, memory, and the metric.',
|
||||||
|
keywords: 'concepts terms glossary seat agent skill autonomy gate memory',
|
||||||
|
body: `## The terms you'll see everywhere
|
||||||
|
|
||||||
|
- **Organization → Division → Product → Team → Seat** — your structure. A *team* runs a board; a *seat* is a role on a team.
|
||||||
|
- **Seat states (the triad)** — *Human* (slate), *Open* (amber), or *AI* (indigo). An open seat can be staffed by an agent.
|
||||||
|
- **Agent** — the AI in a seat: a name + persona, matched **skills**, an **autonomy** level, a model connection, and optional docs/tools.
|
||||||
|
- **Skill** — a reusable capability (a SKILL.md) such as *spec-writing* or *test-plan-generation*. Gated by golden tests.
|
||||||
|
- **Product identity (PRODUCT.md)** — a brief shared by every agent on the product, injected into every run.
|
||||||
|
- **Autonomy dial** — *Draft-only / Gated / Autonomous*: whether an action waits for a human or runs immediately.
|
||||||
|
- **Action & risk** — every output is an action with a risk tag (*read / draft / publish / destructive*).
|
||||||
|
- **Action gate** — compares autonomy to risk; holds the action for review or executes it. **Destructive always waits for a human.**
|
||||||
|
- **Review inbox** — where held actions wait. You see the action, the result, and the run log, then approve / edit / send back.
|
||||||
|
- **Working memory** — decisions you approve become recallable team and product memory, read at the next run.
|
||||||
|
- **Human edit distance** — the north-star metric: how much you change agent output before approving.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'quick-start',
|
||||||
|
category: 'Getting started',
|
||||||
|
title: 'Quick start: zero to your first AI agent',
|
||||||
|
summary: 'The whole A-to-Z in seven steps. The Get started page tracks your progress.',
|
||||||
|
keywords: 'quick start setup onboarding first agent a to z',
|
||||||
|
body: `## From zero to a working AI team
|
||||||
|
|
||||||
|
The **Get started** page (top of the sidebar) tracks these steps and shows what's done.
|
||||||
|
|
||||||
|
1. **Sign in** — or, first time, choose *Bootstrap the owner* to create your org.
|
||||||
|
2. **Model the org** (Structure) — add a product and at least one team under it.
|
||||||
|
3. **Give the product an identity** (Structure → Identity) — write a short PRODUCT.md brief.
|
||||||
|
4. **Connect a model** (AI seats → Model connections) — add your API key (BYOK).
|
||||||
|
5. **Staff a seat** (AI seats) — pick a role, choose skills + autonomy + model, save. The seat turns AI.
|
||||||
|
6. **Fill the backlog** (Board) — create tasks and assign them to agents or humans.
|
||||||
|
7. **Review the first output** (Review inbox) — the agent's proposal waits for your approval.
|
||||||
|
|
||||||
|
### Example
|
||||||
|
Create product **HRM**, team **HRM Engineering**, staff a **Backend Engineer** seat with an agent named *Dex* (skills: code-implementation, bug-diagnosis; autonomy: Gated). Create a story, assign it to Dex, and watch it run — its output lands in your review inbox.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'model-org',
|
||||||
|
category: 'Build your team',
|
||||||
|
title: 'Model your org: divisions, products, teams',
|
||||||
|
summary: 'Build the spine: Organization → Divisions → Products/Services → Teams.',
|
||||||
|
keywords: 'structure org division product team build spine',
|
||||||
|
body: `## Model the organization
|
||||||
|
|
||||||
|
Go to **Structure**.
|
||||||
|
|
||||||
|
1. *(Optional)* Add **Divisions** — top-level slices (Technical, Finance, HR…).
|
||||||
|
2. Add a **Product** or **Service** — engineering divisions ship products; others run services. Example: *HRM*.
|
||||||
|
3. Add **Teams** and attach each to a product. A team is the unit that runs a board. Example: *HRM Product*, *HRM Engineering*, *HRM Quality*.
|
||||||
|
|
||||||
|
### Why product matters
|
||||||
|
A team should sit **under a product** — that's what lets the product's shared identity and memory reach the team's agents. Teams with no product still work, but they don't get product-level context.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'product-identity',
|
||||||
|
category: 'Build your team',
|
||||||
|
title: 'Write a product identity (PRODUCT.md)',
|
||||||
|
summary: 'A brief shared by every agent on the product, injected into every run.',
|
||||||
|
keywords: 'product identity product.md brief shared context',
|
||||||
|
body: `## Give the product an identity
|
||||||
|
|
||||||
|
On **Structure**, click **Identity** on a product. Write a PRODUCT.md — frontmatter + a Markdown brief — using the Edit / Preview tabs, then **Save**. It is now injected into every agent run on the product.
|
||||||
|
|
||||||
|
### What to include
|
||||||
|
- What the product is and who it serves.
|
||||||
|
- Its modules / scope.
|
||||||
|
- Conventions every contributor must follow.
|
||||||
|
|
||||||
|
### Example PRODUCT.md
|
||||||
|
|
||||||
|
---
|
||||||
|
product: HRM
|
||||||
|
version: 1.0.0
|
||||||
|
summary: A modular HRM application for mid-sized companies.
|
||||||
|
---
|
||||||
|
|
||||||
|
# HRM — Human Resource Management
|
||||||
|
|
||||||
|
Employees, attendance, leave, payroll, recruitment, performance.
|
||||||
|
|
||||||
|
## Conventions
|
||||||
|
- Money in minor units (integers); currency per company.
|
||||||
|
- Dates/times stored in UTC; shown in the company timezone.
|
||||||
|
- PII is access-controlled by role; least privilege.
|
||||||
|
|
||||||
|
**Tip:** keep reusable templates in **Product profiles** and apply one to a product in a click.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'byok',
|
||||||
|
category: 'Build your team',
|
||||||
|
title: 'Connect a model (BYOK)',
|
||||||
|
summary: 'Add your own API key — owner-only, encrypted, never returned.',
|
||||||
|
keywords: 'byok model api key connection openai endpoint',
|
||||||
|
body: `## Connect a model
|
||||||
|
|
||||||
|
Go to **AI seats → Model connections**.
|
||||||
|
|
||||||
|
1. Click **Add**. Enter a **name**, **provider** (OpenAI-compatible), **model**, and your **API key**.
|
||||||
|
2. *(Optional)* Set a **Base URL** for a gateway. You can paste a base URL *or* the full chat-completions URL — both work.
|
||||||
|
3. Save, then click **Test** to confirm a call succeeds.
|
||||||
|
|
||||||
|
### Security
|
||||||
|
Keys are **owner-only**, **encrypted at rest**, used **server-side only**, and **never returned** to a client after saving. Team owners assign a connection from a list; they never see the key.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'staff-seat',
|
||||||
|
category: 'Build your team',
|
||||||
|
title: 'Staff a seat with an AI agent',
|
||||||
|
summary: 'Turn an open role-seat into a governed AI teammate.',
|
||||||
|
keywords: 'staff seat agent configure autonomy skills persona',
|
||||||
|
body: `## Staff a seat
|
||||||
|
|
||||||
|
Go to **AI seats**, pick a team, and select a role-seat (e.g. *Product Owner*).
|
||||||
|
|
||||||
|
1. *(Optional)* **Start from a profile** (AGENTS.md) to prefill identity, skills, and persona.
|
||||||
|
2. Give the agent a **name** and **monogram**.
|
||||||
|
3. Set **autonomy** — start with **Gated** so output waits for review.
|
||||||
|
4. Pick **skills** — the configurator suggests a set for the role.
|
||||||
|
5. Choose the **model connection**. *(Optional)* attach **docs** and **MCP tool servers**.
|
||||||
|
6. **Save** — the seat turns AI and the agent appears with a live animated face.
|
||||||
|
|
||||||
|
### Example
|
||||||
|
*Ava*, Product Owner — skills: spec-writing, story-breakdown, requirements-analysis; autonomy: Gated; model: your connection. Ava now drafts specs and child stories for any task you assign her, holding them in review.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'board-assign',
|
||||||
|
category: 'Run & review',
|
||||||
|
title: 'Create and assign work on the board',
|
||||||
|
summary: 'Tasks across Backlog → In progress → In review → Done, assigned to humans or AI.',
|
||||||
|
keywords: 'board task assign columns story spec drag',
|
||||||
|
body: `## Run delivery on the board
|
||||||
|
|
||||||
|
Go to **Board** and pick a team.
|
||||||
|
|
||||||
|
1. **Create a task** — give it a title and type (Spec, Story, Test, Review).
|
||||||
|
2. **Assign** it to a human or to an **AI agent** (open the task to assign).
|
||||||
|
3. **Move** tasks across the columns: Backlog → In progress → In review → Done.
|
||||||
|
|
||||||
|
Assigning a task to an AI seat **dispatches a run**: the work is queued and executed off the request path, and the agent's face animates *thinking → working*. A Gated agent's output then waits in the **Review inbox**.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'review-inbox',
|
||||||
|
category: 'Run & review',
|
||||||
|
title: 'Read the review: action, result, and run log',
|
||||||
|
summary: 'Every held item shows what the AI will do, what it produced, and how it got there.',
|
||||||
|
keywords: 'review inbox action result run log trace approve transparency',
|
||||||
|
body: `## The review inbox in depth
|
||||||
|
|
||||||
|
Every held action is shown in three parts so you can decide with full context.
|
||||||
|
|
||||||
|
1. **Action** — a plain statement of what approving does (e.g. "write this artifact to the board and create N child tasks"), with a warning for destructive actions.
|
||||||
|
2. **Result** — the proposed **artifact** (editable) and the proposed **child tasks**. As you edit, a live **diff** highlights exactly what you changed.
|
||||||
|
3. **Run log** — expand it to see *how the agent got there*:
|
||||||
|
- latency, the agent and its autonomy;
|
||||||
|
- which **skills** were applied;
|
||||||
|
- which **tools** were available and **actually called** (with success/failure);
|
||||||
|
- how many **memory hits** were recalled and whether the **product identity** was included;
|
||||||
|
- the **raw model output** and the full **assembled prompt**.
|
||||||
|
|
||||||
|
### Example
|
||||||
|
Ava's "Employee management" spec shows: *write-spec · Draft*, the drafted spec, and a run log reading "21.3s · skills: spec-writing, story-breakdown · product identity included". You read exactly what she did before approving.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'approve-edit-sendback',
|
||||||
|
category: 'Run & review',
|
||||||
|
title: 'Approve, edit & approve, or send back',
|
||||||
|
summary: 'Your three choices — and the one that feeds the metric.',
|
||||||
|
keywords: 'approve edit send back decision edit distance metric',
|
||||||
|
body: `## Your three decisions
|
||||||
|
|
||||||
|
- **Approve** — the result executes: the artifact lands on the board and any child tasks are created. Recorded with zero edit distance.
|
||||||
|
- **Edit & approve** — your edited version executes, and the **edit distance** (how much you changed) is recorded. *This is the metric.*
|
||||||
|
- **Send back** — nothing executes; the item returns to the agent to try again.
|
||||||
|
|
||||||
|
### Why edit-and-approve matters
|
||||||
|
The whole bet is that you **edit a little** rather than rewrite from scratch. Edit-and-approve is what feeds human edit distance — the signal that proves an agent is trustworthy. Watch it fall over a sprint in **Analytics**.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'handoff-memory',
|
||||||
|
category: 'Run & review',
|
||||||
|
title: 'The PO→QA handoff and working memory',
|
||||||
|
summary: 'Done stories hand off to QA automatically; approvals become shared memory.',
|
||||||
|
keywords: 'handoff qa done memory correction product team shared',
|
||||||
|
body: `## Automatic handoff
|
||||||
|
|
||||||
|
When a story is marked **Done** on a team, TeamUp automatically creates a **QA task** for the team's QA agent, with provenance back to the story. The QA agent drafts a test plan that waits in review.
|
||||||
|
|
||||||
|
Guardrails prevent loops: QA output never re-triggers QA, and a task gets at most one handoff.
|
||||||
|
|
||||||
|
## Working memory
|
||||||
|
|
||||||
|
Every decision you approve — and especially every **correction** — is written to **working memory** and read back, by relevance, at the next run.
|
||||||
|
|
||||||
|
Memory is **layered**: a product's decisions are shared by **every agent across the product's teams**, while team memory keeps local context. So your corrections compound into institutional knowledge instead of being repeated.
|
||||||
|
|
||||||
|
### Example
|
||||||
|
You correct Ava's spec to say "money in minor units". Next time *any* HRM agent runs, that correction is recalled — Dex the backend engineer sees it too.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'libraries',
|
||||||
|
category: 'Libraries',
|
||||||
|
title: 'Skills, agent profiles & product profiles',
|
||||||
|
summary: 'Reusable, versioned building blocks — author, version, publish, install, apply.',
|
||||||
|
keywords: 'skills agent profiles product profiles library marketplace version fork publish',
|
||||||
|
body: `## Three reusable libraries
|
||||||
|
|
||||||
|
- **Skills** (SKILL.md) — capabilities an agent runs (spec-writing, code-implementation…). Free builtins ship for everyone; author your own, version them, and publish to the marketplace. A skill must be **Published** (a role + a passing golden test) to run.
|
||||||
|
- **Agent profiles** (AGENTS.md) — reusable agent definitions (identity + skills + autonomy + persona). Apply one to a seat to prefill it.
|
||||||
|
- **Product profiles** (PRODUCT.md) — reusable product identities. **Apply** one to a product to set its shared identity.
|
||||||
|
|
||||||
|
### Common actions (all three libraries)
|
||||||
|
1. **View** — read the full .md (Edit / Preview).
|
||||||
|
2. **Edit** / **New version** — change your own, or bump the version.
|
||||||
|
3. **Fork** — copy a builtin into your org to customize.
|
||||||
|
4. **Publish / Unpublish** — list or unlist on the marketplace.
|
||||||
|
5. **Install** — copy a marketplace item into your library.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'analytics',
|
||||||
|
category: 'Insights & governance',
|
||||||
|
title: 'Analytics & the metric',
|
||||||
|
summary: 'Approval rate, tasks done, and human edit distance per agent.',
|
||||||
|
keywords: 'analytics metric edit distance approval rate performance trend',
|
||||||
|
body: `## Proving the bet
|
||||||
|
|
||||||
|
The **Analytics** view tracks the numbers that matter:
|
||||||
|
- **Approval rate** and **tasks done**;
|
||||||
|
- **Human edit distance** per agent, with a trend line.
|
||||||
|
|
||||||
|
If edit distance is **low and falling** across a sprint, your AI teammates are trusted and saving you more time than they cost to supervise. The **Performance** view puts humans and AI on the same accountability scale.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'governance',
|
||||||
|
category: 'Insights & governance',
|
||||||
|
title: 'Governance & safety — what TeamUp guarantees',
|
||||||
|
summary: 'Permission checks, the action gate, BYOK, and data-not-instructions.',
|
||||||
|
keywords: 'governance safety security permission gate destructive byok air-gap',
|
||||||
|
body: `## What TeamUp guarantees
|
||||||
|
|
||||||
|
- **Permission on every mutation**, at the right scope. The UI is never trusted for authorization.
|
||||||
|
- **AI output is a proposal.** Nothing takes effect until the action gate allows it — and **destructive actions always wait for a human**, whatever the autonomy.
|
||||||
|
- **BYOK keys** are owner-only, encrypted at rest, used server-side only, never returned to a client.
|
||||||
|
- **Retrieved content** (docs, code, tool output) is treated as **data, not instructions**.
|
||||||
|
- **Skills are golden-tested** — only passing skills can run or be published.
|
||||||
|
- **Self-hostable and air-gappable** as a single unit.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'troubleshooting',
|
||||||
|
category: 'Troubleshooting',
|
||||||
|
title: 'Troubleshooting & tips',
|
||||||
|
summary: 'Identity not injecting, skills not running, model test failing, and more.',
|
||||||
|
keywords: 'troubleshooting tips problems identity skill draft model test fails',
|
||||||
|
body: `## Common issues
|
||||||
|
|
||||||
|
- **Agent identity doesn't inject?** A team must be **under a product**, and that product must have a **PRODUCT.md identity**, for the shared identity to appear in runs.
|
||||||
|
- **A skill won't run?** It must be **Published** — give it at least one role and a passing golden test.
|
||||||
|
- **Model test fails?** Check the provider, model name, key, and endpoint. A full *chat-completions* URL works as well as a base URL.
|
||||||
|
- **Nothing in the review inbox?** Assign a task to a **Gated** AI seat — Autonomous agents execute low-risk actions directly without holding.
|
||||||
|
|
||||||
|
## Tips
|
||||||
|
- **Start agents Gated**, then raise autonomy once you trust them.
|
||||||
|
- **Edit-and-approve** (don't rewrite) — that's what feeds the metric.
|
||||||
|
- **Mark a story Done** to fire the PO→QA handoff.`,
|
||||||
|
},
|
||||||
|
]
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
import { useMemo, useState } from 'react'
|
||||||
|
import { BookOpen, ChevronDown, ChevronRight, Search } from 'lucide-react'
|
||||||
|
import ReactMarkdown from 'react-markdown'
|
||||||
|
import remarkGfm from 'remark-gfm'
|
||||||
|
import { AppShell } from '@/components/AppShell'
|
||||||
|
import { Card, CardContent } from '@/components/ui/card'
|
||||||
|
import { Input } from '@/components/ui/input'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
import { KB_ARTICLES, KB_CATEGORIES, type KbArticle } from '@/lib/kbArticles'
|
||||||
|
import '@/components/markdown.css'
|
||||||
|
|
||||||
|
export function KnowledgeBasePage() {
|
||||||
|
const [query, setQuery] = useState('')
|
||||||
|
const [open, setOpen] = useState<Set<string>>(new Set())
|
||||||
|
|
||||||
|
const matches = useMemo(() => {
|
||||||
|
const q = query.trim().toLowerCase()
|
||||||
|
if (!q) return KB_ARTICLES
|
||||||
|
return KB_ARTICLES.filter((a) =>
|
||||||
|
`${a.title} ${a.summary} ${a.keywords} ${a.body}`.toLowerCase().includes(q),
|
||||||
|
)
|
||||||
|
}, [query])
|
||||||
|
|
||||||
|
const grouped = useMemo(() => {
|
||||||
|
return KB_CATEGORIES.map((category) => ({
|
||||||
|
category,
|
||||||
|
articles: matches.filter((a) => a.category === category),
|
||||||
|
})).filter((g) => g.articles.length > 0)
|
||||||
|
}, [matches])
|
||||||
|
|
||||||
|
const toggle = (id: string) =>
|
||||||
|
setOpen((s) => {
|
||||||
|
const next = new Set(s)
|
||||||
|
next.has(id) ? next.delete(id) : next.add(id)
|
||||||
|
return next
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AppShell>
|
||||||
|
<div className="mx-auto max-w-3xl p-6">
|
||||||
|
<header className="mb-5">
|
||||||
|
<h1 className="flex items-center gap-2 text-2xl font-semibold tracking-tight">
|
||||||
|
<BookOpen className="size-6" /> Knowledge base
|
||||||
|
</h1>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
How to work with TeamUp — concepts, step-by-step guides, and examples.
|
||||||
|
</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div className="relative mb-6">
|
||||||
|
<Search className="pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" />
|
||||||
|
<Input
|
||||||
|
value={query}
|
||||||
|
onChange={(e) => setQuery(e.target.value)}
|
||||||
|
placeholder="Search the knowledge base…"
|
||||||
|
className="pl-9"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{grouped.length === 0 && (
|
||||||
|
<p className="text-sm text-muted-foreground">No articles match “{query}”.</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-6">
|
||||||
|
{grouped.map((group) => (
|
||||||
|
<section key={group.category} className="flex flex-col gap-2">
|
||||||
|
<h2 className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
|
||||||
|
{group.category}
|
||||||
|
</h2>
|
||||||
|
{group.articles.map((article) => (
|
||||||
|
<ArticleCard
|
||||||
|
key={article.id}
|
||||||
|
article={article}
|
||||||
|
open={open.has(article.id) || (!!query && matches.length <= 4)}
|
||||||
|
onToggle={() => toggle(article.id)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</section>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</AppShell>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function ArticleCard({ article, open, onToggle }: { article: KbArticle; open: boolean; onToggle: () => void }) {
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<CardContent className="py-0">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={onToggle}
|
||||||
|
className="flex w-full items-start gap-3 py-4 text-left"
|
||||||
|
>
|
||||||
|
{open ? <ChevronDown className="mt-0.5 size-4 shrink-0 text-muted-foreground" /> : <ChevronRight className="mt-0.5 size-4 shrink-0 text-muted-foreground" />}
|
||||||
|
<span className="min-w-0">
|
||||||
|
<span className="block text-sm font-medium">{article.title}</span>
|
||||||
|
<span className={cn('block text-xs text-muted-foreground', open && 'sr-only')}>{article.summary}</span>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
{open && (
|
||||||
|
<div className="md-prose pb-5 pl-7">
|
||||||
|
<ReactMarkdown remarkPlugins={[remarkGfm]}>{article.body}</ReactMarkdown>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user