@misterhuydo/sentinel 1.4.50 → 1.4.51

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.
@@ -1,6 +1,6 @@
1
1
  {
2
- "message": "Auto-checkpoint at 2026-03-25T11:24:19.838Z",
3
- "checkpoint_at": "2026-03-25T11:24:19.839Z",
2
+ "message": "Auto-checkpoint at 2026-03-25T11:34:55.696Z",
3
+ "checkpoint_at": "2026-03-25T11:34:55.697Z",
4
4
  "active_files": [],
5
5
  "notes": [],
6
6
  "mtime_snapshot": {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@misterhuydo/sentinel",
3
- "version": "1.4.50",
3
+ "version": "1.4.51",
4
4
  "description": "Sentinel — Autonomous DevOps Agent installer and manager",
5
5
  "bin": {
6
6
  "sentinel": "./bin/sentinel.js"
@@ -395,6 +395,30 @@ _TOOLS = [
395
395
  "required": ["fingerprint"],
396
396
  },
397
397
  },
398
+ {
399
+ "name": "retry_issue",
400
+ "description": (
401
+ "Re-queue a previously failed or blocked issue from the archive without requiring the "
402
+ "user to re-type the context. Scans issues/.done/ for the most recent matching file "
403
+ "and re-submits it to Sentinel. "
404
+ "Use when the user says things like: 'retry the last issue', 're-raise the umlaut fix', "
405
+ "'try that again', 'retry Whydah-TypeLib', 'run the last failed fix again'."
406
+ ),
407
+ "input_schema": {
408
+ "type": "object",
409
+ "properties": {
410
+ "project": {
411
+ "type": "string",
412
+ "description": "Project short name (e.g. '1881'). Required.",
413
+ },
414
+ "keyword": {
415
+ "type": "string",
416
+ "description": "Optional keyword to match against archived issue content (e.g. 'umlaut', 'Whydah-TypeLib')",
417
+ },
418
+ },
419
+ "required": ["project"],
420
+ },
421
+ },
398
422
  {
399
423
  "name": "list_pending_prs",
400
424
  "description": "List all open Sentinel PRs awaiting admin review.",
@@ -1247,6 +1271,65 @@ async def _run_tool(name: str, inputs: dict, cfg_loader, store, slack_client=Non
1247
1271
  "note": f"Delivered to '{project_label}'. Sentinel will process it on the next poll cycle.",
1248
1272
  })
1249
1273
 
1274
+ if name == "retry_issue":
1275
+ project_arg = inputs.get("project", "").strip()
1276
+ keyword = inputs.get("keyword", "").strip().lower()
1277
+
1278
+ project_dirs = _find_project_dirs(project_arg) if project_arg else _find_project_dirs()
1279
+ if not project_dirs:
1280
+ return json.dumps({"error": f"No project found matching '{project_arg}'"})
1281
+ if len(project_dirs) > 1 and project_arg:
1282
+ return json.dumps({"error": f"Ambiguous project '{project_arg}' — matches: {[_read_project_name(d) for d in project_dirs]}"})
1283
+
1284
+ project_dir = project_dirs[0]
1285
+ done_dir = project_dir / "issues" / ".done"
1286
+ if not done_dir.exists():
1287
+ return json.dumps({"error": "No archived issues found — issues/.done/ does not exist"})
1288
+
1289
+ # Find all archived issue files, newest first
1290
+ candidates = sorted(
1291
+ [f for f in done_dir.iterdir() if f.is_file() and not f.name.startswith(".")],
1292
+ key=lambda f: f.stat().st_mtime,
1293
+ reverse=True,
1294
+ )
1295
+ if not candidates:
1296
+ return json.dumps({"error": "No archived issues found in issues/.done/"})
1297
+
1298
+ # Filter by keyword if provided
1299
+ if keyword:
1300
+ matched = []
1301
+ for f in candidates:
1302
+ try:
1303
+ content = f.read_text(encoding="utf-8", errors="replace")
1304
+ if keyword in content.lower():
1305
+ matched.append(f)
1306
+ except OSError:
1307
+ pass
1308
+ if not matched:
1309
+ return json.dumps({"error": f"No archived issues match keyword '{keyword}'"})
1310
+ candidates = matched
1311
+
1312
+ source_file = candidates[0]
1313
+ content = source_file.read_text(encoding="utf-8", errors="replace")
1314
+
1315
+ # Re-submit as a fresh issue file (new name = new fingerprint = no cooldown block)
1316
+ issues_dir = project_dir / "issues"
1317
+ issues_dir.mkdir(exist_ok=True)
1318
+ fname = f"retry-{source_file.stem[-8:]}-{uuid.uuid4().hex[:6]}.txt"
1319
+ (issues_dir / fname).write_text(content, encoding="utf-8")
1320
+ (project_dir / "SENTINEL_POLL_NOW").touch()
1321
+
1322
+ project_label = _read_project_name(project_dir.resolve())
1323
+ logger.info("Boss retry_issue: re-queued '%s' as '%s' for %s", source_file.name, fname, project_label)
1324
+ return json.dumps({
1325
+ "status": "re-queued",
1326
+ "project": project_label,
1327
+ "original_file": source_file.name,
1328
+ "new_file": fname,
1329
+ "note": f"Re-submitted '{source_file.name}' to '{project_label}'. Poll triggered.",
1330
+ })
1331
+
1332
+
1250
1333
  if name == "get_fix_details":
1251
1334
  fp = inputs["fingerprint"]
1252
1335
  fix = store.get_confirmed_fix(fp) or store.get_marker_seen_fix(fp)