@@ -130,16 +130,12 @@ def normalize_github_state(state: str) -> str:
130130def canonical_repo_slug (repo_slug : str ) -> str :
131131 value = repo_slug .strip ()
132132 if "/" not in value :
133- raise ValueError (
134- "Repository must use owner/repo format (for example octocat/hello-world)"
135- )
133+ raise ValueError ("Repository must use owner/repo format (for example octocat/hello-world)" )
136134 owner , name = value .split ("/" , 1 )
137135 owner = owner .strip ()
138136 name = name .strip ()
139137 if not owner or not name or " " in owner or " " in name :
140- raise ValueError (
141- "Repository must use owner/repo format (for example octocat/hello-world)"
142- )
138+ raise ValueError ("Repository must use owner/repo format (for example octocat/hello-world)" )
143139 return f"{ owner } /{ name } "
144140
145141
@@ -310,9 +306,7 @@ async def _gh_list_comments(repo_slug: str, number: int) -> list[dict[str, Any]]
310306
311307 Returns a list of comment dicts (id, body, user, created_at, updated_at).
312308 """
313- resolved = resolve_spawn_command (
314- "gh" , "api" , f"repos/{ repo_slug } /issues/{ number } /comments"
315- )
309+ resolved = resolve_spawn_command ("gh" , "api" , f"repos/{ repo_slug } /issues/{ number } /comments" )
316310 proc = await asyncio .create_subprocess_exec (
317311 * resolved ,
318312 env = build_sanitized_subprocess_environment (),
@@ -589,9 +583,7 @@ def _render_criteria_comment(criteria: list[Any]) -> str:
589583 try :
590584 verdicts = list (getattr (crit , "verdicts" , []) or [])
591585 if verdicts :
592- latest = sorted (
593- verdicts , key = lambda v : getattr (v , "created_at" , "" ), reverse = True
594- )
586+ latest = sorted (verdicts , key = lambda v : getattr (v , "created_at" , "" ), reverse = True )
595587 top_verdict = getattr (latest [0 ], "verdict" , "" ).upper ()
596588 is_done = top_verdict == "PASS"
597589 except Exception :
@@ -641,8 +633,11 @@ async def _sync_criteria_via_comment(
641633 new_body = _render_criteria_comment (criteria )
642634 comments = await _gh_list_comments (repo_slug , number )
643635 tagged = next (
644- (c for c in comments if isinstance (c .get ("body" ), str ) and
645- c ["body" ].startswith (_KAGAN_CRITERIA_MARKER )),
636+ (
637+ c
638+ for c in comments
639+ if isinstance (c .get ("body" ), str ) and c ["body" ].startswith (_KAGAN_CRITERIA_MARKER )
640+ ),
646641 None ,
647642 )
648643 if tagged is not None :
@@ -652,14 +647,10 @@ async def _sync_criteria_via_comment(
652647 else :
653648 await _gh_create_comment (repo_slug , number , new_body )
654649 except Exception as exc :
655- logger .warning (
656- "Failed to sync criteria comment for {}#{}: {}" , repo_slug , number , exc
657- )
650+ logger .warning ("Failed to sync criteria comment for {}#{}: {}" , repo_slug , number , exc )
658651
659652
660- async def _pull_criteria_from_comment (
661- repo_slug : str , number : int
662- ) -> list [tuple [str , bool ]] | None :
653+ async def _pull_criteria_from_comment (repo_slug : str , number : int ) -> list [tuple [str , bool ]] | None :
663654 """Find the tagged criteria comment and parse its lines.
664655
665656 Returns ``[(text, done)]`` if a tagged comment exists, else ``None``.
@@ -669,8 +660,11 @@ async def _pull_criteria_from_comment(
669660 except KaganError :
670661 return None
671662 tagged = next (
672- (c for c in comments if isinstance (c .get ("body" ), str ) and
673- c ["body" ].startswith (_KAGAN_CRITERIA_MARKER )),
663+ (
664+ c
665+ for c in comments
666+ if isinstance (c .get ("body" ), str ) and c ["body" ].startswith (_KAGAN_CRITERIA_MARKER )
667+ ),
674668 None ,
675669 )
676670 if tagged is None :
@@ -723,18 +717,28 @@ async def _resolve_task_repo_id(
723717 client : KaganCore ,
724718 project_id : str ,
725719 configured_repo_id : str | None ,
726- ) -> str | None :
727- """Pick the repo_id that imported tasks should belong to, when available."""
720+ ) -> str :
721+ """Pick the repo_id that imported tasks should belong to.
722+
723+ Raises:
724+ KaganError: If no repo_id can be determined (no repos attached to
725+ the project, or multiple repos with none selected).
726+ """
728727 if configured_repo_id :
729728 return configured_repo_id
730729
731730 try :
732731 repos = await client .projects .repos (project_id )
733- except KaganError :
734- return None
732+ except KaganError as exc :
733+ raise KaganError (
734+ "Failed to resolve repository for import. Attach a repository to the project first."
735+ ) from exc
735736
736737 if not repos :
737- return None
738+ raise KaganError (
739+ "No repositories attached to this project. "
740+ "Attach a repository before importing GitHub issues."
741+ )
738742
739743 settings = await client .settings .get ()
740744 selected_repo_id = settings .get (f"ui.selected_repo.{ project_id } " )
@@ -744,7 +748,11 @@ async def _resolve_task_repo_id(
744748 if len (repos ) == 1 :
745749 return repos [0 ].id
746750
747- return None
751+ repo_paths = ", " .join (r .path for r in repos )
752+ raise KaganError (
753+ f"Multiple repositories attached to this project ({ repo_paths } ). "
754+ "Select a repository in the TUI/web UI before importing, or pass target_repo_id explicitly."
755+ )
748756
749757
750758# ---------------------------------------------------------------------------
@@ -764,8 +772,8 @@ class GitHubIntegration:
764772 from kagan.core.integrations import github
765773
766774 result = await github.sync(client, config, project_id)
767- items = await github.preview(client, config, project_id)
768- checks = github.preflight()
775+ items = await github.preview(client, config, project_id)
776+ checks = github.preflight()
769777 """
770778
771779 id : str = "github"
@@ -836,9 +844,7 @@ def preflight(self) -> list[PreflightCheckResult]:
836844
837845 # -- Internal helpers ---------------------------------------------------
838846
839- async def _fetch_issues (
840- self , config : GitHubConfig
841- ) -> list [GitHubIssue ]:
847+ async def _fetch_issues (self , config : GitHubConfig ) -> list [GitHubIssue ]:
842848 """Validate prerequisites and fetch issues from GitHub."""
843849 if _gh_path () is None :
844850 raise KaganError ("GitHub CLI (gh) not found. Install → https://cli.github.com" )
@@ -954,9 +960,7 @@ async def _refresh_existing_task(
954960 update_kwargs ["acceptance_criteria" ] = [text for text , _done in pulled ]
955961
956962 if not update_kwargs :
957- logger .debug (
958- "GitHub pull: issue #{} unchanged, skipping task {}" , number , task_id
959- )
963+ logger .debug ("GitHub pull: issue #{} unchanged, skipping task {}" , number , task_id )
960964 return ImportResult (
961965 created = result .created ,
962966 updated = result .updated ,
@@ -1066,7 +1070,8 @@ async def push_task_change(
10661070 current_labels = await _gh_issue_labels (slug , number )
10671071 add_labels = [new_label ] if new_label not in current_labels else []
10681072 remove_labels = [
1069- lbl for lbl in current_labels
1073+ lbl
1074+ for lbl in current_labels
10701075 if lbl in _ALL_PRIORITY_LABELS and lbl != new_label
10711076 ]
10721077 await _gh_ensure_label (slug , new_label )
0 commit comments