@taiga-ai/mcp-server 0.26.0-dev.485 → 0.26.0-dev.487

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 (2) hide show
  1. package/dist/index.js +83 -13
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -913,8 +913,9 @@ const INITIATIVE_STATES = [
913
913
  * for every reader that means "on the line": the flag the API clears when an
914
914
  * initiative leaves it, the remediation settle on the same departure, and the
915
915
  * MCP's account of where an initiative stands (PRO-2840). Everything else
916
- * (backlog, todo, done, canceled) is off it. The lifecycle derivation does not
917
- * read it: it derives the stage from the run alone (PRO-2828).
916
+ * (backlog, todo, done, canceled) is off it. The lifecycle derivation draws a
917
+ * narrower line (PRO-2828): a run that ended is the initiative's state only at
918
+ * a station, Plan or Build; in Queue it is history, as it is off the line.
918
919
  */
919
920
  const LINE_STATES = [
920
921
  "queue",
@@ -1384,6 +1385,49 @@ z.object({
1384
1385
  ...e.failingChecks === void 0 ? {} : { failingChecks: e.failingChecks },
1385
1386
  ...e.blockers === void 0 ? {} : { blockers: e.blockers }
1386
1387
  }));
1388
+ /**
1389
+ * What a run's hardening came to, coverage included (PRO-2829). `passed`
1390
+ * counted only blocking findings, so a run whose scanners never ran said
1391
+ * "passed" all the same.
1392
+ *
1393
+ * - `failed`: blocking findings remain (the gaps are still listed).
1394
+ * - `not_scanned`: no scanner that applies to the repository ran to the end.
1395
+ * - `passed_with_gaps`: nothing blocks, but some scanner did not run.
1396
+ * - `passed`: nothing blocks and every scanner that applies ran.
1397
+ */
1398
+ const HARDENING_VERDICTS = [
1399
+ "passed",
1400
+ "passed_with_gaps",
1401
+ "not_scanned",
1402
+ "failed"
1403
+ ];
1404
+ /**
1405
+ * The verdict and the gaps of a hardening result (PRO-2829), for every reader
1406
+ * (the run read, the web's run page, the MCP), from what the agent reported.
1407
+ *
1408
+ * A gap is a scanner that applies and did not run: `not_run` (the scan never
1409
+ * reached it), `skipped` (its tool was missing), or `failed` with a reason,
1410
+ * which is a scanner that could not finish. A `failed` scanner without one
1411
+ * found something (tflint fails on a MAJOR that blocks nothing, and rows from
1412
+ * before `scannerReasons` used `failed` for "had findings"), so it ran and is
1413
+ * no gap. A scanner listed in `notApplicable` is no gap; on a result from
1414
+ * before that list, a skipped tflint is read the same way, as the only reason
1415
+ * the agent ever skipped it was a repository with no Terraform.
1416
+ */
1417
+ function hardeningCoverage(result) {
1418
+ const notApplicable = new Set(result.notApplicable ?? []);
1419
+ const applies = Object.entries(result.scanners ?? {}).filter(([scanner, status]) => !notApplicable.has(scanner) && !(result.notApplicable === void 0 && scanner === "tflint" && status === "skipped"));
1420
+ const reasons = result.scannerReasons ?? {};
1421
+ const gaps = applies.filter(([scanner, status]) => status === "not_run" || status === "skipped" || status === "failed" && scanner in reasons).map(([scanner, status]) => ({
1422
+ scanner,
1423
+ status,
1424
+ reason: reasons[scanner] ?? null
1425
+ }));
1426
+ return {
1427
+ verdict: !result.passed ? "failed" : gaps.length === applies.length ? "not_scanned" : gaps.length > 0 ? "passed_with_gaps" : "passed",
1428
+ gaps
1429
+ };
1430
+ }
1387
1431
  [...BUILD_RUN_ACTIVE_STATUSES.filter((s) => s !== "reviewing")];
1388
1432
  //#endregion
1389
1433
  //#region ../../packages/types/dist/agents.js
@@ -3941,6 +3985,34 @@ function hardeningSaid(blocking, named) {
3941
3985
  const recorded = named.length > shown.length ? `, ${named.length} recorded in taiga_get_run` : "";
3942
3986
  return `${count}: ${shown.join("; ")}${rest > 0 ? `; and ${rest} more${recorded}` : ""}.`;
3943
3987
  }
3988
+ /** How each hardening verdict reads (PRO-2829): "passed" only when every scanner that applies ran. */
3989
+ const VERDICT_SAID = {
3990
+ passed: "Hardening passed: every scanner that applies ran and nothing blocks",
3991
+ passed_with_gaps: "Hardening passed, but not every scanner ran, so its checks are missing from the result",
3992
+ not_scanned: "No security scanner ran in hardening: \"passed\" only means no blocking finding was recorded",
3993
+ failed: "Hardening failed: blocking findings remain"
3994
+ };
3995
+ /**
3996
+ * What a run's hardening came to, coverage included (PRO-2829), from the
3997
+ * verdict the API reads off it, or read here from an API from before that.
3998
+ * Null without a hardening result.
3999
+ */
4000
+ function scanSaid(hardening) {
4001
+ if (!hardening || typeof hardening !== "object") return null;
4002
+ const h = hardening;
4003
+ const coverage = typeof h.verdict === "string" && HARDENING_VERDICTS.includes(h.verdict) && Array.isArray(h.gaps) ? {
4004
+ verdict: h.verdict,
4005
+ gaps: h.gaps
4006
+ } : typeof h.passed === "boolean" && h.scanners ? hardeningCoverage(h) : null;
4007
+ if (!coverage) return null;
4008
+ const gaps = coverage.gaps.map((gap) => `${gap.scanner}${gap.status === "failed" ? " did not finish" : ""}${gap.reason ? ` (${gap.reason})` : ""}`);
4009
+ const missing = gaps.length > 0 ? `; did not run: ${gaps.join(", ")}` : "";
4010
+ return {
4011
+ verdict: coverage.verdict,
4012
+ gaps: gaps.length,
4013
+ said: `${VERDICT_SAID[coverage.verdict]}${missing}.`
4014
+ };
4015
+ }
3944
4016
  /**
3945
4017
  * What each failedReason means for the next move (PRO-2800): reviewers met dispatch_rejected and agent_reported as
3946
4018
  * bare enums. As @taiga/types documents them.
@@ -3967,9 +4039,11 @@ function readableRun(run, opts = {}) {
3967
4039
  const hardening = run["hardeningResult"];
3968
4040
  const blocking = typeof hardening?.blockingFindings === "number" ? hardening.blockingFindings : 0;
3969
4041
  const named = Array.isArray(hardening?.blocking) ? hardening.blocking : [];
4042
+ const scan = scanSaid(hardening);
3970
4043
  return {
3971
4044
  ...rest,
3972
4045
  ...means ? { failedReasonMeans: means } : {},
4046
+ ...scan && scan.gaps + (scan.verdict === "not_scanned" ? 1 : 0) > 0 ? { scanCoverage: scan.said } : {},
3973
4047
  ...blocking > 0 ? { hardeningNote: hardeningSaid(blocking, named) } : {},
3974
4048
  ...opts.row || error === void 0 ? {} : { error },
3975
4049
  ...opts.row || reviewEscalation === void 0 ? {} : { reviewEscalation },
@@ -4171,7 +4245,7 @@ function registerBuildTools(server, client) {
4171
4245
  }));
4172
4246
  server.registerTool("taiga_get_run", {
4173
4247
  title: "Get run",
4174
- description: "Read one run with its steps: outcome, disposition, pull request, hardening, and per step its title, status, commit, files changed, verification and headline (first paragraph of the agent's report). verification is Taiga's own check after the step (tests, lint, build), so a docs-only step can fail on the repository's failing tests (verificationNote says when one failed throughout). A failed or stopped run has stoppedIn, lastProgressAt and failedReasonMeans (what its failedReason means). full: true adds complete instructions and reports. actions: resume and retry_from_failed_step are taiga_resume_initiative; webOnlyActions (start_over) are in the web; to stop it, move the initiative out of Build. A maintenance run has no initiative and is retried from its finding.",
4248
+ description: "Read one run with its steps: outcome, disposition, pull request, hardening (scanCoverage names the security scanners that did not run, and why; hardeningResult.verdict is passed only when every one ran), and per step its title, status, commit, files changed, verification and headline (first paragraph of the agent's report). verification is Taiga's own check after the step (tests, lint, build), so a docs-only step can fail on the repository's failing tests (verificationNote says when one failed throughout). A failed or stopped run has stoppedIn, lastProgressAt and failedReasonMeans (what its failedReason means). full: true adds complete instructions and reports. actions: resume and retry_from_failed_step are taiga_resume_initiative; webOnlyActions (start_over) are in the web; to stop it, move the initiative out of Build. A maintenance run has no initiative and is retried from its finding.",
4175
4249
  inputSchema: z.object({
4176
4250
  runId,
4177
4251
  full: z.boolean().optional().describe("Carry each step's complete instructions and report (default false: a summary)")
@@ -4602,31 +4676,27 @@ const outcomeOf = (item) => withSituation(item, {
4602
4676
  criteria: criteriaCounts(item)
4603
4677
  });
4604
4678
  const outcome = (item) => toolResult(outcomeOf(item));
4605
- /** The statuses an initiative in each board column can have: the API keeps in_progress after a first run. */
4679
+ /** The statuses an initiative in each board column can have: the column decides it (statusForState, PRO-2828). */
4606
4680
  const COLUMN_STATUSES = {
4607
4681
  backlog: [
4608
4682
  "all",
4609
4683
  "open",
4610
- "backlog",
4611
- "in_progress"
4684
+ "backlog"
4612
4685
  ],
4613
4686
  todo: [
4614
4687
  "all",
4615
4688
  "open",
4616
- "backlog",
4617
- "in_progress"
4689
+ "backlog"
4618
4690
  ],
4619
4691
  queue: [
4620
4692
  "all",
4621
4693
  "open",
4622
- "backlog",
4623
- "in_progress"
4694
+ "backlog"
4624
4695
  ],
4625
4696
  plan: [
4626
4697
  "all",
4627
4698
  "open",
4628
- "backlog",
4629
- "in_progress"
4699
+ "backlog"
4630
4700
  ],
4631
4701
  build: [
4632
4702
  "all",
@@ -4679,7 +4749,7 @@ function registerBacklogReads(server, client) {
4679
4749
  "in_progress",
4680
4750
  "done",
4681
4751
  "canceled"
4682
- ]).optional().describe("open (default without state): not done or canceled; all: every one; in_progress covers everything between backlog and done"),
4752
+ ]).optional().describe("open (default without state): not done or canceled; all: every one; in_progress is the Build column, backlog every other open one"),
4683
4753
  planningStatus: z.enum([
4684
4754
  "unplanned",
4685
4755
  "planning",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taiga-ai/mcp-server",
3
- "version": "0.26.0-dev.485",
3
+ "version": "0.26.0-dev.487",
4
4
  "description": "Taiga for AI coding assistants: read products, policies, initiatives and runs, and operate the line, over MCP",
5
5
  "mcpName": "ga.tai/mcp-server",
6
6
  "type": "module",