lwazi 1.3.0 → 1.3.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/bin/lwazi.js CHANGED
@@ -10,6 +10,10 @@ switch (command) {
10
10
  require("./install");
11
11
  break;
12
12
 
13
+ case "update":
14
+ require("./update");
15
+ break;
16
+
13
17
  case "uninstall":
14
18
  require("./uninstall");
15
19
  break;
@@ -22,6 +26,7 @@ Commands:
22
26
 
23
27
  lwazi install Install Lwazi into this Laravel project
24
28
  lwazi install --url <url> Install and crawl website for navigation
29
+ lwazi update Update Lwazi to latest version
25
30
  lwazi uninstall Remove Lwazi from this project
26
31
  `);
27
32
  }
package/bin/update.js ADDED
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+ const { execSync } = require("child_process");
6
+
7
+ const projectRoot = process.cwd();
8
+ const targetDir = path.join(projectRoot, "lwazi");
9
+ const packageDir = path.resolve(__dirname, "..");
10
+
11
+ console.log("Updating Lwazi...");
12
+
13
+ if (!fs.existsSync(path.join(projectRoot, "artisan"))) {
14
+ console.error("This is not a Laravel project.");
15
+ process.exit(1);
16
+ }
17
+
18
+ if (!fs.existsSync(targetDir)) {
19
+ console.log("Lwazi not installed. Running install instead...");
20
+ require("./install");
21
+ return;
22
+ }
23
+
24
+ console.log("Updating Lwazi files...");
25
+
26
+ const ignore = new Set([
27
+ "node_modules",
28
+ ".git",
29
+ "package-lock.json"
30
+ ]);
31
+
32
+ function copyDirectory(src, dest) {
33
+ fs.mkdirSync(dest, { recursive: true });
34
+
35
+ for (const item of fs.readdirSync(src)) {
36
+ if (ignore.has(item)) continue;
37
+
38
+ const s = path.join(src, item);
39
+ const d = path.join(dest, item);
40
+
41
+ if (fs.statSync(s).isDirectory()) {
42
+ copyDirectory(s, d);
43
+ } else {
44
+ fs.copyFileSync(s, d);
45
+ }
46
+ }
47
+ }
48
+
49
+ copyDirectory(packageDir, targetDir);
50
+
51
+ console.log("Running composer update...");
52
+ try {
53
+ execSync("composer update lwazi/core --no-interaction", {
54
+ stdio: "inherit",
55
+ cwd: projectRoot,
56
+ shell: true,
57
+ });
58
+ } catch (e) {
59
+ console.log("Composer update skipped or failed.");
60
+ }
61
+
62
+ console.log("Clearing caches...");
63
+ try {
64
+ execSync("php artisan config:clear && php artisan cache:clear", {
65
+ stdio: "ignore",
66
+ cwd: projectRoot,
67
+ shell: true,
68
+ });
69
+ } catch (e) {
70
+ // Ignore cache errors
71
+ }
72
+
73
+ console.log("\nLwazi updated successfully!");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lwazi",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
4
4
  "description": "Lwazi is an AI assistant for Laravel. Install with one command to add an AI assistant to your Laravel app.",
5
5
  "main": "bin/lwazi.js",
6
6
  "bin": {
@@ -25,6 +25,8 @@ class AnalyzeProjectCommand extends Command
25
25
 
26
26
  $crawler = new NavigationCrawler($rootUrl);
27
27
  $manifest = $crawler->crawl();
28
+
29
+ $manifest = $this->mergeWithRoutes($manifest, $rootUrl);
28
30
 
29
31
  $storagePath = storage_path('lwazi');
30
32
  if (!is_dir($storagePath)) {
@@ -50,4 +52,80 @@ class AnalyzeProjectCommand extends Command
50
52
 
51
53
  return 0;
52
54
  }
55
+
56
+ protected function mergeWithRoutes(array $manifest, string $rootUrl): array
57
+ {
58
+ $knowledgePath = storage_path('lwazi/project_knowledge.json');
59
+ if (!file_exists($knowledgePath)) {
60
+ return $manifest;
61
+ }
62
+
63
+ $knowledge = json_decode(file_get_contents($knowledgePath), true);
64
+ $routes = $knowledge['routes'] ?? [];
65
+
66
+ if (empty($routes)) {
67
+ return $manifest;
68
+ }
69
+
70
+ $root = $this->extractRoot($rootUrl);
71
+
72
+ foreach ($routes as $route) {
73
+ $path = '/' . ltrim($route['uri'], '/');
74
+
75
+ if (in_array($path, ['/storage/{path}', '/sanctum/csrf-cookie'])) {
76
+ continue;
77
+ }
78
+
79
+ if (isset($manifest['flat'][$root . $path])) {
80
+ continue;
81
+ }
82
+
83
+ $label = $this->humanizeRoutePath($path);
84
+
85
+ $manifest['nodes'][$root . $path] = [
86
+ 'url' => $root . $path,
87
+ 'title' => $label,
88
+ 'headings' => [$label],
89
+ ];
90
+
91
+ $manifest['flat'][$root . $path] = [
92
+ 'label' => $label,
93
+ 'segments' => array_filter(explode('/', trim($path, '/'))),
94
+ '_path' => $root . $path,
95
+ '_weight' => 2,
96
+ ];
97
+
98
+ $manifest['adjacency'][$root . $path] = [];
99
+
100
+ if (!isset($manifest['adjacency'][$root . '/'])) {
101
+ $manifest['adjacency'][$root . '/'] = [];
102
+ }
103
+ $manifest['adjacency'][$root . '/'][] = $root . $path;
104
+ }
105
+
106
+ return $manifest;
107
+ }
108
+
109
+ protected function extractRoot(string $url): string
110
+ {
111
+ $parts = parse_url($url);
112
+ $scheme = $parts['scheme'] ?? 'http';
113
+ $host = $parts['host'] ?? 'localhost';
114
+ $port = $parts['port'] ?? ($scheme === 'https' ? 443 : 80);
115
+
116
+ $root = $scheme . '://' . $host;
117
+ if (($scheme === 'http' && $port !== 80) || ($scheme === 'https' && $port !== 443)) {
118
+ $root .= ':' . $port;
119
+ }
120
+
121
+ return $root;
122
+ }
123
+
124
+ protected function humanizeRoutePath(string $path): string
125
+ {
126
+ $path = preg_replace('/\{[^}]+\}/', '', $path);
127
+ $path = str_replace(['/', '-', '_'], ' ', $path);
128
+ $path = preg_replace('/\s+/', ' ', $path);
129
+ return ucwords(trim($path));
130
+ }
53
131
  }
@@ -111,6 +111,9 @@ class SetupCommand extends Command
111
111
  try {
112
112
  $crawler = new NavigationCrawler($url, true);
113
113
  $manifest = $crawler->crawl();
114
+
115
+ $manifest = $this->mergeWithRoutes($manifest, $url);
116
+
114
117
  $crawler->saveManifest();
115
118
 
116
119
  $this->info("Crawled " . count($manifest['nodes']) . " pages.");
@@ -129,4 +132,80 @@ class SetupCommand extends Command
129
132
  $this->warn('Website crawling failed: ' . $e->getMessage());
130
133
  }
131
134
  }
135
+
136
+ protected function mergeWithRoutes(array $manifest, string $rootUrl): array
137
+ {
138
+ $knowledgePath = storage_path('lwazi/project_knowledge.json');
139
+ if (!file_exists($knowledgePath)) {
140
+ return $manifest;
141
+ }
142
+
143
+ $knowledge = json_decode(file_get_contents($knowledgePath), true);
144
+ $routes = $knowledge['routes'] ?? [];
145
+
146
+ if (empty($routes)) {
147
+ return $manifest;
148
+ }
149
+
150
+ $root = $this->extractRoot($rootUrl);
151
+
152
+ foreach ($routes as $route) {
153
+ $path = '/' . ltrim($route['uri'], '/');
154
+
155
+ if (in_array($path, ['/storage/{path}', '/sanctum/csrf-cookie'])) {
156
+ continue;
157
+ }
158
+
159
+ if (isset($manifest['flat'][$root . $path])) {
160
+ continue;
161
+ }
162
+
163
+ $label = $this->humanizeRoutePath($path);
164
+
165
+ $manifest['nodes'][$root . $path] = [
166
+ 'url' => $root . $path,
167
+ 'title' => $label,
168
+ 'headings' => [$label],
169
+ ];
170
+
171
+ $manifest['flat'][$root . $path] = [
172
+ 'label' => $label,
173
+ 'segments' => array_filter(explode('/', trim($path, '/'))),
174
+ '_path' => $root . $path,
175
+ '_weight' => 2,
176
+ ];
177
+
178
+ $manifest['adjacency'][$root . $path] = [];
179
+
180
+ if (!isset($manifest['adjacency'][$root . '/'])) {
181
+ $manifest['adjacency'][$root . '/'] = [];
182
+ }
183
+ $manifest['adjacency'][$root . '/'][] = $root . $path;
184
+ }
185
+
186
+ return $manifest;
187
+ }
188
+
189
+ protected function extractRoot(string $url): string
190
+ {
191
+ $parts = parse_url($url);
192
+ $scheme = $parts['scheme'] ?? 'http';
193
+ $host = $parts['host'] ?? 'localhost';
194
+ $port = $parts['port'] ?? ($scheme === 'https' ? 443 : 80);
195
+
196
+ $root = $scheme . '://' . $host;
197
+ if (($scheme === 'http' && $port !== 80) || ($scheme === 'https' && $port !== 443)) {
198
+ $root .= ':' . $port;
199
+ }
200
+
201
+ return $root;
202
+ }
203
+
204
+ protected function humanizeRoutePath(string $path): string
205
+ {
206
+ $path = preg_replace('/\{[^}]+\}/', '', $path);
207
+ $path = str_replace(['/', '-', '_'], ' ', $path);
208
+ $path = preg_replace('/\s+/', ' ', $path);
209
+ return ucwords(trim($path));
210
+ }
132
211
  }