@aexol/spectral 0.6.4 → 0.6.9

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/cli.js CHANGED
@@ -138,7 +138,14 @@ function delegateToPi(args) {
138
138
  const piBin = resolvePiBin();
139
139
  const child = spawn(process.execPath, [piBin, ...args], {
140
140
  stdio: "inherit",
141
- env: process.env,
141
+ env: {
142
+ ...process.env,
143
+ // Prevent git from opening an interactive editor when the agent
144
+ // runs commands like `git rebase --continue`, `git commit` (without -m),
145
+ // or `git merge`. Without this, the editor hangs forever waiting for
146
+ // TTY input that doesn't exist in the agent's non-interactive shell.
147
+ GIT_EDITOR: "true",
148
+ },
142
149
  });
143
150
  // Forward common termination signals to pi so its TUI can clean up.
144
151
  const signals = ["SIGINT", "SIGTERM", "SIGHUP", "SIGQUIT"];
@@ -265,6 +265,7 @@ export async function runServe(opts = {}) {
265
265
  store,
266
266
  manager,
267
267
  relay,
268
+ subscribers,
268
269
  cwd,
269
270
  });
270
271
  return;
@@ -45,8 +45,23 @@ function renderContentToString(content) {
45
45
  }
46
46
  if (typeof first.text === "string")
47
47
  return first.text;
48
+ // For image/audio/binary content types, return a metadata placeholder
49
+ // instead of serializing the raw base64 data into the model context.
50
+ if (first.type === "image" || first.type === "audio") {
51
+ const mimeType = typeof first.mimeType === "string" ? first.mimeType : first.type;
52
+ const dataLen = typeof first.data === "string" ? first.data.length : 0;
53
+ return `[${first.type}: ${mimeType}${dataLen ? `, ${dataLen.toLocaleString()} bytes base64` : ""}]`;
54
+ }
48
55
  try {
49
- return JSON.stringify(content, null, 2);
56
+ // Strip large binary fields from serialization to prevent context overflow.
57
+ const safe = content.map((c) => {
58
+ const copy = { ...c };
59
+ if ("data" in copy && typeof copy.data === "string" && copy.data.length > 200) {
60
+ copy.data = `[${copy.data.length.toLocaleString()} bytes base64 omitted]`;
61
+ }
62
+ return copy;
63
+ });
64
+ return JSON.stringify(safe, null, 2);
50
65
  }
51
66
  catch {
52
67
  return String(content);
@@ -57,7 +57,14 @@ export function transformMcpContent(content) {
57
57
  }
58
58
  if (c.type === "resource") {
59
59
  const resourceUri = c.resource?.uri ?? "(no URI)";
60
- const resourceContent = c.resource?.text ?? (c.resource ? JSON.stringify(c.resource) : "(no content)");
60
+ // Blob resources contain base64 binary data never serialize the raw
61
+ // blob into the model context (causes context overflow and dead sessions).
62
+ const resourceContent = c.resource?.text ??
63
+ ("blob" in (c.resource ?? {}) && c.resource.blob
64
+ ? `[Binary blob: ${c.resource.mimeType ?? "application/octet-stream"}]`
65
+ : c.resource
66
+ ? JSON.stringify(c.resource)
67
+ : "(no content)");
61
68
  return truncateTextBlock({
62
69
  type: "text",
63
70
  text: `[Resource: ${resourceUri}]\n${resourceContent}`,
@@ -77,6 +84,15 @@ export function transformMcpContent(content) {
77
84
  text: `[Audio content: ${c.mimeType ?? "audio/*"}]`,
78
85
  };
79
86
  }
80
- return truncateTextBlock({ type: "text", text: JSON.stringify(c) });
87
+ // Unknown content type serialize as text but strip any potentially
88
+ // large binary fields (data, blob) to avoid context overflow.
89
+ const safe = { ...c };
90
+ if ("data" in safe && typeof safe.data === "string" && safe.data.length > 200) {
91
+ safe.data = `[${safe.data.length.toLocaleString()} bytes base64 omitted]`;
92
+ }
93
+ if ("blob" in safe && typeof safe.blob === "string" && safe.blob.length > 200) {
94
+ safe.blob = `[${safe.blob.length.toLocaleString()} bytes base64 omitted]`;
95
+ }
96
+ return truncateTextBlock({ type: "text", text: JSON.stringify(safe) });
81
97
  });
82
98
  }