Switch to double quotes and reformat codebase with Biome
This commit is contained in:
@@ -1,123 +1,123 @@
|
||||
import { createContext, useContext, useEffect, useRef, useState } from 'react'
|
||||
import { createContext, useContext, useEffect, useRef, useState } from "react";
|
||||
|
||||
// --- Vector math utilities ---
|
||||
|
||||
interface Vec2 {
|
||||
x: number
|
||||
y: number
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
function vec2(x: number, y: number): Vec2 {
|
||||
return { x, y }
|
||||
return { x, y };
|
||||
}
|
||||
|
||||
function add(a: Vec2, b: Vec2): Vec2 {
|
||||
return { x: a.x + b.x, y: a.y + b.y }
|
||||
return { x: a.x + b.x, y: a.y + b.y };
|
||||
}
|
||||
|
||||
function sub(a: Vec2, b: Vec2): Vec2 {
|
||||
return { x: a.x - b.x, y: a.y - b.y }
|
||||
return { x: a.x - b.x, y: a.y - b.y };
|
||||
}
|
||||
|
||||
function scale(v: Vec2, s: number): Vec2 {
|
||||
return { x: v.x * s, y: v.y * s }
|
||||
return { x: v.x * s, y: v.y * s };
|
||||
}
|
||||
|
||||
function length(v: Vec2): number {
|
||||
return Math.sqrt(v.x * v.x + v.y * v.y)
|
||||
return Math.sqrt(v.x * v.x + v.y * v.y);
|
||||
}
|
||||
|
||||
function normalize(v: Vec2): Vec2 {
|
||||
const len = length(v)
|
||||
if (len === 0) return { x: 0, y: 0 }
|
||||
return { x: v.x / len, y: v.y / len }
|
||||
const len = length(v);
|
||||
if (len === 0) return { x: 0, y: 0 };
|
||||
return { x: v.x / len, y: v.y / len };
|
||||
}
|
||||
|
||||
function lerpAngle(a: number, b: number, t: number): number {
|
||||
// Handle angle wrapping for smooth interpolation
|
||||
let diff = b - a
|
||||
while (diff > 180) diff -= 360
|
||||
while (diff < -180) diff += 360
|
||||
return a + diff * t
|
||||
let diff = b - a;
|
||||
while (diff > 180) diff -= 360;
|
||||
while (diff < -180) diff += 360;
|
||||
return a + diff * t;
|
||||
}
|
||||
|
||||
// --- Cursor context ---
|
||||
|
||||
const CursorContext = createContext<Vec2 | null>(null)
|
||||
const CursorContext = createContext<Vec2 | null>(null);
|
||||
|
||||
export function CursorFieldProvider({ children }: { children: React.ReactNode }) {
|
||||
const [cursor, setCursor] = useState<Vec2 | null>(null)
|
||||
const [cursor, setCursor] = useState<Vec2 | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
function handleMouseMove(e: MouseEvent) {
|
||||
setCursor({ x: e.clientX, y: e.clientY })
|
||||
setCursor({ x: e.clientX, y: e.clientY });
|
||||
}
|
||||
|
||||
function handleMouseLeave() {
|
||||
setCursor(null)
|
||||
setCursor(null);
|
||||
}
|
||||
|
||||
window.addEventListener('mousemove', handleMouseMove)
|
||||
window.addEventListener('mouseleave', handleMouseLeave)
|
||||
window.addEventListener("mousemove", handleMouseMove);
|
||||
window.addEventListener("mouseleave", handleMouseLeave);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('mousemove', handleMouseMove)
|
||||
window.removeEventListener('mouseleave', handleMouseLeave)
|
||||
}
|
||||
}, [])
|
||||
window.removeEventListener("mousemove", handleMouseMove);
|
||||
window.removeEventListener("mouseleave", handleMouseLeave);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return <CursorContext.Provider value={cursor}>{children}</CursorContext.Provider>
|
||||
return <CursorContext.Provider value={cursor}>{children}</CursorContext.Provider>;
|
||||
}
|
||||
|
||||
// --- Physics simulation state ---
|
||||
|
||||
interface ButterflyState {
|
||||
position: Vec2 // Current offset from origin
|
||||
velocity: Vec2 // Current velocity
|
||||
heading: number // Current facing angle in degrees (0 = down, positive = clockwise)
|
||||
targetHeading: number // Target heading to smoothly rotate towards
|
||||
position: Vec2; // Current offset from origin
|
||||
velocity: Vec2; // Current velocity
|
||||
heading: number; // Current facing angle in degrees (0 = down, positive = clockwise)
|
||||
targetHeading: number; // Target heading to smoothly rotate towards
|
||||
}
|
||||
|
||||
// Physics constants
|
||||
const WANDER_STRENGTH = 0.15 // How strongly butterflies wander
|
||||
const WANDER_RADIUS = 40 // Max wander distance from origin
|
||||
const RETURN_STRENGTH = 0.02 // How strongly butterflies return to origin
|
||||
const REPULSION_RADIUS = 150 // Cursor repulsion detection radius
|
||||
const REPULSION_STRENGTH = 2.5 // How strongly cursor repels
|
||||
const MAX_SPEED = 1.5 // Maximum velocity
|
||||
const FRICTION = 0.98 // Velocity damping per frame
|
||||
const HEADING_SMOOTHING = 0.08 // How quickly heading catches up to velocity direction
|
||||
const WANDER_STRENGTH = 0.15; // How strongly butterflies wander
|
||||
const WANDER_RADIUS = 40; // Max wander distance from origin
|
||||
const RETURN_STRENGTH = 0.02; // How strongly butterflies return to origin
|
||||
const REPULSION_RADIUS = 150; // Cursor repulsion detection radius
|
||||
const REPULSION_STRENGTH = 2.5; // How strongly cursor repels
|
||||
const MAX_SPEED = 1.5; // Maximum velocity
|
||||
const FRICTION = 0.98; // Velocity damping per frame
|
||||
const HEADING_SMOOTHING = 0.08; // How quickly heading catches up to velocity direction
|
||||
|
||||
// --- Butterfly visual component ---
|
||||
|
||||
interface ButterflyVisualProps {
|
||||
size?: number
|
||||
color?: string
|
||||
delay?: number
|
||||
duration?: number
|
||||
direction?: 'left' | 'right'
|
||||
heading?: number
|
||||
size?: number;
|
||||
color?: string;
|
||||
delay?: number;
|
||||
duration?: number;
|
||||
direction?: "left" | "right";
|
||||
heading?: number;
|
||||
}
|
||||
|
||||
function ButterflyVisual({
|
||||
size = 40,
|
||||
color = '#e8a87c',
|
||||
color = "#e8a87c",
|
||||
delay = 0,
|
||||
duration = 0.5,
|
||||
direction = 'right',
|
||||
direction = "right",
|
||||
heading = 0,
|
||||
}: ButterflyVisualProps) {
|
||||
const wingStyle = {
|
||||
animationDelay: `${delay}s`,
|
||||
animationDuration: `${duration}s`,
|
||||
}
|
||||
};
|
||||
|
||||
const scaleX = direction === 'left' ? -1 : 1
|
||||
const scaleX = direction === "left" ? -1 : 1;
|
||||
|
||||
// Base butterfly has rotate(30deg), so it's already looking 30° down from horizontal.
|
||||
// To keep it in the bottom half (never looking up), rotateZ range is -30° to +90°
|
||||
// -30° = horizontal, 0° = 30° down (base), +90° = 120° down (nearly straight down)
|
||||
const clampedHeading = Math.max(-30, Math.min(90, heading))
|
||||
const clampedHeading = Math.max(-30, Math.min(90, heading));
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -132,15 +132,21 @@ function ButterflyVisual({
|
||||
width={size}
|
||||
height={size * 0.8}
|
||||
style={{
|
||||
filter: 'url(#painterly)',
|
||||
filter: "url(#painterly)",
|
||||
transform: `scaleX(${scaleX}) rotateY(25deg) rotate(30deg) rotateZ(${clampedHeading}deg)`,
|
||||
transition: 'transform 0.3s ease-out',
|
||||
transition: "transform 0.3s ease-out",
|
||||
}}
|
||||
>
|
||||
<defs>
|
||||
<filter id="painterly" x="-20%" y="-20%" width="140%" height="140%">
|
||||
<feTurbulence type="fractalNoise" baseFrequency="0.03" numOctaves="2" result="noise" />
|
||||
<feDisplacementMap in="SourceGraphic" in2="noise" scale="2" xChannelSelector="R" yChannelSelector="G" />
|
||||
<feDisplacementMap
|
||||
in="SourceGraphic"
|
||||
in2="noise"
|
||||
scale="2"
|
||||
xChannelSelector="R"
|
||||
yChannelSelector="G"
|
||||
/>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
@@ -159,184 +165,200 @@ function ButterflyVisual({
|
||||
<path d="M38 20 Q20 2 8 10 Q0 18 10 28 Q25 32 38 22" fill={color} />
|
||||
</g>
|
||||
|
||||
<g className="animate-flutter-left origin-[38px_20px]" style={{ ...wingStyle, animationDelay: `${delay + 0.05}s` }}>
|
||||
<g
|
||||
className="animate-flutter-left origin-[38px_20px]"
|
||||
style={{ ...wingStyle, animationDelay: `${delay + 0.05}s` }}
|
||||
>
|
||||
<path d="M38 22 Q25 30 18 38 Q28 42 36 34 Q40 28 38 24" fill={color} />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function darken(hex: string, amount: number): string {
|
||||
const num = parseInt(hex.replace('#', ''), 16)
|
||||
const r = Math.max(0, (num >> 16) - amount)
|
||||
const g = Math.max(0, ((num >> 8) & 0x00ff) - amount)
|
||||
const b = Math.max(0, (num & 0x0000ff) - amount)
|
||||
return `#${((r << 16) | (g << 8) | b).toString(16).padStart(6, '0')}`
|
||||
const num = parseInt(hex.replace("#", ""), 16);
|
||||
const r = Math.max(0, (num >> 16) - amount);
|
||||
const g = Math.max(0, ((num >> 8) & 0x00ff) - amount);
|
||||
const b = Math.max(0, (num & 0x0000ff) - amount);
|
||||
return `#${((r << 16) | (g << 8) | b).toString(16).padStart(6, "0")}`;
|
||||
}
|
||||
|
||||
// --- Main FloatingButterfly component with physics ---
|
||||
|
||||
interface FloatingButterflyProps {
|
||||
style?: React.CSSProperties
|
||||
size?: number
|
||||
color?: string
|
||||
delay?: number
|
||||
duration?: number
|
||||
direction?: 'left' | 'right'
|
||||
style?: React.CSSProperties;
|
||||
size?: number;
|
||||
color?: string;
|
||||
delay?: number;
|
||||
duration?: number;
|
||||
direction?: "left" | "right";
|
||||
}
|
||||
|
||||
export function FloatingButterfly({ style, size, color, delay, duration, direction }: FloatingButterflyProps) {
|
||||
export function FloatingButterfly({
|
||||
style,
|
||||
size,
|
||||
color,
|
||||
delay,
|
||||
duration,
|
||||
direction,
|
||||
}: FloatingButterflyProps) {
|
||||
// Initialize state lazily to avoid hydration mismatch
|
||||
const initialStateRef = useRef<{ x: number; y: number; heading: number } | null>(null)
|
||||
if (initialStateRef.current === null && typeof window !== 'undefined') {
|
||||
const initialStateRef = useRef<{ x: number; y: number; heading: number } | null>(null);
|
||||
if (initialStateRef.current === null && typeof window !== "undefined") {
|
||||
// Random starting position within wander radius
|
||||
const angle = Math.random() * Math.PI * 2
|
||||
const dist = Math.random() * WANDER_RADIUS * 0.5
|
||||
const angle = Math.random() * Math.PI * 2;
|
||||
const dist = Math.random() * WANDER_RADIUS * 0.5;
|
||||
initialStateRef.current = {
|
||||
x: Math.cos(angle) * dist,
|
||||
y: Math.sin(angle) * dist,
|
||||
heading: Math.random() * 40 - 10,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const [mounted, setMounted] = useState(false)
|
||||
const [renderState, setRenderState] = useState({ x: 0, y: 0, heading: 0 })
|
||||
const [mounted, setMounted] = useState(false);
|
||||
const [renderState, setRenderState] = useState({ x: 0, y: 0, heading: 0 });
|
||||
|
||||
const stateRef = useRef<ButterflyState | null>(null)
|
||||
const elementRef = useRef<HTMLDivElement>(null)
|
||||
const rafRef = useRef<number | null>(null)
|
||||
const wanderAngleRef = useRef(Math.random() * Math.PI * 2)
|
||||
const lastTimeRef = useRef<number | null>(null)
|
||||
const stateRef = useRef<ButterflyState | null>(null);
|
||||
const elementRef = useRef<HTMLDivElement>(null);
|
||||
const rafRef = useRef<number | null>(null);
|
||||
const wanderAngleRef = useRef(Math.random() * Math.PI * 2);
|
||||
const lastTimeRef = useRef<number | null>(null);
|
||||
|
||||
const cursor = useContext(CursorContext)
|
||||
const cursorRef = useRef<Vec2 | null>(null)
|
||||
const cursor = useContext(CursorContext);
|
||||
const cursorRef = useRef<Vec2 | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
// Initialize physics state on mount with pre-computed random values
|
||||
const init = initialStateRef.current || { x: 0, y: 0, heading: 0 }
|
||||
const init = initialStateRef.current || { x: 0, y: 0, heading: 0 };
|
||||
stateRef.current = {
|
||||
position: vec2(init.x, init.y),
|
||||
velocity: vec2(0, 0),
|
||||
heading: init.heading,
|
||||
targetHeading: init.heading,
|
||||
}
|
||||
};
|
||||
// Set initial render state to match physics state
|
||||
setRenderState({ x: init.x, y: init.y, heading: init.heading })
|
||||
setMounted(true)
|
||||
}, [])
|
||||
setRenderState({ x: init.x, y: init.y, heading: init.heading });
|
||||
setMounted(true);
|
||||
}, []);
|
||||
|
||||
// Keep cursor ref in sync
|
||||
useEffect(() => {
|
||||
cursorRef.current = cursor
|
||||
}, [cursor])
|
||||
cursorRef.current = cursor;
|
||||
}, [cursor]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!mounted) return
|
||||
if (!mounted) return;
|
||||
|
||||
function simulate(timestamp: number) {
|
||||
const state = stateRef.current
|
||||
if (!state) return
|
||||
const state = stateRef.current;
|
||||
if (!state) return;
|
||||
|
||||
// Skip first frame to let things settle, then start tracking time
|
||||
if (lastTimeRef.current === null) {
|
||||
lastTimeRef.current = timestamp
|
||||
rafRef.current = requestAnimationFrame(simulate)
|
||||
return
|
||||
lastTimeRef.current = timestamp;
|
||||
rafRef.current = requestAnimationFrame(simulate);
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate delta time (cap at 50ms to avoid huge jumps)
|
||||
const dt = Math.min((timestamp - lastTimeRef.current) / 16.67, 3)
|
||||
lastTimeRef.current = timestamp
|
||||
const dt = Math.min((timestamp - lastTimeRef.current) / 16.67, 3);
|
||||
lastTimeRef.current = timestamp;
|
||||
|
||||
let acceleration = vec2(0, 0)
|
||||
let acceleration = vec2(0, 0);
|
||||
|
||||
// 1. Wander behavior - gentle random movement
|
||||
wanderAngleRef.current += (Math.random() - 0.5) * 0.3 * dt
|
||||
wanderAngleRef.current += (Math.random() - 0.5) * 0.3 * dt;
|
||||
const wanderForce = vec2(
|
||||
Math.cos(wanderAngleRef.current) * WANDER_STRENGTH,
|
||||
Math.sin(wanderAngleRef.current) * WANDER_STRENGTH
|
||||
)
|
||||
acceleration = add(acceleration, wanderForce)
|
||||
Math.sin(wanderAngleRef.current) * WANDER_STRENGTH,
|
||||
);
|
||||
acceleration = add(acceleration, wanderForce);
|
||||
|
||||
// 2. Return to origin - soft spring force
|
||||
const distFromOrigin = length(state.position)
|
||||
const distFromOrigin = length(state.position);
|
||||
if (distFromOrigin > WANDER_RADIUS * 0.5) {
|
||||
const returnForce = scale(normalize(scale(state.position, -1)), RETURN_STRENGTH * (distFromOrigin / WANDER_RADIUS))
|
||||
acceleration = add(acceleration, returnForce)
|
||||
const returnForce = scale(
|
||||
normalize(scale(state.position, -1)),
|
||||
RETURN_STRENGTH * (distFromOrigin / WANDER_RADIUS),
|
||||
);
|
||||
acceleration = add(acceleration, returnForce);
|
||||
}
|
||||
|
||||
// 3. Cursor repulsion
|
||||
const currentCursor = cursorRef.current
|
||||
const currentCursor = cursorRef.current;
|
||||
if (currentCursor && elementRef.current) {
|
||||
const rect = elementRef.current.getBoundingClientRect()
|
||||
const worldPos = vec2(rect.left + rect.width / 2 + state.position.x, rect.top + rect.height / 2 + state.position.y)
|
||||
const rect = elementRef.current.getBoundingClientRect();
|
||||
const worldPos = vec2(
|
||||
rect.left + rect.width / 2 + state.position.x,
|
||||
rect.top + rect.height / 2 + state.position.y,
|
||||
);
|
||||
|
||||
const toCursor = sub(worldPos, currentCursor)
|
||||
const distToCursor = length(toCursor)
|
||||
const toCursor = sub(worldPos, currentCursor);
|
||||
const distToCursor = length(toCursor);
|
||||
|
||||
if (distToCursor < REPULSION_RADIUS && distToCursor > 0) {
|
||||
// Inverse square falloff for more natural feel
|
||||
const strength = REPULSION_STRENGTH * Math.pow(1 - distToCursor / REPULSION_RADIUS, 2)
|
||||
const repulsionForce = scale(normalize(toCursor), strength)
|
||||
acceleration = add(acceleration, repulsionForce)
|
||||
const strength = REPULSION_STRENGTH * Math.pow(1 - distToCursor / REPULSION_RADIUS, 2);
|
||||
const repulsionForce = scale(normalize(toCursor), strength);
|
||||
acceleration = add(acceleration, repulsionForce);
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Update velocity with acceleration
|
||||
state.velocity = add(state.velocity, scale(acceleration, dt))
|
||||
state.velocity = add(state.velocity, scale(acceleration, dt));
|
||||
|
||||
// 5. Apply friction
|
||||
state.velocity = scale(state.velocity, Math.pow(FRICTION, dt))
|
||||
state.velocity = scale(state.velocity, Math.pow(FRICTION, dt));
|
||||
|
||||
// 6. Clamp velocity to max speed
|
||||
const speed = length(state.velocity)
|
||||
const speed = length(state.velocity);
|
||||
if (speed > MAX_SPEED) {
|
||||
state.velocity = scale(normalize(state.velocity), MAX_SPEED)
|
||||
state.velocity = scale(normalize(state.velocity), MAX_SPEED);
|
||||
}
|
||||
|
||||
// 7. Update position
|
||||
state.position = add(state.position, scale(state.velocity, dt))
|
||||
state.position = add(state.position, scale(state.velocity, dt));
|
||||
|
||||
// 8. Soft clamp position to wander radius
|
||||
const posLen = length(state.position)
|
||||
const posLen = length(state.position);
|
||||
if (posLen > WANDER_RADIUS) {
|
||||
state.position = scale(normalize(state.position), WANDER_RADIUS)
|
||||
state.position = scale(normalize(state.position), WANDER_RADIUS);
|
||||
// Dampen velocity when hitting boundary
|
||||
state.velocity = scale(state.velocity, 0.8)
|
||||
state.velocity = scale(state.velocity, 0.8);
|
||||
}
|
||||
|
||||
// 9. Calculate target heading from velocity direction
|
||||
if (speed > 0.1) {
|
||||
// Convert velocity to angle: 0 = moving down screen (+Y), positive = clockwise
|
||||
const rawHeading = Math.atan2(state.velocity.x, state.velocity.y) * (180 / Math.PI)
|
||||
const rawHeading = Math.atan2(state.velocity.x, state.velocity.y) * (180 / Math.PI);
|
||||
// Clamp to valid range: -30° to +90° (relative to base 30° rotation)
|
||||
state.targetHeading = Math.max(-30, Math.min(90, rawHeading))
|
||||
state.targetHeading = Math.max(-30, Math.min(90, rawHeading));
|
||||
}
|
||||
|
||||
// 10. Smoothly interpolate heading towards target (already clamped)
|
||||
state.heading = lerpAngle(state.heading, state.targetHeading, HEADING_SMOOTHING * dt)
|
||||
state.heading = lerpAngle(state.heading, state.targetHeading, HEADING_SMOOTHING * dt);
|
||||
|
||||
// Update render state
|
||||
setRenderState({
|
||||
x: state.position.x,
|
||||
y: state.position.y,
|
||||
heading: state.heading,
|
||||
})
|
||||
});
|
||||
|
||||
rafRef.current = requestAnimationFrame(simulate)
|
||||
rafRef.current = requestAnimationFrame(simulate);
|
||||
}
|
||||
|
||||
rafRef.current = requestAnimationFrame(simulate)
|
||||
rafRef.current = requestAnimationFrame(simulate);
|
||||
|
||||
return () => {
|
||||
if (rafRef.current) {
|
||||
cancelAnimationFrame(rafRef.current)
|
||||
cancelAnimationFrame(rafRef.current);
|
||||
}
|
||||
}
|
||||
}, [mounted])
|
||||
};
|
||||
}, [mounted]);
|
||||
|
||||
if (!mounted) return null
|
||||
if (!mounted) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -347,7 +369,14 @@ export function FloatingButterfly({ style, size, color, delay, duration, directi
|
||||
transform: `translate(${renderState.x}px, ${renderState.y}px)`,
|
||||
}}
|
||||
>
|
||||
<ButterflyVisual size={size} color={color} delay={delay} duration={duration} direction={direction} heading={renderState.heading} />
|
||||
<ButterflyVisual
|
||||
size={size}
|
||||
color={color}
|
||||
delay={delay}
|
||||
duration={duration}
|
||||
direction={direction}
|
||||
heading={renderState.heading}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,8 @@
|
||||
export function pageMeta(title: string, description: string) {
|
||||
return [
|
||||
{ title },
|
||||
{ name: 'description', content: description },
|
||||
{ property: 'og:title', content: title },
|
||||
{ property: 'og:description', content: description },
|
||||
]
|
||||
{ name: "description", content: description },
|
||||
{ property: "og:title", content: title },
|
||||
{ property: "og:description", content: description },
|
||||
];
|
||||
}
|
||||
|
||||
@@ -8,332 +8,332 @@
|
||||
// You should NOT make any changes in this file as it will be overwritten.
|
||||
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
|
||||
|
||||
import { Route as rootRouteImport } from './routes/__root'
|
||||
import { Route as PrivacyRouteImport } from './routes/privacy'
|
||||
import { Route as OpencodeRouteImport } from './routes/opencode'
|
||||
import { Route as DocsRouteImport } from './routes/docs'
|
||||
import { Route as CodexRouteImport } from './routes/codex'
|
||||
import { Route as ClaudeCodeRouteImport } from './routes/claude-code'
|
||||
import { Route as ChangelogRouteImport } from './routes/changelog'
|
||||
import { Route as IndexRouteImport } from './routes/index'
|
||||
import { Route as DocsIndexRouteImport } from './routes/docs/index'
|
||||
import { Route as DocsWorktreesRouteImport } from './routes/docs/worktrees'
|
||||
import { Route as DocsVoiceRouteImport } from './routes/docs/voice'
|
||||
import { Route as DocsUpdatesRouteImport } from './routes/docs/updates'
|
||||
import { Route as DocsSecurityRouteImport } from './routes/docs/security'
|
||||
import { Route as DocsConfigurationRouteImport } from './routes/docs/configuration'
|
||||
import { Route as DocsCliRouteImport } from './routes/docs/cli'
|
||||
import { Route as DocsBestPracticesRouteImport } from './routes/docs/best-practices'
|
||||
import { Route as rootRouteImport } from "./routes/__root";
|
||||
import { Route as PrivacyRouteImport } from "./routes/privacy";
|
||||
import { Route as OpencodeRouteImport } from "./routes/opencode";
|
||||
import { Route as DocsRouteImport } from "./routes/docs";
|
||||
import { Route as CodexRouteImport } from "./routes/codex";
|
||||
import { Route as ClaudeCodeRouteImport } from "./routes/claude-code";
|
||||
import { Route as ChangelogRouteImport } from "./routes/changelog";
|
||||
import { Route as IndexRouteImport } from "./routes/index";
|
||||
import { Route as DocsIndexRouteImport } from "./routes/docs/index";
|
||||
import { Route as DocsWorktreesRouteImport } from "./routes/docs/worktrees";
|
||||
import { Route as DocsVoiceRouteImport } from "./routes/docs/voice";
|
||||
import { Route as DocsUpdatesRouteImport } from "./routes/docs/updates";
|
||||
import { Route as DocsSecurityRouteImport } from "./routes/docs/security";
|
||||
import { Route as DocsConfigurationRouteImport } from "./routes/docs/configuration";
|
||||
import { Route as DocsCliRouteImport } from "./routes/docs/cli";
|
||||
import { Route as DocsBestPracticesRouteImport } from "./routes/docs/best-practices";
|
||||
|
||||
const PrivacyRoute = PrivacyRouteImport.update({
|
||||
id: '/privacy',
|
||||
path: '/privacy',
|
||||
id: "/privacy",
|
||||
path: "/privacy",
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
} as any);
|
||||
const OpencodeRoute = OpencodeRouteImport.update({
|
||||
id: '/opencode',
|
||||
path: '/opencode',
|
||||
id: "/opencode",
|
||||
path: "/opencode",
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
} as any);
|
||||
const DocsRoute = DocsRouteImport.update({
|
||||
id: '/docs',
|
||||
path: '/docs',
|
||||
id: "/docs",
|
||||
path: "/docs",
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
} as any);
|
||||
const CodexRoute = CodexRouteImport.update({
|
||||
id: '/codex',
|
||||
path: '/codex',
|
||||
id: "/codex",
|
||||
path: "/codex",
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
} as any);
|
||||
const ClaudeCodeRoute = ClaudeCodeRouteImport.update({
|
||||
id: '/claude-code',
|
||||
path: '/claude-code',
|
||||
id: "/claude-code",
|
||||
path: "/claude-code",
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
} as any);
|
||||
const ChangelogRoute = ChangelogRouteImport.update({
|
||||
id: '/changelog',
|
||||
path: '/changelog',
|
||||
id: "/changelog",
|
||||
path: "/changelog",
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
} as any);
|
||||
const IndexRoute = IndexRouteImport.update({
|
||||
id: '/',
|
||||
path: '/',
|
||||
id: "/",
|
||||
path: "/",
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
} as any);
|
||||
const DocsIndexRoute = DocsIndexRouteImport.update({
|
||||
id: '/',
|
||||
path: '/',
|
||||
id: "/",
|
||||
path: "/",
|
||||
getParentRoute: () => DocsRoute,
|
||||
} as any)
|
||||
} as any);
|
||||
const DocsWorktreesRoute = DocsWorktreesRouteImport.update({
|
||||
id: '/worktrees',
|
||||
path: '/worktrees',
|
||||
id: "/worktrees",
|
||||
path: "/worktrees",
|
||||
getParentRoute: () => DocsRoute,
|
||||
} as any)
|
||||
} as any);
|
||||
const DocsVoiceRoute = DocsVoiceRouteImport.update({
|
||||
id: '/voice',
|
||||
path: '/voice',
|
||||
id: "/voice",
|
||||
path: "/voice",
|
||||
getParentRoute: () => DocsRoute,
|
||||
} as any)
|
||||
} as any);
|
||||
const DocsUpdatesRoute = DocsUpdatesRouteImport.update({
|
||||
id: '/updates',
|
||||
path: '/updates',
|
||||
id: "/updates",
|
||||
path: "/updates",
|
||||
getParentRoute: () => DocsRoute,
|
||||
} as any)
|
||||
} as any);
|
||||
const DocsSecurityRoute = DocsSecurityRouteImport.update({
|
||||
id: '/security',
|
||||
path: '/security',
|
||||
id: "/security",
|
||||
path: "/security",
|
||||
getParentRoute: () => DocsRoute,
|
||||
} as any)
|
||||
} as any);
|
||||
const DocsConfigurationRoute = DocsConfigurationRouteImport.update({
|
||||
id: '/configuration',
|
||||
path: '/configuration',
|
||||
id: "/configuration",
|
||||
path: "/configuration",
|
||||
getParentRoute: () => DocsRoute,
|
||||
} as any)
|
||||
} as any);
|
||||
const DocsCliRoute = DocsCliRouteImport.update({
|
||||
id: '/cli',
|
||||
path: '/cli',
|
||||
id: "/cli",
|
||||
path: "/cli",
|
||||
getParentRoute: () => DocsRoute,
|
||||
} as any)
|
||||
} as any);
|
||||
const DocsBestPracticesRoute = DocsBestPracticesRouteImport.update({
|
||||
id: '/best-practices',
|
||||
path: '/best-practices',
|
||||
id: "/best-practices",
|
||||
path: "/best-practices",
|
||||
getParentRoute: () => DocsRoute,
|
||||
} as any)
|
||||
} as any);
|
||||
|
||||
export interface FileRoutesByFullPath {
|
||||
'/': typeof IndexRoute
|
||||
'/changelog': typeof ChangelogRoute
|
||||
'/claude-code': typeof ClaudeCodeRoute
|
||||
'/codex': typeof CodexRoute
|
||||
'/docs': typeof DocsRouteWithChildren
|
||||
'/opencode': typeof OpencodeRoute
|
||||
'/privacy': typeof PrivacyRoute
|
||||
'/docs/best-practices': typeof DocsBestPracticesRoute
|
||||
'/docs/cli': typeof DocsCliRoute
|
||||
'/docs/configuration': typeof DocsConfigurationRoute
|
||||
'/docs/security': typeof DocsSecurityRoute
|
||||
'/docs/updates': typeof DocsUpdatesRoute
|
||||
'/docs/voice': typeof DocsVoiceRoute
|
||||
'/docs/worktrees': typeof DocsWorktreesRoute
|
||||
'/docs/': typeof DocsIndexRoute
|
||||
"/": typeof IndexRoute;
|
||||
"/changelog": typeof ChangelogRoute;
|
||||
"/claude-code": typeof ClaudeCodeRoute;
|
||||
"/codex": typeof CodexRoute;
|
||||
"/docs": typeof DocsRouteWithChildren;
|
||||
"/opencode": typeof OpencodeRoute;
|
||||
"/privacy": typeof PrivacyRoute;
|
||||
"/docs/best-practices": typeof DocsBestPracticesRoute;
|
||||
"/docs/cli": typeof DocsCliRoute;
|
||||
"/docs/configuration": typeof DocsConfigurationRoute;
|
||||
"/docs/security": typeof DocsSecurityRoute;
|
||||
"/docs/updates": typeof DocsUpdatesRoute;
|
||||
"/docs/voice": typeof DocsVoiceRoute;
|
||||
"/docs/worktrees": typeof DocsWorktreesRoute;
|
||||
"/docs/": typeof DocsIndexRoute;
|
||||
}
|
||||
export interface FileRoutesByTo {
|
||||
'/': typeof IndexRoute
|
||||
'/changelog': typeof ChangelogRoute
|
||||
'/claude-code': typeof ClaudeCodeRoute
|
||||
'/codex': typeof CodexRoute
|
||||
'/opencode': typeof OpencodeRoute
|
||||
'/privacy': typeof PrivacyRoute
|
||||
'/docs/best-practices': typeof DocsBestPracticesRoute
|
||||
'/docs/cli': typeof DocsCliRoute
|
||||
'/docs/configuration': typeof DocsConfigurationRoute
|
||||
'/docs/security': typeof DocsSecurityRoute
|
||||
'/docs/updates': typeof DocsUpdatesRoute
|
||||
'/docs/voice': typeof DocsVoiceRoute
|
||||
'/docs/worktrees': typeof DocsWorktreesRoute
|
||||
'/docs': typeof DocsIndexRoute
|
||||
"/": typeof IndexRoute;
|
||||
"/changelog": typeof ChangelogRoute;
|
||||
"/claude-code": typeof ClaudeCodeRoute;
|
||||
"/codex": typeof CodexRoute;
|
||||
"/opencode": typeof OpencodeRoute;
|
||||
"/privacy": typeof PrivacyRoute;
|
||||
"/docs/best-practices": typeof DocsBestPracticesRoute;
|
||||
"/docs/cli": typeof DocsCliRoute;
|
||||
"/docs/configuration": typeof DocsConfigurationRoute;
|
||||
"/docs/security": typeof DocsSecurityRoute;
|
||||
"/docs/updates": typeof DocsUpdatesRoute;
|
||||
"/docs/voice": typeof DocsVoiceRoute;
|
||||
"/docs/worktrees": typeof DocsWorktreesRoute;
|
||||
"/docs": typeof DocsIndexRoute;
|
||||
}
|
||||
export interface FileRoutesById {
|
||||
__root__: typeof rootRouteImport
|
||||
'/': typeof IndexRoute
|
||||
'/changelog': typeof ChangelogRoute
|
||||
'/claude-code': typeof ClaudeCodeRoute
|
||||
'/codex': typeof CodexRoute
|
||||
'/docs': typeof DocsRouteWithChildren
|
||||
'/opencode': typeof OpencodeRoute
|
||||
'/privacy': typeof PrivacyRoute
|
||||
'/docs/best-practices': typeof DocsBestPracticesRoute
|
||||
'/docs/cli': typeof DocsCliRoute
|
||||
'/docs/configuration': typeof DocsConfigurationRoute
|
||||
'/docs/security': typeof DocsSecurityRoute
|
||||
'/docs/updates': typeof DocsUpdatesRoute
|
||||
'/docs/voice': typeof DocsVoiceRoute
|
||||
'/docs/worktrees': typeof DocsWorktreesRoute
|
||||
'/docs/': typeof DocsIndexRoute
|
||||
__root__: typeof rootRouteImport;
|
||||
"/": typeof IndexRoute;
|
||||
"/changelog": typeof ChangelogRoute;
|
||||
"/claude-code": typeof ClaudeCodeRoute;
|
||||
"/codex": typeof CodexRoute;
|
||||
"/docs": typeof DocsRouteWithChildren;
|
||||
"/opencode": typeof OpencodeRoute;
|
||||
"/privacy": typeof PrivacyRoute;
|
||||
"/docs/best-practices": typeof DocsBestPracticesRoute;
|
||||
"/docs/cli": typeof DocsCliRoute;
|
||||
"/docs/configuration": typeof DocsConfigurationRoute;
|
||||
"/docs/security": typeof DocsSecurityRoute;
|
||||
"/docs/updates": typeof DocsUpdatesRoute;
|
||||
"/docs/voice": typeof DocsVoiceRoute;
|
||||
"/docs/worktrees": typeof DocsWorktreesRoute;
|
||||
"/docs/": typeof DocsIndexRoute;
|
||||
}
|
||||
export interface FileRouteTypes {
|
||||
fileRoutesByFullPath: FileRoutesByFullPath
|
||||
fileRoutesByFullPath: FileRoutesByFullPath;
|
||||
fullPaths:
|
||||
| '/'
|
||||
| '/changelog'
|
||||
| '/claude-code'
|
||||
| '/codex'
|
||||
| '/docs'
|
||||
| '/opencode'
|
||||
| '/privacy'
|
||||
| '/docs/best-practices'
|
||||
| '/docs/cli'
|
||||
| '/docs/configuration'
|
||||
| '/docs/security'
|
||||
| '/docs/updates'
|
||||
| '/docs/voice'
|
||||
| '/docs/worktrees'
|
||||
| '/docs/'
|
||||
fileRoutesByTo: FileRoutesByTo
|
||||
| "/"
|
||||
| "/changelog"
|
||||
| "/claude-code"
|
||||
| "/codex"
|
||||
| "/docs"
|
||||
| "/opencode"
|
||||
| "/privacy"
|
||||
| "/docs/best-practices"
|
||||
| "/docs/cli"
|
||||
| "/docs/configuration"
|
||||
| "/docs/security"
|
||||
| "/docs/updates"
|
||||
| "/docs/voice"
|
||||
| "/docs/worktrees"
|
||||
| "/docs/";
|
||||
fileRoutesByTo: FileRoutesByTo;
|
||||
to:
|
||||
| '/'
|
||||
| '/changelog'
|
||||
| '/claude-code'
|
||||
| '/codex'
|
||||
| '/opencode'
|
||||
| '/privacy'
|
||||
| '/docs/best-practices'
|
||||
| '/docs/cli'
|
||||
| '/docs/configuration'
|
||||
| '/docs/security'
|
||||
| '/docs/updates'
|
||||
| '/docs/voice'
|
||||
| '/docs/worktrees'
|
||||
| '/docs'
|
||||
| "/"
|
||||
| "/changelog"
|
||||
| "/claude-code"
|
||||
| "/codex"
|
||||
| "/opencode"
|
||||
| "/privacy"
|
||||
| "/docs/best-practices"
|
||||
| "/docs/cli"
|
||||
| "/docs/configuration"
|
||||
| "/docs/security"
|
||||
| "/docs/updates"
|
||||
| "/docs/voice"
|
||||
| "/docs/worktrees"
|
||||
| "/docs";
|
||||
id:
|
||||
| '__root__'
|
||||
| '/'
|
||||
| '/changelog'
|
||||
| '/claude-code'
|
||||
| '/codex'
|
||||
| '/docs'
|
||||
| '/opencode'
|
||||
| '/privacy'
|
||||
| '/docs/best-practices'
|
||||
| '/docs/cli'
|
||||
| '/docs/configuration'
|
||||
| '/docs/security'
|
||||
| '/docs/updates'
|
||||
| '/docs/voice'
|
||||
| '/docs/worktrees'
|
||||
| '/docs/'
|
||||
fileRoutesById: FileRoutesById
|
||||
| "__root__"
|
||||
| "/"
|
||||
| "/changelog"
|
||||
| "/claude-code"
|
||||
| "/codex"
|
||||
| "/docs"
|
||||
| "/opencode"
|
||||
| "/privacy"
|
||||
| "/docs/best-practices"
|
||||
| "/docs/cli"
|
||||
| "/docs/configuration"
|
||||
| "/docs/security"
|
||||
| "/docs/updates"
|
||||
| "/docs/voice"
|
||||
| "/docs/worktrees"
|
||||
| "/docs/";
|
||||
fileRoutesById: FileRoutesById;
|
||||
}
|
||||
export interface RootRouteChildren {
|
||||
IndexRoute: typeof IndexRoute
|
||||
ChangelogRoute: typeof ChangelogRoute
|
||||
ClaudeCodeRoute: typeof ClaudeCodeRoute
|
||||
CodexRoute: typeof CodexRoute
|
||||
DocsRoute: typeof DocsRouteWithChildren
|
||||
OpencodeRoute: typeof OpencodeRoute
|
||||
PrivacyRoute: typeof PrivacyRoute
|
||||
IndexRoute: typeof IndexRoute;
|
||||
ChangelogRoute: typeof ChangelogRoute;
|
||||
ClaudeCodeRoute: typeof ClaudeCodeRoute;
|
||||
CodexRoute: typeof CodexRoute;
|
||||
DocsRoute: typeof DocsRouteWithChildren;
|
||||
OpencodeRoute: typeof OpencodeRoute;
|
||||
PrivacyRoute: typeof PrivacyRoute;
|
||||
}
|
||||
|
||||
declare module '@tanstack/react-router' {
|
||||
declare module "@tanstack/react-router" {
|
||||
interface FileRoutesByPath {
|
||||
'/privacy': {
|
||||
id: '/privacy'
|
||||
path: '/privacy'
|
||||
fullPath: '/privacy'
|
||||
preLoaderRoute: typeof PrivacyRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/opencode': {
|
||||
id: '/opencode'
|
||||
path: '/opencode'
|
||||
fullPath: '/opencode'
|
||||
preLoaderRoute: typeof OpencodeRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/docs': {
|
||||
id: '/docs'
|
||||
path: '/docs'
|
||||
fullPath: '/docs'
|
||||
preLoaderRoute: typeof DocsRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/codex': {
|
||||
id: '/codex'
|
||||
path: '/codex'
|
||||
fullPath: '/codex'
|
||||
preLoaderRoute: typeof CodexRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/claude-code': {
|
||||
id: '/claude-code'
|
||||
path: '/claude-code'
|
||||
fullPath: '/claude-code'
|
||||
preLoaderRoute: typeof ClaudeCodeRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/changelog': {
|
||||
id: '/changelog'
|
||||
path: '/changelog'
|
||||
fullPath: '/changelog'
|
||||
preLoaderRoute: typeof ChangelogRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/': {
|
||||
id: '/'
|
||||
path: '/'
|
||||
fullPath: '/'
|
||||
preLoaderRoute: typeof IndexRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/docs/': {
|
||||
id: '/docs/'
|
||||
path: '/'
|
||||
fullPath: '/docs/'
|
||||
preLoaderRoute: typeof DocsIndexRouteImport
|
||||
parentRoute: typeof DocsRoute
|
||||
}
|
||||
'/docs/worktrees': {
|
||||
id: '/docs/worktrees'
|
||||
path: '/worktrees'
|
||||
fullPath: '/docs/worktrees'
|
||||
preLoaderRoute: typeof DocsWorktreesRouteImport
|
||||
parentRoute: typeof DocsRoute
|
||||
}
|
||||
'/docs/voice': {
|
||||
id: '/docs/voice'
|
||||
path: '/voice'
|
||||
fullPath: '/docs/voice'
|
||||
preLoaderRoute: typeof DocsVoiceRouteImport
|
||||
parentRoute: typeof DocsRoute
|
||||
}
|
||||
'/docs/updates': {
|
||||
id: '/docs/updates'
|
||||
path: '/updates'
|
||||
fullPath: '/docs/updates'
|
||||
preLoaderRoute: typeof DocsUpdatesRouteImport
|
||||
parentRoute: typeof DocsRoute
|
||||
}
|
||||
'/docs/security': {
|
||||
id: '/docs/security'
|
||||
path: '/security'
|
||||
fullPath: '/docs/security'
|
||||
preLoaderRoute: typeof DocsSecurityRouteImport
|
||||
parentRoute: typeof DocsRoute
|
||||
}
|
||||
'/docs/configuration': {
|
||||
id: '/docs/configuration'
|
||||
path: '/configuration'
|
||||
fullPath: '/docs/configuration'
|
||||
preLoaderRoute: typeof DocsConfigurationRouteImport
|
||||
parentRoute: typeof DocsRoute
|
||||
}
|
||||
'/docs/cli': {
|
||||
id: '/docs/cli'
|
||||
path: '/cli'
|
||||
fullPath: '/docs/cli'
|
||||
preLoaderRoute: typeof DocsCliRouteImport
|
||||
parentRoute: typeof DocsRoute
|
||||
}
|
||||
'/docs/best-practices': {
|
||||
id: '/docs/best-practices'
|
||||
path: '/best-practices'
|
||||
fullPath: '/docs/best-practices'
|
||||
preLoaderRoute: typeof DocsBestPracticesRouteImport
|
||||
parentRoute: typeof DocsRoute
|
||||
}
|
||||
"/privacy": {
|
||||
id: "/privacy";
|
||||
path: "/privacy";
|
||||
fullPath: "/privacy";
|
||||
preLoaderRoute: typeof PrivacyRouteImport;
|
||||
parentRoute: typeof rootRouteImport;
|
||||
};
|
||||
"/opencode": {
|
||||
id: "/opencode";
|
||||
path: "/opencode";
|
||||
fullPath: "/opencode";
|
||||
preLoaderRoute: typeof OpencodeRouteImport;
|
||||
parentRoute: typeof rootRouteImport;
|
||||
};
|
||||
"/docs": {
|
||||
id: "/docs";
|
||||
path: "/docs";
|
||||
fullPath: "/docs";
|
||||
preLoaderRoute: typeof DocsRouteImport;
|
||||
parentRoute: typeof rootRouteImport;
|
||||
};
|
||||
"/codex": {
|
||||
id: "/codex";
|
||||
path: "/codex";
|
||||
fullPath: "/codex";
|
||||
preLoaderRoute: typeof CodexRouteImport;
|
||||
parentRoute: typeof rootRouteImport;
|
||||
};
|
||||
"/claude-code": {
|
||||
id: "/claude-code";
|
||||
path: "/claude-code";
|
||||
fullPath: "/claude-code";
|
||||
preLoaderRoute: typeof ClaudeCodeRouteImport;
|
||||
parentRoute: typeof rootRouteImport;
|
||||
};
|
||||
"/changelog": {
|
||||
id: "/changelog";
|
||||
path: "/changelog";
|
||||
fullPath: "/changelog";
|
||||
preLoaderRoute: typeof ChangelogRouteImport;
|
||||
parentRoute: typeof rootRouteImport;
|
||||
};
|
||||
"/": {
|
||||
id: "/";
|
||||
path: "/";
|
||||
fullPath: "/";
|
||||
preLoaderRoute: typeof IndexRouteImport;
|
||||
parentRoute: typeof rootRouteImport;
|
||||
};
|
||||
"/docs/": {
|
||||
id: "/docs/";
|
||||
path: "/";
|
||||
fullPath: "/docs/";
|
||||
preLoaderRoute: typeof DocsIndexRouteImport;
|
||||
parentRoute: typeof DocsRoute;
|
||||
};
|
||||
"/docs/worktrees": {
|
||||
id: "/docs/worktrees";
|
||||
path: "/worktrees";
|
||||
fullPath: "/docs/worktrees";
|
||||
preLoaderRoute: typeof DocsWorktreesRouteImport;
|
||||
parentRoute: typeof DocsRoute;
|
||||
};
|
||||
"/docs/voice": {
|
||||
id: "/docs/voice";
|
||||
path: "/voice";
|
||||
fullPath: "/docs/voice";
|
||||
preLoaderRoute: typeof DocsVoiceRouteImport;
|
||||
parentRoute: typeof DocsRoute;
|
||||
};
|
||||
"/docs/updates": {
|
||||
id: "/docs/updates";
|
||||
path: "/updates";
|
||||
fullPath: "/docs/updates";
|
||||
preLoaderRoute: typeof DocsUpdatesRouteImport;
|
||||
parentRoute: typeof DocsRoute;
|
||||
};
|
||||
"/docs/security": {
|
||||
id: "/docs/security";
|
||||
path: "/security";
|
||||
fullPath: "/docs/security";
|
||||
preLoaderRoute: typeof DocsSecurityRouteImport;
|
||||
parentRoute: typeof DocsRoute;
|
||||
};
|
||||
"/docs/configuration": {
|
||||
id: "/docs/configuration";
|
||||
path: "/configuration";
|
||||
fullPath: "/docs/configuration";
|
||||
preLoaderRoute: typeof DocsConfigurationRouteImport;
|
||||
parentRoute: typeof DocsRoute;
|
||||
};
|
||||
"/docs/cli": {
|
||||
id: "/docs/cli";
|
||||
path: "/cli";
|
||||
fullPath: "/docs/cli";
|
||||
preLoaderRoute: typeof DocsCliRouteImport;
|
||||
parentRoute: typeof DocsRoute;
|
||||
};
|
||||
"/docs/best-practices": {
|
||||
id: "/docs/best-practices";
|
||||
path: "/best-practices";
|
||||
fullPath: "/docs/best-practices";
|
||||
preLoaderRoute: typeof DocsBestPracticesRouteImport;
|
||||
parentRoute: typeof DocsRoute;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
interface DocsRouteChildren {
|
||||
DocsBestPracticesRoute: typeof DocsBestPracticesRoute
|
||||
DocsCliRoute: typeof DocsCliRoute
|
||||
DocsConfigurationRoute: typeof DocsConfigurationRoute
|
||||
DocsSecurityRoute: typeof DocsSecurityRoute
|
||||
DocsUpdatesRoute: typeof DocsUpdatesRoute
|
||||
DocsVoiceRoute: typeof DocsVoiceRoute
|
||||
DocsWorktreesRoute: typeof DocsWorktreesRoute
|
||||
DocsIndexRoute: typeof DocsIndexRoute
|
||||
DocsBestPracticesRoute: typeof DocsBestPracticesRoute;
|
||||
DocsCliRoute: typeof DocsCliRoute;
|
||||
DocsConfigurationRoute: typeof DocsConfigurationRoute;
|
||||
DocsSecurityRoute: typeof DocsSecurityRoute;
|
||||
DocsUpdatesRoute: typeof DocsUpdatesRoute;
|
||||
DocsVoiceRoute: typeof DocsVoiceRoute;
|
||||
DocsWorktreesRoute: typeof DocsWorktreesRoute;
|
||||
DocsIndexRoute: typeof DocsIndexRoute;
|
||||
}
|
||||
|
||||
const DocsRouteChildren: DocsRouteChildren = {
|
||||
@@ -345,9 +345,9 @@ const DocsRouteChildren: DocsRouteChildren = {
|
||||
DocsVoiceRoute: DocsVoiceRoute,
|
||||
DocsWorktreesRoute: DocsWorktreesRoute,
|
||||
DocsIndexRoute: DocsIndexRoute,
|
||||
}
|
||||
};
|
||||
|
||||
const DocsRouteWithChildren = DocsRoute._addFileChildren(DocsRouteChildren)
|
||||
const DocsRouteWithChildren = DocsRoute._addFileChildren(DocsRouteChildren);
|
||||
|
||||
const rootRouteChildren: RootRouteChildren = {
|
||||
IndexRoute: IndexRoute,
|
||||
@@ -357,16 +357,16 @@ const rootRouteChildren: RootRouteChildren = {
|
||||
DocsRoute: DocsRouteWithChildren,
|
||||
OpencodeRoute: OpencodeRoute,
|
||||
PrivacyRoute: PrivacyRoute,
|
||||
}
|
||||
};
|
||||
export const routeTree = rootRouteImport
|
||||
._addFileChildren(rootRouteChildren)
|
||||
._addFileTypes<FileRouteTypes>()
|
||||
._addFileTypes<FileRouteTypes>();
|
||||
|
||||
import type { getRouter } from './router.tsx'
|
||||
import type { createStart } from '@tanstack/react-start'
|
||||
declare module '@tanstack/react-start' {
|
||||
import type { getRouter } from "./router.tsx";
|
||||
import type { createStart } from "@tanstack/react-start";
|
||||
declare module "@tanstack/react-start" {
|
||||
interface Register {
|
||||
ssr: true
|
||||
router: Awaited<ReturnType<typeof getRouter>>
|
||||
ssr: true;
|
||||
router: Awaited<ReturnType<typeof getRouter>>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { createRouter } from '@tanstack/react-router'
|
||||
import { routeTree } from './routeTree.gen'
|
||||
import { createRouter } from "@tanstack/react-router";
|
||||
import { routeTree } from "./routeTree.gen";
|
||||
|
||||
export function getRouter() {
|
||||
return createRouter({
|
||||
routeTree,
|
||||
scrollRestoration: true,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
declare module '@tanstack/react-router' {
|
||||
declare module "@tanstack/react-router" {
|
||||
interface Register {
|
||||
router: ReturnType<typeof getRouter>
|
||||
router: ReturnType<typeof getRouter>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +1,33 @@
|
||||
import type { ReactNode } from 'react'
|
||||
import {
|
||||
Outlet,
|
||||
createRootRoute,
|
||||
HeadContent,
|
||||
Scripts,
|
||||
} from '@tanstack/react-router'
|
||||
import type { ReactNode } from "react";
|
||||
import { Outlet, createRootRoute, HeadContent, Scripts } from "@tanstack/react-router";
|
||||
|
||||
export const Route = createRootRoute({
|
||||
head: () => ({
|
||||
meta: [
|
||||
{ charSet: 'utf-8' },
|
||||
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
|
||||
{ name: 'theme-color', content: '#0a0a0a' },
|
||||
{ property: 'og:site_name', content: 'Paseo' },
|
||||
{ property: 'og:type', content: 'website' },
|
||||
{ property: 'og:image', content: 'https://paseo.sh/og-image.png' },
|
||||
{ name: 'twitter:card', content: 'summary_large_image' },
|
||||
{ name: 'twitter:image', content: 'https://paseo.sh/og-image.png' },
|
||||
{ charSet: "utf-8" },
|
||||
{ name: "viewport", content: "width=device-width, initial-scale=1" },
|
||||
{ name: "theme-color", content: "#0a0a0a" },
|
||||
{ property: "og:site_name", content: "Paseo" },
|
||||
{ property: "og:type", content: "website" },
|
||||
{ property: "og:image", content: "https://paseo.sh/og-image.png" },
|
||||
{ name: "twitter:card", content: "summary_large_image" },
|
||||
{ name: "twitter:image", content: "https://paseo.sh/og-image.png" },
|
||||
],
|
||||
links: [
|
||||
{ rel: 'icon', href: '/favicon.ico', sizes: '48x48' },
|
||||
{ rel: 'icon', href: '/favicon.svg', type: 'image/svg+xml' },
|
||||
{ rel: 'apple-touch-icon', href: '/favicon.svg' },
|
||||
{ rel: "icon", href: "/favicon.ico", sizes: "48x48" },
|
||||
{ rel: "icon", href: "/favicon.svg", type: "image/svg+xml" },
|
||||
{ rel: "apple-touch-icon", href: "/favicon.svg" },
|
||||
],
|
||||
}),
|
||||
component: RootComponent,
|
||||
})
|
||||
});
|
||||
|
||||
function RootComponent() {
|
||||
return (
|
||||
<RootDocument>
|
||||
<Outlet />
|
||||
</RootDocument>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function RootDocument({ children }: Readonly<{ children: ReactNode }>) {
|
||||
@@ -46,5 +41,5 @@ function RootDocument({ children }: Readonly<{ children: ReactNode }>) {
|
||||
<Scripts />
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { Link, createFileRoute } from '@tanstack/react-router'
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
import changelogMarkdown from '../../../../CHANGELOG.md?raw'
|
||||
import { pageMeta } from '~/meta'
|
||||
import '~/styles.css'
|
||||
import { Link, createFileRoute } from "@tanstack/react-router";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import changelogMarkdown from "../../../../CHANGELOG.md?raw";
|
||||
import { pageMeta } from "~/meta";
|
||||
import "~/styles.css";
|
||||
|
||||
export const Route = createFileRoute('/changelog')({
|
||||
export const Route = createFileRoute("/changelog")({
|
||||
head: () => ({
|
||||
meta: pageMeta(
|
||||
'Changelog - Paseo',
|
||||
'Product updates, fixes, and improvements shipped in each Paseo release.',
|
||||
"Changelog - Paseo",
|
||||
"Product updates, fixes, and improvements shipped in each Paseo release.",
|
||||
),
|
||||
}),
|
||||
component: Changelog,
|
||||
})
|
||||
});
|
||||
|
||||
function Changelog() {
|
||||
return (
|
||||
@@ -73,5 +73,5 @@ function Changelog() {
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import { LandingPage } from '~/components/landing-page'
|
||||
import { pageMeta } from '~/meta'
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { LandingPage } from "~/components/landing-page";
|
||||
import { pageMeta } from "~/meta";
|
||||
|
||||
export const Route = createFileRoute('/claude-code')({
|
||||
export const Route = createFileRoute("/claude-code")({
|
||||
head: () => ({
|
||||
meta: pageMeta(
|
||||
'Claude Code Mobile App – Ship from your phone | Paseo',
|
||||
'Run Claude Code from your phone. Launch agents, check on progress, review diffs, and merge — all from your pocket. Self-hosted, your code stays on your machine.',
|
||||
"Claude Code Mobile App – Ship from your phone | Paseo",
|
||||
"Run Claude Code from your phone. Launch agents, check on progress, review diffs, and merge — all from your pocket. Self-hosted, your code stays on your machine.",
|
||||
),
|
||||
}),
|
||||
component: ClaudeCodePage,
|
||||
})
|
||||
});
|
||||
|
||||
function ClaudeCodePage() {
|
||||
return (
|
||||
@@ -18,5 +18,5 @@ function ClaudeCodePage() {
|
||||
title="Ship with Claude Code from your phone"
|
||||
subtitle="Launch agents, check on progress, and merge from anywhere. Your Claude Code setup, your machine, your pocket."
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import { LandingPage } from '~/components/landing-page'
|
||||
import { pageMeta } from '~/meta'
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { LandingPage } from "~/components/landing-page";
|
||||
import { pageMeta } from "~/meta";
|
||||
|
||||
export const Route = createFileRoute('/codex')({
|
||||
export const Route = createFileRoute("/codex")({
|
||||
head: () => ({
|
||||
meta: pageMeta(
|
||||
'Codex Mobile App – Run Codex from anywhere | Paseo',
|
||||
'Run OpenAI Codex from your phone. Kick off agents, monitor progress, and ship code without being at your desk. Self-hosted, your code never leaves your machine.',
|
||||
"Codex Mobile App – Run Codex from anywhere | Paseo",
|
||||
"Run OpenAI Codex from your phone. Kick off agents, monitor progress, and ship code without being at your desk. Self-hosted, your code never leaves your machine.",
|
||||
),
|
||||
}),
|
||||
component: CodexPage,
|
||||
})
|
||||
});
|
||||
|
||||
function CodexPage() {
|
||||
return (
|
||||
@@ -18,5 +18,5 @@ function CodexPage() {
|
||||
title="Run Codex from anywhere"
|
||||
subtitle="Kick off Codex agents on your machine from your phone. Check in on the train, review on the couch, merge from the park."
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import { createFileRoute, Link, Outlet } from '@tanstack/react-router'
|
||||
import '~/styles.css'
|
||||
import { createFileRoute, Link, Outlet } from "@tanstack/react-router";
|
||||
import "~/styles.css";
|
||||
|
||||
export const Route = createFileRoute('/docs')({
|
||||
export const Route = createFileRoute("/docs")({
|
||||
component: DocsLayout,
|
||||
})
|
||||
});
|
||||
|
||||
const navigation = [
|
||||
{ name: 'Getting started', href: '/docs' },
|
||||
{ name: 'Updates', href: '/docs/updates' },
|
||||
{ name: 'Voice', href: '/docs/voice' },
|
||||
{ name: 'Git worktrees', href: '/docs/worktrees' },
|
||||
{ name: 'CLI', href: '/docs/cli' },
|
||||
{ name: 'Configuration', href: '/docs/configuration' },
|
||||
{ name: 'Security', href: '/docs/security' },
|
||||
{ name: 'Best practices', href: '/docs/best-practices' },
|
||||
]
|
||||
{ name: "Getting started", href: "/docs" },
|
||||
{ name: "Updates", href: "/docs/updates" },
|
||||
{ name: "Voice", href: "/docs/voice" },
|
||||
{ name: "Git worktrees", href: "/docs/worktrees" },
|
||||
{ name: "CLI", href: "/docs/cli" },
|
||||
{ name: "Configuration", href: "/docs/configuration" },
|
||||
{ name: "Security", href: "/docs/security" },
|
||||
{ name: "Best practices", href: "/docs/best-practices" },
|
||||
];
|
||||
|
||||
function DocsLayout() {
|
||||
return (
|
||||
@@ -32,7 +32,7 @@ function DocsLayout() {
|
||||
to={item.href}
|
||||
activeOptions={{ exact: true }}
|
||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||
activeProps={{ className: 'text-foreground' }}
|
||||
activeProps={{ className: "text-foreground" }}
|
||||
>
|
||||
{item.name}
|
||||
</Link>
|
||||
@@ -54,7 +54,7 @@ function DocsLayout() {
|
||||
to={item.href}
|
||||
activeOptions={{ exact: true }}
|
||||
className="block px-3 py-2 text-sm rounded-md text-muted-foreground hover:text-foreground hover:bg-muted transition-colors"
|
||||
activeProps={{ className: 'bg-muted text-foreground' }}
|
||||
activeProps={{ className: "bg-muted text-foreground" }}
|
||||
>
|
||||
{item.name}
|
||||
</Link>
|
||||
@@ -66,5 +66,5 @@ function DocsLayout() {
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import { pageMeta } from '~/meta'
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { pageMeta } from "~/meta";
|
||||
|
||||
export const Route = createFileRoute('/docs/best-practices')({
|
||||
export const Route = createFileRoute("/docs/best-practices")({
|
||||
head: () => ({
|
||||
meta: pageMeta(
|
||||
'Best Practices - Paseo Docs',
|
||||
'Tips for getting the most out of Paseo and mobile-first agent workflows.',
|
||||
"Best Practices - Paseo Docs",
|
||||
"Tips for getting the most out of Paseo and mobile-first agent workflows.",
|
||||
),
|
||||
}),
|
||||
component: BestPractices,
|
||||
})
|
||||
});
|
||||
|
||||
function BestPractices() {
|
||||
return (
|
||||
@@ -17,131 +17,118 @@ function BestPractices() {
|
||||
<div>
|
||||
<h1 className="text-3xl font-medium font-title mb-4">Best Practices</h1>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
What I've learned from using Paseo daily. Not rules, just patterns
|
||||
that have worked for me.
|
||||
What I've learned from using Paseo daily. Not rules, just patterns that have worked for
|
||||
me.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Agents replace typing, not thinking</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Your role has changed. You're no longer the one writing code line by
|
||||
line. You're the one making decisions: what to build, how it should
|
||||
work, what the architecture looks like. The agent executes, but you
|
||||
direct.
|
||||
Your role has changed. You're no longer the one writing code line by line. You're the one
|
||||
making decisions: what to build, how it should work, what the architecture looks like. The
|
||||
agent executes, but you direct.
|
||||
</p>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
You can't just say "implement feature X" and walk away. You still
|
||||
have to do the hard part: deciding what to build, how it fits into
|
||||
the system, what trade-offs to make. Thinking is not optional. At
|
||||
least for now, agents replace the typing, not the thinking.
|
||||
You can't just say "implement feature X" and walk away. You still have to do the hard
|
||||
part: deciding what to build, how it fits into the system, what trade-offs to make.
|
||||
Thinking is not optional. At least for now, agents replace the typing, not the thinking.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Verification loops</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
The agent needs a way to verify its work. TDD is one implementation of
|
||||
this pattern: get the agent to write a failing test, verify it fails
|
||||
for the right reasons, then tell it to make the test pass. The agent
|
||||
can loop on its own because it knows what "done" means.
|
||||
The agent needs a way to verify its work. TDD is one implementation of this pattern: get
|
||||
the agent to write a failing test, verify it fails for the right reasons, then tell it to
|
||||
make the test pass. The agent can loop on its own because it knows what "done" means.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Invest in tooling</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
It's not just test runners. For web apps, something like Playwright
|
||||
MCP lets the agent take screenshots and verify UI changes. For a SaaS
|
||||
app I built a CLI that wraps all the business logic so the agent could
|
||||
launch jobs, check statuses, and scrape data without going through the
|
||||
UI.
|
||||
It's not just test runners. For web apps, something like Playwright MCP lets the agent
|
||||
take screenshots and verify UI changes. For a SaaS app I built a CLI that wraps all the
|
||||
business logic so the agent could launch jobs, check statuses, and scrape data without
|
||||
going through the UI.
|
||||
</p>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Code is cheap with coding agents. I would have never written that CLI
|
||||
before because it felt like wasted effort. Now I bootstrap tooling
|
||||
first. It pays off exponentially.
|
||||
Code is cheap with coding agents. I would have never written that CLI before because it
|
||||
felt like wasted effort. Now I bootstrap tooling first. It pays off exponentially.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Agents are cheap</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Don't be shy about running multiple agents. Paseo lets you launch
|
||||
agents in isolated worktrees. Kick one off with voice while walking,
|
||||
then kick off another. They work independently. You get a notification
|
||||
when they're done.
|
||||
Don't be shy about running multiple agents. Paseo lets you launch agents in isolated
|
||||
worktrees. Kick one off with voice while walking, then kick off another. They work
|
||||
independently. You get a notification when they're done.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Use voice extensively</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
It's much more natural to use voice to communicate ideas and pull them
|
||||
out of your brain. The agent will parse and organize your thoughts
|
||||
better than if you try to write the perfect prompt. You don't need to
|
||||
organize anything. Just talk.
|
||||
It's much more natural to use voice to communicate ideas and pull them out of your brain.
|
||||
The agent will parse and organize your thoughts better than if you try to write the
|
||||
perfect prompt. You don't need to organize anything. Just talk.
|
||||
</p>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Current speech-to-text models are really good. They catch accents,
|
||||
acronyms, technical terms. And even when they don't, the LLM will
|
||||
infer what you meant.
|
||||
Current speech-to-text models are really good. They catch accents, acronyms, technical
|
||||
terms. And even when they don't, the LLM will infer what you meant.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Understand the type of work</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Sometimes you need to plan: design a spec, verify it, get the agent to
|
||||
follow through. Maybe it takes a couple of agents to work through it.
|
||||
Other times it's conversational: kick off a single agent and start
|
||||
talking, asking questions. Match your approach to the task.
|
||||
Sometimes you need to plan: design a spec, verify it, get the agent to follow through.
|
||||
Maybe it takes a couple of agents to work through it. Other times it's conversational:
|
||||
kick off a single agent and start talking, asking questions. Match your approach to the
|
||||
task.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Iterate and refactor often</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Don't expect perfect. Expect working. Make it work, make it correct,
|
||||
make it beautiful. Each iteration gets you closer. With tests,
|
||||
refactoring is cheap.
|
||||
Don't expect perfect. Expect working. Make it work, make it correct, make it beautiful.
|
||||
Each iteration gets you closer. With tests, refactoring is cheap.
|
||||
</p>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
I don't let myself add too many features before stopping to refactor.
|
||||
Sometimes I kick off an agent and have it trace code paths, explain
|
||||
dependencies, show me how modules connect. I make mental notes during
|
||||
code review and circle back.
|
||||
I don't let myself add too many features before stopping to refactor. Sometimes I kick off
|
||||
an agent and have it trace code paths, explain dependencies, show me how modules connect.
|
||||
I make mental notes during code review and circle back.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Use agents to check agents</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
If an agent implements something and you ask it to review its own
|
||||
work, it will never find issues. Launch a separate agent with a fresh
|
||||
context to review the first agent's code. It will catch things the
|
||||
first agent missed or glossed over. An agent might say it's done when
|
||||
it's not. Another agent can detect that.
|
||||
If an agent implements something and you ask it to review its own work, it will never find
|
||||
issues. Launch a separate agent with a fresh context to review the first agent's code. It
|
||||
will catch things the first agent missed or glossed over. An agent might say it's done
|
||||
when it's not. Another agent can detect that.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Learn your agents' quirks</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
People argue about which model is better. That's the wrong question.
|
||||
Each model has strengths and weaknesses. Knowing them is more useful
|
||||
than chasing benchmarks. Benchmarks don't mean anything. You need to
|
||||
try the models yourself to form an opinion.
|
||||
People argue about which model is better. That's the wrong question. Each model has
|
||||
strengths and weaknesses. Knowing them is more useful than chasing benchmarks. Benchmarks
|
||||
don't mean anything. You need to try the models yourself to form an opinion.
|
||||
</p>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
I use Claude Code as my main driver because it's quick and uses tools
|
||||
well. But sometimes it jumps to conclusions and gives up too easily.
|
||||
Codex is frustratingly slow but goes deep, doesn't stop, and is
|
||||
methodical. It's also stubborn and too serious. These aren't good or
|
||||
bad traits, just differences you learn to work around. Use the right
|
||||
model for the job.
|
||||
I use Claude Code as my main driver because it's quick and uses tools well. But sometimes
|
||||
it jumps to conclusions and gives up too easily. Codex is frustratingly slow but goes
|
||||
deep, doesn't stop, and is methodical. It's also stubborn and too serious. These aren't
|
||||
good or bad traits, just differences you learn to work around. Use the right model for the
|
||||
job.
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import { pageMeta } from '~/meta'
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { pageMeta } from "~/meta";
|
||||
|
||||
export const Route = createFileRoute('/docs/cli')({
|
||||
export const Route = createFileRoute("/docs/cli")({
|
||||
head: () => ({
|
||||
meta: pageMeta(
|
||||
'CLI - Paseo Docs',
|
||||
'Paseo CLI reference: manage agents, daemons, permissions, and worktrees from your terminal.',
|
||||
"CLI - Paseo Docs",
|
||||
"Paseo CLI reference: manage agents, daemons, permissions, and worktrees from your terminal.",
|
||||
),
|
||||
}),
|
||||
component: CLI,
|
||||
})
|
||||
});
|
||||
|
||||
function Code({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="bg-card border border-border rounded-lg p-4 font-mono text-sm overflow-x-auto">
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function CLI() {
|
||||
@@ -25,8 +25,8 @@ function CLI() {
|
||||
<div>
|
||||
<h1 className="text-3xl font-medium font-title mb-4">CLI</h1>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
The Paseo CLI lets you manage agents from your terminal. It's the same interface exposed by
|
||||
the daemon's API, so anything you can do in the app you can do from the command line.
|
||||
The Paseo CLI lets you manage agents from your terminal. It's the same interface exposed
|
||||
by the daemon's API, so anything you can do in the app you can do from the command line.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -67,16 +67,16 @@ paseo run --output-schema schema.json "extract release notes"
|
||||
paseo run --output-schema '{"type":"object","properties":{"summary":{"type":"string"}},"required":["summary"]}' "summarize release notes"`}</pre>
|
||||
</Code>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
The <code className="font-mono">--worktree</code> flag creates the agent in an isolated git
|
||||
worktree, useful for parallel feature development.
|
||||
The <code className="font-mono">--worktree</code> flag creates the agent in an isolated
|
||||
git worktree, useful for parallel feature development.
|
||||
</p>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Use <code className="font-mono">--output-schema</code> to return only matching JSON output.
|
||||
You can pass a schema file path or an inline JSON schema object.
|
||||
This mode cannot be used with <code className="font-mono">--detach</code>.
|
||||
Use <code className="font-mono">--output-schema</code> to return only matching JSON
|
||||
output. You can pass a schema file path or an inline JSON schema object. This mode cannot
|
||||
be used with <code className="font-mono">--detach</code>.
|
||||
</p>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
By default, <code className="font-mono">paseo run</code> waits for completion. Use{' '}
|
||||
By default, <code className="font-mono">paseo run</code> waits for completion. Use{" "}
|
||||
<code className="font-mono">--detach</code> to run in the background.
|
||||
</p>
|
||||
</section>
|
||||
@@ -96,13 +96,15 @@ paseo ls -a -g --json # Full list as JSON`}</pre>
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Streaming output</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Use <code className="font-mono">paseo attach</code> to stream an agent's output in real-time:
|
||||
Use <code className="font-mono">paseo attach</code> to stream an agent's output in
|
||||
real-time:
|
||||
</p>
|
||||
<Code>
|
||||
<pre className="text-white/80">{`paseo attach abc123 # Attach to agent (Ctrl+C to detach)`}</pre>
|
||||
</Code>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Agent IDs can be shortened — <code className="font-mono">abc</code> works if it's unambiguous.
|
||||
Agent IDs can be shortened — <code className="font-mono">abc</code> works if it's
|
||||
unambiguous.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
@@ -180,7 +182,8 @@ paseo daemon status # Check status
|
||||
paseo daemon stop # Stop the daemon`}</pre>
|
||||
</Code>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Use <code className="font-mono">PASEO_HOME</code> to run multiple isolated daemon instances.
|
||||
Use <code className="font-mono">PASEO_HOME</code> to run multiple isolated daemon
|
||||
instances.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
@@ -197,9 +200,7 @@ paseo run --detach "implement the API" --name api-agent
|
||||
paseo wait api-agent
|
||||
paseo logs api-agent --tail 5`}</pre>
|
||||
</Code>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Simple implement + verify loop:
|
||||
</p>
|
||||
<p className="text-white/60 leading-relaxed">Simple implement + verify loop:</p>
|
||||
<Code>
|
||||
<pre className="text-white/80">{`# Requires jq
|
||||
while true; do
|
||||
@@ -236,7 +237,8 @@ paseo ls -q # IDs only (quiet)`}</pre>
|
||||
<h2 className="text-xl font-medium">Global options</h2>
|
||||
<ul className="text-white/60 space-y-2 list-disc list-inside">
|
||||
<li>
|
||||
<code className="font-mono">--host <host:port></code> — connect to a different daemon
|
||||
<code className="font-mono">--host <host:port></code> — connect to a different
|
||||
daemon
|
||||
</li>
|
||||
<li>
|
||||
<code className="font-mono">--json</code> — JSON output
|
||||
@@ -250,5 +252,5 @@ paseo ls -q # IDs only (quiet)`}</pre>
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import { pageMeta } from '~/meta'
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { pageMeta } from "~/meta";
|
||||
|
||||
export const Route = createFileRoute('/docs/configuration')({
|
||||
export const Route = createFileRoute("/docs/configuration")({
|
||||
head: () => ({
|
||||
meta: pageMeta(
|
||||
'Configuration - Paseo Docs',
|
||||
'Configure Paseo via config.json, environment variables, and CLI overrides.',
|
||||
"Configuration - Paseo Docs",
|
||||
"Configure Paseo via config.json, environment variables, and CLI overrides.",
|
||||
),
|
||||
}),
|
||||
component: Configuration,
|
||||
})
|
||||
});
|
||||
|
||||
function Configuration() {
|
||||
return (
|
||||
@@ -17,8 +17,8 @@ function Configuration() {
|
||||
<div>
|
||||
<h1 className="text-3xl font-medium font-title mb-4">Configuration</h1>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Paseo loads configuration from a single JSON file in your Paseo home directory, with optional
|
||||
environment variable and CLI overrides.
|
||||
Paseo loads configuration from a single JSON file in your Paseo home directory, with
|
||||
optional environment variable and CLI overrides.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -33,24 +33,26 @@ function Configuration() {
|
||||
<span>~/.paseo/config.json</span>
|
||||
</div>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
You can change the home directory by setting <code className="font-mono">PASEO_HOME</code>{' '}
|
||||
or passing <code className="font-mono">--home</code> to <code className="font-mono">paseo daemon start</code>.
|
||||
You can change the home directory by setting <code className="font-mono">PASEO_HOME</code>{" "}
|
||||
or passing <code className="font-mono">--home</code> to{" "}
|
||||
<code className="font-mono">paseo daemon start</code>.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Precedence</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Paseo merges configuration in this order:
|
||||
</p>
|
||||
<p className="text-white/60 leading-relaxed">Paseo merges configuration in this order:</p>
|
||||
<ol className="text-white/60 space-y-2 list-decimal list-inside">
|
||||
<li>Defaults</li>
|
||||
<li><code className="font-mono">config.json</code></li>
|
||||
<li>
|
||||
<code className="font-mono">config.json</code>
|
||||
</li>
|
||||
<li>Environment variables</li>
|
||||
<li>CLI flags</li>
|
||||
</ol>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Lists append across sources (for example, <code className="font-mono">allowedHosts</code> and
|
||||
Lists append across sources (for example, <code className="font-mono">allowedHosts</code>{" "}
|
||||
and
|
||||
<code className="font-mono">cors.allowedOrigins</code>).
|
||||
</p>
|
||||
</section>
|
||||
@@ -61,7 +63,7 @@ function Configuration() {
|
||||
Minimal example that configures listening address, host allowlist, provider keys, and MCP:
|
||||
</p>
|
||||
<pre className="bg-card border border-border rounded-lg p-4 font-mono text-sm overflow-x-auto text-white/80">
|
||||
{`{
|
||||
{`{
|
||||
"$schema": "https://paseo.sh/schemas/paseo.config.v1.json",
|
||||
"version": 1,
|
||||
"providers": {
|
||||
@@ -79,19 +81,21 @@ function Configuration() {
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Agent provider runtime settings</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Use <code className="font-mono">agents.providers</code> to customize how Paseo launches agent provider CLIs.
|
||||
This works for <code className="font-mono">claude</code>, <code className="font-mono">codex</code>, and
|
||||
Use <code className="font-mono">agents.providers</code> to customize how Paseo launches
|
||||
agent provider CLIs. This works for <code className="font-mono">claude</code>,{" "}
|
||||
<code className="font-mono">codex</code>, and
|
||||
<code className="font-mono"> opencode</code>.
|
||||
</p>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
<code className="font-mono">command.mode</code> can be <code className="font-mono">default</code>,{' '}
|
||||
<code className="font-mono">append</code>, or <code className="font-mono">replace</code>. Use{' '}
|
||||
<code className="font-mono">env</code> to inject provider-specific environment variables.
|
||||
<code className="font-mono">command.mode</code> can be{" "}
|
||||
<code className="font-mono">default</code>, <code className="font-mono">append</code>, or{" "}
|
||||
<code className="font-mono">replace</code>. Use <code className="font-mono">env</code> to
|
||||
inject provider-specific environment variables.
|
||||
</p>
|
||||
|
||||
<h3 className="text-lg font-medium">Enable Claude Code Chrome MCP</h3>
|
||||
<pre className="bg-card border border-border rounded-lg p-4 font-mono text-sm overflow-x-auto text-white/80">
|
||||
{`{
|
||||
{`{
|
||||
"agents": {
|
||||
"providers": {
|
||||
"claude": {
|
||||
@@ -105,9 +109,11 @@ function Configuration() {
|
||||
}`}
|
||||
</pre>
|
||||
|
||||
<h3 className="text-lg font-medium">Point Claude to Anthropic-compatible endpoints (z.ai example)</h3>
|
||||
<h3 className="text-lg font-medium">
|
||||
Point Claude to Anthropic-compatible endpoints (z.ai example)
|
||||
</h3>
|
||||
<pre className="bg-card border border-border rounded-lg p-4 font-mono text-sm overflow-x-auto text-white/80">
|
||||
{`{
|
||||
{`{
|
||||
"agents": {
|
||||
"providers": {
|
||||
"claude": {
|
||||
@@ -124,11 +130,11 @@ function Configuration() {
|
||||
|
||||
<h3 className="text-lg font-medium">Run Claude through Docker</h3>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Create a wrapper script that runs Claude in Docker, then tell Paseo to replace the Claude launch command
|
||||
with that script.
|
||||
Create a wrapper script that runs Claude in Docker, then tell Paseo to replace the Claude
|
||||
launch command with that script.
|
||||
</p>
|
||||
<pre className="bg-card border border-border rounded-lg p-4 font-mono text-sm overflow-x-auto text-white/80">
|
||||
{`{
|
||||
{`{
|
||||
"agents": {
|
||||
"providers": {
|
||||
"claude": {
|
||||
@@ -142,7 +148,7 @@ function Configuration() {
|
||||
}`}
|
||||
</pre>
|
||||
<pre className="bg-card border border-border rounded-lg p-4 font-mono text-sm overflow-x-auto text-white/80">
|
||||
{`#!/usr/bin/env bash
|
||||
{`#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
docker run --rm -i \\
|
||||
-v "$PWD":"$PWD" \\
|
||||
@@ -156,13 +162,16 @@ docker run --rm -i \\
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Voice</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Voice is configured through <code className="font-mono">features.dictation</code> and{' '}
|
||||
<code className="font-mono">features.voiceMode</code>, with provider credentials under{' '}
|
||||
Voice is configured through <code className="font-mono">features.dictation</code> and{" "}
|
||||
<code className="font-mono">features.voiceMode</code>, with provider credentials under{" "}
|
||||
<code className="font-mono">providers</code>.
|
||||
</p>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
For voice philosophy, architecture, and complete local/OpenAI setup examples, see{' '}
|
||||
<a href="/docs/voice" className="underline hover:text-white/80">Voice docs</a>.
|
||||
For voice philosophy, architecture, and complete local/OpenAI setup examples, see{" "}
|
||||
<a href="/docs/voice" className="underline hover:text-white/80">
|
||||
Voice docs
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
@@ -176,16 +185,16 @@ docker run --rm -i \\
|
||||
Console: <code className="font-mono">info</code> and above
|
||||
</li>
|
||||
<li>
|
||||
File (<code className="font-mono">$PASEO_HOME/daemon.log</code>):{' '}
|
||||
File (<code className="font-mono">$PASEO_HOME/daemon.log</code>):{" "}
|
||||
<code className="font-mono">trace</code> and above
|
||||
</li>
|
||||
<li>
|
||||
File rotation: <code className="font-mono">10m</code> max file size,{' '}
|
||||
File rotation: <code className="font-mono">10m</code> max file size,{" "}
|
||||
<code className="font-mono">2</code> retained files total (active + 1 rotated)
|
||||
</li>
|
||||
</ul>
|
||||
<pre className="bg-card border border-border rounded-lg p-4 font-mono text-sm overflow-x-auto text-white/80">
|
||||
{`{
|
||||
{`{
|
||||
"log": {
|
||||
"console": {
|
||||
"level": "info",
|
||||
@@ -203,7 +212,7 @@ docker run --rm -i \\
|
||||
}`}
|
||||
</pre>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Legacy fields <code className="font-mono">log.level</code> and{' '}
|
||||
Legacy fields <code className="font-mono">log.level</code> and{" "}
|
||||
<code className="font-mono">log.format</code> are still supported and map to the new
|
||||
destination settings.
|
||||
</p>
|
||||
@@ -212,22 +221,75 @@ docker run --rm -i \\
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Common env vars</h2>
|
||||
<ul className="text-white/60 space-y-2 list-disc list-inside">
|
||||
<li><code className="font-mono">PASEO_HOME</code> — set Paseo home directory</li>
|
||||
<li><code className="font-mono">PASEO_LISTEN</code> — override <code className="font-mono">daemon.listen</code></li>
|
||||
<li><code className="font-mono">PASEO_ALLOWED_HOSTS</code> — override/extend <code className="font-mono">daemon.allowedHosts</code></li>
|
||||
<li><code className="font-mono">PASEO_LOG_CONSOLE_LEVEL</code> — override <code className="font-mono">log.console.level</code></li>
|
||||
<li><code className="font-mono">PASEO_LOG_FILE_LEVEL</code> — override <code className="font-mono">log.file.level</code></li>
|
||||
<li><code className="font-mono">PASEO_LOG_FILE_PATH</code> — override <code className="font-mono">log.file.path</code></li>
|
||||
<li><code className="font-mono">PASEO_LOG_FILE_ROTATE_SIZE</code> — override <code className="font-mono">log.file.rotate.maxSize</code></li>
|
||||
<li><code className="font-mono">PASEO_LOG_FILE_ROTATE_COUNT</code> — override <code className="font-mono">log.file.rotate.maxFiles</code></li>
|
||||
<li><code className="font-mono">PASEO_LOG</code>, <code className="font-mono">PASEO_LOG_FORMAT</code> — legacy log overrides (still supported)</li>
|
||||
<li><code className="font-mono">OPENAI_API_KEY</code> — override OpenAI provider key</li>
|
||||
<li><code className="font-mono">PASEO_VOICE_LLM_PROVIDER</code> — override voice LLM provider (<code className="font-mono">claude</code>, <code className="font-mono">codex</code>, <code className="font-mono">opencode</code>)</li>
|
||||
<li><code className="font-mono">PASEO_DICTATION_STT_PROVIDER</code>, <code className="font-mono">PASEO_VOICE_STT_PROVIDER</code>, <code className="font-mono">PASEO_VOICE_TTS_PROVIDER</code> — override voice provider selection (<code className="font-mono">local</code> or <code className="font-mono">openai</code>)</li>
|
||||
<li><code className="font-mono">PASEO_LOCAL_MODELS_DIR</code> — control local model directory</li>
|
||||
<li><code className="font-mono">PASEO_DICTATION_LOCAL_STT_MODEL</code> — override local dictation STT model</li>
|
||||
<li><code className="font-mono">PASEO_VOICE_LOCAL_STT_MODEL</code>, <code className="font-mono">PASEO_VOICE_LOCAL_TTS_MODEL</code> — override local voice STT/TTS models</li>
|
||||
<li><code className="font-mono">PASEO_VOICE_LOCAL_TTS_SPEAKER_ID</code>, <code className="font-mono">PASEO_VOICE_LOCAL_TTS_SPEED</code> — optional local voice TTS tuning</li>
|
||||
<li>
|
||||
<code className="font-mono">PASEO_HOME</code> — set Paseo home directory
|
||||
</li>
|
||||
<li>
|
||||
<code className="font-mono">PASEO_LISTEN</code> — override{" "}
|
||||
<code className="font-mono">daemon.listen</code>
|
||||
</li>
|
||||
<li>
|
||||
<code className="font-mono">PASEO_ALLOWED_HOSTS</code> — override/extend{" "}
|
||||
<code className="font-mono">daemon.allowedHosts</code>
|
||||
</li>
|
||||
<li>
|
||||
<code className="font-mono">PASEO_LOG_CONSOLE_LEVEL</code> — override{" "}
|
||||
<code className="font-mono">log.console.level</code>
|
||||
</li>
|
||||
<li>
|
||||
<code className="font-mono">PASEO_LOG_FILE_LEVEL</code> — override{" "}
|
||||
<code className="font-mono">log.file.level</code>
|
||||
</li>
|
||||
<li>
|
||||
<code className="font-mono">PASEO_LOG_FILE_PATH</code> — override{" "}
|
||||
<code className="font-mono">log.file.path</code>
|
||||
</li>
|
||||
<li>
|
||||
<code className="font-mono">PASEO_LOG_FILE_ROTATE_SIZE</code> — override{" "}
|
||||
<code className="font-mono">log.file.rotate.maxSize</code>
|
||||
</li>
|
||||
<li>
|
||||
<code className="font-mono">PASEO_LOG_FILE_ROTATE_COUNT</code> — override{" "}
|
||||
<code className="font-mono">log.file.rotate.maxFiles</code>
|
||||
</li>
|
||||
<li>
|
||||
<code className="font-mono">PASEO_LOG</code>,{" "}
|
||||
<code className="font-mono">PASEO_LOG_FORMAT</code> — legacy log overrides (still
|
||||
supported)
|
||||
</li>
|
||||
<li>
|
||||
<code className="font-mono">OPENAI_API_KEY</code> — override OpenAI provider key
|
||||
</li>
|
||||
<li>
|
||||
<code className="font-mono">PASEO_VOICE_LLM_PROVIDER</code> — override voice LLM
|
||||
provider (<code className="font-mono">claude</code>,{" "}
|
||||
<code className="font-mono">codex</code>, <code className="font-mono">opencode</code>)
|
||||
</li>
|
||||
<li>
|
||||
<code className="font-mono">PASEO_DICTATION_STT_PROVIDER</code>,{" "}
|
||||
<code className="font-mono">PASEO_VOICE_STT_PROVIDER</code>,{" "}
|
||||
<code className="font-mono">PASEO_VOICE_TTS_PROVIDER</code> — override voice provider
|
||||
selection (<code className="font-mono">local</code> or{" "}
|
||||
<code className="font-mono">openai</code>)
|
||||
</li>
|
||||
<li>
|
||||
<code className="font-mono">PASEO_LOCAL_MODELS_DIR</code> — control local model
|
||||
directory
|
||||
</li>
|
||||
<li>
|
||||
<code className="font-mono">PASEO_DICTATION_LOCAL_STT_MODEL</code> — override local
|
||||
dictation STT model
|
||||
</li>
|
||||
<li>
|
||||
<code className="font-mono">PASEO_VOICE_LOCAL_STT_MODEL</code>,{" "}
|
||||
<code className="font-mono">PASEO_VOICE_LOCAL_TTS_MODEL</code> — override local voice
|
||||
STT/TTS models
|
||||
</li>
|
||||
<li>
|
||||
<code className="font-mono">PASEO_VOICE_LOCAL_TTS_SPEAKER_ID</code>,{" "}
|
||||
<code className="font-mono">PASEO_VOICE_LOCAL_TTS_SPEED</code> — optional local voice
|
||||
TTS tuning
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
@@ -241,5 +303,5 @@ docker run --rm -i \\
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import { pageMeta } from '~/meta'
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { pageMeta } from "~/meta";
|
||||
|
||||
export const Route = createFileRoute('/docs/')({
|
||||
export const Route = createFileRoute("/docs/")({
|
||||
head: () => ({
|
||||
meta: pageMeta(
|
||||
'Getting Started - Paseo Docs',
|
||||
'Learn how to set up and use Paseo to manage your coding agents from anywhere.',
|
||||
"Getting Started - Paseo Docs",
|
||||
"Learn how to set up and use Paseo to manage your coding agents from anywhere.",
|
||||
),
|
||||
}),
|
||||
component: GettingStarted,
|
||||
})
|
||||
});
|
||||
|
||||
function GettingStarted() {
|
||||
return (
|
||||
@@ -17,15 +17,14 @@ function GettingStarted() {
|
||||
<div>
|
||||
<h1 className="text-3xl font-medium font-title mb-4">Getting Started</h1>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Paseo connects to your local development environment and lets you manage your coding agents from anywhere.
|
||||
Paseo connects to your local development environment and lets you manage your coding
|
||||
agents from anywhere.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Installation</h2>
|
||||
<p className="text-white/60">
|
||||
Install the CLI globally and run it on your machine:
|
||||
</p>
|
||||
<p className="text-white/60">Install the CLI globally and run it on your machine:</p>
|
||||
<div className="bg-card border border-border rounded-lg p-4 font-mono text-sm">
|
||||
<span className="text-muted-foreground select-none">$ </span>
|
||||
<span>npm install -g @getpaseo/cli</span>
|
||||
@@ -39,14 +38,16 @@ function GettingStarted() {
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Connect the App</h2>
|
||||
<p className="text-white/60">
|
||||
Open Paseo on your phone and scan the QR code displayed in your terminal, or enter the server address manually.
|
||||
Open Paseo on your phone and scan the QR code displayed in your terminal, or enter the
|
||||
server address manually.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Prerequisites</h2>
|
||||
<p className="text-white/60">
|
||||
Paseo wraps CLI tools like Claude Code and Codex. You'll need to have them installed and configured with your own credentials before Paseo can manage them.
|
||||
Paseo wraps CLI tools like Claude Code and Codex. You'll need to have them installed and
|
||||
configured with your own credentials before Paseo can manage them.
|
||||
</p>
|
||||
<ul className="text-white/60 space-y-2 list-disc list-inside">
|
||||
<li>
|
||||
@@ -85,11 +86,12 @@ function GettingStarted() {
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Voice Setup</h2>
|
||||
<p className="text-white/60">
|
||||
Paseo includes first-class voice support with a local-first architecture and configurable speech
|
||||
providers.
|
||||
Paseo includes first-class voice support with a local-first architecture and configurable
|
||||
speech providers.
|
||||
</p>
|
||||
<p className="text-white/60">
|
||||
For architecture, local model behavior, and provider configuration, see the Voice docs page.
|
||||
For architecture, local model behavior, and provider configuration, see the Voice docs
|
||||
page.
|
||||
</p>
|
||||
<a href="/docs/voice" className="underline hover:text-white/80">
|
||||
Voice docs
|
||||
@@ -122,5 +124,5 @@ function GettingStarted() {
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import { pageMeta } from '~/meta'
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { pageMeta } from "~/meta";
|
||||
|
||||
export const Route = createFileRoute('/docs/security')({
|
||||
export const Route = createFileRoute("/docs/security")({
|
||||
head: () => ({
|
||||
meta: pageMeta(
|
||||
'Security - Paseo Docs',
|
||||
'Security model for Paseo: architecture overview, connection methods, relay encryption, and best practices.',
|
||||
"Security - Paseo Docs",
|
||||
"Security model for Paseo: architecture overview, connection methods, relay encryption, and best practices.",
|
||||
),
|
||||
}),
|
||||
component: Security,
|
||||
})
|
||||
});
|
||||
|
||||
function Callout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="bg-primary/10 border border-primary/30 rounded-lg p-4 text-white/80">
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function Security() {
|
||||
@@ -79,7 +79,7 @@ function Security() {
|
||||
<h3 className="text-lg font-medium mt-6">How it works</h3>
|
||||
<ol className="text-white/60 space-y-2 list-decimal list-inside">
|
||||
<li>
|
||||
The daemon generates a persistent ECDH keypair and stores it in{' '}
|
||||
The daemon generates a persistent ECDH keypair and stores it in{" "}
|
||||
<code className="font-mono">$PASEO_HOME/daemon-keypair.json</code>
|
||||
</li>
|
||||
<li>
|
||||
@@ -140,7 +140,7 @@ function Security() {
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Direct connections</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
By default, the daemon listens on <code className="font-mono">127.0.0.1:6767</code>{' '}
|
||||
By default, the daemon listens on <code className="font-mono">127.0.0.1:6767</code>{" "}
|
||||
(localhost only). This is safe for local CLI usage but not reachable from your phone or
|
||||
other devices.
|
||||
</p>
|
||||
@@ -155,7 +155,7 @@ function Security() {
|
||||
|
||||
<h3 className="text-lg font-medium mt-6">VPN access</h3>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
If you prefer direct connections over the relay, you can use a VPN like{' '}
|
||||
If you prefer direct connections over the relay, you can use a VPN like{" "}
|
||||
<a
|
||||
href="https://tailscale.com"
|
||||
target="_blank"
|
||||
@@ -170,7 +170,7 @@ function Security() {
|
||||
<p className="text-white/60 leading-relaxed">To set this up:</p>
|
||||
<ol className="text-white/60 space-y-2 list-decimal list-inside">
|
||||
<li>
|
||||
Install Tailscale on your machine and phone and join them to the same{' '}
|
||||
Install Tailscale on your machine and phone and join them to the same{" "}
|
||||
<a
|
||||
href="https://tailscale.com/kb/1136/tailnet"
|
||||
target="_blank"
|
||||
@@ -181,14 +181,16 @@ function Security() {
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
Configure the daemon to listen on your Tailscale IP (e.g.,{' '}
|
||||
Configure the daemon to listen on your Tailscale IP (e.g.,{" "}
|
||||
<code className="font-mono">100.x.y.z:6767</code>)
|
||||
</li>
|
||||
<li>
|
||||
Add your Tailscale hostname to <code className="font-mono">allowedHosts</code> and{' '}
|
||||
Add your Tailscale hostname to <code className="font-mono">allowedHosts</code> and{" "}
|
||||
<code className="font-mono">cors.allowedOrigins</code>
|
||||
</li>
|
||||
<li>Add the daemon as a direct connection in the Paseo app using the Tailscale address</li>
|
||||
<li>
|
||||
Add the daemon as a direct connection in the Paseo app using the Tailscale address
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<h3 className="text-lg font-medium mt-6">Binding to 0.0.0.0</h3>
|
||||
@@ -196,7 +198,7 @@ function Security() {
|
||||
<strong>Warning:</strong> Binding to <code className="font-mono">0.0.0.0</code> makes the
|
||||
daemon reachable on all network interfaces, including public Wi-Fi and local networks.
|
||||
This can expose your daemon to unauthorized access. If you must bind to all interfaces,
|
||||
ensure you have proper firewall rules and review your{' '}
|
||||
ensure you have proper firewall rules and review your{" "}
|
||||
<code className="font-mono">allowedHosts</code> configuration.
|
||||
</div>
|
||||
</section>
|
||||
@@ -214,17 +216,17 @@ function Security() {
|
||||
on incoming requests. Requests with unrecognized hosts are rejected.
|
||||
</p>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Configure via <code className="font-mono">daemon.allowedHosts</code> in{' '}
|
||||
Configure via <code className="font-mono">daemon.allowedHosts</code> in{" "}
|
||||
<code className="font-mono">config.json</code>:
|
||||
</p>
|
||||
<ul className="text-white/60 space-y-2 list-disc list-inside">
|
||||
<li>
|
||||
Default (<code className="font-mono">[]</code>): allow{' '}
|
||||
<code className="font-mono">localhost</code>,{' '}
|
||||
Default (<code className="font-mono">[]</code>): allow{" "}
|
||||
<code className="font-mono">localhost</code>,{" "}
|
||||
<code className="font-mono">*.localhost</code>, and all IP addresses
|
||||
</li>
|
||||
<li>
|
||||
<code className="font-mono">['.example.com']</code>: allow{' '}
|
||||
<code className="font-mono">['.example.com']</code>: allow{" "}
|
||||
<code className="font-mono">example.com</code> and any subdomain, plus defaults
|
||||
</li>
|
||||
<li>
|
||||
@@ -283,5 +285,5 @@ function Security() {
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import { pageMeta } from '~/meta'
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { pageMeta } from "~/meta";
|
||||
|
||||
export const Route = createFileRoute('/docs/updates')({
|
||||
export const Route = createFileRoute("/docs/updates")({
|
||||
head: () => ({
|
||||
meta: pageMeta(
|
||||
'Updates - Paseo Docs',
|
||||
'How to update Paseo daemon and apps across web, desktop, and mobile.',
|
||||
"Updates - Paseo Docs",
|
||||
"How to update Paseo daemon and apps across web, desktop, and mobile.",
|
||||
),
|
||||
}),
|
||||
component: UpdatesDocs,
|
||||
})
|
||||
});
|
||||
|
||||
function UpdatesDocs() {
|
||||
return (
|
||||
@@ -53,7 +53,7 @@ function UpdatesDocs() {
|
||||
className="underline hover:text-white/80"
|
||||
>
|
||||
app.paseo.sh
|
||||
</a>{' '}
|
||||
</a>{" "}
|
||||
is always up to date. No manual update needed.
|
||||
</p>
|
||||
</section>
|
||||
@@ -82,5 +82,5 @@ function UpdatesDocs() {
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import { pageMeta } from '~/meta'
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { pageMeta } from "~/meta";
|
||||
|
||||
export const Route = createFileRoute('/docs/voice')({
|
||||
export const Route = createFileRoute("/docs/voice")({
|
||||
head: () => ({
|
||||
meta: pageMeta(
|
||||
'Voice - Paseo Docs',
|
||||
'Paseo voice architecture, local-first model execution, and provider configuration.',
|
||||
"Voice - Paseo Docs",
|
||||
"Paseo voice architecture, local-first model execution, and provider configuration.",
|
||||
),
|
||||
}),
|
||||
component: VoiceDocs,
|
||||
})
|
||||
});
|
||||
|
||||
function VoiceDocs() {
|
||||
return (
|
||||
@@ -17,17 +17,17 @@ function VoiceDocs() {
|
||||
<div>
|
||||
<h1 className="text-3xl font-medium font-title mb-4">Voice</h1>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Paseo has first-class voice support for dictation and realtime conversations with your coding
|
||||
environment.
|
||||
Paseo has first-class voice support for dictation and realtime conversations with your
|
||||
coding environment.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Philosophy</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Voice is local-first. You can run speech fully on-device, or choose OpenAI for speech features.
|
||||
For voice reasoning/orchestration, Paseo reuses agent providers already installed and authenticated
|
||||
on your machine.
|
||||
Voice is local-first. You can run speech fully on-device, or choose OpenAI for speech
|
||||
features. For voice reasoning/orchestration, Paseo reuses agent providers already
|
||||
installed and authenticated on your machine.
|
||||
</p>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
This keeps credentials and execution in your environment and avoids introducing a separate
|
||||
@@ -38,9 +38,16 @@ function VoiceDocs() {
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Architecture</h2>
|
||||
<ul className="text-white/60 space-y-2 list-disc list-inside">
|
||||
<li>Speech I/O: STT and TTS providers per feature (<code className="font-mono">local</code> or <code className="font-mono">openai</code>)</li>
|
||||
<li>
|
||||
Speech I/O: STT and TTS providers per feature (<code className="font-mono">local</code>{" "}
|
||||
or <code className="font-mono">openai</code>)
|
||||
</li>
|
||||
<li>Local speech runtime: ONNX models executed on CPU by default</li>
|
||||
<li>Voice LLM orchestration: hidden agent session using your configured provider (<code className="font-mono">claude</code>, <code className="font-mono">codex</code>, or <code className="font-mono">opencode</code>)</li>
|
||||
<li>
|
||||
Voice LLM orchestration: hidden agent session using your configured provider (
|
||||
<code className="font-mono">claude</code>, <code className="font-mono">codex</code>, or{" "}
|
||||
<code className="font-mono">opencode</code>)
|
||||
</li>
|
||||
<li>Tooling path: MCP stdio bridge for voice tools and agent control</li>
|
||||
</ul>
|
||||
</section>
|
||||
@@ -48,16 +55,17 @@ function VoiceDocs() {
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Local Speech</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Local speech defaults to model IDs <code className="font-mono">parakeet-tdt-0.6b-v3-int8</code>{' '}
|
||||
(STT) and <code className="font-mono">kokoro-en-v0_19</code> (TTS, speaker 0 / voice 00).
|
||||
Local speech defaults to model IDs{" "}
|
||||
<code className="font-mono">parakeet-tdt-0.6b-v3-int8</code> (STT) and{" "}
|
||||
<code className="font-mono">kokoro-en-v0_19</code> (TTS, speaker 0 / voice 00).
|
||||
</p>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Missing models are downloaded at daemon startup into{' '}
|
||||
<code className="font-mono">$PASEO_HOME/models/local-speech</code>.
|
||||
Downloads happen only for missing files.
|
||||
Missing models are downloaded at daemon startup into{" "}
|
||||
<code className="font-mono">$PASEO_HOME/models/local-speech</code>. Downloads happen only
|
||||
for missing files.
|
||||
</p>
|
||||
<pre className="bg-card border border-border rounded-lg p-4 font-mono text-sm overflow-x-auto text-white/80">
|
||||
{`{
|
||||
{`{
|
||||
"version": 1,
|
||||
"features": {
|
||||
"dictation": { "stt": { "provider": "local", "model": "parakeet-tdt-0.6b-v3-int8" } },
|
||||
@@ -79,11 +87,12 @@ function VoiceDocs() {
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">OpenAI Speech Option</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
You can switch dictation, voice STT, and voice TTS to OpenAI by setting provider fields to{' '}
|
||||
<code className="font-mono">openai</code> and providing <code className="font-mono">OPENAI_API_KEY</code>.
|
||||
You can switch dictation, voice STT, and voice TTS to OpenAI by setting provider fields to{" "}
|
||||
<code className="font-mono">openai</code> and providing{" "}
|
||||
<code className="font-mono">OPENAI_API_KEY</code>.
|
||||
</p>
|
||||
<pre className="bg-card border border-border rounded-lg p-4 font-mono text-sm overflow-x-auto text-white/80">
|
||||
{`{
|
||||
{`{
|
||||
"version": 1,
|
||||
"features": {
|
||||
"dictation": { "stt": { "provider": "openai" } },
|
||||
@@ -102,22 +111,42 @@ function VoiceDocs() {
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Environment Variables</h2>
|
||||
<ul className="text-white/60 space-y-2 list-disc list-inside">
|
||||
<li><code className="font-mono">OPENAI_API_KEY</code> — OpenAI speech credentials</li>
|
||||
<li><code className="font-mono">PASEO_VOICE_LLM_PROVIDER</code> — voice agent provider override</li>
|
||||
<li><code className="font-mono">PASEO_LOCAL_MODELS_DIR</code> — local model storage directory</li>
|
||||
<li><code className="font-mono">PASEO_DICTATION_LOCAL_STT_MODEL</code> — local dictation STT model ID</li>
|
||||
<li><code className="font-mono">PASEO_VOICE_LOCAL_STT_MODEL</code>, <code className="font-mono">PASEO_VOICE_LOCAL_TTS_MODEL</code> — local voice STT/TTS model IDs</li>
|
||||
<li><code className="font-mono">PASEO_VOICE_LOCAL_TTS_SPEAKER_ID</code>, <code className="font-mono">PASEO_VOICE_LOCAL_TTS_SPEED</code> — optional local voice TTS tuning</li>
|
||||
<li>
|
||||
<code className="font-mono">OPENAI_API_KEY</code> — OpenAI speech credentials
|
||||
</li>
|
||||
<li>
|
||||
<code className="font-mono">PASEO_VOICE_LLM_PROVIDER</code> — voice agent provider
|
||||
override
|
||||
</li>
|
||||
<li>
|
||||
<code className="font-mono">PASEO_LOCAL_MODELS_DIR</code> — local model storage
|
||||
directory
|
||||
</li>
|
||||
<li>
|
||||
<code className="font-mono">PASEO_DICTATION_LOCAL_STT_MODEL</code> — local dictation STT
|
||||
model ID
|
||||
</li>
|
||||
<li>
|
||||
<code className="font-mono">PASEO_VOICE_LOCAL_STT_MODEL</code>,{" "}
|
||||
<code className="font-mono">PASEO_VOICE_LOCAL_TTS_MODEL</code> — local voice STT/TTS
|
||||
model IDs
|
||||
</li>
|
||||
<li>
|
||||
<code className="font-mono">PASEO_VOICE_LOCAL_TTS_SPEAKER_ID</code>,{" "}
|
||||
<code className="font-mono">PASEO_VOICE_LOCAL_TTS_SPEED</code> — optional local voice
|
||||
TTS tuning
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Operational Notes</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Realtime voice can launch and control agents. Treat voice prompts with the same care as direct
|
||||
agent instructions, especially when specifying working directories or destructive operations.
|
||||
Realtime voice can launch and control agents. Treat voice prompts with the same care as
|
||||
direct agent instructions, especially when specifying working directories or destructive
|
||||
operations.
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import { pageMeta } from '~/meta'
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { pageMeta } from "~/meta";
|
||||
|
||||
export const Route = createFileRoute('/docs/worktrees')({
|
||||
export const Route = createFileRoute("/docs/worktrees")({
|
||||
head: () => ({
|
||||
meta: pageMeta(
|
||||
'Git Worktrees - Paseo Docs',
|
||||
'Run agents in isolated git worktrees for parallel feature development.',
|
||||
"Git Worktrees - Paseo Docs",
|
||||
"Run agents in isolated git worktrees for parallel feature development.",
|
||||
),
|
||||
}),
|
||||
component: Worktrees,
|
||||
})
|
||||
});
|
||||
|
||||
function Code({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="bg-card border border-border rounded-lg p-4 font-mono text-sm overflow-x-auto">
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function Worktrees() {
|
||||
@@ -25,8 +25,8 @@ function Worktrees() {
|
||||
<div>
|
||||
<h1 className="text-3xl font-medium font-title mb-4">Git Worktrees</h1>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Git worktrees let you have multiple working directories from the same repository.
|
||||
Paseo uses them to run agents in isolated branches without switching contexts.
|
||||
Git worktrees let you have multiple working directories from the same repository. Paseo
|
||||
uses them to run agents in isolated branches without switching contexts.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -34,14 +34,14 @@ function Worktrees() {
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Why worktrees?</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Without worktrees, running multiple agents on the same repo means they share the
|
||||
working directory. One agent's changes interfere with another's. You can't safely
|
||||
run parallel tasks.
|
||||
Without worktrees, running multiple agents on the same repo means they share the working
|
||||
directory. One agent's changes interfere with another's. You can't safely run parallel
|
||||
tasks.
|
||||
</p>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
With worktrees, each agent gets its own directory and branch. They can work
|
||||
simultaneously without conflict. When an agent finishes, you review the diff,
|
||||
merge the branch, and archive the worktree.
|
||||
With worktrees, each agent gets its own directory and branch. They can work simultaneously
|
||||
without conflict. When an agent finishes, you review the diff, merge the branch, and
|
||||
archive the worktree.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
@@ -61,9 +61,9 @@ function Worktrees() {
|
||||
└── swift-hare/ # random slug`}</pre>
|
||||
</Code>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
The hash avoids collisions between repositories that share the same directory or
|
||||
remote name. Worktree directory names are random slugs — the branch name is
|
||||
separate and chosen when you first launch an agent in the worktree.
|
||||
The hash avoids collisions between repositories that share the same directory or remote
|
||||
name. Worktree directory names are random slugs — the branch name is separate and chosen
|
||||
when you first launch an agent in the worktree.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
@@ -71,12 +71,12 @@ function Worktrees() {
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Branches</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
When you create a worktree, Paseo generates a random directory name. The branch name
|
||||
is set when you first launch an agent — Paseo generates one automatically.
|
||||
When you create a worktree, Paseo generates a random directory name. The branch name is
|
||||
set when you first launch an agent — Paseo generates one automatically.
|
||||
</p>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
This means the worktree directory and branch are independent. You can rename the
|
||||
branch later without affecting the worktree path.
|
||||
This means the worktree directory and branch are independent. You can rename the branch
|
||||
later without affecting the worktree path.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
@@ -84,14 +84,13 @@ function Worktrees() {
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Multiple agents per worktree</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
You can launch multiple agents into the same worktree. They share the working
|
||||
directory and branch, which is useful when you want agents to collaborate on
|
||||
the same feature or when one agent hands off to another.
|
||||
You can launch multiple agents into the same worktree. They share the working directory
|
||||
and branch, which is useful when you want agents to collaborate on the same feature or
|
||||
when one agent hands off to another.
|
||||
</p>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Be mindful of conflicts — agents working on the same files simultaneously can
|
||||
step on each other. This works best when agents have distinct responsibilities
|
||||
or run sequentially.
|
||||
Be mindful of conflicts — agents working on the same files simultaneously can step on each
|
||||
other. This works best when agents have distinct responsibilities or run sequentially.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
@@ -100,7 +99,7 @@ function Worktrees() {
|
||||
<h2 className="text-xl font-medium">Setup with paseo.json</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
When Paseo creates a worktree, it's a fresh checkout. Dependencies aren't installed,
|
||||
config files aren't copied. You can automate setup by creating a{' '}
|
||||
config files aren't copied. You can automate setup by creating a{" "}
|
||||
<code className="font-mono">paseo.json</code> file in your repository root:
|
||||
</p>
|
||||
<Code>
|
||||
@@ -114,14 +113,14 @@ function Worktrees() {
|
||||
}`}</pre>
|
||||
</Code>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
The <code className="font-mono">setup</code> array contains shell commands that run
|
||||
after the worktree is created. Use it to install dependencies, copy local config
|
||||
files, or run any other initialization.
|
||||
The <code className="font-mono">setup</code> array contains shell commands that run after
|
||||
the worktree is created. Use it to install dependencies, copy local config files, or run
|
||||
any other initialization.
|
||||
</p>
|
||||
<div className="bg-yellow-500/10 border border-yellow-500/30 rounded-lg p-4 text-white/80">
|
||||
<strong>Important:</strong> Setup commands come from{' '}
|
||||
<code className="font-mono">paseo.json</code> in the selected base branch. If you pick{' '}
|
||||
<code className="font-mono">main</code>, Paseo reads the committed file on{' '}
|
||||
<strong>Important:</strong> Setup commands come from{" "}
|
||||
<code className="font-mono">paseo.json</code> in the selected base branch. If you pick{" "}
|
||||
<code className="font-mono">main</code>, Paseo reads the committed file on{" "}
|
||||
<code className="font-mono">main</code>. Local or uncommitted changes in another branch
|
||||
are not used for that worktree.
|
||||
</div>
|
||||
@@ -139,7 +138,7 @@ function Worktrees() {
|
||||
path (original repository root)
|
||||
</li>
|
||||
<li>
|
||||
<code className="font-mono">$PASEO_ROOT_PATH</code> — legacy alias of{' '}
|
||||
<code className="font-mono">$PASEO_ROOT_PATH</code> — legacy alias of{" "}
|
||||
<code className="font-mono">$PASEO_SOURCE_CHECKOUT_PATH</code>
|
||||
</li>
|
||||
<li>
|
||||
@@ -149,7 +148,8 @@ function Worktrees() {
|
||||
<code className="font-mono">$PASEO_BRANCH_NAME</code> — the branch name created
|
||||
</li>
|
||||
<li>
|
||||
<code className="font-mono">$PASEO_WORKTREE_PORT</code> — an available local port for setup scripts
|
||||
<code className="font-mono">$PASEO_WORKTREE_PORT</code> — an available local port for
|
||||
setup scripts
|
||||
</li>
|
||||
</ul>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
@@ -211,47 +211,29 @@ function Worktrees() {
|
||||
{/* Workflow */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Workflow</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
The typical workflow is:
|
||||
</p>
|
||||
<p className="text-white/60 leading-relaxed">The typical workflow is:</p>
|
||||
<ol className="text-white/60 space-y-2 list-decimal list-inside">
|
||||
<li>
|
||||
Create a worktree — Paseo creates the directory and runs setup
|
||||
</li>
|
||||
<li>
|
||||
Launch an agent — Paseo creates or assigns a branch
|
||||
</li>
|
||||
<li>
|
||||
Agent works in isolation — changes stay in its worktree
|
||||
</li>
|
||||
<li>
|
||||
Review the diff — compare against the base branch
|
||||
</li>
|
||||
<li>
|
||||
Merge or discard — if approved, merge the branch; otherwise archive
|
||||
</li>
|
||||
<li>
|
||||
Archive the worktree — cleans up the directory and optionally the branch
|
||||
</li>
|
||||
<li>Create a worktree — Paseo creates the directory and runs setup</li>
|
||||
<li>Launch an agent — Paseo creates or assigns a branch</li>
|
||||
<li>Agent works in isolation — changes stay in its worktree</li>
|
||||
<li>Review the diff — compare against the base branch</li>
|
||||
<li>Merge or discard — if approved, merge the branch; otherwise archive</li>
|
||||
<li>Archive the worktree — cleans up the directory and optionally the branch</li>
|
||||
</ol>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
You can run multiple agents in different worktrees simultaneously. Each worktree
|
||||
has its own branch and working directory.
|
||||
You can run multiple agents in different worktrees simultaneously. Each worktree has its
|
||||
own branch and working directory.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{/* CLI reference */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">CLI reference</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Create an agent in a new worktree:
|
||||
</p>
|
||||
<p className="text-white/60 leading-relaxed">Create an agent in a new worktree:</p>
|
||||
<Code>
|
||||
<pre className="text-white/80">{`paseo run --worktree feature-auth --base main "implement auth"`}</pre>
|
||||
</Code>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
List all worktrees:
|
||||
</p>
|
||||
<p className="text-white/60 leading-relaxed">List all worktrees:</p>
|
||||
<Code>
|
||||
<pre className="text-white/80">{`paseo worktree ls`}</pre>
|
||||
</Code>
|
||||
@@ -267,14 +249,14 @@ function Worktrees() {
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Metadata</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Paseo stores metadata in each worktree's git directory to track the base branch.
|
||||
This is used for diff operations and to know what branch to merge into.
|
||||
Paseo stores metadata in each worktree's git directory to track the base branch. This is
|
||||
used for diff operations and to know what branch to merge into.
|
||||
</p>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
You don't need to manage this manually — Paseo handles it when creating and
|
||||
archiving worktrees.
|
||||
You don't need to manage this manually — Paseo handles it when creating and archiving
|
||||
worktrees.
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,22 +1,27 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import { LandingPage } from '~/components/landing-page'
|
||||
import { pageMeta } from '~/meta'
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { LandingPage } from "~/components/landing-page";
|
||||
import { pageMeta } from "~/meta";
|
||||
|
||||
export const Route = createFileRoute('/')({
|
||||
export const Route = createFileRoute("/")({
|
||||
head: () => ({
|
||||
meta: pageMeta(
|
||||
'Paseo – Run Claude Code, Codex, and OpenCode from everywhere',
|
||||
'A self-hosted daemon for Claude Code, Codex, and OpenCode. Agents run on your machine with your full dev environment. Connect from phone, desktop, or web.',
|
||||
"Paseo – Run Claude Code, Codex, and OpenCode from everywhere",
|
||||
"A self-hosted daemon for Claude Code, Codex, and OpenCode. Agents run on your machine with your full dev environment. Connect from phone, desktop, or web.",
|
||||
),
|
||||
}),
|
||||
component: Home,
|
||||
})
|
||||
});
|
||||
|
||||
function Home() {
|
||||
return (
|
||||
<LandingPage
|
||||
title={<>All your coding agents,<br className="hidden md:block" /> from anywhere</>}
|
||||
title={
|
||||
<>
|
||||
All your coding agents,
|
||||
<br className="hidden md:block" /> from anywhere
|
||||
</>
|
||||
}
|
||||
subtitle="Run any coding agent from your phone, desktop, or terminal. Self-hosted, multi-provider, open source."
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import { LandingPage } from '~/components/landing-page'
|
||||
import { pageMeta } from '~/meta'
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { LandingPage } from "~/components/landing-page";
|
||||
import { pageMeta } from "~/meta";
|
||||
|
||||
export const Route = createFileRoute('/opencode')({
|
||||
export const Route = createFileRoute("/opencode")({
|
||||
head: () => ({
|
||||
meta: pageMeta(
|
||||
'OpenCode Mobile App – Code from anywhere | Paseo',
|
||||
'Run OpenCode from your phone. Launch agents, watch them work, and ship code from wherever you are. Self-hosted, open source, your code stays local.',
|
||||
"OpenCode Mobile App – Code from anywhere | Paseo",
|
||||
"Run OpenCode from your phone. Launch agents, watch them work, and ship code from wherever you are. Self-hosted, open source, your code stays local.",
|
||||
),
|
||||
}),
|
||||
component: OpenCodePage,
|
||||
})
|
||||
});
|
||||
|
||||
function OpenCodePage() {
|
||||
return (
|
||||
@@ -18,5 +18,5 @@ function OpenCodePage() {
|
||||
title="Run OpenCode from your phone"
|
||||
subtitle="Launch agents, check on builds, and ship code from anywhere. Same setup, same machine, just not at your desk."
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import { pageMeta } from '~/meta'
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { pageMeta } from "~/meta";
|
||||
|
||||
export const Route = createFileRoute('/privacy')({
|
||||
export const Route = createFileRoute("/privacy")({
|
||||
head: () => ({
|
||||
meta: pageMeta(
|
||||
'Privacy Policy - Paseo',
|
||||
'Privacy policy for Paseo - a self-hosted agent manager with no tracking or analytics.',
|
||||
"Privacy Policy - Paseo",
|
||||
"Privacy policy for Paseo - a self-hosted agent manager with no tracking or analytics.",
|
||||
),
|
||||
}),
|
||||
component: Privacy,
|
||||
})
|
||||
});
|
||||
|
||||
function Privacy() {
|
||||
return (
|
||||
@@ -19,8 +19,8 @@ function Privacy() {
|
||||
|
||||
<div className="space-y-6 text-white/70 leading-relaxed">
|
||||
<p>
|
||||
Paseo is a self-hosted tool for managing coding agents. Your code and
|
||||
data stay on your machine.
|
||||
Paseo is a self-hosted tool for managing coding agents. Your code and data stay on your
|
||||
machine.
|
||||
</p>
|
||||
|
||||
<section className="space-y-3">
|
||||
@@ -31,8 +31,8 @@ function Privacy() {
|
||||
<section className="space-y-3">
|
||||
<h2 className="text-xl font-medium text-white">The relay server</h2>
|
||||
<p>
|
||||
If you use the optional encrypted relay to connect your phone to your
|
||||
daemon, the relay sees:
|
||||
If you use the optional encrypted relay to connect your phone to your daemon, the
|
||||
relay sees:
|
||||
</p>
|
||||
<ul className="list-disc list-inside space-y-1 ml-4">
|
||||
<li>IP addresses and connection timing</li>
|
||||
@@ -40,49 +40,40 @@ function Privacy() {
|
||||
<li>Session IDs</li>
|
||||
</ul>
|
||||
<p>
|
||||
All messages between your phone and daemon are end-to-end encrypted
|
||||
with AES-256-GCM. The relay cannot read your messages, see your code,
|
||||
or decrypt your traffic.
|
||||
All messages between your phone and daemon are end-to-end encrypted with AES-256-GCM.
|
||||
The relay cannot read your messages, see your code, or decrypt your traffic.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="space-y-3">
|
||||
<h2 className="text-xl font-medium text-white">Analytics and tracking</h2>
|
||||
<p>
|
||||
We don't use analytics, tracking pixels, cookies, or ads. The app
|
||||
doesn't phone home.
|
||||
We don't use analytics, tracking pixels, cookies, or ads. The app doesn't phone home.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="space-y-3">
|
||||
<h2 className="text-xl font-medium text-white">Third-party services</h2>
|
||||
<p>
|
||||
Paseo wraps agent providers like Claude Code, Codex, and OpenCode.
|
||||
Those tools communicate with their own APIs (Anthropic, OpenAI, etc.)
|
||||
using your credentials. Paseo doesn't manage or intercept those API
|
||||
calls.
|
||||
Paseo wraps agent providers like Claude Code, Codex, and OpenCode. Those tools
|
||||
communicate with their own APIs (Anthropic, OpenAI, etc.) using your credentials.
|
||||
Paseo doesn't manage or intercept those API calls.
|
||||
</p>
|
||||
<p>
|
||||
If you use voice features with cloud providers (OpenAI speech), your
|
||||
voice data is sent to those services according to their privacy
|
||||
policies.
|
||||
If you use voice features with cloud providers (OpenAI speech), your voice data is
|
||||
sent to those services according to their privacy policies.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="space-y-3">
|
||||
<h2 className="text-xl font-medium text-white">
|
||||
We don't sell your data
|
||||
</h2>
|
||||
<p>
|
||||
We don't have your data to sell. Paseo is self-hosted and
|
||||
local-first.
|
||||
</p>
|
||||
<h2 className="text-xl font-medium text-white">We don't sell your data</h2>
|
||||
<p>We don't have your data to sell. Paseo is self-hosted and local-first.</p>
|
||||
</section>
|
||||
|
||||
<section className="space-y-3">
|
||||
<h2 className="text-xl font-medium text-white">Questions</h2>
|
||||
<p>
|
||||
If you have questions about privacy, open an issue on{' '}
|
||||
If you have questions about privacy, open an issue on{" "}
|
||||
<a
|
||||
href="https://github.com/getpaseo/paseo"
|
||||
target="_blank"
|
||||
@@ -99,5 +90,5 @@ function Privacy() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import 'tailwindcss';
|
||||
@import "tailwindcss";
|
||||
|
||||
/* Title font - centralized for easy changes */
|
||||
.font-title {
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
declare module '*.md?raw' {
|
||||
const content: string
|
||||
export default content
|
||||
declare module "*.md?raw" {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user