@taiga-ai/mcp-server 0.16.0-dev.430 → 0.16.0-dev.433

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/index.js CHANGED
@@ -578,6 +578,42 @@ const pagination = {
578
578
  const slug = z.string().max(63).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "A slug is lowercase words joined by hyphens").describe("Product slug, as it appears in Taiga URLs");
579
579
  const remoteUrl = z.string().min(1).max(2048).describe("The git remote URL of a checkout (git remote get-url origin): https://, git@host:owner/repo.git or ssh://");
580
580
  //#endregion
581
+ //#region ../../packages/types/dist/lenient-schema.js
582
+ /**
583
+ * Building blocks for reading JSON that arrives untyped -- a stored jsonb
584
+ * column, an agent's answer, a document body -- as a parsed type rather than
585
+ * a `Record<string, unknown>` narrowed field by field (PRO-2514).
586
+ *
587
+ * They encode the two leniencies a hand-written reader already had, so a
588
+ * schema built from them accepts, drops and defaults exactly what the reader
589
+ * it replaces did:
590
+ *
591
+ * - `lenient`: a field read only when it has the expected type, and otherwise
592
+ * treated as absent. Never wrap a schema whose failure must surface: the
593
+ * catch would swallow it and read a malformed value as "absent".
594
+ * - `lenientList`: a list read item by item, where an item that does not parse
595
+ * is skipped and the rest survive.
596
+ */
597
+ /** A field read only when it has the expected type; anything else reads as absent. */
598
+ function lenient(schema) {
599
+ return schema.optional().catch(void 0);
600
+ }
601
+ /**
602
+ * The item schema of every `lenientList`, which its transform otherwise hides:
603
+ * read by the agent contract test, which walks each schema's keys to hold the
604
+ * shared fixtures to them.
605
+ */
606
+ const lenientListItems = /* @__PURE__ */ new WeakMap();
607
+ /** A list read item by item: an item that does not parse is skipped, as the reader's `continue` did. */
608
+ function lenientList(item) {
609
+ const list = z.array(z.unknown()).transform((items) => items.flatMap((entry) => {
610
+ const parsed = item.safeParse(entry);
611
+ return parsed.success ? [parsed.data] : [];
612
+ }));
613
+ lenientListItems.set(list, item);
614
+ return list;
615
+ }
616
+ //#endregion
581
617
  //#region ../../packages/types/dist/tenants.js
582
618
  const TENANT_DOCUMENT_TYPES = [
583
619
  "policy_security",
@@ -1221,6 +1257,57 @@ const BUILD_RUN_ACTIVE_STATUSES = BUILD_RUN_STATUSES.filter((s) => runDispositio
1221
1257
  * DIFFERENT, narrower concept: transitions a user command may never leave.
1222
1258
  */
1223
1259
  const BUILD_RUN_TERMINAL_STATUSES = BUILD_RUN_STATUSES.filter((s) => runDisposition(s) !== "in_flight");
1260
+ /**
1261
+ * Why the agent kept a pull request a draft, as recorded in its description
1262
+ * (PRO-2298). The API reads these markers; the run's escalation carries them.
1263
+ */
1264
+ const READY_BLOCKERS = [
1265
+ "plan_incomplete",
1266
+ "tests_failed",
1267
+ "bar_lowering",
1268
+ "bar_lowering_unchecked",
1269
+ "criticals_unresolved"
1270
+ ];
1271
+ /**
1272
+ * Why the autonomous loop handed a live pull request to a person (PRO-2409).
1273
+ * One per path that sets `reviewEscalated`:
1274
+ * - `ci_fix_cap`: CI kept failing after the automatic fix rounds ran out;
1275
+ * - `ci_fix_stalled`: a fix round on the failing head pushed nothing;
1276
+ * - `conflict_cap`: the branch still conflicts with its base at the cap;
1277
+ * - `ready_blocked`: CI is green, but the description says why it is a draft;
1278
+ * - `check_weakened`: CI went green only after a fix loosened a check;
1279
+ * - `review_bot_cap`: a review BOT (Copilot, CodeRabbit) kept reviewing after
1280
+ * the automatic rounds ran out (PRO-2480). A bot's review never resets the
1281
+ * streak a person's does, so the agent and the bot cannot loop without end.
1282
+ */
1283
+ const REVIEW_ESCALATION_REASONS = [
1284
+ "ci_fix_cap",
1285
+ "ci_fix_stalled",
1286
+ "conflict_cap",
1287
+ "ready_blocked",
1288
+ "check_weakened",
1289
+ "review_bot_cap"
1290
+ ];
1291
+ /** One failing check as stored: the name is required, and the link survives only as http(s). */
1292
+ const storedFailingCheck = z.object({
1293
+ name: z.string(),
1294
+ url: z.string().regex(/^https?:\/\//i).nullable().catch(null)
1295
+ });
1296
+ z.object({
1297
+ reason: z.enum(REVIEW_ESCALATION_REASONS),
1298
+ rounds: lenient(z.number().refine((n) => Number.isInteger(n) && n >= 0)),
1299
+ headSha: lenient(z.string()),
1300
+ file: lenient(z.string()),
1301
+ failingChecks: lenient(lenientList(storedFailingCheck)),
1302
+ blockers: lenient(lenientList(z.enum(READY_BLOCKERS)))
1303
+ }).transform((e) => ({
1304
+ reason: e.reason,
1305
+ ...e.rounds === void 0 ? {} : { rounds: e.rounds },
1306
+ ...e.headSha === void 0 ? {} : { headSha: e.headSha },
1307
+ ...e.file === void 0 ? {} : { file: e.file },
1308
+ ...e.failingChecks === void 0 ? {} : { failingChecks: e.failingChecks },
1309
+ ...e.blockers === void 0 ? {} : { blockers: e.blockers }
1310
+ }));
1224
1311
  [...BUILD_RUN_ACTIVE_STATUSES.filter((s) => s !== "reviewing")];
1225
1312
  //#endregion
1226
1313
  //#region ../../packages/types/dist/agents.js
@@ -1241,26 +1328,30 @@ const BACKLOG_GENERATION_EMPTY_OUTCOMES = [
1241
1328
  "out_of_scope",
1242
1329
  "needs_decision"
1243
1330
  ];
1244
- function isRecord$1(value) {
1245
- return typeof value === "object" && value !== null;
1246
- }
1247
- function isStringOrNull(value) {
1248
- return value === null || typeof value === "string";
1249
- }
1250
- function isDecisionReason(value) {
1251
- if (!isRecord$1(value)) return false;
1252
- const { policyCode, quotedSpan, conflict, alternative, overridePath } = value;
1253
- return [
1254
- policyCode,
1255
- quotedSpan,
1256
- conflict,
1257
- alternative,
1258
- overridePath
1259
- ].every((f) => typeof f === "string");
1260
- }
1261
- function isCoveringInitiative(value) {
1262
- return isRecord$1(value) && typeof value.id === "string" && value.id.length > 0 && typeof value.title === "string" && typeof value.priority === "number";
1263
- }
1331
+ const decisionReason = z.object({
1332
+ policyCode: z.string(),
1333
+ quotedSpan: z.string(),
1334
+ conflict: z.string(),
1335
+ alternative: z.string(),
1336
+ overridePath: z.string()
1337
+ });
1338
+ const coveringInitiative = z.object({
1339
+ id: z.string().min(1),
1340
+ title: z.string(),
1341
+ priority: z.number()
1342
+ });
1343
+ /**
1344
+ * The empty verdict, every field checked: the reader navigates on
1345
+ * `coveredInitiative.id` and renders `message`.
1346
+ */
1347
+ const backlogGenerationEmptyResult = z.object({
1348
+ outcome: z.enum(BACKLOG_GENERATION_EMPTY_OUTCOMES),
1349
+ message: z.string().nullable(),
1350
+ coveredByPriority: z.number().refine(Number.isInteger).nullable(),
1351
+ coveredById: z.string().nullable(),
1352
+ coveredInitiative: coveringInitiative.nullable(),
1353
+ reason: decisionReason.nullable().optional()
1354
+ });
1264
1355
  /**
1265
1356
  * Narrow an `agent_jobs.result` blob to {@link BacklogGenerationEmptyResult}.
1266
1357
  *
@@ -1270,10 +1361,9 @@ function isCoveringInitiative(value) {
1270
1361
  * would hand the page an undefined id to navigate to.
1271
1362
  */
1272
1363
  function isBacklogGenerationEmptyResult(value) {
1273
- if (!isRecord$1(value)) return false;
1274
- const { outcome, message, coveredByPriority, coveredById, coveredInitiative, reason } = value;
1275
- return typeof outcome === "string" && BACKLOG_GENERATION_EMPTY_OUTCOMES.includes(outcome) && isStringOrNull(message) && (coveredByPriority === null || typeof coveredByPriority === "number" && Number.isInteger(coveredByPriority)) && isStringOrNull(coveredById) && (coveredInitiative === null || isCoveringInitiative(coveredInitiative)) && (reason === null || reason === void 0 || isDecisionReason(reason));
1364
+ return backlogGenerationEmptyResult.safeParse(value).success;
1276
1365
  }
1366
+ const withCreatedInitiativeIds = z.object({ createdInitiativeIds: z.array(z.string().min(1)) });
1277
1367
  /**
1278
1368
  * The initiatives a `generate_backlog` job created, from its stored result
1279
1369
  * (PRO-2483), or null when the result does not say: a job finished before the
@@ -1282,11 +1372,13 @@ function isBacklogGenerationEmptyResult(value) {
1282
1372
  * the reader navigates on them.
1283
1373
  */
1284
1374
  function createdInitiativeIdsOf(result) {
1285
- if (!isRecord$1(result)) return null;
1286
- const ids = result.createdInitiativeIds;
1287
- if (!Array.isArray(ids) || !ids.every((id) => typeof id === "string" && id.length > 0)) return null;
1288
- return ids;
1375
+ const parsed = withCreatedInitiativeIds.safeParse(result);
1376
+ return parsed.success ? parsed.data.createdInitiativeIds : null;
1289
1377
  }
1378
+ const withCreatedInitiatives = z.object({ createdInitiatives: z.array(z.object({
1379
+ id: z.string().min(1),
1380
+ title: z.string()
1381
+ })).min(1) });
1290
1382
  /**
1291
1383
  * The initiatives a `generate_backlog` run added, each with the title it was
1292
1384
  * created with, as the worker writes them on `agent_jobs.result.createdInitiatives`
@@ -1299,13 +1391,8 @@ function createdInitiativeIdsOf(result) {
1299
1391
  * read, so a reader never links an undefined id.
1300
1392
  */
1301
1393
  function createdInitiativesOf(result) {
1302
- if (!isRecord$1(result)) return null;
1303
- const list = result.createdInitiatives;
1304
- if (!Array.isArray(list) || list.length === 0) return null;
1305
- return list.every((entry) => isRecord$1(entry) && typeof entry.id === "string" && entry.id.length > 0 && typeof entry.title === "string") ? list.map((entry) => ({
1306
- id: entry.id,
1307
- title: entry.title
1308
- })) : null;
1394
+ const parsed = withCreatedInitiatives.safeParse(result);
1395
+ return parsed.success ? parsed.data.createdInitiatives : null;
1309
1396
  }
1310
1397
  //#endregion
1311
1398
  //#region ../../packages/types/dist/environments.js
@@ -1322,6 +1409,14 @@ const ENVIRONMENT_TYPES = [
1322
1409
  //#endregion
1323
1410
  //#region ../../packages/types/dist/maintenance.js
1324
1411
  /**
1412
+ * Maintenance taxonomy + API DTOs.
1413
+ *
1414
+ * Single source of truth shared by the `maintenance_*` DB schema, the
1415
+ * maintenance API, the maintenance batch agent's output contract, and the
1416
+ * Maintaining page. Maintenance is the operational-hygiene system of record,
1417
+ * deliberately SEPARATE from the product backlog.
1418
+ */
1419
+ /**
1325
1420
  * - vulnerable_dependency / outdated_dependency: the dependency scanner
1326
1421
  * - secret: the secrets scanner -- a credential committed to the repository
1327
1422
  * - sast: the SAST scanner over Taiga-authored rules -- an insecure code pattern
@@ -1406,6 +1501,34 @@ const MAINTENANCE_IGNORE_REASONS = [
1406
1501
  "wont_fix"
1407
1502
  ];
1408
1503
  new Set(CODE_REMEDIATION_KINDS);
1504
+ /** A non-empty string, or null. */
1505
+ const locationText = z.string().min(1).nullable().catch(null);
1506
+ /**
1507
+ * A source line is a positive integer or nothing: the response schema declares
1508
+ * `int()`, so a fractional value here would fail serialization for the WHOLE
1509
+ * list rather than blank one row; and 0 is "no position" for scanners that
1510
+ * report [0, 0] for a check with no concrete line.
1511
+ */
1512
+ const sourceLine = z.number().refine((n) => Number.isInteger(n) && n > 0).nullable().catch(null);
1513
+ z.object({
1514
+ rule_id: locationText,
1515
+ rule_url: locationText,
1516
+ tool: locationText,
1517
+ line: sourceLine,
1518
+ end_line: sourceLine,
1519
+ resource: locationText,
1520
+ in_tree: z.unknown().optional(),
1521
+ commit: z.string().regex(/^[0-9a-f]{7,40}$/).nullable().catch(null)
1522
+ }).transform((d) => ({
1523
+ ruleId: d.rule_id,
1524
+ ruleUrl: d.rule_url,
1525
+ tool: d.tool,
1526
+ line: d.line,
1527
+ endLine: d.end_line,
1528
+ resource: d.resource,
1529
+ historyOnly: d.in_tree === false,
1530
+ commit: d.commit
1531
+ }));
1409
1532
  //#endregion
1410
1533
  //#region ../../packages/types/dist/design-tokens.js
1411
1534
  /**
@@ -1557,6 +1680,31 @@ const ARTIFACT_SCALE_FRAME_NAMES = [
1557
1680
  [...ARTIFACT_CSS_VARIABLES, ...ARTIFACT_SCALE_FRAME_NAMES];
1558
1681
  new Set(ARTIFACT_CSS_VARIABLES.filter((name) => name !== "--radius"));
1559
1682
  //#endregion
1683
+ //#region ../../packages/types/dist/design-emitter.js
1684
+ /**
1685
+ * Design-system emitter (PRO-887): materialise the tenant's INTERNAL Design
1686
+ * Standards document into real files for a generated application.
1687
+ *
1688
+ * Until this existed the internal path reached code only as plan prose: the
1689
+ * planner named tokens, INSPECT flagged literal colours, but nothing wrote a
1690
+ * token file, so the coder hedged every colour as `var(--x, #hex)`. A linked
1691
+ * design system ships its own package and never needs this -- the dispatcher
1692
+ * gates the emitter off whenever one is linked (single source of truth).
1693
+ *
1694
+ * Pure and deterministic: same document, same bytes. No DOM, no React, no
1695
+ * I/O -- the API computes the files, the coding agent writes them.
1696
+ */
1697
+ const tokenObject = z.record(z.string(), z.unknown());
1698
+ z.object({
1699
+ tokenConvention: lenient(z.string()),
1700
+ tokens: lenient(tokenObject),
1701
+ components: lenient(tokenObject)
1702
+ });
1703
+ z.object({
1704
+ fontFamily: lenient(z.union([z.string(), z.number()])),
1705
+ fontSize: lenient(z.union([z.string(), z.number()]))
1706
+ });
1707
+ //#endregion
1560
1708
  //#region ../../packages/types/dist/auto-build.js
1561
1709
  /**
1562
1710
  * Auto-build: the review-plans gate (PRO-1305).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taiga-ai/mcp-server",
3
- "version": "0.16.0-dev.430",
3
+ "version": "0.16.0-dev.433",
4
4
  "description": "MCP server for Taiga -- gives AI assistants access to documents and products",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -5,9 +5,10 @@
5
5
  *
6
6
  * - Removes `private: true` so npm allows publishing
7
7
  * - Sets publishConfig to public access
8
- * - Strips workspace-only dependencies. `@taiga/types` is compile-time only
9
- * (erased); `@taiga/feature-flags` is a runtime value but is bundled into
10
- * dist/index.js by the rolldown build, so neither is a real runtime dep.
8
+ * - Strips workspace-only dependencies. `@taiga/types` and
9
+ * `@taiga/feature-flags` carry runtime code, but the rolldown build bundles
10
+ * both into dist/index.js, so neither is a real runtime dep. The `zod` that
11
+ * `@taiga/types` imports stays external: it is a declared dependency.
11
12
  * - Drops the `types` entry points — the published artifact is a bundled CLI
12
13
  * and ships no `.d.ts` (the build runs `tsc --noEmit`).
13
14
  * - Marks the bin entry executable. The bundler emits dist/index.js with mode