@aexol/spectral 0.9.43 → 0.9.45

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.
Files changed (41) hide show
  1. package/dist/extensions/seo/analyzers/content-optimize.d.ts +3 -0
  2. package/dist/extensions/seo/analyzers/content-optimize.d.ts.map +1 -0
  3. package/dist/extensions/seo/analyzers/content-optimize.js +190 -0
  4. package/dist/extensions/seo/analyzers/crawler.d.ts +4 -0
  5. package/dist/extensions/seo/analyzers/crawler.d.ts.map +1 -0
  6. package/dist/extensions/seo/analyzers/crawler.js +327 -0
  7. package/dist/extensions/seo/analyzers/keywords.d.ts +9 -0
  8. package/dist/extensions/seo/analyzers/keywords.d.ts.map +1 -0
  9. package/dist/extensions/seo/analyzers/keywords.js +183 -0
  10. package/dist/extensions/seo/analyzers/performance.d.ts +6 -0
  11. package/dist/extensions/seo/analyzers/performance.d.ts.map +1 -0
  12. package/dist/extensions/seo/analyzers/performance.js +257 -0
  13. package/dist/extensions/seo/analyzers/readability.d.ts +4 -0
  14. package/dist/extensions/seo/analyzers/readability.d.ts.map +1 -0
  15. package/dist/extensions/seo/analyzers/readability.js +121 -0
  16. package/dist/extensions/seo/index.d.ts.map +1 -1
  17. package/dist/extensions/seo/index.js +47 -9
  18. package/dist/extensions/seo/tools/batch.d.ts +4 -0
  19. package/dist/extensions/seo/tools/batch.d.ts.map +1 -0
  20. package/dist/extensions/seo/tools/batch.js +193 -0
  21. package/dist/extensions/seo/tools/crawl.d.ts +4 -0
  22. package/dist/extensions/seo/tools/crawl.d.ts.map +1 -0
  23. package/dist/extensions/seo/tools/crawl.js +166 -0
  24. package/dist/extensions/seo/tools/full-audit.d.ts +1 -0
  25. package/dist/extensions/seo/tools/full-audit.d.ts.map +1 -1
  26. package/dist/extensions/seo/tools/keywords.d.ts +4 -0
  27. package/dist/extensions/seo/tools/keywords.d.ts.map +1 -0
  28. package/dist/extensions/seo/tools/keywords.js +234 -0
  29. package/dist/extensions/seo/tools/performance.d.ts +4 -0
  30. package/dist/extensions/seo/tools/performance.d.ts.map +1 -0
  31. package/dist/extensions/seo/tools/performance.js +201 -0
  32. package/dist/extensions/seo/types.d.ts +156 -0
  33. package/dist/extensions/seo/types.d.ts.map +1 -1
  34. package/dist/extensions/seo/utils/url-safety.d.ts.map +1 -1
  35. package/dist/extensions/seo/utils/url-safety.js +7 -5
  36. package/dist/server/handlers/paths-pick-directory.d.ts +0 -27
  37. package/dist/server/handlers/paths-pick-directory.d.ts.map +1 -1
  38. package/dist/server/handlers/paths-pick-directory.js +86 -92
  39. package/dist/server/session-stream.d.ts.map +1 -1
  40. package/dist/server/session-stream.js +3 -78
  41. package/package.json +1 -1
@@ -1,30 +1,18 @@
1
- /**
2
- * Native OS folder picker dialog for project path selection.
3
- *
4
- * The "New Project" form in the browser has a "Browse..." button next to
5
- * the typed-path autocomplete. When clicked, it sends
6
- * `POST /api/paths/pick-directory` to this handler, which launches a
7
- * native OS folder picker dialog on the user's machine and returns the
8
- * absolute path of the selected directory (or `null` when cancelled).
9
- *
10
- * Platform strategy (tried in order):
11
- * 1. macOS — osascript (built-in, zero deps, native look)
12
- * 2. Linux — zenity (GTK), kdialog (KDE), then Python tkinter
13
- * 3. Windows — PowerShell (COM object)
14
- * 4. Fallback — Python tkinter (bundled with most Python installs)
15
- *
16
- * The first available tool wins; if none work, we return an error.
17
- * All child processes are synchronous — the browser is waiting anyway.
18
- */
19
1
  import { execSync } from "node:child_process";
20
2
  import { platform } from "node:os";
21
- // ---------------------------------------------------------------------------
22
- // Platform pickers
23
- // ---------------------------------------------------------------------------
24
- /**
25
- * macOS: Use `osascript` with AppleScript to show a native folder chooser.
26
- * Returns the POSIX path or empty string on cancel.
27
- */
3
+ function execErrMsg(err) {
4
+ if (err && typeof err === "object" && "message" in err) {
5
+ return String(err.message);
6
+ }
7
+ return String(err);
8
+ }
9
+ function execErrStatus(err) {
10
+ if (err && typeof err === "object" && "status" in err) {
11
+ const s = err.status;
12
+ return typeof s === "number" ? s : undefined;
13
+ }
14
+ return undefined;
15
+ }
28
16
  function pickMacOS() {
29
17
  const script = `
30
18
  set folderPath to POSIX path of (choose folder with prompt "Select project directory")
@@ -36,75 +24,95 @@ function pickMacOS() {
36
24
  timeout: 180_000, // 3 min — user might take time to browse
37
25
  });
38
26
  const trimmed = result.trim();
39
- return trimmed || null;
27
+ if (!trimmed) {
28
+ return { path: null };
29
+ }
30
+ return { path: trimmed };
40
31
  }
41
- catch {
42
- // osascript exits non-zero on user cancel (error -128) treat as silent cancel
43
- return null;
32
+ catch (err) {
33
+ if (execErrStatus(err) === 1) {
34
+ return { path: null }; // user cancelled
35
+ }
36
+ return {
37
+ path: null,
38
+ error: `Could not open folder picker (macOS): ${execErrMsg(err)}`,
39
+ };
44
40
  }
45
41
  }
46
- /**
47
- * Run a command that shows a folder picker dialog. Returns `null` on user
48
- * cancellation, and falls through (re-throws) if the tool is not installed.
49
- */
50
42
  function tryDialog(cmd) {
51
43
  try {
52
44
  const result = execSync(cmd, { encoding: "utf-8", timeout: 180_000 });
53
45
  return result.trim() || null;
54
46
  }
55
47
  catch (err) {
56
- // exit code 1 = user cancelled the dialog → stop here, return null
57
- // exit code 127 = command not found → re-throw so the next picker tries
58
- const status = err && typeof err === "object" && "status" in err
59
- ? err.status
60
- : undefined;
61
- if (status === 1)
62
- return null;
48
+ if (execErrStatus(err) === 1)
49
+ return null; // user cancelled
63
50
  throw err;
64
51
  }
65
52
  }
66
- /**
67
- * Linux: Try zenity (GTK), then kdialog (KDE), then Python tkinter.
68
- */
69
53
  function pickLinux() {
70
54
  try {
71
- return tryDialog(`zenity --file-selection --directory --title="Select project directory"`);
55
+ const p = tryDialog(`zenity --file-selection --directory --title="Select project directory"`);
56
+ return p ? { path: p } : { path: null };
72
57
  }
73
58
  catch {
74
- // zenity not available
75
59
  }
76
60
  try {
77
- return tryDialog(`kdialog --getexistingdirectory "${process.env.HOME ?? "/"}" --title "Select project directory"`);
61
+ const p = tryDialog(`kdialog --getexistingdirectory "${process.env.HOME ?? "/"}" --title "Select project directory"`);
62
+ return p ? { path: p } : { path: null };
78
63
  }
79
64
  catch {
80
- // kdialog not available
81
65
  }
82
- // Try Python tkinter
83
- return pickTkinter();
66
+ const tkResult = pickTkinter();
67
+ if (tkResult.path === null && !tkResult.error) {
68
+ return {
69
+ path: null,
70
+ error: "No folder picker available. Install zenity, kdialog, or python3-tk.",
71
+ };
72
+ }
73
+ return tkResult;
84
74
  }
85
- /**
86
- * Windows: Use PowerShell to invoke the COM Shell.Application folder picker.
87
- */
88
75
  function pickWindows() {
89
76
  const script = `
90
- Add-Type -AssemblyName System.Windows.Forms
91
- $f = New-Object System.Windows.Forms.FolderBrowserDialog
92
- $f.Description = "Select project directory"
93
- $f.ShowNewFolderButton = $true
94
- if ($f.ShowDialog() -eq "OK") { $f.SelectedPath } else { "" }
77
+ try {
78
+ Add-Type -AssemblyName System.Windows.Forms -ErrorAction Stop
79
+ $f = New-Object System.Windows.Forms.FolderBrowserDialog
80
+ $f.Description = "Select project directory"
81
+ $f.ShowNewFolderButton = $true
82
+ if ($f.ShowDialog() -eq "OK") { Write-Output $f.SelectedPath } else { Write-Output "__CANCELLED__" }
83
+ } catch {
84
+ Write-Error $_.Exception.Message
85
+ [Environment]::Exit(99)
86
+ }
95
87
  `.trim();
88
+ // -EncodedCommand (UTF-16LE base64) avoids cmd.exe quote-escaping bugs
89
+ // that would corrupt -Command strings containing nested double-quotes.
90
+ const encoded = Buffer.from(script, "utf-16le").toString("base64");
96
91
  try {
97
- const result = execSync(`powershell -NoProfile -Command "${script.replace(/"/g, '\\"')}"`, { encoding: "utf-8", timeout: 180_000 });
98
- return result.trim() || null;
92
+ const result = execSync(`powershell -STA -NoProfile -EncodedCommand ${encoded}`, { encoding: "utf-8", timeout: 180_000 });
93
+ const trimmed = result.trim();
94
+ if (!trimmed || trimmed === "__CANCELLED__") {
95
+ return { path: null };
96
+ }
97
+ return { path: trimmed };
99
98
  }
100
- catch {
101
- return null;
99
+ catch (err) {
100
+ const status = execErrStatus(err);
101
+ if (status === 99) {
102
+ const stderr = err && typeof err === "object" && "stderr" in err
103
+ ? String(err.stderr).trim()
104
+ : "";
105
+ return {
106
+ path: null,
107
+ error: stderr || "Folder picker could not be displayed.",
108
+ };
109
+ }
110
+ return {
111
+ path: null,
112
+ error: `Could not open folder picker (Windows): ${execErrMsg(err)}`,
113
+ };
102
114
  }
103
115
  }
104
- /**
105
- * Python tkinter fallback — works on any OS with Python + tkinter installed.
106
- * tkinter is bundled with macOS Python from python.org and most Linux distros.
107
- */
108
116
  function pickTkinter() {
109
117
  const script = `import tkinter as tk
110
118
  from tkinter import filedialog
@@ -120,61 +128,47 @@ print(d if d else "")
120
128
  encoding: "utf-8",
121
129
  timeout: 180_000,
122
130
  });
123
- return result.trim() || null;
131
+ const trimmed = result.trim();
132
+ return trimmed ? { path: trimmed } : { path: null };
124
133
  }
125
134
  catch {
126
- // python3 not found — try 'python' (Windows)
127
135
  try {
128
136
  const result = execSync(`python -c "${script}"`, {
129
137
  encoding: "utf-8",
130
138
  timeout: 180_000,
131
139
  });
132
- return result.trim() || null;
140
+ const trimmed = result.trim();
141
+ return trimmed ? { path: trimmed } : { path: null };
133
142
  }
134
143
  catch {
135
- return null;
144
+ return {
145
+ path: null,
146
+ error: "Python/tkinter not available for folder picker. Type the path manually.",
147
+ };
136
148
  }
137
149
  }
138
150
  }
139
- // ---------------------------------------------------------------------------
140
- // Public handler
141
- // ---------------------------------------------------------------------------
142
- /**
143
- * Attempt to open a native folder picker dialog and return the selected path.
144
- *
145
- * Synchronous and blocking by design — the browser form is waiting for a
146
- * response and the user is interacting with the dialog.
147
- */
148
151
  export function handlePickDirectory() {
149
152
  const os = platform();
150
153
  try {
151
- let path = null;
152
154
  if (os === "darwin") {
153
- path = pickMacOS();
155
+ return pickMacOS();
154
156
  }
155
157
  else if (os === "linux") {
156
- path = pickLinux();
158
+ return pickLinux();
157
159
  }
158
160
  else if (os === "win32") {
159
- path = pickWindows();
161
+ return pickWindows();
160
162
  }
161
163
  else {
162
- // Unknown platform — try Python tkinter as universal fallback
163
- path = pickTkinter();
164
- }
165
- if (path === null) {
166
- // User cancelled or no picker available. Distinguish:
167
- // When no path AND no error in the platform pickers, it's a cancel.
168
- return { path: null };
164
+ return pickTkinter();
169
165
  }
170
- return { path };
171
166
  }
172
167
  catch (err) {
173
- // The platform picker failed entirely (not cancelled — process error).
174
168
  const msg = err instanceof Error ? err.message : String(err);
175
169
  return {
176
170
  path: null,
177
- error: `Could not open folder picker: ${msg}`,
171
+ error: `Could not open folder picker (${os}): ${msg}`,
178
172
  };
179
173
  }
180
174
  }
@@ -1 +1 @@
1
- {"version":3,"file":"session-stream.d.ts","sourceRoot":"","sources":["../../src/server/session-stream.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session-stream.d.ts","sourceRoot":"","sources":["../../src/server/session-stream.ts"],"names":[],"mappings":"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"}
@@ -125,80 +125,6 @@ function selectHistoryForBridge(history, hasMemorySnapshot) {
125
125
  const selected = history.slice(Math.max(0, start + 1));
126
126
  return selected.length > 0 ? selected : [history[history.length - 1]];
127
127
  }
128
- function prunePersistedHistoryAfterCompaction(store, sessionId, bridge) {
129
- if (!bridge.getSessionBranch)
130
- return;
131
- try {
132
- const entries = bridge.getSessionBranch();
133
- // Pi appends a CompactionEntry (type "compaction") at the end whose
134
- // firstKeptEntryId marks the first branch entry that survives in LLM context.
135
- // We scan backwards so we find the most recent compaction (there should be
136
- // exactly one per compaction pass).
137
- const lastCompaction = [...entries].reverse().find((entry) => entry.type === "compaction");
138
- const firstKeptEntryId = lastCompaction?.firstKeptEntryId;
139
- if (!firstKeptEntryId) {
140
- console.log(`[spectral] prune: skipped – no compaction entry (or no firstKeptEntryId) ` +
141
- `in branch for ${sessionId} (${entries.length} entries)`);
142
- return;
143
- }
144
- const keptEntryIndex = entries.findIndex((entry) => entry.id === firstKeptEntryId);
145
- if (keptEntryIndex === -1) {
146
- console.log(`[spectral] prune: skipped – firstKeptEntryId "${firstKeptEntryId}" ` +
147
- `not found in ${entries.length} branch entries for ${sessionId}`);
148
- return;
149
- }
150
- // Count user + assistant messages in the kept portion of the branch.
151
- // Tool-result entries are separate branch entries but are NOT separate
152
- // SQLite rows – they live inside the assistant message's events_jsonl.
153
- // Counting them would inflate the count and make the prune a silent no-op.
154
- let keptPersistedMessageCount = 0;
155
- for (let i = keptEntryIndex; i < entries.length; i++) {
156
- const entry = entries[i];
157
- if (entry.type !== "message")
158
- continue;
159
- const role = entry.message && typeof entry.message === "object" && "role" in entry.message
160
- ? entry.message.role
161
- : undefined;
162
- if (role === "user" || role === "assistant" || role === "system") {
163
- keptPersistedMessageCount += 1;
164
- }
165
- }
166
- const existing = store.getMessages(sessionId);
167
- if (existing.length === 0)
168
- return;
169
- console.log(`[spectral] prune: ${sessionId} – branch has ${entries.length} entries, ` +
170
- `kept portion starts at index ${keptEntryIndex} (id=${firstKeptEntryId}), ` +
171
- `${keptPersistedMessageCount} user+assistant entries kept, ` +
172
- `${existing.length} SQLite messages`);
173
- if (keptPersistedMessageCount <= 0) {
174
- console.log(`[spectral] prune: skipped – no kept messages for ${sessionId}`);
175
- return;
176
- }
177
- if (keptPersistedMessageCount >= existing.length) {
178
- console.log(`[spectral] prune: skipped – kept message count (${keptPersistedMessageCount}) ` +
179
- `>= SQLite message count (${existing.length}) for ${sessionId}`);
180
- return;
181
- }
182
- const keptMessages = existing.slice(-keptPersistedMessageCount);
183
- const pruned = existing.length - keptMessages.length;
184
- store.deleteMessagesBySession(sessionId);
185
- for (const msg of keptMessages) {
186
- store.appendMessage(sessionId, {
187
- id: msg.id,
188
- role: msg.role,
189
- content: msg.content,
190
- eventsJsonl: msg.events,
191
- createdAt: msg.createdAt,
192
- images: msg.images,
193
- });
194
- }
195
- console.log(`[spectral] prune: finished – dropped ${pruned} pre-compaction message(s) ` +
196
- `for ${sessionId}, ${keptMessages.length} kept`);
197
- }
198
- catch (err) {
199
- console.warn(`[spectral] warn: failed to prune persisted history after compaction for ${sessionId}: ${err instanceof Error ? err.message : String(err)}`);
200
- }
201
- }
202
128
  function defaultMemoryStatus(cwd) {
203
129
  const config = loadConfig(cwd);
204
130
  return {
@@ -484,7 +410,6 @@ export class SessionStreamManager {
484
410
  // Prune persisted history synchronously after compaction, so even if the
485
411
  // compaction_end event handler races or misses, SQLite reflects the
486
412
  // compacted state before the next user prompt or reconnect.
487
- prunePersistedHistoryAfterCompaction(this.store, sessionId, stream.bridge);
488
413
  }
489
414
  finally {
490
415
  if (stream.compacting)
@@ -1270,9 +1195,9 @@ export class SessionStreamManager {
1270
1195
  }
1271
1196
  if (event.type === "compaction_end") {
1272
1197
  stream.compacting = false;
1273
- if (!event.aborted) {
1274
- prunePersistedHistoryAfterCompaction(this.store, stream.sessionId, stream.bridge);
1275
- }
1198
+ // Keep the full persisted history in SQLite after compaction.
1199
+ // The LLM context is already compacted via the bridge/session manager;
1200
+ // landing can still load older messages through the REST endpoint.
1276
1201
  persistObservationalMemorySnapshot(this.store, stream.sessionId, stream.bridge);
1277
1202
  // Drain the prompt queue after compaction finishes. During
1278
1203
  // compaction, auto-dequeue in agent_end is skipped to prevent the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aexol/spectral",
3
- "version": "0.9.43",
3
+ "version": "0.9.45",
4
4
  "description": "AI coding agent for Aexol with relay-based browser access.",
5
5
  "type": "module",
6
6
  "private": false,