Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
69c64ae
Loops: restyle overview + relabel Automations→Loops (UI)
brsbl Jun 20, 2026
0d6b342
Loops: group rows by project, single last-run status icon, menu icons
brsbl Jun 20, 2026
43d07b5
Loops: keep enabled dot for not-yet-run loops
brsbl Jun 20, 2026
dc9e7b8
Loops: detail health rollup + restyle (stage 2)
brsbl Jun 20, 2026
98d03a6
Loops: inline edit on the detail page (stage 3)
brsbl Jun 20, 2026
75971f0
Add Skills listing + delete backend (Loops & Skills S1)
brsbl Jun 19, 2026
0a19449
Skills: view + route + nav wired to S1 backend (stage 5)
brsbl Jun 20, 2026
438bf74
Theme: darken primary for confident CTAs (stage 8)
brsbl Jun 20, 2026
ba4b54c
Loops/Skills build: record stage status in plan
brsbl Jun 20, 2026
e50f480
Loops: declutter detail header + always-present View thread
brsbl Jun 20, 2026
23c947e
Skills: create via prompt (seeded composer) — stage 6
brsbl Jun 20, 2026
c9158d8
Loops/Skills build: stage 6 done, record stage 7 plan (view/edit)
brsbl Jun 20, 2026
2a47a68
Skills: view + edit (stage 7)
brsbl Jun 20, 2026
a9498d9
Loops/Skills build: all stages complete + smoke-tested
brsbl Jun 20, 2026
93160d3
Skills: bb-first, collapsible scrollable provider sections
brsbl Jun 20, 2026
166e88f
Loops: collapsible project groups on the overview
brsbl Jun 20, 2026
7056a60
Loops: personal loops flat, only real projects get headers
brsbl Jun 20, 2026
d6cb664
Plan: shared composer extraction (no upstream PR to piggyback)
brsbl Jun 20, 2026
66f6037
Extract shared composer hook (useComposerArea)
brsbl Jun 20, 2026
b40688e
Skills: open SKILL.md in the user's editor from the detail view
brsbl Jun 20, 2026
f916bea
Timeline: "Created loop" notice linking to a loop made from a thread
brsbl Jun 20, 2026
4b6ce61
Skills: always read from disk (drop client-side staleTime)
brsbl Jun 23, 2026
ed094b0
Loops/Skills: design-pass polish (carets, search, width, tokens)
brsbl Jun 23, 2026
19c1fb8
Skills: align the search bar with the sidebar search
brsbl Jun 23, 2026
cb09d82
Loops detail: View thread is per-run, not a top-level action
brsbl Jun 23, 2026
79b1abb
Loops/Skills: empty-state teaching, create chip, and design-review po…
brsbl Jun 23, 2026
b95e72a
Loops detail: calmer hierarchy + prompt-box-style execution/environment
brsbl Jun 24, 2026
01bb732
Loops/Skills overviews: search on Loops, loading skeletons, polish
brsbl Jun 24, 2026
8bad85a
Stories: Skills/Loops/Loop-detail states for iterating in Ladle
brsbl Jun 24, 2026
f459bf4
Spike: edit script loops from the detail page
brsbl Jun 24, 2026
7910fd4
Merge remote-tracking branch 'origin/main' into bb/extract-shared-com…
brsbl Jun 24, 2026
a9fd514
Fix root composer hook lint dependency
brsbl Jun 24, 2026
c5ba120
Loops/Skills: library model, split create button, semantic run history
brsbl Jun 24, 2026
b821288
Merge useComposerArea (#283) into the redesign branch
brsbl Jun 24, 2026
5660121
Merge script-loop editing (#337) into the redesign branch
brsbl Jun 24, 2026
e8089c0
Add agent loop edit composer
brsbl Jun 24, 2026
96aacde
Merge origin/main into the redesign branch
brsbl Jun 24, 2026
68cadb7
Move skills cache writes into a cache owner (fix CI guard)
brsbl Jun 24, 2026
f8b837d
Merge remote-tracking branch 'origin/main' into bb/redesign-thread-p-…
brsbl Jun 25, 2026
55f5e12
Merge branch 'bb/item-c-agent-loop-prompt-box-edit-composer-thr_x5kn8…
brsbl Jun 25, 2026
ea8936d
Fix list-skills typecheck after main merge (additionalSkillsRootPaths)
brsbl Jun 25, 2026
8bf3222
Skills detail: render SKILL.md via the shared markdown viewer
brsbl Jun 25, 2026
e474d2b
Skills overview: lead with the page description above the search
brsbl Jun 25, 2026
c255440
Skills stories: open the real detail popup on row-click
brsbl Jun 25, 2026
90d8cb4
Skills overview: quiet the page description to a subtle caption
brsbl Jun 25, 2026
c0ea2cf
Skills: pointer cursor on skill rows
brsbl Jun 25, 2026
3c5d21f
Skills detail: render via the real file viewer (FilePreview); neutral…
brsbl Jun 25, 2026
b1e246e
Skills detail: header-anchored actions + edit in place
brsbl Jun 25, 2026
a4897e1
Skills: recoverable error state instead of a vague dead end
brsbl Jun 25, 2026
455aede
Skills detail: render frontmatter via the file viewer; Edit + overflo…
brsbl Jun 25, 2026
1700ebd
Skills detail: move actions onto the viewer's own toolbar
brsbl Jun 25, 2026
b9515ca
Skills detail: single overflow by the title, no viewer toolbar
brsbl Jun 25, 2026
297026a
Skills detail: left-align the overflow next to the title
brsbl Jun 25, 2026
a037043
Skills detail: tighten the actions menu width to its content
brsbl Jun 25, 2026
6ac222d
Skills detail: Cancel/Save and delete-confirm in a modal footer
brsbl Jun 25, 2026
c6b85da
Dialog: pointer cursor on the close (✕) button
brsbl Jun 25, 2026
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
7 changes: 7 additions & 0 deletions apps/app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
APP_ROOT_ROUTE_PATH,
AUTH_CALLBACK_ROUTE_PATH,
AUTOMATIONS_ROUTE_PATH,
SKILLS_ROUTE_PATH,
AUTOMATION_DETAIL_ROUTE_PATH,
LEGACY_PROJECT_COMPOSE_ROUTE_PATH,
POPOUT_ROUTE_PATH,
Expand Down Expand Up @@ -52,6 +53,11 @@ const AutomationDetailView = lazy(() =>
default: m.AutomationDetailView,
})),
);
const SkillsView = lazy(() =>
import("./views/SkillsView").then((m) => ({
default: m.SkillsView,
})),
);
const ProjectSettingsView = lazy(() =>
import("./views/ProjectSettingsView").then((m) => ({
default: m.ProjectSettingsView,
Expand Down Expand Up @@ -109,6 +115,7 @@ function AppRoutes() {
path={AUTOMATION_DETAIL_ROUTE_PATH}
element={<AutomationDetailView />}
/>
<Route path={SKILLS_ROUTE_PATH} element={<SkillsView />} />
<Route
path={LEGACY_PROJECT_COMPOSE_ROUTE_PATH}
element={<RootComposeRoute />}
Expand Down
214 changes: 214 additions & 0 deletions apps/app/src/components/create-via-prompt-examples.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
import { Button } from "@/components/ui/button.js";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu.js";
import { Icon } from "@/components/ui/icon.js";
import {
CREATE_LOOP_PROMPT,
CREATE_SKILL_PROMPT,
} from "@/components/promptbox/PromptBoxActionsMenu";

export type CreateViaPromptKind = "skill" | "loop";

interface Example {
label: string;
/** Completes the "Create a new bb {kind} …" prompt; also shown on the card. */
description: string;
}

interface KindConfig {
prefix: string;
explainer: string;
examples: readonly Example[];
}

// The description completes the prompt prefix, so each card both teaches and
// seeds the composer. Skills are standard Agent Skills whose bb edge is being
// cross-provider; loops are cheap scripts that escalate to threads.
const CONFIG: Record<CreateViaPromptKind, KindConfig> = {
skill: {
prefix: CREATE_SKILL_PROMPT,
explainer:
"Write a skill once, and every agent in bb can run it, whatever the provider.",
examples: [
{
label: "Repro & fix",
description:
"turns a bug report into a failing test, then makes it pass",
},
{
label: "Scaffold to our patterns",
description:
"scaffolds a new component with its test and story to match our conventions",
},
{
label: "Onboard to a subsystem",
description:
"traces how a feature works across the codebase and writes an explainer",
},
],
},
loop: {
prefix: CREATE_LOOP_PROMPT,
explainer:
"Pay for agents only when there's real work, and fan a problem out across many threads in parallel.",
examples: [
{
label: "Flaky-test sweep",
description:
"run nightly, find flaky tests with a script, and spawn a fixer thread for each one",
},
{
label: "Silent health watch",
description:
"check the app every 15 minutes with a cheap script and spawn a thread only when something breaks",
},
{
label: "Error sentinel",
description:
"poll the error dashboard hourly and spawn a triage thread only on a new spike",
},
],
},
};

export interface CreateExample {
label: string;
description: string;
/** Full composer prompt seeded when this example is picked. */
prompt: string;
}

/**
* The shared create-via-prompt content for a kind: the marketing one-liner and
* the examples with their full seeded prompts. Surfaces render it how they like
* (cards, chips) without duplicating the copy.
*/
export function getCreateExamples(kind: CreateViaPromptKind): {
explainer: string;
examples: CreateExample[];
} {
const config = CONFIG[kind];
return {
explainer: config.explainer,
examples: config.examples.map((example) => ({
label: example.label,
description: example.description,
prompt: `${config.prefix}${example.description}.`,
})),
};
}

export interface CreateViaPromptExamplesProps {
kind: CreateViaPromptKind;
/** Opens the composer seeded with the given full prompt. */
onCreate: (prompt: string) => void;
}

/**
* Teaching panel for the Loops empty state: a one-line explainer plus clickable
* example cards that seed the create-via-prompt composer.
*/
export function CreateViaPromptExamples({
kind,
onCreate,
}: CreateViaPromptExamplesProps) {
const { explainer, examples } = getCreateExamples(kind);
return (
<div>
<p className="max-w-prose text-sm text-muted-foreground">{explainer}</p>
<p className="mt-3 text-xs font-medium text-subtle-foreground">
Start from an example
</p>
<div className="mt-1.5 grid gap-2 sm:grid-cols-2 lg:grid-cols-3">
{examples.map((example) => (
<button
key={example.label}
type="button"
onClick={() => onCreate(example.prompt)}
className="rounded-lg border border-border bg-background p-3 text-left transition-colors hover:border-file-accent/50 hover:bg-state-hover focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
>
<span className="block text-sm font-medium text-foreground">
{example.label}
</span>
<span className="mt-1 block text-xs leading-snug text-subtle-foreground">
{example.description}
</span>
</button>
))}
</div>
</div>
);
}

export interface CreateWithTemplatesButtonProps {
kind: CreateViaPromptKind;
/** Main-button text, e.g. "New loop" or "New bb skill". */
label: string;
/** Blank when called with no argument; seeded when given an example prompt. */
onCreate: (prompt?: string) => void;
}

/**
* Split (combo) button: the left half creates a blank one immediately; the right
* half opens a menu of example templates that seed the composer. Shared by the
* Skills and Loops library toolbars.
*/
export function CreateWithTemplatesButton({
kind,
label,
onCreate,
}: CreateWithTemplatesButtonProps) {
const { examples } = getCreateExamples(kind);
return (
<div className="flex shrink-0 items-stretch">
<Button
type="button"
size="sm"
className="rounded-r-none"
onClick={() => onCreate()}
>
<Icon name="Plus" className="size-4" />
{label}
</Button>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
type="button"
size="sm"
aria-label={`${label} from a template`}
className="rounded-l-none border-l border-background/25 px-1.5"
>
<Icon name="ChevronDown" className="size-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
align="end"
className="w-72"
mobileTitle="Start from an example"
>
<DropdownMenuLabel className="text-xs font-normal text-subtle-foreground">
Start from an example
</DropdownMenuLabel>
{examples.map((example) => (
<DropdownMenuItem
key={example.label}
onSelect={() => onCreate(example.prompt)}
>
<div className="flex min-w-0 flex-col">
<span className="text-sm text-foreground">{example.label}</span>
<span className="text-xs text-muted-foreground">
{example.description}
</span>
</div>
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
</div>
);
}
5 changes: 3 additions & 2 deletions apps/app/src/components/layout/AppLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,8 @@ function SidebarTriggerOverlay({
const routeTitles: Record<string, { title: string; subtitle?: string }> = {
"/": { title: "bb" },
"/settings": { title: "Settings" },
"/automations": { title: "Automations" },
"/automations": { title: "Loops" },
"/skills": { title: "Skills" },
};

interface AppHeaderProps {
Expand Down Expand Up @@ -461,7 +462,7 @@ export function AppLayout({ children }: AppLayoutProps) {
title: "",
subtitle: undefined,
breadcrumbs: [
{ label: "Automations", to: getAutomationsRoutePath() },
{ label: "Loops", to: getAutomationsRoutePath() },
{ label: automationName },
],
}
Expand Down
2 changes: 2 additions & 0 deletions apps/app/src/components/promptbox/PromptBoxActionsMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ interface PromptBoxActionsMenuProps {
}

export const CREATE_LOOP_PROMPT = "Create a new bb loop to ";
// Skill creation always targets a bb skill (the only manageable scope).
export const CREATE_SKILL_PROMPT = "Create a new bb skill that ";
export const LOOP_PROMPT_ACTION: PromptBoxAction = {
kind: "loop",
text: CREATE_LOOP_PROMPT,
Expand Down
Loading
Loading