@helloleo/plugins 0.2.0-beta.1 → 0.2.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -24886,12 +24886,16 @@ function projectUuidFromDir(dir) {
24886
24886
  return path3.basename(dir);
24887
24887
  }
24888
24888
  var scaffoldOptionsSchema = exports_external2.object({
24889
- name: exports_external2.string().describe("Short product name. Used as the page title and package name."),
24889
+ name: exports_external2.string().describe("Human-readable name for SEO, e.g. 'Shipping Platform'"),
24890
24890
  description: exports_external2.string().describe("One sentence describing the app. Used as the meta description.")
24891
24891
  });
24892
+ function slugify(name) {
24893
+ return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "leo-project";
24894
+ }
24892
24895
  async function writeBaseScaffold({ name, description }, dir) {
24893
24896
  const created = await renderTemplate(SCAFFOLD_VERSION, {
24894
24897
  LEO_NAME: name,
24898
+ LEO_SLUG: slugify(name),
24895
24899
  LEO_DESCRIPTION: description,
24896
24900
  LEO_PROJECT_UUID: projectUuidFromDir(dir)
24897
24901
  }, dir);
@@ -24917,7 +24921,7 @@ var scaffold = tool({
24917
24921
  "",
24918
24922
  "Notes:",
24919
24923
  "- This is a TanStack Start project. The server entry is src/server.ts. There is no index.html or client entrypoint.",
24920
- "- Add routes as files in src/routes/. Do not default export route components.",
24924
+ "- Add routes as files in src/routes/. Do not default export route components. Routing and layout conventions are documented in src/routes/README.md, read it before adding routes.",
24921
24925
  "- For JSON/CRUD APIs, add server routes under src/routes/api/. Read src/routes/api/health.ts first, add routes as needed.",
24922
24926
  "- shadcn/ui components are already in src/components/ui.",
24923
24927
  "- Design system: edit the tokens in src/styles.css (light and dark blocks).",
@@ -0,0 +1,5 @@
1
+ # Supply-chain guard: refuse npm package versions published less than 3 days
2
+ # ago (compromised-release window). Versions already in bun.lock install
3
+ # normally. Escape hatch: minimumReleaseAgeExcludes = ["pkg"].
4
+ [install]
5
+ minimumReleaseAge = 259200
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "{{LEO_NAME}}",
2
+ "name": "{{LEO_SLUG}}",
3
3
  "private": true,
4
4
  "type": "module",
5
5
  "imports": {
@@ -0,0 +1,85 @@
1
+ # Routes
2
+
3
+ TanStack Start uses file-based routing. Every `.ts` and `.tsx` file in this directory is a route. Do not create `src/pages/`, `app/layout.tsx`, or similar, those are Next.js / Remix conventions. The only root layout is `src/routes/__root.tsx`.
4
+
5
+ ## Conventions
6
+
7
+ | File | URL |
8
+ | --- | --- |
9
+ | `index.tsx` | `/` |
10
+ | `about.tsx` | `/about` |
11
+ | `users/index.tsx` | `/users` |
12
+ | `users/$id.tsx` | `/users/:id` (dynamic — bare `$`, no curly braces) |
13
+ | `posts.$postId.tsx` | `/posts/:postId` (dots nest without folders) |
14
+ | `admin/route.tsx` | layout for every `/admin/*` page; must render `<Outlet />` |
15
+ | `admin_.login.tsx` | `/admin/login`, but outside the admin layout (trailing `_` escapes it) |
16
+ | `_auth.tsx` | pathless layout; wraps `_auth/*` files without adding a URL segment |
17
+ | `api/health.ts` | server route for defining API endpoints |
18
+ | `__root.tsx` | app shell, wraps every page; preserve `<Outlet />` |
19
+
20
+ ## Rules
21
+
22
+ - A layout route without `<Outlet />` renders its children nowhere.
23
+ - Do not `export default` route components, it breaks code splitting. Use a named function passed as `component:`.
24
+ - `routeTree.gen.ts` is auto-generated. Don't edit it by hand.
25
+
26
+ ## Authenticated routes
27
+
28
+ Put protected pages inside a pathless `_authed` layout and keep login/signup
29
+ at the top level, outside it. Never special-case the login pathname inside
30
+ the layout guard.
31
+
32
+ ```
33
+ routes/
34
+ __root.tsx loads the session into router context (context.user)
35
+ _authed.tsx pathless guard — no URL segment of its own
36
+ _authed/
37
+ dashboard.tsx /dashboard (protected)
38
+ posts.tsx /posts (protected)
39
+ index.tsx / (public)
40
+ login.tsx /login (public)
41
+ logout.tsx /logout
42
+ ```
43
+
44
+ `_authed.tsx` does the guarding, all children inherit it:
45
+
46
+ ```tsx _authed.tsx
47
+ export const Route = createFileRoute('/_authed')({
48
+ beforeLoad: ({ context }) => {
49
+ if (!context.user) {
50
+ throw redirect({ to: '/login' })
51
+ }
52
+ },
53
+ })
54
+ ```
55
+
56
+ The session is loaded once in `__root.tsx`'s `beforeLoad` and returned into
57
+ the router context so every route can read `context.user`. `getUser()` is
58
+ whatever your auth uses (e.g. verify a JWT from localStorage) and resolves to
59
+ the user or null:
60
+
61
+ ```tsx __root.tsx
62
+ // (keep the existing head() and component, add this)
63
+ export const Route = createRootRouteWithContext<{ queryClient: QueryClient }>()({
64
+ beforeLoad: async () => {
65
+ const user = await getUser()
66
+ return { user }
67
+ },
68
+ // ...head, component unchanged
69
+ })
70
+ ```
71
+
72
+ Components read it with `Route.useRouteContext()` (e.g. to swap a Login link
73
+ for a Logout button in the shell):
74
+
75
+ ```tsx
76
+ function Route() {
77
+ const { user } = Route.useRouteContext()
78
+
79
+ return (
80
+ <nav>
81
+ {user ? <Link to="/logout">Logout</Link> : <Link to="/login">Login</Link>}
82
+ </nav>
83
+ )
84
+ }
85
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@helloleo/plugins",
3
- "version": "0.2.0-beta.1",
3
+ "version": "0.2.0-beta.2",
4
4
  "description": "OpenCode plugins for HelloLeo",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -0,0 +1,5 @@
1
+ # Supply-chain guard: refuse npm package versions published less than 3 days
2
+ # ago (compromised-release window). Versions already in bun.lock install
3
+ # normally. Escape hatch: minimumReleaseAgeExcludes = ["pkg"].
4
+ [install]
5
+ minimumReleaseAge = 259200
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "{{LEO_NAME}}",
2
+ "name": "{{LEO_SLUG}}",
3
3
  "private": true,
4
4
  "type": "module",
5
5
  "imports": {
@@ -0,0 +1,85 @@
1
+ # Routes
2
+
3
+ TanStack Start uses file-based routing. Every `.ts` and `.tsx` file in this directory is a route. Do not create `src/pages/`, `app/layout.tsx`, or similar, those are Next.js / Remix conventions. The only root layout is `src/routes/__root.tsx`.
4
+
5
+ ## Conventions
6
+
7
+ | File | URL |
8
+ | --- | --- |
9
+ | `index.tsx` | `/` |
10
+ | `about.tsx` | `/about` |
11
+ | `users/index.tsx` | `/users` |
12
+ | `users/$id.tsx` | `/users/:id` (dynamic — bare `$`, no curly braces) |
13
+ | `posts.$postId.tsx` | `/posts/:postId` (dots nest without folders) |
14
+ | `admin/route.tsx` | layout for every `/admin/*` page; must render `<Outlet />` |
15
+ | `admin_.login.tsx` | `/admin/login`, but outside the admin layout (trailing `_` escapes it) |
16
+ | `_auth.tsx` | pathless layout; wraps `_auth/*` files without adding a URL segment |
17
+ | `api/health.ts` | server route for defining API endpoints |
18
+ | `__root.tsx` | app shell, wraps every page; preserve `<Outlet />` |
19
+
20
+ ## Rules
21
+
22
+ - A layout route without `<Outlet />` renders its children nowhere.
23
+ - Do not `export default` route components, it breaks code splitting. Use a named function passed as `component:`.
24
+ - `routeTree.gen.ts` is auto-generated. Don't edit it by hand.
25
+
26
+ ## Authenticated routes
27
+
28
+ Put protected pages inside a pathless `_authed` layout and keep login/signup
29
+ at the top level, outside it. Never special-case the login pathname inside
30
+ the layout guard.
31
+
32
+ ```
33
+ routes/
34
+ __root.tsx loads the session into router context (context.user)
35
+ _authed.tsx pathless guard — no URL segment of its own
36
+ _authed/
37
+ dashboard.tsx /dashboard (protected)
38
+ posts.tsx /posts (protected)
39
+ index.tsx / (public)
40
+ login.tsx /login (public)
41
+ logout.tsx /logout
42
+ ```
43
+
44
+ `_authed.tsx` does the guarding, all children inherit it:
45
+
46
+ ```tsx _authed.tsx
47
+ export const Route = createFileRoute('/_authed')({
48
+ beforeLoad: ({ context }) => {
49
+ if (!context.user) {
50
+ throw redirect({ to: '/login' })
51
+ }
52
+ },
53
+ })
54
+ ```
55
+
56
+ The session is loaded once in `__root.tsx`'s `beforeLoad` and returned into
57
+ the router context so every route can read `context.user`. `getUser()` is
58
+ whatever your auth uses (e.g. verify a JWT from localStorage) and resolves to
59
+ the user or null:
60
+
61
+ ```tsx __root.tsx
62
+ // (keep the existing head() and component, add this)
63
+ export const Route = createRootRouteWithContext<{ queryClient: QueryClient }>()({
64
+ beforeLoad: async () => {
65
+ const user = await getUser()
66
+ return { user }
67
+ },
68
+ // ...head, component unchanged
69
+ })
70
+ ```
71
+
72
+ Components read it with `Route.useRouteContext()` (e.g. to swap a Login link
73
+ for a Logout button in the shell):
74
+
75
+ ```tsx
76
+ function Route() {
77
+ const { user } = Route.useRouteContext()
78
+
79
+ return (
80
+ <nav>
81
+ {user ? <Link to="/logout">Logout</Link> : <Link to="/login">Login</Link>}
82
+ </nav>
83
+ )
84
+ }
85
+ ```
@@ -29,12 +29,22 @@ export function projectUuidFromDir(dir: string): string {
29
29
  const scaffoldOptionsSchema = z.object({
30
30
  name: z
31
31
  .string()
32
- .describe("Short product name. Used as the page title and package name."),
32
+ .describe("Human-readable name for SEO, e.g. 'Shipping Platform'"),
33
33
  description: z
34
34
  .string()
35
35
  .describe("One sentence describing the app. Used as the meta description."),
36
36
  });
37
37
 
38
+ /** Kebab-case slug of the product name, for package.json. */
39
+ function slugify(name: string): string {
40
+ return (
41
+ name
42
+ .toLowerCase()
43
+ .replace(/[^a-z0-9]+/g, "-")
44
+ .replace(/^-+|-+$/g, "") || "leo-project"
45
+ );
46
+ }
47
+
38
48
  /**
39
49
  * Write the base scaffold by rendering the TanStack Start template into dir,
40
50
  * then adding .leo/project.json (template metadata the tool owns, not part of
@@ -48,6 +58,7 @@ export async function writeBaseScaffold(
48
58
  SCAFFOLD_VERSION,
49
59
  {
50
60
  LEO_NAME: name,
61
+ LEO_SLUG: slugify(name),
51
62
  LEO_DESCRIPTION: description,
52
63
  LEO_PROJECT_UUID: projectUuidFromDir(dir),
53
64
  },
@@ -87,7 +98,7 @@ export const scaffold = tool({
87
98
  "",
88
99
  "Notes:",
89
100
  "- This is a TanStack Start project. The server entry is src/server.ts. There is no index.html or client entrypoint.",
90
- "- Add routes as files in src/routes/. Do not default export route components.",
101
+ "- Add routes as files in src/routes/. Do not default export route components. Routing and layout conventions are documented in src/routes/README.md, read it before adding routes.",
91
102
  "- For JSON/CRUD APIs, add server routes under src/routes/api/. Read src/routes/api/health.ts first, add routes as needed.",
92
103
  "- shadcn/ui components are already in src/components/ui.",
93
104
  "- Design system: edit the tokens in src/styles.css (light and dark blocks).",