@misterhuydo/sentinel 1.5.32 → 1.5.33

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.32",
3
+ "version": "1.5.33",
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.32"
1
+ __version__ = "1.5.33"
@@ -1181,6 +1181,15 @@ _TOOLS = [
1181
1181
  "type": "string",
1182
1182
  "description": "Project short name this bot's issues should be routed to (e.g. '1881', 'elprint'). Infer from context or ask user before calling.",
1183
1183
  },
1184
+ "target_repo": {
1185
+ "type": "string",
1186
+ "description": (
1187
+ "Optional. Repo name to route this bot's issues to (e.g. '1881-SSOLoginWebApp'). "
1188
+ "Required when the project has multiple repos — without it, routing will fail. "
1189
+ "If the project has only one repo, omit this and it will be auto-selected. "
1190
+ "Ask the user which repo if unclear."
1191
+ ),
1192
+ },
1184
1193
  },
1185
1194
  "required": ["user_ids"],
1186
1195
  },
@@ -3095,6 +3104,18 @@ async def _run_tool(name: str, inputs: dict, cfg_loader, store, slack_client=Non
3095
3104
  "action_needed": "Ask the user to specify the project, then retry with project filled in.",
3096
3105
  })
3097
3106
 
3107
+ target_repo_arg = (inputs.get("target_repo") or "").strip()
3108
+ # Fuzzy-match target_repo against known repos
3109
+ resolved_target_repo = ""
3110
+ if target_repo_arg and hasattr(cfg_loader, "repos"):
3111
+ if target_repo_arg in cfg_loader.repos:
3112
+ resolved_target_repo = target_repo_arg
3113
+ else:
3114
+ for rname in cfg_loader.repos:
3115
+ if target_repo_arg.lower() in rname.lower() or rname.lower() in target_repo_arg.lower():
3116
+ resolved_target_repo = rname
3117
+ break
3118
+
3098
3119
  results = []
3099
3120
  for uid in user_ids:
3100
3121
  if not slack_client:
@@ -3107,9 +3128,9 @@ async def _run_tool(name: str, inputs: dict, cfg_loader, store, slack_client=Non
3107
3128
  results.append({"user_id": uid, "status": "skipped", "reason": "not a bot — only bots can be watched passively"})
3108
3129
  continue
3109
3130
  bot_name = user.get("real_name") or user.get("name") or uid
3110
- store.add_watched_bot(uid, bot_name, added_by="boss", project_name=resolved_project)
3111
- logger.info("Boss: now watching bot %s (%s) → project '%s'", bot_name, uid, resolved_project or "unset")
3112
- results.append({"user_id": uid, "bot_name": bot_name, "project": resolved_project, "status": "watching"})
3131
+ store.add_watched_bot(uid, bot_name, added_by="boss", project_name=resolved_project, target_repo=resolved_target_repo)
3132
+ logger.info("Boss: now watching bot %s (%s) → project '%s', repo '%s'", bot_name, uid, resolved_project or "unset", resolved_target_repo or "auto")
3133
+ results.append({"user_id": uid, "bot_name": bot_name, "project": resolved_project, "target_repo": resolved_target_repo or "auto", "status": "watching"})
3113
3134
  except Exception as e:
3114
3135
  results.append({"user_id": uid, "status": "error", "reason": str(e)})
3115
3136
  return json.dumps({"results": results})
@@ -214,6 +214,7 @@ async def _handle_bot_message(event: dict, client, cfg_loader, store) -> None:
214
214
  None,
215
215
  )
216
216
  project_name = (bot_info or {}).get("project_name") or ""
217
+ target_repo = (bot_info or {}).get("target_repo") or ""
217
218
 
218
219
  # Resolve the project issues directory
219
220
  workspace = _Path(cfg_loader.sentinel.workspace_dir).parent
@@ -238,9 +239,11 @@ async def _handle_bot_message(event: dict, client, cfg_loader, store) -> None:
238
239
 
239
240
  uid = _uuid.uuid4().hex[:8]
240
241
  fname = f"bot-{project_name or 'unknown'}-{uid}.txt"
242
+ target_line = f"TARGET_REPO: {target_repo}\n" if target_repo else ""
241
243
  content = (
242
244
  f"SOURCE: Slack bot {bot_id} in channel {channel}\n"
243
- f"SLACK_TS: {ts}\n\n"
245
+ f"SLACK_TS: {ts}\n"
246
+ f"{target_line}\n"
244
247
  f"{text}"
245
248
  )
246
249
  (issues_dir / fname).write_text(content, encoding="utf-8")
@@ -99,6 +99,7 @@ class StateStore:
99
99
  ("add_fix_outcome", "ALTER TABLE fixes ADD COLUMN fix_outcome TEXT"),
100
100
  ("add_marker_seen_at", "ALTER TABLE fixes ADD COLUMN marker_seen_at TEXT"),
101
101
  ("add_watched_bots_project", "ALTER TABLE watched_bots ADD COLUMN project_name TEXT"),
102
+ ("add_watched_bots_target_repo", "ALTER TABLE watched_bots ADD COLUMN target_repo TEXT"),
102
103
  ("add_alert_thread_ts", "ALTER TABLE errors ADD COLUMN alert_thread_ts TEXT"),
103
104
  ("add_alert_channel", "ALTER TABLE errors ADD COLUMN alert_channel TEXT"),
104
105
  # knowledge_cache was originally created with a 'cached_at' column; rebuilt with 'expires_at'
@@ -408,13 +409,13 @@ class StateStore:
408
409
  "(bot_id TEXT PRIMARY KEY, bot_name TEXT, added_by TEXT, added_at TEXT)"
409
410
  )
410
411
 
411
- def add_watched_bot(self, bot_id: str, bot_name: str, added_by: str = "config", project_name: str = ""):
412
+ def add_watched_bot(self, bot_id: str, bot_name: str, added_by: str = "config", project_name: str = "", target_repo: str = ""):
412
413
  with self._conn() as conn:
413
414
  self._ensure_watched_bots_table(conn)
414
415
  conn.execute(
415
- "INSERT OR REPLACE INTO watched_bots (bot_id, bot_name, added_by, added_at, project_name) "
416
- "VALUES (?, ?, ?, ?, ?)",
417
- (bot_id, bot_name, added_by, _now(), project_name or None),
416
+ "INSERT OR REPLACE INTO watched_bots (bot_id, bot_name, added_by, added_at, project_name, target_repo) "
417
+ "VALUES (?, ?, ?, ?, ?, ?)",
418
+ (bot_id, bot_name, added_by, _now(), project_name or None, target_repo or None),
418
419
  )
419
420
 
420
421
  def remove_watched_bot(self, bot_id: str) -> bool: