@uluops/ops-sdk 0.1.0

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 (139) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1647 -0
  3. package/dist/client.d.ts +191 -0
  4. package/dist/client.d.ts.map +1 -0
  5. package/dist/client.js +168 -0
  6. package/dist/client.js.map +1 -0
  7. package/dist/config/constants.d.ts +27 -0
  8. package/dist/config/constants.d.ts.map +1 -0
  9. package/dist/config/constants.js +35 -0
  10. package/dist/config/constants.js.map +1 -0
  11. package/dist/config/index.d.ts +4 -0
  12. package/dist/config/index.d.ts.map +1 -0
  13. package/dist/config/index.js +4 -0
  14. package/dist/config/index.js.map +1 -0
  15. package/dist/config/loaders.d.ts +41 -0
  16. package/dist/config/loaders.d.ts.map +1 -0
  17. package/dist/config/loaders.js +43 -0
  18. package/dist/config/loaders.js.map +1 -0
  19. package/dist/config/validators.d.ts +163 -0
  20. package/dist/config/validators.d.ts.map +1 -0
  21. package/dist/config/validators.js +126 -0
  22. package/dist/config/validators.js.map +1 -0
  23. package/dist/errors/errors.d.ts +7 -0
  24. package/dist/errors/errors.d.ts.map +1 -0
  25. package/dist/errors/errors.js +7 -0
  26. package/dist/errors/errors.js.map +1 -0
  27. package/dist/errors/index.d.ts +2 -0
  28. package/dist/errors/index.d.ts.map +1 -0
  29. package/dist/errors/index.js +2 -0
  30. package/dist/errors/index.js.map +1 -0
  31. package/dist/http/auth-strategy.d.ts +5 -0
  32. package/dist/http/auth-strategy.d.ts.map +1 -0
  33. package/dist/http/auth-strategy.js +5 -0
  34. package/dist/http/auth-strategy.js.map +1 -0
  35. package/dist/http/fetch-adapter.d.ts +5 -0
  36. package/dist/http/fetch-adapter.d.ts.map +1 -0
  37. package/dist/http/fetch-adapter.js +2 -0
  38. package/dist/http/fetch-adapter.js.map +1 -0
  39. package/dist/http/http-client.d.ts +29 -0
  40. package/dist/http/http-client.d.ts.map +1 -0
  41. package/dist/http/http-client.js +25 -0
  42. package/dist/http/http-client.js.map +1 -0
  43. package/dist/http/index.d.ts +6 -0
  44. package/dist/http/index.d.ts.map +1 -0
  45. package/dist/http/index.js +3 -0
  46. package/dist/http/index.js.map +1 -0
  47. package/dist/index.d.ts +8 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/dist/index.js +13 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/operations/admin.d.ts +89 -0
  52. package/dist/operations/admin.d.ts.map +1 -0
  53. package/dist/operations/admin.js +134 -0
  54. package/dist/operations/admin.js.map +1 -0
  55. package/dist/operations/analytics.d.ts +81 -0
  56. package/dist/operations/analytics.d.ts.map +1 -0
  57. package/dist/operations/analytics.js +111 -0
  58. package/dist/operations/analytics.js.map +1 -0
  59. package/dist/operations/auth.d.ts +81 -0
  60. package/dist/operations/auth.d.ts.map +1 -0
  61. package/dist/operations/auth.js +115 -0
  62. package/dist/operations/auth.js.map +1 -0
  63. package/dist/operations/index.d.ts +8 -0
  64. package/dist/operations/index.d.ts.map +1 -0
  65. package/dist/operations/index.js +8 -0
  66. package/dist/operations/index.js.map +1 -0
  67. package/dist/operations/issues.d.ts +59 -0
  68. package/dist/operations/issues.d.ts.map +1 -0
  69. package/dist/operations/issues.js +141 -0
  70. package/dist/operations/issues.js.map +1 -0
  71. package/dist/operations/projects.d.ts +56 -0
  72. package/dist/operations/projects.d.ts.map +1 -0
  73. package/dist/operations/projects.js +99 -0
  74. package/dist/operations/projects.js.map +1 -0
  75. package/dist/operations/query-utils.d.ts +20 -0
  76. package/dist/operations/query-utils.d.ts.map +1 -0
  77. package/dist/operations/query-utils.js +22 -0
  78. package/dist/operations/query-utils.js.map +1 -0
  79. package/dist/operations/runs.d.ts +53 -0
  80. package/dist/operations/runs.d.ts.map +1 -0
  81. package/dist/operations/runs.js +118 -0
  82. package/dist/operations/runs.js.map +1 -0
  83. package/dist/operations/taxonomy.d.ts +7 -0
  84. package/dist/operations/taxonomy.d.ts.map +1 -0
  85. package/dist/operations/taxonomy.js +7 -0
  86. package/dist/operations/taxonomy.js.map +1 -0
  87. package/dist/types/analytics.d.ts +338 -0
  88. package/dist/types/analytics.d.ts.map +1 -0
  89. package/dist/types/analytics.js +2 -0
  90. package/dist/types/analytics.js.map +1 -0
  91. package/dist/types/auth.d.ts +303 -0
  92. package/dist/types/auth.d.ts.map +1 -0
  93. package/dist/types/auth.js +2 -0
  94. package/dist/types/auth.js.map +1 -0
  95. package/dist/types/enums.d.ts +200 -0
  96. package/dist/types/enums.d.ts.map +1 -0
  97. package/dist/types/enums.js +222 -0
  98. package/dist/types/enums.js.map +1 -0
  99. package/dist/types/index.d.ts +10 -0
  100. package/dist/types/index.d.ts.map +1 -0
  101. package/dist/types/index.js +16 -0
  102. package/dist/types/index.js.map +1 -0
  103. package/dist/types/issues.d.ts +182 -0
  104. package/dist/types/issues.d.ts.map +1 -0
  105. package/dist/types/issues.js +2 -0
  106. package/dist/types/issues.js.map +1 -0
  107. package/dist/types/projects.d.ts +123 -0
  108. package/dist/types/projects.d.ts.map +1 -0
  109. package/dist/types/projects.js +2 -0
  110. package/dist/types/projects.js.map +1 -0
  111. package/dist/types/response-schemas.d.ts +3441 -0
  112. package/dist/types/response-schemas.d.ts.map +1 -0
  113. package/dist/types/response-schemas.js +388 -0
  114. package/dist/types/response-schemas.js.map +1 -0
  115. package/dist/types/responses.d.ts +84 -0
  116. package/dist/types/responses.d.ts.map +1 -0
  117. package/dist/types/responses.js +2 -0
  118. package/dist/types/responses.js.map +1 -0
  119. package/dist/types/runs.d.ts +232 -0
  120. package/dist/types/runs.d.ts.map +1 -0
  121. package/dist/types/runs.js +2 -0
  122. package/dist/types/runs.js.map +1 -0
  123. package/dist/types/schemas.d.ts +914 -0
  124. package/dist/types/schemas.d.ts.map +1 -0
  125. package/dist/types/schemas.js +280 -0
  126. package/dist/types/schemas.js.map +1 -0
  127. package/dist/utils/helpers.d.ts +64 -0
  128. package/dist/utils/helpers.d.ts.map +1 -0
  129. package/dist/utils/helpers.js +127 -0
  130. package/dist/utils/helpers.js.map +1 -0
  131. package/dist/utils/index.d.ts +3 -0
  132. package/dist/utils/index.d.ts.map +1 -0
  133. package/dist/utils/index.js +3 -0
  134. package/dist/utils/index.js.map +1 -0
  135. package/dist/utils/logger.d.ts +5 -0
  136. package/dist/utils/logger.d.ts.map +1 -0
  137. package/dist/utils/logger.js +5 -0
  138. package/dist/utils/logger.js.map +1 -0
  139. package/package.json +103 -0
package/README.md ADDED
@@ -0,0 +1,1647 @@
1
+ # @uluops/ops-sdk
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@uluops/ops-sdk.svg)](https://www.npmjs.com/package/@uluops/ops-sdk)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
5
+ [![Node.js](https://img.shields.io/badge/Node.js-18+-green.svg)](https://nodejs.org/)
6
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](https://www.typescriptlang.org/)
7
+
8
+ Official TypeScript SDK with Zod runtime validation for the UluOps validation tracker API. Track validation runs, manage issues, analyze trends, and integrate AI validation pipelines into your workflow.
9
+
10
+ **Current version: 0.1.0** | [Changelog](./CHANGELOG.md)
11
+
12
+ ## Quick Start
13
+
14
+ ### Programmatic Usage
15
+
16
+ ```typescript
17
+ import { OpsClient } from '@uluops/ops-sdk';
18
+
19
+ // Defaults to http://localhost:3100/api/v1 for local dev.
20
+ // For production, set ULUOPS_BASE_URL env var or pass baseUrl below.
21
+ const client = new OpsClient({
22
+ apiKey: 'ulr_your-api-key-here', // Create via client.auth.createApiKey() or admin dashboard
23
+ });
24
+
25
+ // Save a validation run
26
+ const result = await client.runs.save({
27
+ project: 'my-project',
28
+ workflowType: 'post-implementation',
29
+ validators: [
30
+ { name: 'code-validator', score: 85, status: 'PASS' },
31
+ { name: 'test-architect', score: 72, status: 'APPROVED' },
32
+ ],
33
+ recommendations: [
34
+ {
35
+ validator: 'code-validator',
36
+ title: 'Missing error handling',
37
+ priority: 'suggested',
38
+ filePath: 'src/api/client.ts',
39
+ lineNumber: 42,
40
+ },
41
+ ],
42
+ });
43
+
44
+ console.log(`Run #${result.run.runNumber} saved: ${result.correlation.newIssues} new issues`);
45
+ ```
46
+
47
+ ### Search Issues
48
+
49
+ ```typescript
50
+ const issues = await client.issues.search({
51
+ query: 'authentication',
52
+ status: 'open',
53
+ priority: 'critical',
54
+ });
55
+
56
+ for (const issue of issues) {
57
+ console.log(`[${issue.severity}] ${issue.title} — ${issue.filePath}:${issue.lineNumber}`);
58
+ }
59
+ ```
60
+
61
+ ### Project Analytics
62
+
63
+ ```typescript
64
+ const burndown = await client.analytics.getBurndown({
65
+ project: 'my-project',
66
+ days: 30,
67
+ });
68
+
69
+ for (const [domain, trend] of Object.entries(burndown.trends)) {
70
+ console.log(`${domain}: ${trend.direction} (rate: ${trend.rate})`);
71
+ }
72
+ ```
73
+
74
+ ## Table of Contents
75
+
76
+ - [Overview](#overview)
77
+ - [Features](#features)
78
+ - [Installation](#installation)
79
+ - [Authentication](#authentication)
80
+ - [TypeScript Support](#typescript-support)
81
+ - [API Reference](#api-reference)
82
+ - [Auth Operations](#auth-operations)
83
+ - [Project Operations](#project-operations)
84
+ - [Run Operations](#run-operations)
85
+ - [Issue Operations](#issue-operations)
86
+ - [Analytics Operations](#analytics-operations)
87
+ - [Taxonomy Operations](#taxonomy-operations)
88
+ - [Health Check](#health-check)
89
+ - [Admin Operations](#admin-operations)
90
+ - [Environment Variables](#environment-variables)
91
+ - [Error Handling](#error-handling)
92
+ - [Advanced Usage](#advanced-usage)
93
+ - [Troubleshooting](#troubleshooting)
94
+ - [License](#license)
95
+
96
+ ## Overview
97
+
98
+ The UluOps SDK provides programmatic access to the UluOps validation tracker API, enabling you to:
99
+
100
+ - **Track Validation Runs**: Save validation pipeline results with validator scores and recommendations
101
+ - **Manage Issues**: Create, search, update, and track issues across projects
102
+ - **Analyze Trends**: Get burndown charts, velocity metrics, and taxonomy distribution analytics
103
+ - **Automate Workflows**: Integrate validation tracking into CI/CD pipelines
104
+
105
+ The SDK covers **74 API endpoints** across 7 operation domains with full TypeScript support.
106
+
107
+ ## Features
108
+
109
+ - **Full API Coverage**: Access all 74 endpoints across auth, projects, runs, issues, analytics, taxonomy, and admin domains
110
+ - **Type-Safe**: Complete TypeScript definitions with Zod runtime validation
111
+ - **Dual Authentication**: API key (preferred) and JWT session support
112
+ - **Automatic Retries**: Exponential backoff for transient errors (502, 503, 504, 429)
113
+ - **Error Hierarchy**: Typed errors for precise error handling
114
+ - **Subpath Exports**: Import only what you need (`@uluops/ops-sdk/types`, `@uluops/ops-sdk/errors`)
115
+
116
+ ## Installation
117
+
118
+ ```bash
119
+ # npm
120
+ npm install @uluops/ops-sdk
121
+
122
+ # yarn
123
+ yarn add @uluops/ops-sdk
124
+
125
+ # pnpm
126
+ pnpm add @uluops/ops-sdk
127
+ ```
128
+
129
+ **Requirements:**
130
+ - Node.js 18.0.0 or higher
131
+ - TypeScript 5.0+ (for TypeScript users)
132
+
133
+ ## Authentication
134
+
135
+ The SDK supports two authentication methods:
136
+
137
+ ### API Key Authentication (Recommended)
138
+
139
+ API keys provide persistent authentication without session management. Keys must start with the `ulr_` prefix.
140
+
141
+ ```typescript
142
+ import { OpsClient } from '@uluops/ops-sdk';
143
+
144
+ const client = new OpsClient({
145
+ apiKey: 'ulr_your-api-key-here',
146
+ });
147
+
148
+ // Check authentication status
149
+ console.log(client.isAuthenticated()); // true
150
+ console.log(client.getAuthType()); // 'api_key'
151
+ ```
152
+
153
+ ### Session-Based Authentication
154
+
155
+ For interactive applications, you can authenticate with email/password:
156
+
157
+ ```typescript
158
+ import { OpsClient } from '@uluops/ops-sdk';
159
+
160
+ const client = new OpsClient({
161
+ baseUrl: 'https://api.uluops.com/api/v1',
162
+ });
163
+
164
+ // Login to get a session
165
+ const { token, user } = await client.auth.login({
166
+ email: 'user@example.com',
167
+ password: 'your-password',
168
+ });
169
+
170
+ // Create a new client with the session token
171
+ const authenticatedClient = new OpsClient({
172
+ sessionToken: token,
173
+ });
174
+
175
+ // Logout when done
176
+ await authenticatedClient.logout();
177
+ ```
178
+
179
+ ### Credential Priority Chain
180
+
181
+ The SDK loads credentials in the following order:
182
+
183
+ 1. **Constructor arguments**: `apiKey`, `sessionToken`, `email`/`password`
184
+ 2. **Environment variables**: `ULUOPS_API_KEY`, `ULUOPS_EMAIL`, `ULUOPS_PASSWORD`
185
+ 3. **Local `.env` file**: In the current working directory
186
+ 4. **Global credentials**: `~/.uluops/credentials.json`
187
+
188
+ ## TypeScript Support
189
+
190
+ The SDK is written in TypeScript with full type definitions. Import types directly:
191
+
192
+ ```typescript
193
+ // Main client
194
+ import { OpsClient, type OpsClientConfig } from '@uluops/ops-sdk';
195
+
196
+ // Types only
197
+ import type {
198
+ Project,
199
+ Issue,
200
+ Run,
201
+ ValidatorPerformance,
202
+ Priority,
203
+ Status,
204
+ Severity,
205
+ } from '@uluops/ops-sdk/types';
206
+
207
+ // Errors only
208
+ import {
209
+ OpsApiError,
210
+ ValidationError,
211
+ NotFoundError,
212
+ RateLimitError,
213
+ } from '@uluops/ops-sdk/errors';
214
+
215
+ // Config utilities
216
+ import { loadCredentials, DEFAULT_BASE_URL } from '@uluops/ops-sdk/config';
217
+ ```
218
+
219
+ ### Package Exports
220
+
221
+ | Export Path | Contents |
222
+ |------------|----------|
223
+ | `@uluops/ops-sdk` | Main `OpsClient`, `OpsHttpClient`, auth strategies |
224
+ | `@uluops/ops-sdk/types` | All TypeScript types and Zod schemas |
225
+ | `@uluops/ops-sdk/errors` | Error classes and utilities |
226
+ | `@uluops/ops-sdk/config` | Configuration loaders and constants |
227
+
228
+ #### Granular Type Imports
229
+
230
+ For minimal bundle size, import only the type modules you need:
231
+
232
+ ```typescript
233
+ import type { Project } from '@uluops/ops-sdk/types/projects';
234
+ import type { Issue } from '@uluops/ops-sdk/types/issues';
235
+ import type { Run } from '@uluops/ops-sdk/types/runs';
236
+ import type { BurndownResult } from '@uluops/ops-sdk/types/analytics';
237
+ import type { Priority, Status, Severity } from '@uluops/ops-sdk/types/enums';
238
+ import type { ApiResponse } from '@uluops/ops-sdk/types/responses';
239
+ import type { SaveFeaturesListInput } from '@uluops/ops-sdk/types/schemas';
240
+ import type { Credentials } from '@uluops/ops-sdk/types/auth';
241
+ ```
242
+
243
+ ## API Reference
244
+
245
+ ### Client Configuration
246
+
247
+ ```typescript
248
+ const client = new OpsClient({
249
+ // Authentication (choose one)
250
+ apiKey: 'ulr_...', // API key (preferred)
251
+ sessionToken: 'jwt-token', // Existing session token
252
+ email: 'user@example.com', // Email for login
253
+ password: 'password', // Password for login
254
+
255
+ // Connection settings
256
+ baseUrl: 'http://localhost:3100/api/v1', // API base URL
257
+ timeout: 30000, // Request timeout in ms (default: 30000)
258
+ retries: 3, // Retry count for transient errors (default: 3)
259
+ debug: false, // Enable debug logging
260
+
261
+ // Callbacks
262
+ onTokenRefresh: (token) => { /* handle token refresh */ },
263
+ });
264
+ ```
265
+
266
+ ---
267
+
268
+ ### Auth Operations
269
+
270
+ Manage user authentication, API keys, and sessions.
271
+
272
+ #### `client.auth.register(input)`
273
+
274
+ Register a new user account.
275
+
276
+ | Parameter | Type | Required | Description |
277
+ |-----------|------|----------|-------------|
278
+ | `email` | `string` | Yes | User email address |
279
+ | `password` | `string` | Yes | User password |
280
+
281
+ ```typescript
282
+ const { user, token } = await client.auth.register({
283
+ email: 'newuser@example.com',
284
+ password: 'securePassword123',
285
+ });
286
+ ```
287
+
288
+ #### `client.auth.login(input)`
289
+
290
+ Login with email and password.
291
+
292
+ | Parameter | Type | Required | Description |
293
+ |-----------|------|----------|-------------|
294
+ | `email` | `string` | Yes | User email address |
295
+ | `password` | `string` | Yes | User password |
296
+
297
+ ```typescript
298
+ const { user, token } = await client.auth.login({
299
+ email: 'user@example.com',
300
+ password: 'password123',
301
+ });
302
+ ```
303
+
304
+ #### `client.auth.logoutAll()`
305
+
306
+ Revoke all active sessions for the current user.
307
+
308
+ ```typescript
309
+ const { sessionsRevoked } = await client.auth.logoutAll();
310
+ console.log(`Revoked ${sessionsRevoked} sessions`);
311
+ ```
312
+
313
+ #### `client.auth.getMe()`
314
+
315
+ Get the current authenticated user.
316
+
317
+ ```typescript
318
+ const user = await client.auth.getMe();
319
+ console.log(user.email, user.role);
320
+ ```
321
+
322
+ #### `client.auth.getProfile()`
323
+
324
+ Get detailed user profile.
325
+
326
+ ```typescript
327
+ const { user } = await client.auth.getProfile();
328
+ ```
329
+
330
+ #### `client.auth.updateProfile(input)`
331
+
332
+ Update user profile information.
333
+
334
+ | Parameter | Type | Required | Description |
335
+ |-----------|------|----------|-------------|
336
+ | `displayName` | `string` | No | Display name |
337
+ | `bio` | `string` | No | User bio |
338
+ | `avatarUrl` | `string` | No | Avatar URL |
339
+
340
+ ```typescript
341
+ const { user } = await client.auth.updateProfile({
342
+ displayName: 'John Doe',
343
+ bio: 'Software Engineer',
344
+ });
345
+ ```
346
+
347
+ #### `client.auth.changePassword(input)`
348
+
349
+ Change the current user's password.
350
+
351
+ | Parameter | Type | Required | Description |
352
+ |-----------|------|----------|-------------|
353
+ | `currentPassword` | `string` | Yes | Current password |
354
+ | `newPassword` | `string` | Yes | New password |
355
+
356
+ ```typescript
357
+ await client.auth.changePassword({
358
+ currentPassword: 'oldPassword',
359
+ newPassword: 'newSecurePassword',
360
+ });
361
+ ```
362
+
363
+ #### `client.auth.forgotPassword(email)`
364
+
365
+ Request a password reset email.
366
+
367
+ ```typescript
368
+ await client.auth.forgotPassword('user@example.com');
369
+ ```
370
+
371
+ #### `client.auth.resetPassword(input)`
372
+
373
+ Reset password using a reset token.
374
+
375
+ | Parameter | Type | Required | Description |
376
+ |-----------|------|----------|-------------|
377
+ | `token` | `string` | Yes | Reset token from email |
378
+ | `newPassword` | `string` | Yes | New password |
379
+
380
+ ```typescript
381
+ await client.auth.resetPassword({
382
+ token: 'reset-token-from-email',
383
+ newPassword: 'newSecurePassword',
384
+ });
385
+ ```
386
+
387
+ #### `client.auth.listApiKeys()`
388
+
389
+ List all API keys for the current user.
390
+
391
+ ```typescript
392
+ const keys = await client.auth.listApiKeys();
393
+ for (const key of keys) {
394
+ console.log(key.name, key.prefix, key.createdAt);
395
+ }
396
+ ```
397
+
398
+ #### `client.auth.createApiKey(input)`
399
+
400
+ Create a new API key.
401
+
402
+ | Parameter | Type | Required | Description |
403
+ |-----------|------|----------|-------------|
404
+ | `name` | `string` | No | Key name/description |
405
+
406
+ ```typescript
407
+ const { id, name, key } = await client.auth.createApiKey({ name: 'CI Pipeline' });
408
+ console.log('Save this key:', key); // Only shown once!
409
+ ```
410
+
411
+ #### `client.auth.revokeApiKey(keyId)`
412
+
413
+ Revoke an API key.
414
+
415
+ ```typescript
416
+ await client.auth.revokeApiKey('key-id-123');
417
+ ```
418
+
419
+ #### `client.auth.listSessions()`
420
+
421
+ List all active sessions.
422
+
423
+ ```typescript
424
+ const sessions = await client.auth.listSessions();
425
+ for (const session of sessions) {
426
+ console.log(session.userAgent, session.createdAt);
427
+ }
428
+ ```
429
+
430
+ #### `client.auth.revokeSession(sessionId)`
431
+
432
+ Revoke a specific session.
433
+
434
+ ```typescript
435
+ await client.auth.revokeSession('session-id-123');
436
+ ```
437
+
438
+ ---
439
+
440
+ ### Project Operations
441
+
442
+ Manage validation projects.
443
+
444
+ #### `client.projects.list()`
445
+
446
+ List all projects.
447
+
448
+ ```typescript
449
+ const projects = await client.projects.list();
450
+ for (const project of projects) {
451
+ console.log(project.id, project.name, project.createdAt);
452
+ }
453
+ ```
454
+
455
+ #### `client.projects.get(idOrName)`
456
+
457
+ Get a project by ID or name.
458
+
459
+ ```typescript
460
+ const project = await client.projects.get('my-project');
461
+ console.log(project.name, project.runCount, project.issueCount);
462
+ ```
463
+
464
+ #### `client.projects.create(input)`
465
+
466
+ Create a new project.
467
+
468
+ | Parameter | Type | Required | Description |
469
+ |-----------|------|----------|-------------|
470
+ | `name` | `string` | Yes | Project name (unique) |
471
+
472
+ ```typescript
473
+ const project = await client.projects.create({ name: 'new-project' });
474
+ ```
475
+
476
+ #### `client.projects.update(idOrName, input)`
477
+
478
+ Update a project.
479
+
480
+ | Parameter | Type | Required | Description |
481
+ |-----------|------|----------|-------------|
482
+ | `name` | `string` | No | New project name |
483
+
484
+ ```typescript
485
+ const project = await client.projects.update('my-project', {
486
+ name: 'renamed-project',
487
+ });
488
+ ```
489
+
490
+ #### `client.projects.rename(input)`
491
+
492
+ Rename a project.
493
+
494
+ | Parameter | Type | Required | Description |
495
+ |-----------|------|----------|-------------|
496
+ | `project` | `string` | Yes | Current project name or ID |
497
+ | `name` | `string` | Yes | New project name |
498
+
499
+ ```typescript
500
+ const project = await client.projects.rename({
501
+ project: 'old-name',
502
+ name: 'new-name',
503
+ });
504
+ ```
505
+
506
+ #### `client.projects.delete(idOrName, input)`
507
+
508
+ Permanently delete a project.
509
+
510
+ | Parameter | Type | Required | Description |
511
+ |-----------|------|----------|-------------|
512
+ | `confirm` | `boolean` | Yes | Must be `true` |
513
+ | `confirmationPhrase` | `string` | Yes | Must match project name |
514
+
515
+ ```typescript
516
+ await client.projects.delete('my-project', {
517
+ confirm: true,
518
+ confirmationPhrase: 'my-project',
519
+ });
520
+ ```
521
+
522
+ #### `client.projects.softDelete(idOrName, input)`
523
+
524
+ Soft delete a project (can be restored).
525
+
526
+ ```typescript
527
+ await client.projects.softDelete('my-project', {
528
+ confirm: true,
529
+ confirmationPhrase: 'my-project',
530
+ });
531
+ ```
532
+
533
+ #### `client.projects.restore(idOrName)`
534
+
535
+ Restore a soft-deleted project.
536
+
537
+ ```typescript
538
+ const project = await client.projects.restore('my-project');
539
+ ```
540
+
541
+ #### `client.projects.getSummary(idOrName)`
542
+
543
+ Get project summary with statistics.
544
+
545
+ ```typescript
546
+ const summary = await client.projects.getSummary('my-project');
547
+ console.log(`Total runs: ${summary.totalRuns}`);
548
+ console.log(`Total issues: ${summary.totalIssues}`);
549
+ console.log(`Open issues: ${summary.openIssues}`);
550
+ ```
551
+
552
+ #### `client.projects.getTrends(idOrName, query)`
553
+
554
+ Get issue trend data over time.
555
+
556
+ | Parameter | Type | Required | Description |
557
+ |-----------|------|----------|-------------|
558
+ | `days` | `number` | No | Number of days (default: 30) |
559
+
560
+ ```typescript
561
+ const trends = await client.projects.getTrends('my-project', { days: 30 });
562
+ for (const point of trends) {
563
+ console.log(point.date, point.openIssues, point.closedIssues);
564
+ }
565
+ ```
566
+
567
+ #### `client.projects.listIssues(idOrName, query)`
568
+
569
+ List issues for a project with filters.
570
+
571
+ | Parameter | Type | Required | Description |
572
+ |-----------|------|----------|-------------|
573
+ | `status` | `Status` | No | Filter by status |
574
+ | `priority` | `Priority` | No | Filter by priority |
575
+ | `severity` | `Severity` | No | Filter by severity |
576
+ | `validator` | `string` | No | Filter by validator |
577
+ | `limit` | `number` | No | Max results (default: 50) |
578
+ | `offset` | `number` | No | Pagination offset |
579
+
580
+ ```typescript
581
+ const issues = await client.projects.listIssues('my-project', {
582
+ status: 'open',
583
+ priority: 'critical',
584
+ limit: 10,
585
+ });
586
+ ```
587
+
588
+ #### `client.projects.bulkUpdateIssueStatus(idOrName, updates)`
589
+
590
+ Bulk update issue statuses.
591
+
592
+ ```typescript
593
+ const results = await client.projects.bulkUpdateIssueStatus('my-project', [
594
+ { issueId: 'issue-1', status: 'completed', reason: 'Fixed' },
595
+ { issueId: 'issue-2', status: 'deferred', reason: 'Not a priority' },
596
+ ]);
597
+ ```
598
+
599
+ #### `client.projects.mergeIssues(idOrName, input)`
600
+
601
+ Merge duplicate issues.
602
+
603
+ | Parameter | Type | Required | Description |
604
+ |-----------|------|----------|-------------|
605
+ | `targetIssueId` | `string` | Yes | Issue to merge into |
606
+ | `sourceIssueIds` | `string[]` | Yes | Issues to merge from |
607
+ | `strategy` | `string` | No | `'keep_target'` or `'keep_highest_priority'` |
608
+
609
+ ```typescript
610
+ const result = await client.projects.mergeIssues('my-project', {
611
+ targetIssueId: 'issue-1',
612
+ sourceIssueIds: ['issue-2', 'issue-3'],
613
+ strategy: 'keep_target',
614
+ });
615
+ ```
616
+
617
+ ---
618
+
619
+ ### Run Operations
620
+
621
+ Save and manage validation runs.
622
+
623
+ #### `client.runs.save(input)`
624
+
625
+ Save a new validation run.
626
+
627
+ | Parameter | Type | Required | Description |
628
+ |-----------|------|----------|-------------|
629
+ | `project` | `string` | Yes | Project name or ID |
630
+ | `workflowType` | `string` | Yes | Workflow type (e.g., `'post-implementation'`) |
631
+ | `validators` | `Validator[]` | Yes | Array of validator results |
632
+ | `recommendations` | `Recommendation[]` | No | Array of issues/recommendations |
633
+ | `summary` | `object` | No | Summary statistics |
634
+ | `rawMarkdown` | `string` | No | Raw markdown report |
635
+
636
+ ```typescript
637
+ const result = await client.runs.save({
638
+ project: 'my-project',
639
+ workflowType: 'post-implementation',
640
+ validators: [
641
+ {
642
+ name: 'code-validator',
643
+ score: 85,
644
+ status: 'PASS',
645
+ model: 'sonnet',
646
+ tokens: { input_tokens: 1000, output_tokens: 500 },
647
+ },
648
+ {
649
+ name: 'test-architect',
650
+ score: 72,
651
+ status: 'APPROVED',
652
+ },
653
+ ],
654
+ recommendations: [
655
+ {
656
+ validator: 'code-validator',
657
+ title: 'Missing error handling in API client',
658
+ priority: 'suggested',
659
+ severity: 'medium',
660
+ filePath: 'src/api/client.ts',
661
+ lineNumber: 42,
662
+ description: 'Add try-catch for network errors',
663
+ failureCode: 'SEM-VAL/M',
664
+ },
665
+ ],
666
+ summary: {
667
+ averageScore: 78.5,
668
+ allGatesPassed: true,
669
+ },
670
+ });
671
+
672
+ console.log(`Run #${result.run.runNumber} saved`);
673
+ console.log(`Issues: ${result.correlation.newIssues} new, ${result.correlation.recurringIssues} recurring`);
674
+ ```
675
+
676
+ #### `client.runs.validate(input)`
677
+
678
+ Preview what a save would do without persisting.
679
+
680
+ ```typescript
681
+ const preview = await client.runs.validate({
682
+ project: 'my-project',
683
+ workflowType: 'post-implementation',
684
+ validators: [...],
685
+ recommendations: [...],
686
+ });
687
+
688
+ console.log('Would create:', preview.wouldCreate);
689
+ console.log('Would update:', preview.wouldUpdate);
690
+ console.log('Would regress:', preview.wouldRegress);
691
+ ```
692
+
693
+ #### `client.runs.listByProject(projectId, query)`
694
+
695
+ List runs for a project.
696
+
697
+ | Parameter | Type | Required | Description |
698
+ |-----------|------|----------|-------------|
699
+ | `workflowType` | `string` | No | Filter by workflow type |
700
+ | `limit` | `number` | No | Max results |
701
+ | `offset` | `number` | No | Pagination offset |
702
+
703
+ ```typescript
704
+ const runs = await client.runs.listByProject('my-project', {
705
+ workflowType: 'ship',
706
+ limit: 10,
707
+ });
708
+ ```
709
+
710
+ #### `client.runs.getLatest(projectId, workflowType)`
711
+
712
+ Get the latest run for a project.
713
+
714
+ ```typescript
715
+ const latestRun = await client.runs.getLatest('my-project', 'post-implementation');
716
+ ```
717
+
718
+ #### `client.runs.get(runId)`
719
+
720
+ Get a run by ID.
721
+
722
+ ```typescript
723
+ const run = await client.runs.get('run-uuid-here');
724
+ ```
725
+
726
+ #### `client.runs.getDetails(projectId, runNumber)`
727
+
728
+ Get detailed run information with recommendations.
729
+
730
+ ```typescript
731
+ const details = await client.runs.getDetails('my-project', 5);
732
+ console.log(details.validators);
733
+ console.log(details.recommendations);
734
+ ```
735
+
736
+ #### `client.runs.diff(query)`
737
+
738
+ Compare two runs to see fixed/new issues.
739
+
740
+ | Parameter | Type | Required | Description |
741
+ |-----------|------|----------|-------------|
742
+ | `project` | `string` | Yes | Project name or ID |
743
+ | `baseRun` | `number` | Yes | Base run number |
744
+ | `compareRun` | `number` | Yes | Compare run number |
745
+ | `workflowType` | `string` | No | Filter by workflow type |
746
+
747
+ ```typescript
748
+ const diff = await client.runs.diff({
749
+ project: 'my-project',
750
+ baseRun: 1,
751
+ compareRun: 5,
752
+ });
753
+
754
+ console.log('Fixed issues:', diff.fixed.length);
755
+ console.log('New issues:', diff.new.length);
756
+ console.log('Unchanged:', diff.unchanged.length);
757
+ ```
758
+
759
+ #### `client.runs.archive(input)`
760
+
761
+ Archive old runs.
762
+
763
+ | Parameter | Type | Required | Description |
764
+ |-----------|------|----------|-------------|
765
+ | `project` | `string` | Yes | Project name or ID |
766
+ | `beforeRunNumber` | `number` | No | Archive runs before this number |
767
+ | `beforeDate` | `string` | No | Archive runs before this date |
768
+ | `keepLast` | `number` | No | Keep last N runs |
769
+ | `reason` | `string` | No | Archive reason |
770
+
771
+ ```typescript
772
+ const result = await client.runs.archive({
773
+ project: 'my-project',
774
+ keepLast: 10,
775
+ reason: 'Quarterly cleanup',
776
+ });
777
+ console.log(`Archived ${result.archivedCount} runs`);
778
+ ```
779
+
780
+ #### `client.runs.update(input)`
781
+
782
+ Update run metadata (tokens, scores).
783
+
784
+ ```typescript
785
+ const run = await client.runs.update({
786
+ project: 'my-project',
787
+ runNumber: 5,
788
+ validators: [
789
+ { name: 'code-validator', score: 90, input_tokens: 1500 },
790
+ ],
791
+ });
792
+ ```
793
+
794
+ #### `client.runs.delete(runId)`
795
+
796
+ Delete a run.
797
+
798
+ ```typescript
799
+ await client.runs.delete('run-uuid-here');
800
+ ```
801
+
802
+ ---
803
+
804
+ ### Issue Operations
805
+
806
+ Track and manage validation issues.
807
+
808
+ #### `client.issues.create(input)`
809
+
810
+ Create a user-submitted issue.
811
+
812
+ | Parameter | Type | Required | Description |
813
+ |-----------|------|----------|-------------|
814
+ | `project` | `string` | Yes | Project name or ID |
815
+ | `title` | `string` | Yes | Issue title |
816
+ | `priority` | `Priority` | Yes | `'critical'`, `'suggested'`, `'backlog'` |
817
+ | `severity` | `Severity` | No | `'critical'`, `'high'`, `'medium'`, `'low'`, `'info'` |
818
+ | `type` | `IssueType` | No | `'bug'`, `'feature'`, `'refactor'`, etc. |
819
+ | `filePath` | `string` | No | File path where issue exists |
820
+ | `lineNumber` | `number` | No | Line number |
821
+ | `description` | `string` | No | Detailed description |
822
+ | `failureCode` | `string` | No | Taxonomy code (e.g., `'SEM-VAL/H'`) |
823
+ | `validator` | `string` | No | Validator name (defaults to `'user-submitted'`) |
824
+
825
+ ```typescript
826
+ const issue = await client.issues.create({
827
+ project: 'my-project',
828
+ title: 'Security vulnerability in auth module',
829
+ priority: 'critical',
830
+ severity: 'critical',
831
+ type: 'security',
832
+ filePath: 'src/auth/login.ts',
833
+ lineNumber: 45,
834
+ description: 'SQL injection vulnerability in login query',
835
+ failureCode: 'PRA-SEC/C',
836
+ });
837
+ ```
838
+
839
+ #### `client.issues.search(query)`
840
+
841
+ Search issues across projects.
842
+
843
+ | Parameter | Type | Required | Description |
844
+ |-----------|------|----------|-------------|
845
+ | `query` | `string` | Yes | Search query |
846
+ | `projects` | `string[]` | No | Filter by projects |
847
+ | `validators` | `string[]` | No | Filter by validators |
848
+ | `status` | `Status` | No | Filter by status |
849
+ | `priority` | `Priority` | No | Filter by priority |
850
+ | `severities` | `Severity[]` | No | Filter by severities |
851
+ | `failureDomains` | `string[]` | No | Filter by domains (`'STR'`, `'SEM'`, `'PRA'`, `'EPI'`) |
852
+ | `limit` | `number` | No | Max results |
853
+
854
+ ```typescript
855
+ const issues = await client.issues.search({
856
+ query: 'authentication',
857
+ status: 'open',
858
+ priority: 'critical',
859
+ });
860
+ ```
861
+
862
+ #### `client.issues.get(issueId)`
863
+
864
+ Get an issue by ID.
865
+
866
+ ```typescript
867
+ const issue = await client.issues.get('issue-uuid-here');
868
+ ```
869
+
870
+ #### `client.issues.getDetails(issueId)`
871
+
872
+ Get detailed issue information with occurrences and notes.
873
+
874
+ ```typescript
875
+ const details = await client.issues.getDetails('issue-uuid-here');
876
+ console.log('Occurrences:', details.occurrences);
877
+ console.log('Notes:', details.notes);
878
+ console.log('Related issues:', details.relatedIssues);
879
+ ```
880
+
881
+ #### `client.issues.getByFingerprint(fingerprint, project)`
882
+
883
+ Get an issue by its SHA-256 fingerprint.
884
+
885
+ ```typescript
886
+ const issue = await client.issues.getByFingerprint('abc123...', 'my-project');
887
+ ```
888
+
889
+ #### `client.issues.getHistory(issueId)`
890
+
891
+ Get status change history for an issue.
892
+
893
+ ```typescript
894
+ const history = await client.issues.getHistory('issue-uuid');
895
+ for (const entry of history) {
896
+ console.log(`${entry.fromStatus} -> ${entry.toStatus} at ${entry.changedAt}`);
897
+ }
898
+ ```
899
+
900
+ #### `client.issues.updateStatus(issueId, input)`
901
+
902
+ Update an issue's status.
903
+
904
+ | Parameter | Type | Required | Description |
905
+ |-----------|------|----------|-------------|
906
+ | `status` | `Status` | Yes | New status |
907
+ | `reason` | `string` | No | Reason for change |
908
+
909
+ ```typescript
910
+ const issue = await client.issues.updateStatus('issue-uuid', {
911
+ status: 'completed',
912
+ reason: 'Fixed in PR #123',
913
+ });
914
+ ```
915
+
916
+ #### `client.issues.edit(issueId, input)`
917
+
918
+ Edit issue metadata.
919
+
920
+ | Parameter | Type | Required | Description |
921
+ |-----------|------|----------|-------------|
922
+ | `title` | `string` | No | New title |
923
+ | `filePath` | `string` | No | New file path |
924
+ | `lineNumber` | `number` | No | New line number |
925
+ | `severity` | `Severity` | No | New severity |
926
+ | `category` | `string` | No | New category |
927
+ | `failureCode` | `string` | No | New failure code |
928
+
929
+ ```typescript
930
+ const issue = await client.issues.edit('issue-uuid', {
931
+ title: 'Updated title',
932
+ severity: 'high',
933
+ });
934
+ ```
935
+
936
+ #### `client.issues.addNote(issueId, input)`
937
+
938
+ Add a note to an issue.
939
+
940
+ | Parameter | Type | Required | Description |
941
+ |-----------|------|----------|-------------|
942
+ | `content` | `string` | Yes | Note content |
943
+ | `noteType` | `NoteType` | No | `'context'`, `'resolution'`, `'blocker'` |
944
+
945
+ ```typescript
946
+ const note = await client.issues.addNote('issue-uuid', {
947
+ content: 'Root cause identified: race condition in async handler',
948
+ noteType: 'context',
949
+ });
950
+ ```
951
+
952
+ #### `client.issues.restore(issueId)`
953
+
954
+ Restore a soft-deleted issue.
955
+
956
+ ```typescript
957
+ const issue = await client.issues.restore('issue-uuid');
958
+ ```
959
+
960
+ #### `client.issues.undoLastChange(issueId)`
961
+
962
+ Undo the last status change on an issue.
963
+
964
+ ```typescript
965
+ const issue = await client.issues.undoLastChange('issue-uuid');
966
+ ```
967
+
968
+ #### `client.issues.bulkUpdateStatus(updates)`
969
+
970
+ Bulk update issue statuses.
971
+
972
+ ```typescript
973
+ const results = await client.issues.bulkUpdateStatus([
974
+ { issueId: 'issue-1', status: 'completed' },
975
+ { issueId: 'issue-2', status: 'deferred', reason: 'Not a priority' },
976
+ ]);
977
+
978
+ // Each result includes per-item success/failure — some items may fail
979
+ // while others succeed (e.g., issue not found, invalid status transition)
980
+ for (const result of results) {
981
+ if (!result.success) {
982
+ console.warn(`Failed to update ${result.issueId}: ${result.error}`);
983
+ }
984
+ }
985
+ ```
986
+
987
+ Maximum 100 items per bulk request. The SDK validates this limit client-side via Zod before sending.
988
+
989
+ #### `client.issues.listByProject(projectId, query)`
990
+
991
+ List issues for a specific project.
992
+
993
+ ```typescript
994
+ const issues = await client.issues.listByProject('my-project', {
995
+ status: 'open',
996
+ limit: 20,
997
+ });
998
+ ```
999
+
1000
+ ---
1001
+
1002
+ ### Analytics Operations
1003
+
1004
+ Get insights and metrics about validation trends.
1005
+
1006
+ #### `client.analytics.getValidatorPerformance(query)`
1007
+
1008
+ Get performance metrics by validator.
1009
+
1010
+ | Parameter | Type | Required | Description |
1011
+ |-----------|------|----------|-------------|
1012
+ | `project` | `string` | No | Filter by project |
1013
+ | `days` | `number` | No | Time window (default: 30) |
1014
+
1015
+ ```typescript
1016
+ const performance = await client.analytics.getValidatorPerformance({ days: 30 });
1017
+ for (const validator of performance) {
1018
+ console.log(`${validator.validator}: avg=${validator.avgScore}, pass=${validator.passRate}`);
1019
+ }
1020
+ ```
1021
+
1022
+ #### `client.analytics.getValidatorReliability(query)`
1023
+
1024
+ Get validator reliability statistics (false positive rates, resolution rates).
1025
+
1026
+ | Parameter | Type | Required | Description |
1027
+ |-----------|------|----------|-------------|
1028
+ | `validator` | `string` | No | Filter by validator |
1029
+ | `days` | `number` | No | Time window (default: 90) |
1030
+
1031
+ ```typescript
1032
+ const { validators } = await client.analytics.getValidatorReliability({ days: 90 });
1033
+ for (const v of validators) {
1034
+ console.log(`${v.validator}: reliability=${v.reliabilityScore}`);
1035
+ }
1036
+ ```
1037
+
1038
+ #### `client.analytics.getResolutionRates(query)`
1039
+
1040
+ Get issue resolution rates by project.
1041
+
1042
+ ```typescript
1043
+ const rates = await client.analytics.getResolutionRates();
1044
+ for (const rate of rates) {
1045
+ console.log(`${rate.project}: ${rate.rate * 100}% resolved`);
1046
+ }
1047
+ ```
1048
+
1049
+ #### `client.analytics.getFileHotspots(query)`
1050
+
1051
+ Get files with the most issues.
1052
+
1053
+ | Parameter | Type | Required | Description |
1054
+ |-----------|------|----------|-------------|
1055
+ | `project` | `string` | No | Filter by project |
1056
+ | `days` | `number` | No | Time window |
1057
+ | `limit` | `number` | No | Max results |
1058
+
1059
+ ```typescript
1060
+ const hotspots = await client.analytics.getFileHotspots({ limit: 10 });
1061
+ for (const hotspot of hotspots) {
1062
+ console.log(`${hotspot.filePath}: ${hotspot.issueCount} issues`);
1063
+ }
1064
+ ```
1065
+
1066
+ #### `client.analytics.getTaxonomyDistribution(query)`
1067
+
1068
+ Get issue distribution by failure domain.
1069
+
1070
+ ```typescript
1071
+ const distribution = await client.analytics.getTaxonomyDistribution();
1072
+ for (const d of distribution) {
1073
+ console.log(`${d.domain}: ${d.count} issues`);
1074
+ }
1075
+ // Output: STR: 50, SEM: 80, PRA: 30, EPI: 20
1076
+ ```
1077
+
1078
+ #### `client.analytics.getFullTaxonomy(query)`
1079
+
1080
+ Get comprehensive taxonomy analytics.
1081
+
1082
+ ```typescript
1083
+ const { data, computedAt } = await client.analytics.getFullTaxonomy();
1084
+ console.log('By domain:', data.byDomain);
1085
+ console.log('By mode:', data.byMode);
1086
+ console.log('By severity:', data.bySeverity);
1087
+ ```
1088
+
1089
+ #### `client.analytics.getBurndown(query)`
1090
+
1091
+ Get burndown time series by failure domain.
1092
+
1093
+ | Parameter | Type | Required | Description |
1094
+ |-----------|------|----------|-------------|
1095
+ | `project` | `string` | No | Filter by project |
1096
+ | `days` | `number` | No | Time window (default: 30) |
1097
+ | `granularity` | `string` | No | `'daily'` or `'weekly'` |
1098
+
1099
+ ```typescript
1100
+ const burndown = await client.analytics.getBurndown({ days: 30 });
1101
+ console.log('Time series:', burndown.timeSeries);
1102
+ console.log('Trends:', burndown.trends);
1103
+ // { STR: { direction: 'declining', rate: -0.05 }, ... }
1104
+ ```
1105
+
1106
+ #### `client.analytics.getVelocity(query)`
1107
+
1108
+ Get velocity metrics per failure mode.
1109
+
1110
+ | Parameter | Type | Required | Description |
1111
+ |-----------|------|----------|-------------|
1112
+ | `project` | `string` | No | Filter by project |
1113
+ | `days` | `number` | No | Time window |
1114
+ | `alertThreshold` | `number` | No | Velocity threshold for alerts |
1115
+
1116
+ ```typescript
1117
+ const velocity = await client.analytics.getVelocity({ alertThreshold: 10 });
1118
+ for (const mode of velocity.modes) {
1119
+ console.log(`${mode.mode}: velocity=${mode.velocity}, trend=${mode.trend}`);
1120
+ }
1121
+ console.log('Alerts:', velocity.alerts);
1122
+ ```
1123
+
1124
+ #### `client.analytics.getDiscovery(query)`
1125
+
1126
+ Get discovery timeline (new vs recurring issues).
1127
+
1128
+ | Parameter | Type | Required | Description |
1129
+ |-----------|------|----------|-------------|
1130
+ | `project` | `string` | No | Filter by project |
1131
+ | `days` | `number` | No | Time window |
1132
+ | `groupBy` | `string` | No | `'day'`, `'week'`, `'month'` |
1133
+
1134
+ ```typescript
1135
+ const discovery = await client.analytics.getDiscovery({ groupBy: 'week' });
1136
+ console.log('Timeline:', discovery.timeline);
1137
+ console.log('Summary:', discovery.summary);
1138
+ // { totalNew: 8, totalRecurring: 22, newRate: 0.27 }
1139
+ ```
1140
+
1141
+ #### `client.analytics.getValidatorMatrix(query)`
1142
+
1143
+ Get validator-taxonomy coverage matrix.
1144
+
1145
+ | Parameter | Type | Required | Description |
1146
+ |-----------|------|----------|-------------|
1147
+ | `project` | `string` | No | Filter by project |
1148
+ | `days` | `number` | No | Time window (default: 90) |
1149
+ | `minIssues` | `number` | No | Min issues for inclusion |
1150
+
1151
+ ```typescript
1152
+ const matrix = await client.analytics.getValidatorMatrix();
1153
+ console.log('Coverage matrix:', matrix.matrix);
1154
+ console.log('Blind spots:', matrix.blindSpots); // Domains not detected
1155
+ console.log('Single points:', matrix.singlePoints); // Only one validator detects
1156
+ console.log('High overlap:', matrix.highOverlap); // 3+ validators detect
1157
+ ```
1158
+
1159
+ #### `client.analytics.getTrendSummary(query)`
1160
+
1161
+ Get general trend summary.
1162
+
1163
+ ```typescript
1164
+ const trends = await client.analytics.getTrendSummary();
1165
+ for (const trend of trends) {
1166
+ console.log(`${trend.metric}: ${trend.value} (${trend.change > 0 ? '+' : ''}${trend.change})`);
1167
+ }
1168
+ ```
1169
+
1170
+ #### `client.analytics.getByMetric(metric, query)`
1171
+
1172
+ Get analytics by specific metric name.
1173
+
1174
+ ```typescript
1175
+ const costData = await client.analytics.getByMetric('cost_analysis', { days: 30 });
1176
+ const regressions = await client.analytics.getByMetric('regression_analysis', { project: 'my-project' });
1177
+ ```
1178
+
1179
+ ---
1180
+
1181
+ ### Taxonomy Operations
1182
+
1183
+ Get the failure taxonomy schema.
1184
+
1185
+ #### `client.taxonomy.get()`
1186
+
1187
+ Get the full taxonomy schema with domains, modes, and severities.
1188
+
1189
+ ```typescript
1190
+ const taxonomy = await client.taxonomy.get();
1191
+
1192
+ console.log('Domains:', taxonomy.domains);
1193
+ // [
1194
+ // { code: 'STR', name: 'Structural', modes: ['OMI', 'RED', 'MIS'] },
1195
+ // { code: 'SEM', name: 'Semantic', modes: ['VAL', 'TYP', 'LOG'] },
1196
+ // { code: 'PRA', name: 'Pragmatic', modes: ['PER', 'SEC', 'MAI'] },
1197
+ // { code: 'EPI', name: 'Epistemic', modes: ['DOC', 'NAM', 'CLR'] },
1198
+ // ]
1199
+
1200
+ console.log('Severities:', taxonomy.severities);
1201
+ // ['critical', 'high', 'medium', 'low', 'info']
1202
+
1203
+ console.log('Priorities:', taxonomy.priorities);
1204
+ // ['critical', 'suggested', 'backlog']
1205
+ ```
1206
+
1207
+ ---
1208
+
1209
+ ### Health Check
1210
+
1211
+ Check API server status. This endpoint does not require authentication.
1212
+
1213
+ ```typescript
1214
+ import { OpsHttpClient } from '@uluops/ops-sdk';
1215
+ import type { HealthResponse } from '@uluops/ops-sdk/types';
1216
+
1217
+ const http = new OpsHttpClient({ baseUrl: 'http://localhost:3100/api/v1' });
1218
+ const health = await http.get<HealthResponse>('/health');
1219
+ console.log(health.status); // 'ok' | 'degraded' | 'unhealthy'
1220
+ console.log(health.version); // API version
1221
+ console.log(health.database); // { connected: boolean, latencyMs: number }
1222
+ ```
1223
+
1224
+ ---
1225
+
1226
+ ### Admin Operations
1227
+
1228
+ Administrative operations (requires admin role).
1229
+
1230
+ #### `client.admin.getStats()`
1231
+
1232
+ Get dashboard statistics.
1233
+
1234
+ ```typescript
1235
+ const stats = await client.admin.getStats();
1236
+ console.log(`Users: ${stats.totalUsers} (${stats.activeUsers} active)`);
1237
+ console.log(`Projects: ${stats.totalProjects}`);
1238
+ console.log(`Runs: ${stats.totalRuns}`);
1239
+ console.log(`Issues: ${stats.totalIssues}`);
1240
+ ```
1241
+
1242
+ #### `client.admin.listUsers(query)`
1243
+
1244
+ List all users with pagination.
1245
+
1246
+ | Parameter | Type | Required | Description |
1247
+ |-----------|------|----------|-------------|
1248
+ | `role` | `string` | No | Filter by role |
1249
+ | `search` | `string` | No | Search by email |
1250
+ | `page` | `number` | No | Page number |
1251
+ | `limit` | `number` | No | Results per page |
1252
+
1253
+ ```typescript
1254
+ const { users, pagination } = await client.admin.listUsers({
1255
+ role: 'admin',
1256
+ limit: 20,
1257
+ });
1258
+ ```
1259
+
1260
+ #### `client.admin.getUser(userId)`
1261
+
1262
+ Get user details with stats.
1263
+
1264
+ ```typescript
1265
+ const { user, stats } = await client.admin.getUser('user-id');
1266
+ console.log(`${user.email}: ${stats.projectCount} projects, ${stats.runCount} runs`);
1267
+ ```
1268
+
1269
+ #### `client.admin.createUser(input)`
1270
+
1271
+ Create a new user.
1272
+
1273
+ | Parameter | Type | Required | Description |
1274
+ |-----------|------|----------|-------------|
1275
+ | `email` | `string` | Yes | User email |
1276
+ | `password` | `string` | No | User password |
1277
+ | `role` | `string` | No | User role |
1278
+ | `sendWelcomeEmail` | `boolean` | No | Send welcome email |
1279
+
1280
+ ```typescript
1281
+ const { user, temporaryPassword } = await client.admin.createUser({
1282
+ email: 'newuser@example.com',
1283
+ role: 'developer',
1284
+ sendWelcomeEmail: true,
1285
+ });
1286
+ ```
1287
+
1288
+ #### `client.admin.updateUser(userId, input)`
1289
+
1290
+ Update user attributes.
1291
+
1292
+ | Parameter | Type | Required | Description |
1293
+ |-----------|------|----------|-------------|
1294
+ | `role` | `string` | No | New role |
1295
+ | `subscriptionTier` | `string` | No | New subscription tier |
1296
+
1297
+ ```typescript
1298
+ const { user } = await client.admin.updateUser('user-id', {
1299
+ role: 'admin',
1300
+ subscriptionTier: 'enterprise',
1301
+ });
1302
+ ```
1303
+
1304
+ #### `client.admin.deactivateUser(userId)`
1305
+
1306
+ Deactivate a user account.
1307
+
1308
+ ```typescript
1309
+ const { user } = await client.admin.deactivateUser('user-id');
1310
+ console.log(`User deactivated: ${user.isActive === false}`);
1311
+ ```
1312
+
1313
+ #### `client.admin.reactivateUser(userId)`
1314
+
1315
+ Reactivate a deactivated user.
1316
+
1317
+ ```typescript
1318
+ const { user } = await client.admin.reactivateUser('user-id');
1319
+ ```
1320
+
1321
+ #### `client.admin.resetUserPassword(userId)`
1322
+
1323
+ Trigger a password reset email for a user.
1324
+
1325
+ ```typescript
1326
+ const { message } = await client.admin.resetUserPassword('user-id');
1327
+ ```
1328
+
1329
+ #### `client.admin.bulkDeactivate(userIds)`
1330
+
1331
+ Bulk deactivate multiple users.
1332
+
1333
+ ```typescript
1334
+ const result = await client.admin.bulkDeactivate(['user-1', 'user-2', 'user-3']);
1335
+ console.log(`Deactivated: ${result.success}, Failed: ${result.failed}`);
1336
+ ```
1337
+
1338
+ #### `client.admin.listSessions(query)`
1339
+
1340
+ List all active sessions.
1341
+
1342
+ | Parameter | Type | Required | Description |
1343
+ |-----------|------|----------|-------------|
1344
+ | `userId` | `string` | No | Filter by user |
1345
+ | `page` | `number` | No | Page number |
1346
+ | `limit` | `number` | No | Results per page |
1347
+
1348
+ ```typescript
1349
+ const { sessions, pagination } = await client.admin.listSessions({ userId: 'user-1' });
1350
+ ```
1351
+
1352
+ #### `client.admin.terminateSession(sessionId)`
1353
+
1354
+ Terminate a specific session.
1355
+
1356
+ ```typescript
1357
+ const { message } = await client.admin.terminateSession('session-id');
1358
+ ```
1359
+
1360
+ #### `client.admin.terminateUserSessions(userId)`
1361
+
1362
+ Terminate all sessions for a user.
1363
+
1364
+ ```typescript
1365
+ const { message } = await client.admin.terminateUserSessions('user-id');
1366
+ ```
1367
+
1368
+ #### `client.admin.listKeys(query)`
1369
+
1370
+ List all API keys.
1371
+
1372
+ | Parameter | Type | Required | Description |
1373
+ |-----------|------|----------|-------------|
1374
+ | `userId` | `string` | No | Filter by user |
1375
+ | `search` | `string` | No | Search by name |
1376
+ | `page` | `number` | No | Page number |
1377
+ | `limit` | `number` | No | Results per page |
1378
+
1379
+ ```typescript
1380
+ const { keys, pagination } = await client.admin.listKeys({ search: 'production' });
1381
+ ```
1382
+
1383
+ #### `client.admin.revokeKey(keyId)`
1384
+
1385
+ Revoke an API key.
1386
+
1387
+ ```typescript
1388
+ const { message } = await client.admin.revokeKey('key-id');
1389
+ ```
1390
+
1391
+ ---
1392
+
1393
+ ## CLI
1394
+
1395
+ For command-line usage, see the dedicated CLI package: [`@uluops/cli`](https://www.npmjs.com/package/@uluops/cli).
1396
+
1397
+ ## Environment Variables
1398
+
1399
+ | Variable | Description | Default |
1400
+ |----------|-------------|---------|
1401
+ | `ULUOPS_API_KEY` | API key for authentication | - |
1402
+ | `ULUOPS_EMAIL` | Email for session auth | - |
1403
+ | `ULUOPS_PASSWORD` | Password for session auth | - |
1404
+ | `ULUOPS_BASE_URL` | API base URL | `http://localhost:3100/api/v1` |
1405
+ | `ULUOPS_DEBUG` | Enable debug logging | `false` |
1406
+
1407
+ Create a `.env` file in your project:
1408
+
1409
+ ```env
1410
+ ULUOPS_API_KEY=ulr_your-api-key-here
1411
+ ULUOPS_BASE_URL=https://api.uluops.com/api/v1
1412
+ ```
1413
+
1414
+ Or configure globally in `~/.uluops/.env`.
1415
+
1416
+ > **Note:** The SDK defaults to `http://localhost:3100/api/v1` for local development. For production use, always set `ULUOPS_BASE_URL` or pass `baseUrl` to the constructor.
1417
+
1418
+ ## Error Handling
1419
+
1420
+ The SDK provides typed error classes for precise error handling:
1421
+
1422
+ ```typescript
1423
+ import {
1424
+ OpsApiError,
1425
+ ValidationError,
1426
+ UnauthorizedError,
1427
+ ForbiddenError,
1428
+ NotFoundError,
1429
+ ConflictError,
1430
+ RateLimitError,
1431
+ ServiceUnavailableError,
1432
+ NetworkError,
1433
+ TimeoutError,
1434
+ isOpsApiError,
1435
+ } from '@uluops/ops-sdk/errors';
1436
+
1437
+ try {
1438
+ await client.projects.get('non-existent');
1439
+ } catch (error) {
1440
+ if (error instanceof NotFoundError) {
1441
+ console.log(error.message); // "Project 'non-existent' not found"
1442
+ console.log(error.details); // { id: 'non-existent' }
1443
+ } else if (error instanceof UnauthorizedError) {
1444
+ console.log('Please authenticate');
1445
+ } else if (error instanceof RateLimitError) {
1446
+ console.log(`Rate limited. Retry after ${error.details?.retryAfter}s`);
1447
+ } else if (error instanceof ValidationError) {
1448
+ console.log('Invalid input:', error.details);
1449
+ } else if (isOpsApiError(error)) {
1450
+ console.log(`API error: ${error.code} - ${error.message}`);
1451
+ }
1452
+ }
1453
+ ```
1454
+
1455
+ ### Error Classes
1456
+
1457
+ | Error | Status | Description |
1458
+ |-------|--------|-------------|
1459
+ | `ValidationError` | 400 | Invalid request data |
1460
+ | `UnauthorizedError` | 401 | Authentication required |
1461
+ | `ForbiddenError` | 403 | Access denied |
1462
+ | `NotFoundError` | 404 | Resource not found |
1463
+ | `ConflictError` | 409 | Resource conflict |
1464
+ | `RateLimitError` | 429 | Rate limit exceeded |
1465
+ | `ServiceUnavailableError` | 503 | Server unavailable |
1466
+ | `NetworkError` | - | Connection error |
1467
+ | `TimeoutError` | - | Request timeout |
1468
+
1469
+ ### Automatic Retries
1470
+
1471
+ The SDK automatically retries on transient errors (502, 503, 504, 429) with exponential backoff:
1472
+
1473
+ ```typescript
1474
+ const client = new OpsClient({
1475
+ apiKey: 'ulr_...',
1476
+ retries: 3, // Number of retry attempts (default: 3)
1477
+ timeout: 30000, // Request timeout in ms (default: 30000)
1478
+ });
1479
+ ```
1480
+
1481
+ ### Request Size Limits
1482
+
1483
+ The API enforces the following payload limits:
1484
+
1485
+ - **Request body**: 1 MB maximum for most endpoints
1486
+ - **`raw_markdown`** field in `runs.save()`: up to 100,000 characters
1487
+ - **Recommendations array**: no hard limit, but very large arrays (1000+) may cause timeouts
1488
+ - **Bulk operations**: capped at 100 items per request (e.g., `bulkUpdateStatus`)
1489
+
1490
+ Requests exceeding these limits will receive a `413 Payload Too Large` or `422 Unprocessable Entity` response.
1491
+
1492
+ ## Input Validation
1493
+
1494
+ The SDK includes Zod-based runtime validators for all mutating operations. Import them from `@uluops/ops-sdk/config`:
1495
+
1496
+ ```typescript
1497
+ import { validateCreateProjectInput, InputValidationError } from '@uluops/ops-sdk/config';
1498
+
1499
+ try {
1500
+ const validated = validateCreateProjectInput({ name: '' }); // throws
1501
+ } catch (error) {
1502
+ if (error instanceof InputValidationError) {
1503
+ console.log('Validation errors:', error.errors);
1504
+ // => [{ code: 'too_small', minimum: 1, path: ['name'], message: 'String must contain at least 1 character(s)' }]
1505
+ }
1506
+ }
1507
+ ```
1508
+
1509
+ Available validators: `validateRegisterInput`, `validateLoginInput`, `validateCreateProjectInput`, `validateSaveFeaturesListInput`, `validateCreateUserIssueInput`, `validateUpdateIssueStatusInput`, `validateBulkStatusUpdateInput`, and more. See [`src/config/validators.ts`](./src/config/validators.ts) for the full list.
1510
+
1511
+ ## Advanced Usage
1512
+
1513
+ ### Using the Low-Level HTTP Client
1514
+
1515
+ For advanced use cases, you can use `OpsHttpClient` directly:
1516
+
1517
+ ```typescript
1518
+ import { OpsHttpClient } from '@uluops/ops-sdk';
1519
+
1520
+ const http = new OpsHttpClient({
1521
+ baseUrl: 'http://localhost:3100/api/v1',
1522
+ apiKey: 'ulr_...',
1523
+ });
1524
+
1525
+ // Make raw requests
1526
+ const data = await http.get<MyType>('/custom/endpoint', { param: 'value' });
1527
+ const result = await http.post<MyType>('/custom/endpoint', { body: 'data' });
1528
+ const raw = await http.requestRaw('GET', '/endpoint'); // Without data unwrapping
1529
+ ```
1530
+
1531
+ ### Custom Authentication Strategy
1532
+
1533
+ ```typescript
1534
+ import { OpsHttpClient, createAuthStrategy } from '@uluops/ops-sdk';
1535
+
1536
+ // Create auth strategy manually
1537
+ const authStrategy = createAuthStrategy({
1538
+ apiKey: 'ulr_...',
1539
+ // or
1540
+ sessionToken: 'jwt-token',
1541
+ });
1542
+
1543
+ const http = new OpsHttpClient({
1544
+ baseUrl: 'http://localhost:3100/api/v1',
1545
+ // Auth strategy will be created from config
1546
+ });
1547
+ ```
1548
+
1549
+ ### Loading Credentials Programmatically
1550
+
1551
+ ```typescript
1552
+ import { loadCredentials, loadConfig } from '@uluops/ops-sdk/config';
1553
+
1554
+ // Load from environment and config files
1555
+ const credentials = loadCredentials();
1556
+ console.log(credentials.apiKey);
1557
+ console.log(credentials.email);
1558
+
1559
+ // Load full config
1560
+ const config = loadConfig();
1561
+ console.log(config.baseUrl);
1562
+ ```
1563
+
1564
+ ## Troubleshooting
1565
+
1566
+ ### Invalid API Key Format
1567
+
1568
+ ```
1569
+ Error: Invalid API key format. Keys must start with 'ulr_'
1570
+ ```
1571
+
1572
+ Ensure your API key starts with the `ulr_` prefix. Generate a new key if needed:
1573
+
1574
+ ```typescript
1575
+ const { key } = await client.auth.createApiKey({ name: 'My Key' });
1576
+ console.log(key); // ulr_abc123...
1577
+ ```
1578
+
1579
+ ### Authentication Errors
1580
+
1581
+ ```
1582
+ UnauthorizedError: Authentication required
1583
+ ```
1584
+
1585
+ Check that:
1586
+ 1. Your API key is valid and not revoked
1587
+ 2. Environment variables are set correctly
1588
+ 3. The `.env` file is in the correct location
1589
+
1590
+ ```bash
1591
+ # Verify environment
1592
+ echo $ULUOPS_API_KEY
1593
+ ```
1594
+
1595
+ ### Connection Errors
1596
+
1597
+ ```
1598
+ NetworkError: connect ECONNREFUSED 127.0.0.1:3100
1599
+ ```
1600
+
1601
+ Verify the API server is running and accessible:
1602
+
1603
+ ```bash
1604
+ curl http://localhost:3100/api/v1/health
1605
+ ```
1606
+
1607
+ Or configure the correct base URL:
1608
+
1609
+ ```typescript
1610
+ const client = new OpsClient({
1611
+ baseUrl: 'https://api.uluops.com/api/v1',
1612
+ apiKey: 'ulr_...',
1613
+ });
1614
+ ```
1615
+
1616
+ ### Rate Limiting
1617
+
1618
+ ```
1619
+ RateLimitError: Rate limit exceeded. Retry after 60 seconds
1620
+ ```
1621
+
1622
+ The SDK automatically retries rate-limited requests. For high-volume operations:
1623
+
1624
+ 1. Batch operations where possible
1625
+ 2. Increase retry count: `retries: 5`
1626
+ 3. Add delays between requests
1627
+
1628
+ ### Debug Mode
1629
+
1630
+ Enable debug logging to see request/response details:
1631
+
1632
+ ```typescript
1633
+ const client = new OpsClient({
1634
+ apiKey: 'ulr_...',
1635
+ debug: true,
1636
+ });
1637
+ ```
1638
+
1639
+ Or via environment:
1640
+
1641
+ ```bash
1642
+ ULUOPS_DEBUG=true node app.js
1643
+ ```
1644
+
1645
+ ## License
1646
+
1647
+ MIT License - see [LICENSE](./LICENSE) for details.