@misterhuydo/sentinel 1.5.33 → 1.5.35

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@misterhuydo/sentinel",
3
- "version": "1.5.33",
3
+ "version": "1.5.35",
4
4
  "description": "Sentinel — Autonomous DevOps Agent installer and manager",
5
5
  "bin": {
6
6
  "sentinel": "./bin/sentinel.js"
@@ -1 +1 @@
1
- __version__ = "1.5.33"
1
+ __version__ = "1.5.35"
@@ -415,17 +415,25 @@ async def _handle_issue(event: IssueEvent, cfg_loader: ConfigLoader, store: Stat
415
415
  elif len(cfg_loader.repos) == 1:
416
416
  repo = next(iter(cfg_loader.repos.values()))
417
417
  else:
418
- logger.warning(
419
- "Cannot auto-route %s -- add 'TARGET_REPO: <repo>' as first line in the file",
420
- event.source,
421
- )
422
- _route_alert(
423
- sentinel.slack_bot_token, _origin_ch,
424
- f":warning: *Issue routing failed*{' — ' + '<@' + _submitter_id + '>' if _submitter_id else ''}\n"
425
- f"Multiple repos configured — add `TARGET_REPO: <repo-name>` to the issue.\n"
426
- f"_{event.message[:120]}_",
427
- )
428
- return # Leave the file so admin can add the header
418
+ # Try to infer repo from message content (e.g. "Service: STS" in bot alerts)
419
+ from .slack_bot import _infer_target_repo
420
+ inferred = _infer_target_repo(event.body, cfg_loader.repos)
421
+ if inferred:
422
+ repo = cfg_loader.repos[inferred]
423
+ logger.info("Auto-routed %s → %s (inferred from content)", event.source, inferred)
424
+ else:
425
+ logger.warning(
426
+ "Cannot auto-route %s -- add 'TARGET_REPO: <repo>' as first line in the file",
427
+ event.source,
428
+ )
429
+ _route_alert(
430
+ sentinel.slack_bot_token, _origin_ch,
431
+ f":warning: *Issue routing failed*{' — ' + '<@' + _submitter_id + '>' if _submitter_id else ''}\n"
432
+ f"Multiple repos configured — add `TARGET_REPO: <repo-name>` to the issue.\n"
433
+ f"_{event.message[:120]}_",
434
+ )
435
+ mark_done(event.issue_file) # archive so it doesn't re-prompt every poll
436
+ return
429
437
 
430
438
  auto_commit = resolve_auto_commit(repo, sentinel)
431
439
  auto_release = resolve_auto_release(repo, sentinel)
@@ -181,6 +181,28 @@ import uuid as _uuid
181
181
  from pathlib import Path as _Path
182
182
 
183
183
 
184
+ def _infer_target_repo(text: str, repos: dict) -> str:
185
+ """
186
+ Try to infer a target repo from bot message content.
187
+ Looks for 'Service: <name>' or 'service: <name>' patterns and fuzzy-matches
188
+ the extracted name against known repo names. Returns the repo name on a unique
189
+ match, empty string if ambiguous or no match found.
190
+ """
191
+ import re as _re
192
+ # Extract service hints: "Service: STS", "service: SSOLWA", etc.
193
+ hints = _re.findall(r'(?i)service:\s*(\S+)', text)
194
+ # Also try class names: "Class: SomeService" — extract first word segment
195
+ hints += _re.findall(r'(?i)class:\s*(\w+)', text)
196
+
197
+ for hint in hints:
198
+ hint_lower = hint.lower().rstrip(".,:")
199
+ matches = [r for r in repos if hint_lower in r.lower()]
200
+ if len(matches) == 1:
201
+ return matches[0]
202
+
203
+ return ""
204
+
205
+
184
206
  async def _handle_bot_message(event: dict, client, cfg_loader, store) -> None:
185
207
  """
186
208
  Called when a watched bot posts a message.
@@ -216,6 +238,26 @@ async def _handle_bot_message(event: dict, client, cfg_loader, store) -> None:
216
238
  project_name = (bot_info or {}).get("project_name") or ""
217
239
  target_repo = (bot_info or {}).get("target_repo") or ""
218
240
 
241
+ # Auto-detect target_repo from message content when not pre-configured
242
+ if not target_repo and hasattr(cfg_loader, "repos") and cfg_loader.repos:
243
+ target_repo = _infer_target_repo(text, cfg_loader.repos)
244
+ if not target_repo and len(cfg_loader.repos) > 1:
245
+ # Can't determine — ask in the channel where the bot posted
246
+ bot_name = (bot_info or {}).get("bot_name") or bot_id
247
+ repo_names = ", ".join(f"`{r}`" for r in sorted(cfg_loader.repos))
248
+ try:
249
+ await client.chat_postMessage(
250
+ channel=channel,
251
+ text=(
252
+ f":mag: I got an alert from *{bot_name}* but can't tell which repo to route it to.\n"
253
+ f"Available repos: {repo_names}\n"
254
+ f"Re-register with: `@Sentinel watch @{bot_name} for <repo-name>`"
255
+ ),
256
+ )
257
+ except Exception as _e:
258
+ logger.warning("Bot watcher: could not post routing question: %s", _e)
259
+ return # drop this message — don't queue an unroutable issue
260
+
219
261
  # Resolve the project issues directory
220
262
  workspace = _Path(cfg_loader.sentinel.workspace_dir).parent
221
263
  if project_name: