feat(lanes): surface child worktrees on a lane card + detect Superpowers skills
Adopting the main repo as its own lane now gets stage detection (cwd matches, same as any other lane), and its card lists every managed-worktree lane provisioned from it with a jump-to link. Also add the two missing Skill-tool detect rules (implement, ship) so detection covers all four Superpowers workflow phases, not just plan/review.
This commit is contained in:
@@ -180,9 +180,17 @@ function since(sec: number | null): string {
|
||||
export default function LaneCard({
|
||||
lane,
|
||||
onAction,
|
||||
childWorktrees,
|
||||
onSelectLane,
|
||||
}: {
|
||||
lane: Lane;
|
||||
onAction: (action: string, body?: Record<string, unknown>) => void;
|
||||
/** Other lanes whose `source_repo` is this lane's `cwd` — populated only
|
||||
* when this lane is itself a source repo (typically an adopted one) that
|
||||
* other lanes were provisioned as worktrees from. */
|
||||
childWorktrees?: Lane[];
|
||||
/** Jumps the Workspace page's selection to another lane's card. */
|
||||
onSelectLane?: (id: number) => void;
|
||||
}) {
|
||||
const { t } = useTranslation(["lanes"]);
|
||||
const [destructiveAction, setDestructiveAction] = useState<"reset" | "remove" | "purge" | null>(
|
||||
@@ -452,6 +460,31 @@ export default function LaneCard({
|
||||
</div>
|
||||
</dl>
|
||||
|
||||
{childWorktrees && childWorktrees.length > 0 && (
|
||||
<div
|
||||
data-testid="lane-child-worktrees"
|
||||
className="mb-3 space-y-1 text-[11px] text-fg-secondary"
|
||||
>
|
||||
<div className="text-fg-muted">
|
||||
{t("worktrees.heading", { count: childWorktrees.length })}
|
||||
</div>
|
||||
<ul className="space-y-0.5">
|
||||
{childWorktrees.map((w) => (
|
||||
<li key={w.id}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onSelectLane?.(w.id)}
|
||||
className="truncate text-left text-blue-400 hover:underline"
|
||||
title={w.cwd}
|
||||
>
|
||||
#{w.id} {w.title || w.cwd} · {w.status}
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{integrations && (
|
||||
<div className="mb-2 flex items-center gap-1.5 text-[10px]">
|
||||
{(["tracker", "dev_qc", "ci_wait"] as const).map((name) => (
|
||||
|
||||
@@ -37,6 +37,7 @@ function laneFixture(over: Partial<Lane> = {}): Lane {
|
||||
cwd: "/lanes/repo__feature",
|
||||
branch: "feat/feature",
|
||||
kind: "managed",
|
||||
source_repo: null,
|
||||
pipeline: "default",
|
||||
session_id: null,
|
||||
run_id: null,
|
||||
|
||||
@@ -54,6 +54,7 @@ function makeLane(overrides: Partial<Lane> = {}): Lane {
|
||||
cwd: "/work/demo",
|
||||
branch: "lane/demo",
|
||||
kind: "adopted",
|
||||
source_repo: null,
|
||||
pipeline: "default",
|
||||
session_id: null,
|
||||
run_id: null,
|
||||
@@ -90,6 +91,31 @@ describe("LaneCard status badge", () => {
|
||||
}
|
||||
});
|
||||
|
||||
describe("LaneCard child worktrees", () => {
|
||||
it("lists worktrees provisioned from this lane and jumps to one on click", async () => {
|
||||
const onSelectLane = vi.fn();
|
||||
const worktree = makeLane({ id: 8, title: "Worktree A", status: "running" });
|
||||
render(
|
||||
<LaneCard
|
||||
lane={makeLane({ id: 1 })}
|
||||
onAction={vi.fn()}
|
||||
childWorktrees={[worktree]}
|
||||
onSelectLane={onSelectLane}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId("lane-child-worktrees")).toBeInTheDocument();
|
||||
const user = userEvent.setup();
|
||||
await user.click(screen.getByRole("button", { name: /Worktree A/ }));
|
||||
expect(onSelectLane).toHaveBeenCalledWith(8);
|
||||
});
|
||||
|
||||
it("renders nothing when there are no child worktrees", () => {
|
||||
render(<LaneCard lane={makeLane({ id: 1 })} onAction={vi.fn()} />);
|
||||
expect(screen.queryByTestId("lane-child-worktrees")).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
const pipelineNodes: Lane["pipeline_nodes"] = [
|
||||
{ id: "intake", label: "intake", icon: "📥", gate: false, state: "done" },
|
||||
{ id: "plan", label: "plan", icon: "🧭", gate: false, state: "done" },
|
||||
|
||||
Reference in New Issue
Block a user