@elyracode/laravel-starters 0.3.4

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/README.md ADDED
@@ -0,0 +1,77 @@
1
+ # @elyracode/laravel-starters
2
+
3
+ Fetch, analyze, and customize Laravel starter kits directly from GitHub inside Elyra.
4
+
5
+ Based on the official [Laravel 13.x Starter Kits](https://laravel.com/docs/13.x/starter-kits).
6
+
7
+ ## Install
8
+
9
+ ```
10
+ elyra install npm:@elyracode/laravel-starters
11
+ ```
12
+
13
+ ## What's included
14
+
15
+ ### Commands
16
+
17
+ - `/laravel:starter` -- Interactive selector to browse and load official Laravel starter kits as reference context
18
+
19
+ ### Tools (auto-used by the agent)
20
+
21
+ | Tool | Description |
22
+ |------|-------------|
23
+ | `fetch_laravel_starter` | Fetch an official Laravel starter kit as reference |
24
+ | `fetch_github_repo` | Fetch any public GitHub repository as reference context |
25
+
26
+ ### Available Starter Kits
27
+
28
+ | ID | Name | Stack | UI Library |
29
+ |----|------|-------|------------|
30
+ | `react` | Laravel React Starter Kit | Inertia 2 + React 19 + TypeScript | shadcn/ui |
31
+ | `vue` | Laravel Vue Starter Kit | Inertia 2 + Vue 3 Composition API + TypeScript | shadcn-vue |
32
+ | `svelte` | Laravel Svelte Starter Kit | Inertia 2 + Svelte 5 + TypeScript | shadcn-svelte |
33
+ | `livewire` | Laravel Livewire Starter Kit | Livewire 4 + PHP | Flux UI |
34
+
35
+ All kits include:
36
+ - **Fortify authentication** (login, registration, password reset, email verification)
37
+ - **Two-factor authentication** (TOTP)
38
+ - **Teams support** (optional)
39
+ - **WorkOS AuthKit** variant (social auth, passkeys, magic auth, SSO)
40
+ - **Layout variants**: sidebar, header
41
+ - **Auth layout variants**: simple, card, split
42
+ - **Tailwind CSS 4**
43
+
44
+ ## Usage
45
+
46
+ ### Interactive
47
+ ```
48
+ /laravel:starter
49
+ ```
50
+ Opens a selector to pick a starter kit. The kit's source code is loaded as context so the agent can reference official patterns.
51
+
52
+ ### Automatic
53
+ Just ask the agent:
54
+ ```
55
+ > Set up auth following the Laravel Vue starter kit
56
+ > I need teams support with the Livewire starter kit
57
+ > Build a dashboard using the React starter kit patterns
58
+ > Show me how the Svelte starter kit handles 2FA
59
+ ```
60
+
61
+ The agent automatically uses `fetch_laravel_starter` to load the relevant kit as reference.
62
+
63
+ ### Custom repos
64
+ The agent can also fetch any public GitHub repo:
65
+ ```
66
+ > Fetch the livewire/flux repo and show me the modal component
67
+ > Look at how laravel/fortify handles two-factor authentication
68
+ ```
69
+
70
+ ## How it works
71
+
72
+ 1. Fetches the repository tree via GitHub API (no cloning needed)
73
+ 2. Downloads relevant source files (PHP, Vue, Svelte, TS, JS, Blade, etc.)
74
+ 3. Feeds the code as context to the agent
75
+ 4. The agent uses the official code as reference for your customizations
76
+
77
+ No GitHub token required for public repos.
@@ -0,0 +1,172 @@
1
+ import type { ExtensionAPI } from "@elyracode/coding-agent";
2
+ import { Type } from "typebox";
3
+ import { fetchRepoContents, formatAsContext, STARTER_KITS } from "./starters.js";
4
+
5
+ export default function (elyra: ExtensionAPI): void {
6
+ // -- Command: /laravel:starter --
7
+ // Lists available starter kits and lets the user pick one
8
+ elyra.registerCommand("laravel:starter", {
9
+ description: "Browse and fetch Laravel starter kits as reference context",
10
+ handler: async (_args, ctx) => {
11
+ const options = STARTER_KITS.map((kit) => `${kit.name} [${kit.stack}]`);
12
+
13
+ const selected = await ctx.ui.select("Select a Laravel starter kit", options);
14
+ if (!selected) return;
15
+
16
+ const kit = STARTER_KITS.find((k) => `${k.name} [${k.stack}]` === selected);
17
+ if (!kit) return;
18
+
19
+ ctx.ui.notify(`Fetching ${kit.name} from GitHub...`, "info");
20
+
21
+ try {
22
+ const files = await fetchRepoContents(kit.repo, kit.branch);
23
+ const context = formatAsContext(kit, files);
24
+
25
+ elyra.sendUserMessage([
26
+ {
27
+ type: "text",
28
+ text:
29
+ `I've loaded the ${kit.name} starter kit as reference (${files.length} files from github.com/${kit.repo}).\n\n` +
30
+ `Stack: ${kit.stack}\nFeatures: ${kit.features.join(", ")}\n\n` +
31
+ `Use this as reference to help me scaffold or customize my project. ` +
32
+ `Ask me what I want to build or modify.`,
33
+ },
34
+ { type: "text", text: context },
35
+ ]);
36
+
37
+ ctx.ui.notify(`Loaded ${files.length} files from ${kit.name}`, "info");
38
+ } catch (error) {
39
+ const msg = error instanceof Error ? error.message : String(error);
40
+ ctx.ui.notify(`Failed to fetch starter kit: ${msg}`, "error");
41
+ }
42
+ },
43
+ });
44
+
45
+ // -- Tool: fetch_laravel_starter --
46
+ // The agent can call this tool to fetch a starter kit as reference
47
+ elyra.registerTool({
48
+ name: "fetch_laravel_starter",
49
+ label: "Fetch Laravel Starter Kit",
50
+ description:
51
+ "Fetch an official Laravel starter kit from GitHub as reference code. " +
52
+ "Use this when the user asks about Laravel auth, teams, 2FA, profile management, " +
53
+ "or wants to scaffold a Laravel project following official patterns. " +
54
+ "Available kits: react (React 19 + shadcn/ui), vue (Vue 3 + shadcn-vue), " +
55
+ "svelte (Svelte 5 + shadcn-svelte), livewire (Livewire 4 + Flux UI). " +
56
+ "All kits include Fortify auth, 2FA, teams, and WorkOS support.",
57
+ parameters: Type.Object({
58
+ kit_id: Type.String({
59
+ description: "The starter kit ID: react, vue, svelte, livewire",
60
+ }),
61
+ }),
62
+ execute: async (_toolCallId, params) => {
63
+ const kit = STARTER_KITS.find((k) => k.id === params.kit_id);
64
+ if (!kit) {
65
+ const available = STARTER_KITS.map((k) => k.id).join(", ");
66
+ return {
67
+ content: [{ type: "text", text: `Unknown kit ID: ${params.kit_id}. Available: ${available}` }],
68
+ details: {},
69
+ };
70
+ }
71
+
72
+ try {
73
+ const files = await fetchRepoContents(kit.repo, kit.branch);
74
+ const context = formatAsContext(kit, files);
75
+
76
+ const truncated =
77
+ context.length > 100000
78
+ ? `${context.slice(0, 100000)}\n\n... (truncated, ${files.length} files total)`
79
+ : context;
80
+
81
+ return {
82
+ content: [{ type: "text", text: truncated }],
83
+ details: {
84
+ kit: kit.name,
85
+ stack: kit.stack,
86
+ fileCount: files.length,
87
+ features: kit.features,
88
+ },
89
+ };
90
+ } catch (error) {
91
+ const msg = error instanceof Error ? error.message : String(error);
92
+ return {
93
+ content: [{ type: "text", text: `Failed to fetch ${kit.name}: ${msg}` }],
94
+ details: {},
95
+ };
96
+ }
97
+ },
98
+ });
99
+
100
+ // -- Tool: fetch_github_repo --
101
+ // Generic tool to fetch any GitHub repo as reference
102
+ elyra.registerTool({
103
+ name: "fetch_github_repo",
104
+ label: "Fetch GitHub Repository",
105
+ description:
106
+ "Fetch source code from any public GitHub repository as reference context. " +
107
+ "Useful for studying implementation patterns, understanding libraries, " +
108
+ "or using official repos as scaffolding reference. " +
109
+ "Returns file contents filtered to code files (PHP, JS, TS, Vue, etc.).",
110
+ parameters: Type.Object({
111
+ repo: Type.String({
112
+ description: "GitHub owner/repo (e.g., 'laravel/framework', 'livewire/livewire')",
113
+ }),
114
+ branch: Type.Optional(Type.String({ description: "Branch name (default: main)" })),
115
+ path_filter: Type.Optional(
116
+ Type.String({
117
+ description: "Only include files under this path prefix (e.g., 'src/', 'stubs/')",
118
+ }),
119
+ ),
120
+ max_files: Type.Optional(Type.Number({ description: "Maximum number of files to fetch (default: 100)" })),
121
+ }),
122
+ execute: async (_toolCallId, params) => {
123
+ try {
124
+ const branch = params.branch || "main";
125
+ const maxFiles = params.max_files || 100;
126
+
127
+ let files = await fetchRepoContents(params.repo, branch, { maxFiles });
128
+
129
+ if (params.path_filter) {
130
+ files = files.filter((f) => f.path.startsWith(params.path_filter!));
131
+ }
132
+
133
+ if (files.length === 0) {
134
+ return {
135
+ content: [{ type: "text", text: `No matching files found in ${params.repo}@${branch}` }],
136
+ details: {},
137
+ };
138
+ }
139
+
140
+ const lines: string[] = [
141
+ `# Repository: github.com/${params.repo} (${branch})`,
142
+ `Files: ${files.length}`,
143
+ "",
144
+ ];
145
+ for (const file of files) {
146
+ lines.push(`## ${file.path}`);
147
+ lines.push("```");
148
+ lines.push(file.content);
149
+ lines.push("```");
150
+ lines.push("");
151
+ }
152
+
153
+ const context = lines.join("\n");
154
+ const truncated =
155
+ context.length > 100000
156
+ ? `${context.slice(0, 100000)}\n\n... (truncated, ${files.length} files total)`
157
+ : context;
158
+
159
+ return {
160
+ content: [{ type: "text", text: truncated }],
161
+ details: { repo: params.repo, branch, fileCount: files.length },
162
+ };
163
+ } catch (error) {
164
+ const msg = error instanceof Error ? error.message : String(error);
165
+ return {
166
+ content: [{ type: "text", text: `Failed to fetch ${params.repo}: ${msg}` }],
167
+ details: {},
168
+ };
169
+ }
170
+ },
171
+ });
172
+ }
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Laravel starter kit registry and GitHub repo fetching.
3
+ */
4
+
5
+ export interface StarterKit {
6
+ id: string;
7
+ name: string;
8
+ description: string;
9
+ repo: string; // GitHub owner/repo
10
+ branch: string;
11
+ stack: string;
12
+ ui: string;
13
+ features: string[];
14
+ layouts: string[];
15
+ authLayouts: string[];
16
+ }
17
+
18
+ /**
19
+ * Official Laravel starter kits (Laravel 13.x).
20
+ * See: https://laravel.com/docs/13.x/starter-kits
21
+ */
22
+ export const STARTER_KITS: StarterKit[] = [
23
+ {
24
+ id: "react",
25
+ name: "Laravel React Starter Kit",
26
+ description: "Inertia 2, React 19, TypeScript, Tailwind 4, shadcn/ui. Full auth with Fortify, 2FA, teams, WorkOS option.",
27
+ repo: "laravel/react-starter-kit",
28
+ branch: "main",
29
+ stack: "react",
30
+ ui: "shadcn/ui",
31
+ features: ["auth", "registration", "password-reset", "email-verification", "2fa", "teams", "workos"],
32
+ layouts: ["sidebar", "header"],
33
+ authLayouts: ["simple", "card", "split"],
34
+ },
35
+ {
36
+ id: "vue",
37
+ name: "Laravel Vue Starter Kit",
38
+ description: "Inertia 2, Vue 3 Composition API, TypeScript, Tailwind, shadcn-vue. Full auth with Fortify, 2FA, teams, WorkOS option.",
39
+ repo: "laravel/vue-starter-kit",
40
+ branch: "main",
41
+ stack: "VILT",
42
+ ui: "shadcn-vue",
43
+ features: ["auth", "registration", "password-reset", "email-verification", "2fa", "teams", "workos"],
44
+ layouts: ["sidebar", "header"],
45
+ authLayouts: ["simple", "card", "split"],
46
+ },
47
+ {
48
+ id: "svelte",
49
+ name: "Laravel Svelte Starter Kit",
50
+ description: "Inertia 2, Svelte 5, TypeScript, Tailwind, shadcn-svelte. Full auth with Fortify, 2FA, teams, WorkOS option.",
51
+ repo: "laravel/svelte-starter-kit",
52
+ branch: "main",
53
+ stack: "svelte",
54
+ ui: "shadcn-svelte",
55
+ features: ["auth", "registration", "password-reset", "email-verification", "2fa", "teams", "workos"],
56
+ layouts: ["sidebar", "header"],
57
+ authLayouts: ["simple", "card", "split"],
58
+ },
59
+ {
60
+ id: "livewire",
61
+ name: "Laravel Livewire Starter Kit",
62
+ description: "Livewire 4, Tailwind, Flux UI. Full auth with Fortify, 2FA, teams, WorkOS option.",
63
+ repo: "laravel/livewire-starter-kit",
64
+ branch: "main",
65
+ stack: "TALL",
66
+ ui: "Flux UI",
67
+ features: ["auth", "registration", "password-reset", "email-verification", "2fa", "teams", "workos"],
68
+ layouts: ["sidebar", "header"],
69
+ authLayouts: ["simple", "card", "split"],
70
+ },
71
+ ];
72
+
73
+ export interface FetchedFile {
74
+ path: string;
75
+ content: string;
76
+ }
77
+
78
+ /**
79
+ * Fetch the contents of a GitHub repo directory tree via the GitHub API.
80
+ * Returns a flat list of file paths and their contents.
81
+ * Uses the GitHub trees API for efficiency (single request for the tree,
82
+ * then fetches individual files via raw.githubusercontent.com).
83
+ */
84
+ export async function fetchRepoContents(
85
+ repo: string,
86
+ branch: string,
87
+ options?: { maxFiles?: number; extensions?: string[] },
88
+ ): Promise<FetchedFile[]> {
89
+ const maxFiles = options?.maxFiles ?? 200;
90
+ const extensions = options?.extensions ?? [
91
+ ".php",
92
+ ".blade.php",
93
+ ".vue",
94
+ ".ts",
95
+ ".tsx",
96
+ ".js",
97
+ ".jsx",
98
+ ".json",
99
+ ".md",
100
+ ".css",
101
+ ".env.example",
102
+ ];
103
+
104
+ // Fetch the repo tree
105
+ const treeUrl = `https://api.github.com/repos/${repo}/git/trees/${branch}?recursive=1`;
106
+ const treeResponse = await fetch(treeUrl, {
107
+ headers: {
108
+ Accept: "application/vnd.github.v3+json",
109
+ "User-Agent": "elyra-laravel-starters",
110
+ },
111
+ });
112
+
113
+ if (!treeResponse.ok) {
114
+ throw new Error(`GitHub API error: ${treeResponse.status} ${treeResponse.statusText}`);
115
+ }
116
+
117
+ const tree = (await treeResponse.json()) as {
118
+ tree: Array<{ path: string; type: string; size?: number }>;
119
+ };
120
+
121
+ // Filter to relevant files
122
+ const relevantFiles = tree.tree
123
+ .filter((entry) => {
124
+ if (entry.type !== "blob") return false;
125
+ if (entry.size && entry.size > 100000) return false; // Skip files > 100KB
126
+ return extensions.some((ext) => entry.path.endsWith(ext));
127
+ })
128
+ .slice(0, maxFiles);
129
+
130
+ // Fetch file contents in parallel (batched to avoid rate limits)
131
+ const batchSize = 10;
132
+ const files: FetchedFile[] = [];
133
+
134
+ for (let i = 0; i < relevantFiles.length; i += batchSize) {
135
+ const batch = relevantFiles.slice(i, i + batchSize);
136
+ const results = await Promise.all(
137
+ batch.map(async (entry) => {
138
+ const rawUrl = `https://raw.githubusercontent.com/${repo}/${branch}/${entry.path}`;
139
+ const response = await fetch(rawUrl, {
140
+ headers: { "User-Agent": "elyra-laravel-starters" },
141
+ });
142
+ if (!response.ok) return null;
143
+ const content = await response.text();
144
+ return { path: entry.path, content };
145
+ }),
146
+ );
147
+ for (const result of results) {
148
+ if (result) files.push(result);
149
+ }
150
+ }
151
+
152
+ return files;
153
+ }
154
+
155
+ /**
156
+ * Format fetched files as a context string for the agent's system prompt.
157
+ */
158
+ export function formatAsContext(kit: StarterKit, files: FetchedFile[]): string {
159
+ const lines: string[] = [
160
+ `# Reference: ${kit.name}`,
161
+ `Repository: github.com/${kit.repo} (${kit.branch})`,
162
+ `Stack: ${kit.stack} | UI: ${kit.ui}`,
163
+ `Features: ${kit.features.join(", ")}`,
164
+ `Layouts: ${kit.layouts.join(", ")} | Auth layouts: ${kit.authLayouts.join(", ")}`,
165
+ "",
166
+ "The following files are from the official Laravel starter kit. Use them as reference",
167
+ "for architecture, patterns, and best practices. Adapt the code to the user's needs.",
168
+ "",
169
+ ];
170
+
171
+ for (const file of files) {
172
+ lines.push(`## ${file.path}`);
173
+ lines.push("```");
174
+ lines.push(file.content);
175
+ lines.push("```");
176
+ lines.push("");
177
+ }
178
+
179
+ return lines.join("\n");
180
+ }
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@elyracode/laravel-starters",
3
+ "version": "0.3.4",
4
+ "description": "Elyra extension for fetching, analyzing, and customizing Laravel starter kits from GitHub",
5
+ "type": "module",
6
+ "keywords": ["elyra-package", "laravel", "starter-kit", "breeze", "jetstream", "scaffolding"],
7
+ "license": "MIT",
8
+ "author": "Knut W. Horne",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/kwhorne/elyra.git",
12
+ "directory": "packages/laravel-starters"
13
+ },
14
+ "elyra": {
15
+ "extensions": ["./extensions/index.ts"]
16
+ },
17
+ "peerDependencies": {
18
+ "@elyracode/coding-agent": "*",
19
+ "typebox": "*"
20
+ },
21
+ "scripts": {
22
+ "clean": "echo 'nothing to clean'",
23
+ "build": "echo 'nothing to build'",
24
+ "check": "echo 'nothing to check'"
25
+ }
26
+ }