Skip to content

Commit ecde27a

Browse files
committed
fix(spec): complete Pilot Shell branding across README, website, console, and assets
Rename standalone "Pilot" to "Pilot Shell" in README FAQ, context preservation, hooks, CLI, and testimonials sections. Rename "Pilot Console" to "Pilot Shell Console" across docs, website, console UI, and installer. Update FAQ section on website. Remove demo GIF files. Integrate recovered logo redesign and console screenshot updates. Add spec task navigation and plan deduplication tests.
1 parent 37f61cc commit ecde27a

46 files changed

Lines changed: 724 additions & 283 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.githooks/pre-commit

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@
88

99
set -eo pipefail
1010

11+
# --- Skip in worktrees (spec workflow commits are squash-merged later) ---
12+
if [ "$(git rev-parse --is-inside-work-tree 2>/dev/null)" = "true" ] && \
13+
[ "$(git rev-parse --git-dir)" != ".git" ]; then
14+
exit 0
15+
fi
16+
1117
# --- 1. Python unit tests ---
1218
LAUNCHER_CHANGED=$(git diff --cached --name-only -- 'launcher/' 'pilot/hooks/' | head -1)
1319
INSTALLER_CHANGED=$(git diff --cached --name-only -- 'installer/' | head -1)

README.md

Lines changed: 40 additions & 44 deletions
Large diffs are not rendered by default.

console/src/services/worker/http/routes/utils/planFileReader.ts

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,35 @@ function getAllPlansDirs(projectRoot: string): string[] {
165165
return dirs;
166166
}
167167

168+
/**
169+
* Deduplicate plans by name. When the same plan exists in both the main
170+
* docs/plans/ dir and a .worktrees/ copy, keep the worktree version
171+
* (that's where active work happens). For same-source duplicates, keep
172+
* the most recently modified.
173+
*/
174+
export function deduplicatePlans(plans: PlanInfo[]): PlanInfo[] {
175+
const byName = new Map<string, PlanInfo>();
176+
for (const plan of plans) {
177+
const existing = byName.get(plan.name);
178+
if (!existing) {
179+
byName.set(plan.name, plan);
180+
continue;
181+
}
182+
const planIsWorktree = plan.filePath.includes("/.worktrees/");
183+
const existingIsWorktree = existing.filePath.includes("/.worktrees/");
184+
if (planIsWorktree && !existingIsWorktree) {
185+
byName.set(plan.name, plan);
186+
} else if (!planIsWorktree && existingIsWorktree) {
187+
} else if (
188+
new Date(plan.modifiedAt).getTime() >
189+
new Date(existing.modifiedAt).getTime()
190+
) {
191+
byName.set(plan.name, plan);
192+
}
193+
}
194+
return Array.from(byName.values());
195+
}
196+
168197
export function getActivePlans(projectRoot: string): PlanInfo[] {
169198
const today = new Date();
170199
today.setHours(0, 0, 0, 0);
@@ -209,7 +238,7 @@ export function getActivePlans(projectRoot: string): PlanInfo[] {
209238
}
210239
}
211240

212-
return activePlans;
241+
return deduplicatePlans(activePlans);
213242
}
214243

215244
export function getAllPlans(projectRoot: string): PlanInfo[] {
@@ -219,7 +248,7 @@ export function getAllPlans(projectRoot: string): PlanInfo[] {
219248
allPlans.push(...scanPlansDir(plansDir));
220249
}
221250

222-
return allPlans
251+
return deduplicatePlans(allPlans)
223252
.sort(
224253
(a, b) =>
225254
new Date(b.modifiedAt).getTime() - new Date(a.modifiedAt).getTime(),
@@ -234,7 +263,7 @@ export function getActiveSpecs(projectRoot: string): PlanInfo[] {
234263
allPlans.push(...scanPlansDir(plansDir));
235264
}
236265

237-
return allPlans.sort(
266+
return deduplicatePlans(allPlans).sort(
238267
(a, b) =>
239268
new Date(b.modifiedAt).getTime() - new Date(a.modifiedAt).getTime(),
240269
);
@@ -251,12 +280,14 @@ export function getPlanStats(projectRoot: string): {
251280
completionTimeline: Array<{ date: string; count: number }>;
252281
recentlyVerified: Array<{ name: string; verifiedAt: string }>;
253282
} {
254-
const allPlans: PlanInfo[] = [];
283+
const rawPlans: PlanInfo[] = [];
255284

256285
for (const plansDir of getAllPlansDirs(projectRoot)) {
257-
allPlans.push(...scanPlansDir(plansDir));
286+
rawPlans.push(...scanPlansDir(plansDir));
258287
}
259288

289+
const allPlans = deduplicatePlans(rawPlans);
290+
260291
if (allPlans.length === 0) {
261292
return {
262293
totalSpecs: 0,

console/src/ui/viewer/layouts/DashboardLayout.tsx

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
import React from 'react';
2-
import { Sidebar } from './Sidebar';
3-
import { Topbar } from './Topbar';
1+
import React from "react";
2+
import { Sidebar } from "./Sidebar";
3+
import { Topbar } from "./Topbar";
44

55
interface DashboardLayoutProps {
66
children: React.ReactNode;
77
currentPath: string;
8-
workerStatus: 'online' | 'offline' | 'processing';
8+
workerStatus: "online" | "offline" | "processing";
99
version?: string;
1010
queueDepth?: number;
11-
theme: 'light' | 'dark';
11+
theme: "light" | "dark";
1212
onToggleTheme: () => void;
1313
onToggleLogs?: () => void;
1414
sidebarCollapsed: boolean;
@@ -27,10 +27,10 @@ export function DashboardLayout({
2727
sidebarCollapsed,
2828
onToggleSidebar,
2929
}: DashboardLayoutProps) {
30-
const themeName = theme === 'dark' ? 'pilot-shell' : 'pilot-shell-light';
30+
const themeName = theme === "dark" ? "pilot-shell" : "pilot-shell-light";
3131

3232
return (
33-
<div className="dashboard-layout flex min-h-screen" data-theme={themeName}>
33+
<div className="dashboard-layout flex h-screen" data-theme={themeName}>
3434
<Sidebar
3535
currentPath={currentPath}
3636
workerStatus={workerStatus}
@@ -39,15 +39,13 @@ export function DashboardLayout({
3939
collapsed={sidebarCollapsed}
4040
onToggleCollapse={onToggleSidebar}
4141
/>
42-
<div className="flex-1 flex flex-col min-w-0">
42+
<div className="flex-1 flex flex-col min-w-0 min-h-0">
4343
<Topbar
4444
theme={theme}
4545
onToggleTheme={onToggleTheme}
4646
onToggleLogs={onToggleLogs}
4747
/>
48-
<main className="flex-1 p-6 overflow-y-auto">
49-
{children}
50-
</main>
48+
<main className="flex-1 p-6 overflow-y-auto min-h-0">{children}</main>
5149
</div>
5250
</div>
5351
);

console/src/ui/viewer/views/Dashboard/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export function DashboardView() {
3333
<p className="text-base-content/60">
3434
{selectedProject
3535
? `Filtered by: ${selectedProject}`
36-
: "Overview of your Pilot Console"}
36+
: "Overview of your Pilot Shell Console"}
3737
</p>
3838
</div>
3939

console/src/ui/viewer/views/Spec/SpecContent.tsx

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import Markdown from 'react-markdown';
2-
import remarkGfm from 'remark-gfm';
1+
import Markdown from "react-markdown";
2+
import remarkGfm from "remark-gfm";
33

44
interface SpecContentProps {
55
content: string;
@@ -11,22 +11,36 @@ export function SpecContent({ content }: SpecContentProps) {
1111
<Markdown
1212
remarkPlugins={[remarkGfm]}
1313
components={{
14-
h3: ({ children }) => (
15-
<h3 className="text-lg font-semibold mt-6 mb-3 pb-2 border-b border-base-300/50 first:mt-0">
16-
{children}
17-
</h3>
18-
),
14+
h3: ({ children }) => {
15+
const text = String(children ?? "");
16+
const taskMatch = text.match(/Task\s+(\d+)/);
17+
const id = taskMatch ? `task-${taskMatch[1]}` : undefined;
18+
return (
19+
<h3
20+
id={id}
21+
className="text-lg font-semibold mt-6 mb-3 pb-2 border-b border-base-300/50 first:mt-0 scroll-mt-4"
22+
>
23+
{children}
24+
</h3>
25+
);
26+
},
1927
h4: ({ children }) => (
20-
<h4 className="text-base font-medium mt-4 mb-2 text-base-content/90">{children}</h4>
28+
<h4 className="text-base font-medium mt-4 mb-2 text-base-content/90">
29+
{children}
30+
</h4>
2131
),
2232
p: ({ children }) => (
23-
<p className="text-sm text-base-content/80 mb-3 leading-relaxed">{children}</p>
33+
<p className="text-sm text-base-content/80 mb-3 leading-relaxed">
34+
{children}
35+
</p>
2436
),
2537
ul: ({ children }) => (
2638
<ul className="text-sm space-y-1.5 mb-4 ml-1">{children}</ul>
2739
),
2840
ol: ({ children }) => (
29-
<ol className="text-sm space-y-1.5 mb-4 ml-1 list-decimal list-inside">{children}</ol>
41+
<ol className="text-sm space-y-1.5 mb-4 ml-1 list-decimal list-inside">
42+
{children}
43+
</ol>
3044
),
3145
li: ({ children }) => (
3246
<li className="text-base-content/80 flex items-start gap-2">
@@ -55,7 +69,9 @@ export function SpecContent({ content }: SpecContentProps) {
5569
</pre>
5670
),
5771
strong: ({ children }) => (
58-
<strong className="font-semibold text-base-content">{children}</strong>
72+
<strong className="font-semibold text-base-content">
73+
{children}
74+
</strong>
5975
),
6076
table: ({ children }) => (
6177
<div className="overflow-x-auto mb-4">
@@ -66,10 +82,14 @@ export function SpecContent({ content }: SpecContentProps) {
6682
<thead className="bg-base-200">{children}</thead>
6783
),
6884
th: ({ children }) => (
69-
<th className="text-left text-xs font-medium text-base-content/70 p-2">{children}</th>
85+
<th className="text-left text-xs font-medium text-base-content/70 p-2">
86+
{children}
87+
</th>
7088
),
7189
td: ({ children }) => (
72-
<td className="text-sm p-2 border-t border-base-300/50">{children}</td>
90+
<td className="text-sm p-2 border-t border-base-300/50">
91+
{children}
92+
</td>
7393
),
7494
blockquote: ({ children }) => (
7595
<blockquote className="border-l-4 border-primary/50 pl-4 py-1 my-3 text-sm text-base-content/70 italic">

console/src/ui/viewer/views/Spec/SpecHeaderCard.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,11 @@ const statusConfig = {
3535
export function SpecHeaderCard({
3636
parsed,
3737
spec,
38+
onTaskClick,
3839
}: {
3940
parsed: ParsedPlan;
4041
spec: SpecMeta;
42+
onTaskClick?: (taskNumber: number) => void;
4143
}) {
4244
const config = statusConfig[spec.status];
4345
const completedCount = parsed.tasks.filter((t) => t.completed).length;
@@ -80,9 +82,12 @@ export function SpecHeaderCard({
8082
{parsed.tasks.map((task) => (
8183
<div
8284
key={task.number}
83-
className={`flex items-center gap-3 p-2 rounded-lg ${
84-
task.completed ? "bg-success/10" : "bg-base-200/50"
85+
className={`flex items-center gap-3 p-2 rounded-lg cursor-pointer transition-colors ${
86+
task.completed
87+
? "bg-success/10 hover:bg-success/15"
88+
: "bg-base-200/50 hover:bg-base-200"
8589
}`}
90+
onClick={() => onTaskClick?.(task.number)}
8691
>
8792
<div
8893
className={`w-5 h-5 rounded-md flex items-center justify-center ${

console/src/ui/viewer/views/Spec/index.tsx

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,34 @@ export function SpecView() {
8585
const [error, setError] = useState<string | null>(null);
8686
const [isDeleting, setIsDeleting] = useState(false);
8787

88+
const headerCardRef = useRef<HTMLDivElement>(null);
89+
const [showBackToTasks, setShowBackToTasks] = useState(false);
90+
91+
const handleTaskClick = useCallback((taskNumber: number) => {
92+
const el = document.getElementById(`task-${taskNumber}`);
93+
if (el) el.scrollIntoView({ behavior: "smooth", block: "start" });
94+
}, []);
95+
96+
const scrollBackToTasks = useCallback(() => {
97+
headerCardRef.current?.scrollIntoView({
98+
behavior: "smooth",
99+
block: "start",
100+
});
101+
}, []);
102+
103+
useEffect(() => {
104+
const mainEl = document.querySelector("main");
105+
if (!mainEl) return;
106+
const onScroll = () => {
107+
if (!headerCardRef.current) return;
108+
const rect = headerCardRef.current.getBoundingClientRect();
109+
const mainTop = mainEl.getBoundingClientRect().top;
110+
setShowBackToTasks(rect.bottom < mainTop);
111+
};
112+
mainEl.addEventListener("scroll", onScroll, { passive: true });
113+
return () => mainEl.removeEventListener("scroll", onScroll);
114+
}, []);
115+
88116
const projectParam = selectedProject
89117
? `?project=${encodeURIComponent(selectedProject)}`
90118
: "";
@@ -333,7 +361,13 @@ export function SpecView() {
333361
</Card>
334362
) : parsed && currentSpec ? (
335363
<>
336-
<SpecHeaderCard parsed={parsed} spec={currentSpec} />
364+
<div ref={headerCardRef}>
365+
<SpecHeaderCard
366+
parsed={parsed}
367+
spec={currentSpec}
368+
onTaskClick={handleTaskClick}
369+
/>
370+
</div>
337371
<WorktreePanel />
338372
{parsed.implementationSection && (
339373
<Card>
@@ -346,6 +380,15 @@ export function SpecView() {
346380
</CardBody>
347381
</Card>
348382
)}
383+
{showBackToTasks && (
384+
<button
385+
onClick={scrollBackToTasks}
386+
className="fixed bottom-6 right-6 btn btn-primary btn-sm shadow-lg gap-1.5 z-50"
387+
>
388+
<Icon icon="lucide:arrow-up" size={14} />
389+
Task List
390+
</button>
391+
)}
349392
</>
350393
) : null}
351394
</div>

0 commit comments

Comments
 (0)