Flatten the repo: move everything out of app/ to the root

Now that the CLI and the Next.js app are one package, nesting it inside
app/ served no purpose - the repo root itself becomes the published
npm package. Merges app/.gitignore and app/README.md into the root
versions, drops the now-duplicate app/LICENSE, and updates path
references (README, docs/ARCHITECTURE.md, docs/CONFIG_REFERENCE.md,
package.json's repository.directory) that assumed the app/ nesting.

Also fixes a real bug this surfaced: the in-app docs viewer resolved
docs/ relative to process.cwd(), which only worked by accident when the
CLI happened to be invoked from app/'s parent directory. A first attempt
at fixing it with import.meta.dirname broke instead, for the same
cross-module-graph reason config-path resolution already documented -
Next compiles Route Handlers through a separate module graph that
doesn't preserve source-relative import.meta paths. Fixed by exposing
the app root via TRIGGERSHELL_APP_ROOT (set once in server.ts, where
import.meta *does* resolve correctly), the same pattern already used
for TRIGGERSHELL_CONFIG_PATH.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-16 11:14:01 +02:00
co-authored by Claude Sonnet 5
parent 30350d80f4
commit 3f379ca2ac
123 changed files with 65 additions and 104 deletions
+95
View File
@@ -0,0 +1,95 @@
"use client";
import { useState } from "react";
import { useRouter, useSearchParams } from "next/navigation";
import { Terminal } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Alert, AlertDescription } from "@/components/ui/alert";
export function LoginForm() {
const router = useRouter();
const searchParams = useSearchParams();
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState<string | null>(null);
const [pending, setPending] = useState(false);
async function handleSubmit(event: React.FormEvent) {
event.preventDefault();
setPending(true);
setError(null);
const response = await fetch("/api/auth/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username, password }),
});
if (response.ok) {
const next = searchParams.get("next") ?? "/";
router.push(next);
router.refresh();
return;
}
const data = await response.json().catch(() => ({}));
setError(data.error ?? "Login failed");
setPending(false);
}
return (
<Card className="w-full max-w-sm">
<CardHeader>
<div className="flex items-center gap-2">
<Terminal className="size-5" />
<CardTitle>TriggerShell</CardTitle>
</div>
<CardDescription>
Sign in to run and monitor your configured scripts.
</CardDescription>
</CardHeader>
<CardContent>
<form onSubmit={handleSubmit} className="flex flex-col gap-4">
{error && (
<Alert variant="destructive">
<AlertDescription>{error}</AlertDescription>
</Alert>
)}
<div className="flex flex-col gap-2">
<Label htmlFor="username">Username</Label>
<Input
id="username"
autoComplete="username"
value={username}
onChange={(event) => setUsername(event.target.value)}
required
/>
</div>
<div className="flex flex-col gap-2">
<Label htmlFor="password">Password</Label>
<Input
id="password"
type="password"
autoComplete="current-password"
value={password}
onChange={(event) => setPassword(event.target.value)}
required
/>
</div>
<Button type="submit" disabled={pending} className="mt-2">
{pending ? "Signing in..." : "Sign in"}
</Button>
</form>
</CardContent>
</Card>
);
}