@walterra/pi-charts 0.0.3 → 0.0.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.
@@ -102,6 +102,23 @@ Reference: https://vega.github.io/vega-lite/docs/`,
102
102
  return { content: [{ type: 'text', text: 'Cancelled' }], details: {} };
103
103
  }
104
104
 
105
+ const getErrorMessage = (err: unknown): string => {
106
+ if (err instanceof Error) return err.message;
107
+ if (typeof err === 'string') return err;
108
+ if (err && typeof err === 'object' && 'message' in err) {
109
+ return String((err as { message?: unknown }).message);
110
+ }
111
+ return 'Unknown error';
112
+ };
113
+
114
+ const getErrorStderr = (err: unknown): string | undefined => {
115
+ if (err && typeof err === 'object' && 'stderr' in err) {
116
+ const stderr = (err as { stderr?: unknown }).stderr;
117
+ return typeof stderr === 'string' ? stderr : undefined;
118
+ }
119
+ return undefined;
120
+ };
121
+
105
122
  try {
106
123
  // Check Python and dependencies, auto-install if needed using uv
107
124
  const ensureDependencies = (): { success: boolean; error?: string } => {
@@ -159,30 +176,44 @@ Reference: https://vega.github.io/vega-lite/docs/`,
159
176
  try {
160
177
  execSync(checkCmd, { encoding: 'utf-8', stdio: 'pipe' });
161
178
  return { success: true };
162
- } catch (err: any) {
179
+ } catch (err: unknown) {
180
+ const errorMsg = getErrorMessage(err);
163
181
  return {
164
182
  success: false,
165
- error: `Failed to setup Python environment with uv.\nPlease run manually: uv run --with altair --with pandas --with vl-convert-python python3\n\nError: ${err.message}`,
183
+ error: `Failed to setup Python environment with uv.\nPlease run manually: uv run --with altair --with pandas --with vl-convert-python python3\n\nError: ${errorMsg}`,
166
184
  };
167
185
  }
168
186
  };
169
187
 
170
188
  const deps = ensureDependencies();
171
189
  if (!deps.success) {
190
+ const errorText = deps.error ?? 'Dependencies not installed';
172
191
  return {
173
- content: [{ type: 'text', text: deps.error! }],
192
+ content: [{ type: 'text', text: errorText }],
174
193
  details: { error: 'Dependencies not installed' },
175
194
  isError: true,
176
195
  };
177
196
  }
178
197
 
179
198
  // Parse and validate the spec
180
- let vegaSpec: any;
199
+ type VegaSpec = {
200
+ $schema?: string;
201
+ width?: number;
202
+ height?: number;
203
+ data?: { values?: unknown[] };
204
+ [key: string]: unknown;
205
+ };
206
+
207
+ let vegaSpec: VegaSpec;
181
208
  try {
182
- vegaSpec = JSON.parse(spec);
209
+ const parsed = JSON.parse(spec);
210
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
211
+ throw new Error('Spec must be a JSON object');
212
+ }
213
+ vegaSpec = parsed as VegaSpec;
183
214
  } catch (e) {
184
215
  return {
185
- content: [{ type: 'text', text: `Invalid JSON in spec: ${e}` }],
216
+ content: [{ type: 'text', text: `Invalid JSON in spec: ${getErrorMessage(e)}` }],
186
217
  details: { error: 'Invalid JSON' },
187
218
  isError: true,
188
219
  };
@@ -260,9 +291,9 @@ print('OK')
260
291
  mkdirSync(dirname(save_path), { recursive: true });
261
292
  copyFileSync(tmpPng, save_path);
262
293
  savedPath = save_path;
263
- } catch (saveErr: any) {
294
+ } catch (saveErr: unknown) {
264
295
  // Don't fail the whole operation, just note the error
265
- console.error(`Failed to save to ${save_path}: ${saveErr.message}`);
296
+ console.error(`Failed to save to ${save_path}: ${getErrorMessage(saveErr)}`);
266
297
  }
267
298
  }
268
299
 
@@ -279,7 +310,9 @@ print('OK')
279
310
 
280
311
  const dataPoints = tsv_data
281
312
  ? tsv_data.trim().split('\n').length - 1
282
- : vegaSpec.data?.values?.length || 0;
313
+ : Array.isArray(vegaSpec.data?.values)
314
+ ? vegaSpec.data?.values.length
315
+ : 0;
283
316
 
284
317
  const textMsg = savedPath
285
318
  ? `Rendered Vega-Lite chart (${dataPoints} data points) - saved to ${savedPath}`
@@ -292,9 +325,9 @@ print('OK')
292
325
  ],
293
326
  details: { dataPoints, width: vegaSpec.width, height: vegaSpec.height, savedPath },
294
327
  };
295
- } catch (error: any) {
328
+ } catch (error: unknown) {
296
329
  // Try to extract Python error details
297
- const errorMsg = error.stderr || error.message;
330
+ const errorMsg = getErrorStderr(error) ?? getErrorMessage(error);
298
331
  return {
299
332
  content: [{ type: 'text', text: `Error rendering chart: ${errorMsg}` }],
300
333
  details: { error: errorMsg },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@walterra/pi-charts",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "Vega-Lite chart extension for pi coding agent - render data visualizations as inline images",
5
5
  "type": "module",
6
6
  "files": [
@@ -10,7 +10,8 @@
10
10
  "pi": {
11
11
  "extensions": [
12
12
  "./extensions/vega-chart/index.ts"
13
- ]
13
+ ],
14
+ "video": "https://www.rafelsberger.at/assets/pi-charts-sm-0003.mp4"
14
15
  },
15
16
  "repository": {
16
17
  "type": "git",