github-agent 1.2.0__tar.gz → 1.4.1__tar.gz

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 (104) hide show
  1. {github_agent-1.2.0 → github_agent-1.4.1}/PKG-INFO +4 -3
  2. {github_agent-1.2.0 → github_agent-1.4.1}/README.md +3 -2
  3. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/agent_server.py +1 -1
  4. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/kg_ingest.py +122 -1
  5. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/mcp_server.py +93 -0
  6. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/ontology/github.ttl +83 -0
  7. github_agent-1.4.1/github_agent/skills/github-backlog-planner/SKILL.md +113 -0
  8. github_agent-1.4.1/github_agent/skills/github-backlog-planner/references/addressed-heuristics.md +59 -0
  9. github_agent-1.4.1/github_agent/skills/github-backlog-planner/scripts/build_plan.py +178 -0
  10. github_agent-1.4.1/github_agent/skills/github-ci-failure-sweep/SKILL.md +111 -0
  11. github_agent-1.4.1/github_agent/skills/github-ci-failure-sweep/references/actions-tool-cheatsheet.md +60 -0
  12. github_agent-1.4.1/github_agent/skills/github-ci-failure-sweep/scripts/summarize_runs.py +156 -0
  13. github_agent-1.4.1/github_agent/skills/github-org-remediation-loop/SKILL.md +295 -0
  14. github_agent-1.4.1/github_agent/skills/github-org-remediation-loop/scripts/classify_fix_risk.py +169 -0
  15. github_agent-1.4.1/github_agent/skills/github-pr-review-sweep/SKILL.md +112 -0
  16. github_agent-1.4.1/github_agent/skills/github-pr-review-sweep/references/pulls-tool-cheatsheet.md +42 -0
  17. github_agent-1.4.1/github_agent/skills/github-pr-review-sweep/scripts/summarize_prs.py +171 -0
  18. github_agent-1.4.1/github_agent/skills/github-project-provisioner/SKILL.md +58 -0
  19. github_agent-1.4.1/github_agent/skills/github-project-provisioner/scripts/enable_pages.py +53 -0
  20. github_agent-1.4.1/github_agent/skills/github-tools/SKILL.md +152 -0
  21. github_agent-1.4.1/github_agent/skills/github-tools/code-reviewer.md +146 -0
  22. github_agent-1.4.1/github_agent/skills/github-tools/scripts/fetch_comments.py +249 -0
  23. github_agent-1.4.1/github_agent/skills/github-tools/scripts/inspect_pr_checks.py +532 -0
  24. github_agent-1.4.1/github_agent/skills/github-triage-resolver/SKILL.md +116 -0
  25. github_agent-1.4.1/github_agent/skills/github-triage-resolver/references/resolution-rules.md +64 -0
  26. github_agent-1.4.1/github_agent/skills/github-triage-resolver/scripts/classify_safe.py +212 -0
  27. github_agent-1.4.1/github_agent/skills/github-triage-resolver/scripts/gh_write.py +131 -0
  28. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent.egg-info/PKG-INFO +4 -3
  29. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent.egg-info/SOURCES.txt +21 -0
  30. {github_agent-1.2.0 → github_agent-1.4.1}/pyproject.toml +1 -1
  31. {github_agent-1.2.0 → github_agent-1.4.1}/tests/test_kg_ingest.py +95 -0
  32. {github_agent-1.2.0 → github_agent-1.4.1}/LICENSE +0 -0
  33. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/__init__.py +0 -0
  34. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/__main__.py +0 -0
  35. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/api/__init__.py +0 -0
  36. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/api/api_client_base.py +0 -0
  37. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/api/api_client_branches.py +0 -0
  38. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/api/api_client_commits.py +0 -0
  39. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/api/api_client_contents.py +0 -0
  40. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/api/api_client_dependabot.py +0 -0
  41. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/api/api_client_issues.py +0 -0
  42. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/api/api_client_orgs.py +0 -0
  43. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/api/api_client_pages.py +0 -0
  44. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/api/api_client_pulls.py +0 -0
  45. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/api/api_client_releases.py +0 -0
  46. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/api/api_client_repos.py +0 -0
  47. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/api/api_client_search.py +0 -0
  48. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/api/api_client_workflows.py +0 -0
  49. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/api_client.py +0 -0
  50. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/auth.py +0 -0
  51. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/connectors/__init__.py +0 -0
  52. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/connectors/mcp_source_presets.json +0 -0
  53. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/github_gql.py +0 -0
  54. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/github_input_models.py +0 -0
  55. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/github_response_models.py +0 -0
  56. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/mcp/__init__.py +0 -0
  57. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/mcp/mcp_action.py +0 -0
  58. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/mcp/mcp_branch.py +0 -0
  59. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/mcp/mcp_collaborator.py +0 -0
  60. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/mcp/mcp_commit.py +0 -0
  61. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/mcp/mcp_content.py +0 -0
  62. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/mcp/mcp_dependabot.py +0 -0
  63. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/mcp/mcp_issue.py +0 -0
  64. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/mcp/mcp_org.py +0 -0
  65. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/mcp/mcp_pull.py +0 -0
  66. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/mcp/mcp_release.py +0 -0
  67. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/mcp/mcp_repo.py +0 -0
  68. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/mcp/mcp_search.py +0 -0
  69. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/mcp_config.json +0 -0
  70. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/ontology/__init__.py +0 -0
  71. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/prompts/__init__.py +0 -0
  72. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/prompts/code_collaboration_specialist.json +0 -0
  73. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/prompts/main_agent.json +0 -0
  74. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/skills/__init__.py +0 -0
  75. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/skills/github-actions-ci-review/SKILL.md +0 -0
  76. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/skills/github-dependabot-review/SKILL.md +0 -0
  77. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/skills/github-issue-tracking/SKILL.md +0 -0
  78. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/skills/github-pull-request-create/SKILL.md +0 -0
  79. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/skills/github-pull-request-review/SKILL.md +0 -0
  80. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent/skills/github-repository-management/SKILL.md +0 -0
  81. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent.egg-info/dependency_links.txt +0 -0
  82. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent.egg-info/entry_points.txt +0 -0
  83. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent.egg-info/requires.txt +0 -0
  84. {github_agent-1.2.0 → github_agent-1.4.1}/github_agent.egg-info/top_level.txt +0 -0
  85. {github_agent-1.2.0 → github_agent-1.4.1}/scripts/security_sanitizer.py +0 -0
  86. {github_agent-1.2.0 → github_agent-1.4.1}/scripts/validate_a2a_agent.py +0 -0
  87. {github_agent-1.2.0 → github_agent-1.4.1}/scripts/verify_api_integration.py +0 -0
  88. {github_agent-1.2.0 → github_agent-1.4.1}/setup.cfg +0 -0
  89. {github_agent-1.2.0 → github_agent-1.4.1}/tests/conftest.py +0 -0
  90. {github_agent-1.2.0 → github_agent-1.4.1}/tests/test_action_discovery.py +0 -0
  91. {github_agent-1.2.0 → github_agent-1.4.1}/tests/test_api_client_endpoints.py +0 -0
  92. {github_agent-1.2.0 → github_agent-1.4.1}/tests/test_api_validation.py +0 -0
  93. {github_agent-1.2.0 → github_agent-1.4.1}/tests/test_auth_client_edge_cases.py +0 -0
  94. {github_agent-1.2.0 → github_agent-1.4.1}/tests/test_concept_parity.py +0 -0
  95. {github_agent-1.2.0 → github_agent-1.4.1}/tests/test_github_agent_api_brute_force_coverage.py +0 -0
  96. {github_agent-1.2.0 → github_agent-1.4.1}/tests/test_github_agent_brute_force_coverage.py +0 -0
  97. {github_agent-1.2.0 → github_agent-1.4.1}/tests/test_github_gql.py +0 -0
  98. {github_agent-1.2.0 → github_agent-1.4.1}/tests/test_hardening.py +0 -0
  99. {github_agent-1.2.0 → github_agent-1.4.1}/tests/test_init_dynamics.py +0 -0
  100. {github_agent-1.2.0 → github_agent-1.4.1}/tests/test_mcp_coverage.py +0 -0
  101. {github_agent-1.2.0 → github_agent-1.4.1}/tests/test_model_fields.py +0 -0
  102. {github_agent-1.2.0 → github_agent-1.4.1}/tests/test_org_crud.py +0 -0
  103. {github_agent-1.2.0 → github_agent-1.4.1}/tests/test_pages.py +0 -0
  104. {github_agent-1.2.0 → github_agent-1.4.1}/tests/test_startup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: github-agent
3
- Version: 1.2.0
3
+ Version: 1.4.1
4
4
  Summary: GitHub Agent for MCP
5
5
  Author-email: Audel Rouhi <knucklessg1@gmail.com>
6
6
  License: MIT
@@ -48,7 +48,7 @@ Dynamic: license-file
48
48
  ![PyPI - Wheel](https://img.shields.io/pypi/wheel/github-agent)
49
49
  ![PyPI - Implementation](https://img.shields.io/pypi/implementation/github-agent)
50
50
 
51
- *Version: 1.2.0*
51
+ *Version: 1.4.1*
52
52
 
53
53
  > **Documentation** — Installation, deployment, and usage across the MCP, API, and
54
54
  > CLI interfaces, including the integrated A2A agent server, are maintained in the
@@ -101,6 +101,7 @@ _Auto-generated from the live MCP server — do not edit by hand._
101
101
  | `github_dependabot` | `DEPENDABOTTOOL` | Review and manage GitHub Dependabot vulnerability alerts. |
102
102
  | `github_discover_graphql_schema` | `GRAPHQLTOOL` | Discover the live GitHub GraphQL schema (types, fields, and attributes) in real-time. |
103
103
  | `github_graphql` | `GRAPHQLTOOL` | Execute raw GraphQL queries and mutations natively on GitHub. |
104
+ | `github_ingest_pipelines` | `INGESTTOOL` | Natively ingest GitHub Actions workflow runs into epistemic-graph. |
104
105
  | `github_ingest_repos` | `INGESTTOOL` | Natively ingest GitHub repositories into epistemic-graph as typed :Repository nodes. |
105
106
  | `github_issues` | `ISSUETOOL` | Manage GitHub issues. |
106
107
  | `github_orgs` | `ORGTOOL` | Manage GitHub organizations. |
@@ -195,7 +196,7 @@ _Auto-generated from the live MCP server — do not edit by hand._
195
196
 
196
197
  </details>
197
198
 
198
- _15 action-routed tool(s) (default) · 76 verbose 1:1 tool(s). Each is enabled unless its `<DOMAIN>TOOL` toggle is set false; `MCP_TOOL_MODE` selects the surface (`condensed` default · `verbose` 1:1 · `both`). Auto-generated — do not edit._
199
+ _16 action-routed tool(s) (default) · 76 verbose 1:1 tool(s). Each is enabled unless its `<DOMAIN>TOOL` toggle is set false; `MCP_TOOL_MODE` selects the surface (`condensed` default · `verbose` 1:1 · `both`). Auto-generated — do not edit._
199
200
  <!-- MCP-TOOLS-TABLE:END -->
200
201
 
201
202
  Detailed tool schemas, parameter shapes, and validation constraints are preserved in [docs/mcp.md](docs/mcp.md).
@@ -20,7 +20,7 @@
20
20
  ![PyPI - Wheel](https://img.shields.io/pypi/wheel/github-agent)
21
21
  ![PyPI - Implementation](https://img.shields.io/pypi/implementation/github-agent)
22
22
 
23
- *Version: 1.2.0*
23
+ *Version: 1.4.1*
24
24
 
25
25
  > **Documentation** — Installation, deployment, and usage across the MCP, API, and
26
26
  > CLI interfaces, including the integrated A2A agent server, are maintained in the
@@ -73,6 +73,7 @@ _Auto-generated from the live MCP server — do not edit by hand._
73
73
  | `github_dependabot` | `DEPENDABOTTOOL` | Review and manage GitHub Dependabot vulnerability alerts. |
74
74
  | `github_discover_graphql_schema` | `GRAPHQLTOOL` | Discover the live GitHub GraphQL schema (types, fields, and attributes) in real-time. |
75
75
  | `github_graphql` | `GRAPHQLTOOL` | Execute raw GraphQL queries and mutations natively on GitHub. |
76
+ | `github_ingest_pipelines` | `INGESTTOOL` | Natively ingest GitHub Actions workflow runs into epistemic-graph. |
76
77
  | `github_ingest_repos` | `INGESTTOOL` | Natively ingest GitHub repositories into epistemic-graph as typed :Repository nodes. |
77
78
  | `github_issues` | `ISSUETOOL` | Manage GitHub issues. |
78
79
  | `github_orgs` | `ORGTOOL` | Manage GitHub organizations. |
@@ -167,7 +168,7 @@ _Auto-generated from the live MCP server — do not edit by hand._
167
168
 
168
169
  </details>
169
170
 
170
- _15 action-routed tool(s) (default) · 76 verbose 1:1 tool(s). Each is enabled unless its `<DOMAIN>TOOL` toggle is set false; `MCP_TOOL_MODE` selects the surface (`condensed` default · `verbose` 1:1 · `both`). Auto-generated — do not edit._
171
+ _16 action-routed tool(s) (default) · 76 verbose 1:1 tool(s). Each is enabled unless its `<DOMAIN>TOOL` toggle is set false; `MCP_TOOL_MODE` selects the surface (`condensed` default · `verbose` 1:1 · `both`). Auto-generated — do not edit._
171
172
  <!-- MCP-TOOLS-TABLE:END -->
172
173
 
173
174
  Detailed tool schemas, parameter shapes, and validation constraints are preserved in [docs/mcp.md](docs/mcp.md).
@@ -4,7 +4,7 @@ import os
4
4
  import sys
5
5
  import warnings
6
6
 
7
- __version__ = "1.2.0"
7
+ __version__ = "1.4.1"
8
8
 
9
9
  logging.basicConfig(
10
10
  level=logging.INFO,
@@ -3,7 +3,8 @@
3
3
  CONCEPT:AU-KG.ingest.enterprise-source-extractor. The github-agent connector natively
4
4
  pushes its data into the ONE epistemic-graph knowledge graph as **typed OWL nodes**
5
5
  (``:Repository``, ``:PullRequest``, ``:Issue``, ``:Release``, ``:Organization``,
6
- ``:Person``) plus links, and release notes as **:Document** nodes for semantic search —
6
+ ``:Person``, ``:PipelineRun``, ``:CheckRun``) plus links, and release notes as
7
+ **:Document** nodes for semantic search —
7
8
  using the lightweight engine client (``GraphComputeEngine()._client`` + ``txn``), the
8
9
  same fast client the blob ``MediaStore`` uses, NOT the heavy in-process ingestion engine.
9
10
 
@@ -355,3 +356,123 @@ def ingest_release_notes(
355
356
  }
356
357
  )
357
358
  return ingest_documents(docs, client=client, graph=graph)
359
+
360
+
361
+ def _duration_seconds(start: Any, end: Any) -> int | None:
362
+ """Whole-second wall-clock duration between two ISO-8601 timestamps, or ``None``."""
363
+ if not start or not end:
364
+ return None
365
+ try:
366
+ import datetime as _dt
367
+
368
+ started = _dt.datetime.fromisoformat(str(start).replace("Z", "+00:00"))
369
+ ended = _dt.datetime.fromisoformat(str(end).replace("Z", "+00:00"))
370
+ return max(0, int((ended - started).total_seconds()))
371
+ except (ValueError, TypeError):
372
+ return None
373
+
374
+
375
+ def ingest_pipeline_runs(
376
+ runs: list[dict[str, Any]],
377
+ *,
378
+ repo_full_name: str | None = None,
379
+ repo_node_id: str | None = None,
380
+ jobs_by_run: dict[int, list[dict[str, Any]]] | None = None,
381
+ client: Any | None = None,
382
+ graph: str | None = None,
383
+ ) -> dict[str, int] | None:
384
+ """Map GitHub Actions workflow runs (+ jobs/check-runs) → ``:PipelineRun``/``:CheckRun``.
385
+
386
+ ``runs``: raw ``WorkflowRun`` records (``client.get_workflow_runs`` /
387
+ ``.get_workflow_run`` ``model_dump()``). ``jobs_by_run``: optional ``{run_id:
388
+ [job_or_check_run, ...]}`` from ``client.get_workflow_run_jobs`` (or the Checks
389
+ API) — each becomes a child ``:CheckRun`` linked via ``hasJob``.
390
+
391
+ Uses the SAME ``:PipelineRun``/``:CheckRun`` classes and ``ranFor``/``hasJob``
392
+ edge names as gitlab-api's ingestion so GitHub Actions and GitLab CI/CD unify
393
+ under one CI node shape in the knowledge graph. ``ranFor`` is emitted once per
394
+ known target — the repo, the head commit, and (if the run lists it) the PR.
395
+ Stable ids: ``github:pipelinerun:<repo>:<id>`` / ``github:checkrun:<repo>:<id>``.
396
+ """
397
+ entities: list[dict[str, Any]] = []
398
+ relationships: list[dict[str, Any]] = []
399
+ jobs_by_run = jobs_by_run or {}
400
+ for run in runs or []:
401
+ run_id = run.get("id")
402
+ if run_id is None:
403
+ continue
404
+ repo = repo_full_name or (run.get("repository") or {}).get("full_name")
405
+ node_id = f"github:pipelinerun:{repo}:{run_id}"
406
+ run_started = run.get("run_started_at")
407
+ run_updated = run.get("updated_at")
408
+ entities.append(
409
+ {
410
+ "id": node_id,
411
+ "type": "PipelineRun",
412
+ "status": run.get("status"),
413
+ "conclusion": run.get("conclusion"),
414
+ "headSha": run.get("head_sha"),
415
+ "headBranch": run.get("head_branch"),
416
+ "event": run.get("event"),
417
+ "htmlUrl": _str(run.get("html_url")),
418
+ "runStartedAt": run_started,
419
+ "runUpdatedAt": run_updated,
420
+ "durationSeconds": _duration_seconds(run_started, run_updated),
421
+ "externalToolId": str(run_id),
422
+ }
423
+ )
424
+
425
+ if repo_node_id:
426
+ relationships.append(
427
+ {"source": node_id, "target": repo_node_id, "type": "ranFor"}
428
+ )
429
+
430
+ head_sha = run.get("head_sha")
431
+ if repo and head_sha:
432
+ commit_id = f"github:commit:{repo}:{head_sha}"
433
+ entities.append(
434
+ {
435
+ "id": commit_id,
436
+ "type": "Commit",
437
+ "sha": head_sha,
438
+ "externalToolId": head_sha,
439
+ }
440
+ )
441
+ relationships.append(
442
+ {"source": node_id, "target": commit_id, "type": "ranFor"}
443
+ )
444
+
445
+ for pr in run.get("pull_requests") or []:
446
+ pr_id = pr.get("id")
447
+ if pr_id is None:
448
+ continue
449
+ relationships.append(
450
+ {
451
+ "source": node_id,
452
+ "target": f"github:pullrequest:{pr_id}",
453
+ "type": "ranFor",
454
+ }
455
+ )
456
+
457
+ for job in jobs_by_run.get(run_id) or []:
458
+ job_id = job.get("id")
459
+ if job_id is None:
460
+ continue
461
+ job_node_id = f"github:checkrun:{repo}:{job_id}"
462
+ entities.append(
463
+ {
464
+ "id": job_node_id,
465
+ "type": "CheckRun",
466
+ "name": job.get("name"),
467
+ "status": job.get("status"),
468
+ "conclusion": job.get("conclusion"),
469
+ "startedAt": job.get("started_at"),
470
+ "completedAt": job.get("completed_at"),
471
+ "htmlUrl": _str(job.get("html_url")),
472
+ "externalToolId": str(job_id),
473
+ }
474
+ )
475
+ relationships.append(
476
+ {"source": node_id, "target": job_node_id, "type": "hasJob"}
477
+ )
478
+ return ingest_entities(entities, relationships, client=client, graph=graph)
@@ -2317,6 +2317,99 @@ def register_ingest_tools(mcp: FastMCP):
2317
2317
  result = ingest_repositories(records)
2318
2318
  return {"listed": len(records), "ingested": result}
2319
2319
 
2320
+ @mcp.tool(tags={"kg"})
2321
+ async def github_ingest_pipelines(
2322
+ params_json: str = Field(
2323
+ default="{}",
2324
+ description="JSON string with required 'owner'/'repo' and optional "
2325
+ "github_actions 'list_runs' filters (status, branch, per_page, "
2326
+ "max_pages), plus 'include_jobs' (default true — fetch each run's "
2327
+ "jobs as :CheckRun children) and 'repo_node_id' (the :Repository "
2328
+ "node id to link runs to via ranFor).",
2329
+ ),
2330
+ client=Depends(get_client),
2331
+ ctx: Context | None = Field(
2332
+ default=None, description="MCP context for progress reporting"
2333
+ ),
2334
+ ) -> Any:
2335
+ """Natively ingest GitHub Actions workflow runs into epistemic-graph.
2336
+
2337
+ Lists recent workflow runs for a repository (+ each run's jobs, unless
2338
+ ``include_jobs=false``) via the GitHub API and pushes them as typed
2339
+ :PipelineRun nodes (with child :CheckRun jobs linked via hasJob, and
2340
+ ranFor links to the repo/head-commit/PR) into the knowledge graph via
2341
+ the fast engine client. Uses the SAME :PipelineRun/:CheckRun classes as
2342
+ gitlab-api's CI ingestion so GitHub Actions and GitLab CI/CD unify under
2343
+ one node shape. Best-effort: returns ``{"ingested": None}`` when no
2344
+ engine is reachable. CONCEPT:AU-KG.ingest.enterprise-source-extractor.
2345
+ """
2346
+ if ctx:
2347
+ await ctx.info(
2348
+ "Ingesting GitHub Actions pipeline runs into the knowledge graph..."
2349
+ )
2350
+ import json
2351
+
2352
+ from github_agent.kg_ingest import ingest_pipeline_runs
2353
+
2354
+ try:
2355
+ kwargs = json.loads(params_json) if params_json else {}
2356
+ except Exception as e:
2357
+ return {"status": 400, "error": f"Invalid params_json: {e}", "data": None}
2358
+
2359
+ owner = kwargs.get("owner")
2360
+ repo = kwargs.get("repo")
2361
+ if not owner or not repo:
2362
+ return {
2363
+ "status": 400,
2364
+ "error": "Missing required 'owner' or 'repo' parameter",
2365
+ "data": None,
2366
+ }
2367
+
2368
+ include_jobs = kwargs.get("include_jobs", True)
2369
+ repo_node_id = kwargs.get("repo_node_id")
2370
+ list_kwargs = {
2371
+ k: v
2372
+ for k, v in kwargs.items()
2373
+ if k in ("owner", "repo", "status", "branch", "per_page", "max_pages")
2374
+ and v is not None
2375
+ }
2376
+
2377
+ response = await run_blocking(client.get_workflow_runs, **list_kwargs)
2378
+ runs = [
2379
+ run.model_dump() if hasattr(run, "model_dump") else run
2380
+ for run in response.data
2381
+ if run is not None
2382
+ ]
2383
+
2384
+ jobs_by_run: dict[int, Any] = {}
2385
+ if include_jobs:
2386
+ for run in runs:
2387
+ run_id = run.get("id")
2388
+ if run_id is None:
2389
+ continue
2390
+ try:
2391
+ jobs_response = await run_blocking(
2392
+ client.get_workflow_run_jobs,
2393
+ owner=owner,
2394
+ repo=repo,
2395
+ run_id=run_id,
2396
+ )
2397
+ jobs_by_run[run_id] = jobs_response.data
2398
+ except Exception as e:
2399
+ logger.debug(
2400
+ "github_ingest_pipelines: jobs fetch failed for run %s: %s",
2401
+ run_id,
2402
+ e,
2403
+ )
2404
+
2405
+ result = ingest_pipeline_runs(
2406
+ runs,
2407
+ repo_full_name=f"{owner}/{repo}",
2408
+ repo_node_id=repo_node_id,
2409
+ jobs_by_run=jobs_by_run,
2410
+ )
2411
+ return {"listed": len(runs), "ingested": result}
2412
+
2320
2413
 
2321
2414
  def register_graphql_tools(mcp: FastMCP):
2322
2415
  from github_agent.auth import get_graphql_client
@@ -49,6 +49,17 @@
49
49
  rdfs:label "Workflow Run" ;
50
50
  rdfs:comment "A single execution of a GitHub Actions workflow for a ref/commit (github_actions)." .
51
51
 
52
+ :PipelineRun a owl:Class ;
53
+ rdfs:label "Pipeline Run" ;
54
+ rdfs:comment """A single CI/CD execution for a ref/commit (github_actions workflow run).
55
+ Shares its class name with the twin gitlab-api producer so GitHub Actions and GitLab
56
+ CI/CD unify under one node shape in the knowledge graph.""" .
57
+
58
+ :CheckRun a owl:Class ;
59
+ rdfs:label "Check Run" ;
60
+ rdfs:comment """A single job/check within a PipelineRun (github_actions workflow-run job or
61
+ GitHub Checks-API check run). Shares its class name with the twin gitlab-api producer.""" .
62
+
52
63
  # --- links (owl:ObjectProperty) ---
53
64
 
54
65
  :belongsToRepository a owl:ObjectProperty ;
@@ -90,6 +101,20 @@
90
101
  rdfs:domain :Workflow ;
91
102
  rdfs:range :WorkflowRun .
92
103
 
104
+ :ranFor a owl:ObjectProperty ;
105
+ rdfs:label "ran for" ;
106
+ rdfs:comment """Links a PipelineRun to what it ran for — its Repository, the Commit it
107
+ built, and/or the PullRequest it validated. Same predicate name as the twin gitlab-api
108
+ producer (Pipeline ranFor Project/Commit/MergeRequest).""" ;
109
+ rdfs:domain :PipelineRun .
110
+
111
+ :hasJob a owl:ObjectProperty ;
112
+ rdfs:label "has job" ;
113
+ rdfs:comment """Links a PipelineRun to a CheckRun (job/check) it contains. Same predicate
114
+ name as the twin gitlab-api producer (Pipeline hasJob Job).""" ;
115
+ rdfs:domain :PipelineRun ;
116
+ rdfs:range :CheckRun .
117
+
93
118
  :documentsRelease a owl:ObjectProperty ;
94
119
  rdfs:label "documents release" ;
95
120
  rdfs:comment "Links a Release to the :Document carrying its release-notes body." ;
@@ -151,6 +176,64 @@
151
176
  rdfs:comment "Result/conclusion of a WorkflowRun (success, failure, in_progress, cancelled)." ;
152
177
  rdfs:range xsd:string .
153
178
 
179
+ :status a owl:DatatypeProperty ;
180
+ rdfs:label "status" ;
181
+ rdfs:comment "Lifecycle status of a PipelineRun or CheckRun (queued, in_progress, completed)." ;
182
+ rdfs:range xsd:string .
183
+
184
+ :conclusion a owl:DatatypeProperty ;
185
+ rdfs:label "conclusion" ;
186
+ rdfs:comment "Terminal result of a PipelineRun or CheckRun (success, failure, cancelled, skipped)." ;
187
+ rdfs:range xsd:string .
188
+
189
+ :headSha a owl:DatatypeProperty ;
190
+ rdfs:label "head sha" ;
191
+ rdfs:comment "Full git commit SHA a PipelineRun ran against." ;
192
+ rdfs:domain :PipelineRun ;
193
+ rdfs:range xsd:string .
194
+
195
+ :headBranch a owl:DatatypeProperty ;
196
+ rdfs:label "head branch" ;
197
+ rdfs:comment "Branch name a PipelineRun ran against." ;
198
+ rdfs:domain :PipelineRun ;
199
+ rdfs:range xsd:string .
200
+
201
+ :event a owl:DatatypeProperty ;
202
+ rdfs:label "event" ;
203
+ rdfs:comment "Triggering event of a PipelineRun (push, pull_request, schedule, workflow_dispatch)." ;
204
+ rdfs:domain :PipelineRun ;
205
+ rdfs:range xsd:string .
206
+
207
+ :runStartedAt a owl:DatatypeProperty ;
208
+ rdfs:label "run started at" ;
209
+ rdfs:comment "Timestamp a PipelineRun started executing." ;
210
+ rdfs:domain :PipelineRun ;
211
+ rdfs:range xsd:dateTime .
212
+
213
+ :runUpdatedAt a owl:DatatypeProperty ;
214
+ rdfs:label "run updated at" ;
215
+ rdfs:comment "Timestamp a PipelineRun was last updated." ;
216
+ rdfs:domain :PipelineRun ;
217
+ rdfs:range xsd:dateTime .
218
+
219
+ :durationSeconds a owl:DatatypeProperty ;
220
+ rdfs:label "duration seconds" ;
221
+ rdfs:comment "Wall-clock duration in seconds of a PipelineRun (runUpdatedAt - runStartedAt)." ;
222
+ rdfs:domain :PipelineRun ;
223
+ rdfs:range xsd:integer .
224
+
225
+ :startedAt a owl:DatatypeProperty ;
226
+ rdfs:label "started at" ;
227
+ rdfs:comment "Timestamp a CheckRun started executing." ;
228
+ rdfs:domain :CheckRun ;
229
+ rdfs:range xsd:dateTime .
230
+
231
+ :completedAt a owl:DatatypeProperty ;
232
+ rdfs:label "completed at" ;
233
+ rdfs:comment "Timestamp a CheckRun finished executing." ;
234
+ rdfs:domain :CheckRun ;
235
+ rdfs:range xsd:dateTime .
236
+
154
237
  <http://knuckles.team/kg/github> a owl:Ontology ;
155
238
  rdfs:label "GitHub Ontology" ;
156
239
  rdfs:comment """GitHub source-control, code-review and CI/CD model — organizations,
@@ -0,0 +1,113 @@
1
+ ---
2
+ name: github-backlog-planner
3
+ skill_type: skill
4
+ description: >-
5
+ Sweeps every open issue and pull request across one or more GitHub accounts
6
+ (a user and/or an organization, all repositories), deeply verifies which items
7
+ are already addressed, and emits a prioritized Markdown remediation plan. Use
8
+ when asked to "go through all issues and PRs", triage a whole backlog, plan
9
+ what to work on next across repos, or audit open work for a user/org. Defaults
10
+ to read-only analysis; write actions (close, comment, open tracking issue) are
11
+ opt-in and run only on explicit confirmation. Do NOT use for a single PR's
12
+ review comments (use github-tools) or interactive one-tracker triage (use
13
+ issue-triage).
14
+ license: MIT
15
+ tags: [github, issues, pull-requests, backlog, triage, planning, ops]
16
+ metadata:
17
+ author: Genius
18
+ version: '1.0.3'
19
+ ---
20
+
21
+ # GitHub Backlog Planner
22
+
23
+ ## Overview
24
+
25
+ Account-wide backlog sweep. Discovers all open issues and PRs across the target
26
+ accounts (e.g. the `Knucklessg1` user **and** the `Knuckles-Team` org), determines
27
+ which are **already addressed** (deep verification, not just labels), and produces a
28
+ single prioritized Markdown plan grouped by account → repo. Read-only by default;
29
+ any change to GitHub is opt-in.
30
+
31
+ Drives the **github-agent** MCP server. No `gh` CLI or local `GITHUB_*` token is
32
+ required — the MCP server holds its own auth.
33
+
34
+ ## Tool access (works under delegation AND the multiplexer)
35
+
36
+ The tools are `github_issues`, `github_pulls`, `github_search`, `github_repos`,
37
+ `github_orgs`, `github_commits`, `github_contents` — each takes `action` + a
38
+ `params_json` **JSON string**.
39
+
40
+ - **Under direct delegation** to `github-agent` (`execute_agent server=github-agent`,
41
+ or the `mcp-client` skill) the tools are already bound by their **native** names
42
+ — call them directly. This is the path this skill is written for.
43
+ - **In the multiplexer / orchestrator** context the same tools carry the `gith__`
44
+ prefix (`gith__issues`, …); mount them first with
45
+ `load_tools(servers=["github-agent"])`.
46
+
47
+ Confirmed action shapes: `github_issues`/`github_pulls` → `list|get|create|update`;
48
+ `github_search` → `repositories|issues|code`; `github_repos` → `list|get`;
49
+ `github_orgs` → `repos|get`.
50
+
51
+ ## Inputs
52
+
53
+ - **accounts**: list of `{login, type}` where type is `user` or `org`. Default:
54
+ `[{login: "Knucklessg1", type: "user"}, {login: "Knuckles-Team", type: "org"}]`.
55
+ - **include_prs**: default true. **include_issues**: default true.
56
+
57
+ ## Workflow
58
+
59
+ ### Step 1 — Discover repositories
60
+ - User: `github_repos action=list` (authenticated user) — keep repos owned by the target login.
61
+ - Org: `github_orgs action=repos {"org": "<login>"}`.
62
+ - Record each repo's `default_branch`, `open_issues_count`, and archived flag. Skip archived repos.
63
+
64
+ ### Step 2 — Bulk-discover open items (efficient path)
65
+ Use search, which returns issues AND PRs compactly account-wide:
66
+ - `github_search action=issues {"q": "user:Knucklessg1 is:open is:issue"}`
67
+ - `github_search action=issues {"q": "user:Knucklessg1 is:open is:pr"}`
68
+ - `github_search action=issues {"q": "org:Knuckles-Team is:open is:issue"}`
69
+ - `github_search action=issues {"q": "org:Knuckles-Team is:open is:pr"}`
70
+
71
+ Paginate (`per_page`, `page`) until exhausted. If search results look incomplete for a
72
+ repo, fall back to per-repo `github_issues action=list {owner, repo, state:"open"}` and
73
+ `github_pulls action=list {owner, repo, state:"open"}`. Remember: the GitHub search/issues
74
+ APIs return PRs in the issues list — distinguish them by the `pull_request` field.
75
+
76
+ ### Step 3 — Heuristic triage (Pass 1)
77
+ Bucket every item using `references/addressed-heuristics.md` (Pass 1 table) into
78
+ `needs-action`, `in-progress`, or *maybe addressed*. This is cheap and uses only the
79
+ data already fetched.
80
+
81
+ ### Step 4 — Deep verification (Pass 2)
82
+ For each *maybe addressed* item, follow Pass 2 in `references/addressed-heuristics.md`:
83
+ `github_issues/pulls action=get` for linked PRs/commits, then `github_commits action=get`
84
+ and/or `github_contents action=get` to **confirm the fix actually landed on the default
85
+ branch** before marking it `addressed`. For items that still need a plan, read the
86
+ relevant source to write a one-line root-cause note and a concrete recommendation.
87
+
88
+ ### Step 5 — Build the plan
89
+ Normalize each item to the JSON schema in `scripts/build_plan.py` (account, repo, kind,
90
+ number, title, url, state, status, evidence, recommendation, priority, labels), then:
91
+
92
+ ```bash
93
+ python scripts/build_plan.py --input items.json --title "GitHub Backlog Plan (<date>)"
94
+ # or: cat items.json | python scripts/build_plan.py
95
+ ```
96
+
97
+ This renders: a summary line, a "verified addressed — safe to close" section, and a
98
+ per-account/per-repo action table sorted by status then priority. Present it to the user.
99
+
100
+ ### Step 6 — Opt-in actions (only on explicit confirmation)
101
+ Never write to GitHub without the user confirming. When asked:
102
+ - Close a verified-addressed issue: `github_issues action=update {owner, repo, number, state:"closed"}`.
103
+ - Post a triage comment: there is **no native comment action or tool** (`github_issues`
104
+ has no `comment` action, and no dedicated comment tool exists). Hand off to
105
+ `github-triage-resolver`, which performs gated writes, to leave the
106
+ `> *This was generated by AI during triage.*` note (mirrors the `issue-triage` convention).
107
+ - Open a tracking issue for orphaned work: `github_issues action=create`.
108
+
109
+ ## Related skills
110
+ - `github-ci-failure-sweep` — cross-link when a PR is blocked by failing CI.
111
+ - `github-tools` — single-PR comments/CI inspection (do not duplicate here).
112
+ - `issue-triage` — interactive, single-tracker state-machine triage.
113
+ - `github-triage-resolver` — performs gated writes (comments, closes) this skill only recommends.
@@ -0,0 +1,59 @@
1
+ # "Already addressed?" — triage + deep-verify decision table
2
+
3
+ Two-pass classification. **Pass 1** is a cheap heuristic that buckets every open item.
4
+ **Pass 2** is a deep, per-item investigation that is run only on the items Pass 1 marks
5
+ as *maybe addressed*, to confirm a fix actually landed before recommending a close.
6
+
7
+ ## Pass 1 — heuristic triage (cheap, from list/search data)
8
+
9
+ ### Issues
10
+ | Signal | Bucket | Status |
11
+ |---|---|---|
12
+ | Has a **linked open PR** (or `linked_pull_requests`, or a PR whose body says `fixes #N`) | in progress | `in-progress` |
13
+ | Has a **merged/closed PR** referencing it, but the issue is still open | maybe addressed → Pass 2 | (`addressed` if confirmed) |
14
+ | Referenced by a recent **commit** on the default branch | maybe addressed → Pass 2 | (`addressed` if confirmed) |
15
+ | Last activity is **stale** and no linked work | needs plan | `needs-action` |
16
+ | Fresh, no linked work | needs plan | `needs-action` |
17
+
18
+ ### Pull requests
19
+ | Signal | Status | Notes |
20
+ |---|---|---|
21
+ | Open, `draft: true` | `in-progress` | Recommend: finish + mark ready. |
22
+ | Open, `mergeable_state: clean`, approved | `needs-action` | Recommend: merge. |
23
+ | Open, failing checks / `mergeable_state: dirty`/`blocked` | `needs-action` | Recommend: fix CI or rebase (cross-link the CI sweep skill). |
24
+ | Open, awaiting review | `needs-action` | Recommend: request/perform review. |
25
+ | Open, conflicts with base | `needs-action` | Recommend: rebase/resolve. |
26
+
27
+ ## Pass 2 — deep verification (only for "maybe addressed")
28
+
29
+ For each candidate, gather enough evidence to either confirm `addressed` (safe to close)
30
+ or downgrade to `needs-action`:
31
+
32
+ 1. `github_issues action=get {owner, repo, number}` — read body + timeline; collect every
33
+ referenced PR number and commit SHA (look for `fixes/closes/resolves #N`, cross-refs).
34
+ 2. For each referenced PR: `github_pulls action=get {owner, repo, number}` — confirm
35
+ `merged: true` and which files it touched (`changed_files`, or list the PR's files).
36
+ 3. Open the **referencing commit / changed file** to verify the fix is real, not a no-op:
37
+ - `github_commits action=get {owner, repo, sha}` for the diff, and/or
38
+ - `github_contents action=get {owner, repo, path, ref}` to read the current source on the
39
+ default branch and confirm the described change is present.
40
+ 4. Verdict:
41
+ - Fix present on default branch + PR merged → `status: addressed`,
42
+ `evidence: "merged PR #N landed <change> in <file>"`, `recommendation: "Close #M"`.
43
+ - Referenced work reverted, or change absent, or PR closed-unmerged → `status: needs-action`,
44
+ `evidence: "PR #N closed without merging / change not on main"`.
45
+
46
+ ## For items that still need a plan
47
+
48
+ Read the relevant source (`github_contents`) around the reported problem to write a
49
+ **one-line root-cause note** and a **concrete recommended next step** (the `recommendation`
50
+ field). Keep it actionable: name the file/function and the change, not "investigate".
51
+
52
+ ## Priority assignment
53
+ - `high` (P1): PRs ready to merge, regressions/bugs blocking users, failing CI on default branch.
54
+ - `medium` (P2): normal bugs, enhancements with a clear path.
55
+ - `low` (P3): nice-to-have enhancements, stale/low-signal items.
56
+
57
+ ## Output contract
58
+ Emit one normalized JSON object per item (see `scripts/build_plan.py` docstring for the schema)
59
+ and pipe the list through `build_plan.py` to render the final Markdown plan.