devops-bot-sdk 1.4.45__tar.gz → 1.4.47__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 (38) hide show
  1. {devops_bot_sdk-1.4.45 → devops_bot_sdk-1.4.47}/PKG-INFO +1 -1
  2. {devops_bot_sdk-1.4.45 → devops_bot_sdk-1.4.47}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
  3. {devops_bot_sdk-1.4.45 → devops_bot_sdk-1.4.47}/pyproject.toml +1 -1
  4. {devops_bot_sdk-1.4.45 → devops_bot_sdk-1.4.47}/sdk/__init__.py +2 -2
  5. {devops_bot_sdk-1.4.45 → devops_bot_sdk-1.4.47}/sdk/client.py +1 -1
  6. {devops_bot_sdk-1.4.45 → devops_bot_sdk-1.4.47}/sdk/ipc/handlers.py +28 -29
  7. {devops_bot_sdk-1.4.45 → devops_bot_sdk-1.4.47}/sdk/run_auto.py +25 -10
  8. {devops_bot_sdk-1.4.45 → devops_bot_sdk-1.4.47}/README.md +0 -0
  9. {devops_bot_sdk-1.4.45 → devops_bot_sdk-1.4.47}/devops_bot_sdk.egg-info/SOURCES.txt +0 -0
  10. {devops_bot_sdk-1.4.45 → devops_bot_sdk-1.4.47}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
  11. {devops_bot_sdk-1.4.45 → devops_bot_sdk-1.4.47}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
  12. {devops_bot_sdk-1.4.45 → devops_bot_sdk-1.4.47}/devops_bot_sdk.egg-info/requires.txt +0 -0
  13. {devops_bot_sdk-1.4.45 → devops_bot_sdk-1.4.47}/devops_bot_sdk.egg-info/top_level.txt +0 -0
  14. {devops_bot_sdk-1.4.45 → devops_bot_sdk-1.4.47}/sdk/collectors/__init__.py +0 -0
  15. {devops_bot_sdk-1.4.45 → devops_bot_sdk-1.4.47}/sdk/collectors/files.py +0 -0
  16. {devops_bot_sdk-1.4.45 → devops_bot_sdk-1.4.47}/sdk/collectors/process.py +0 -0
  17. {devops_bot_sdk-1.4.45 → devops_bot_sdk-1.4.47}/sdk/collectors/screenshot.py +0 -0
  18. {devops_bot_sdk-1.4.45 → devops_bot_sdk-1.4.47}/sdk/config.py +0 -0
  19. {devops_bot_sdk-1.4.45 → devops_bot_sdk-1.4.47}/sdk/crucial.py +0 -0
  20. {devops_bot_sdk-1.4.45 → devops_bot_sdk-1.4.47}/sdk/exceptions.py +0 -0
  21. {devops_bot_sdk-1.4.45 → devops_bot_sdk-1.4.47}/sdk/git_ops.py +0 -0
  22. {devops_bot_sdk-1.4.45 → devops_bot_sdk-1.4.47}/sdk/graphify.py +0 -0
  23. {devops_bot_sdk-1.4.45 → devops_bot_sdk-1.4.47}/sdk/hooks/__init__.py +0 -0
  24. {devops_bot_sdk-1.4.45 → devops_bot_sdk-1.4.47}/sdk/hooks/crucial_guard.py +0 -0
  25. {devops_bot_sdk-1.4.45 → devops_bot_sdk-1.4.47}/sdk/ipc/__init__.py +0 -0
  26. {devops_bot_sdk-1.4.45 → devops_bot_sdk-1.4.47}/sdk/ipc/electron_bridge.py +0 -0
  27. {devops_bot_sdk-1.4.45 → devops_bot_sdk-1.4.47}/sdk/local_exec.py +0 -0
  28. {devops_bot_sdk-1.4.45 → devops_bot_sdk-1.4.47}/sdk/models/__init__.py +0 -0
  29. {devops_bot_sdk-1.4.45 → devops_bot_sdk-1.4.47}/sdk/models/envelope.py +0 -0
  30. {devops_bot_sdk-1.4.45 → devops_bot_sdk-1.4.47}/sdk/models/requests.py +0 -0
  31. {devops_bot_sdk-1.4.45 → devops_bot_sdk-1.4.47}/sdk/models/responses.py +0 -0
  32. {devops_bot_sdk-1.4.45 → devops_bot_sdk-1.4.47}/sdk/models/snapshots.py +0 -0
  33. {devops_bot_sdk-1.4.45 → devops_bot_sdk-1.4.47}/sdk/py.typed +0 -0
  34. {devops_bot_sdk-1.4.45 → devops_bot_sdk-1.4.47}/sdk/sse.py +0 -0
  35. {devops_bot_sdk-1.4.45 → devops_bot_sdk-1.4.47}/sdk/test.py +0 -0
  36. {devops_bot_sdk-1.4.45 → devops_bot_sdk-1.4.47}/sdk/test_pipeline.py +0 -0
  37. {devops_bot_sdk-1.4.45 → devops_bot_sdk-1.4.47}/sdk/updater.py +0 -0
  38. {devops_bot_sdk-1.4.45 → devops_bot_sdk-1.4.47}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devops-bot-sdk
3
- Version: 1.4.45
3
+ Version: 1.4.47
4
4
  Summary: DevOps Bot Desktop SDK — thin client for the AgentOS Electron desktop app
5
5
  Author: noumanaziz2128
6
6
  License-Expression: LicenseRef-Proprietary
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devops-bot-sdk
3
- Version: 1.4.45
3
+ Version: 1.4.47
4
4
  Summary: DevOps Bot Desktop SDK — thin client for the AgentOS Electron desktop app
5
5
  Author: noumanaziz2128
6
6
  License-Expression: LicenseRef-Proprietary
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "devops-bot-sdk"
7
- version = "1.4.45"
7
+ version = "1.4.47"
8
8
  description = "DevOps Bot Desktop SDK — thin client for the AgentOS Electron desktop app"
9
9
  readme = "README.md"
10
10
  license = "LicenseRef-Proprietary"
@@ -1,6 +1,6 @@
1
1
  """AgentOS Desktop SDK — thin HTTPS/SSE client for the Electron app.
2
2
 
3
- Version: 1.4.45
3
+ Version: 1.4.47
4
4
 
5
5
  Public surface:
6
6
  BackendClient.from_config() — create client from ~/.agentos/config.toml
@@ -30,7 +30,7 @@ Rules:
30
30
  - All data egress through submit_webhook only
31
31
  """
32
32
 
33
- __version__ = "1.4.45"
33
+ __version__ = "1.4.47"
34
34
  __author__ = "AgentOS"
35
35
 
36
36
  from sdk.client import BackendClient
@@ -58,7 +58,7 @@ def _notify_status(task_id: str, status: str) -> None:
58
58
  pass
59
59
 
60
60
 
61
- SDK_VERSION = "1.4.45"
61
+ SDK_VERSION = "1.4.47"
62
62
  _POLL_INTERVAL = 3.0
63
63
  _POLL_TIMEOUT = 600.0
64
64
  _ORCHESTRATE_TIMEOUT = 2700.0 # 45 min — covers approval wait + VPS execution time
@@ -653,18 +653,24 @@ async def _handle_story(
653
653
  github_token=await client.github_token(),
654
654
  )
655
655
  prd = (gen.get("result") or "").strip() if gen.get("ok") else ""
656
- if not prd:
656
+ if prd:
657
+ # PRD lives alongside the user story in the (now Task) description.
658
+ new_description = f"{user_story}\n\n---\n\n{_PRD_MARKER}\n\n{prd}"[:20000]
659
+ else:
660
+ # PRD generation failed — do NOT abandon the story: it must still become
661
+ # a Task (per "a Story once run should be changed into Task"). The PRD
662
+ # directive stays in humanInstructions, so the later coding run can still
663
+ # produce the PRD.md file.
657
664
  reason = (gen.get("error") or "PRD generation produced no content")[:200]
658
- # Leave the Story To Do (never mark it In Progress/Done/Backlog) so a
659
- # retry or the developer can pick it up.
660
- await client.log_activity(f"Story {jira_key}: PRD generation failed — {reason}")
665
+ await client.log_activity(
666
+ f"Story {jira_key}: PRD not pre-generated ({reason}) converting to Task anyway"
667
+ )
661
668
  await send(Envelope(
662
- type=Envelope.TYPE_ERROR, thread_id=thread_id,
663
- data={"code": "STORY_PRD_FAILED", "task_id": tid, "message": reason},
669
+ type="step_update", thread_id=thread_id,
670
+ data={"jira_task_id": tid,
671
+ "current_step": f"Story {jira_key}: PRD generation skipped ({reason[:60]}) "
672
+ "— converting to Task"},
664
673
  ).model_dump())
665
- return
666
- # PRD lives alongside the user story in the (now Task) description.
667
- new_description = f"{user_story}\n\n---\n\n{_PRD_MARKER}\n\n{prd}"[:20000]
668
674
 
669
675
  # Convert Story → Task on the SAME ticket; leave it To Do so the next run codes
670
676
  # it. humanInstructions are untouched (they still carry the location + any PRD
@@ -682,14 +688,15 @@ async def _handle_story(
682
688
  ).model_dump())
683
689
  return
684
690
 
691
+ prd_in_desc = bool(new_description) or already_has_prd
685
692
  await client.log_activity(
686
- f"Story {jira_key}: converted to Task{' with PRD' if wants_prd else ''} (To Do)"
693
+ f"Story {jira_key}: converted to Task{' with PRD' if prd_in_desc else ''} (To Do)"
687
694
  )
688
695
  await send(Envelope(
689
696
  type="step_update", thread_id=thread_id,
690
697
  data={"jira_task_id": tid,
691
698
  "current_step": f"Story {jira_key} → Task (To Do)"
692
- + (" + PRD in description" if wants_prd else "")
699
+ + (" + PRD in description" if prd_in_desc else "")
693
700
  + " — run again to implement"},
694
701
  ).model_dump())
695
702
 
@@ -2493,10 +2500,16 @@ async def handle_orchestrate_auto_local_run(
2493
2500
  # raised earliest is started first. Unknown/invalid timestamps sort last.
2494
2501
  tasks = sorted(tasks, key=_task_created_key)
2495
2502
 
2503
+ # `stories_only` (agentos-auto story) → convert Stories to Tasks ONLY: no
2504
+ # epic decomposition, no coding. Narrow the queue to Story-type tickets.
2505
+ stories_only = bool(payload.get("stories_only"))
2506
+ if stories_only:
2507
+ tasks = [t for t in tasks if str(t.get("type") or "").strip().lower() == "story"]
2508
+
2496
2509
  await send(Envelope(
2497
2510
  type="pipeline_started", thread_id=thread_id,
2498
2511
  data={"mode": "orchestrate-auto-local", "task_count": len(tasks),
2499
- "only_keys": only_keys or None},
2512
+ "only_keys": only_keys or None, "stories_only": stories_only or None},
2500
2513
  ).model_dump())
2501
2514
 
2502
2515
  if not tasks:
@@ -2527,21 +2540,6 @@ async def handle_orchestrate_auto_local_run(
2527
2540
  project_path = _resolve_local_project_path(task, payload)
2528
2541
  ttype = str(task.get("type") or "").strip().lower()
2529
2542
 
2530
- # User STORIES are for the ASSIGNED DEVELOPERS, not the bot: the default
2531
- # auto-loop skips Story-type tickets and never marks them In Progress or
2532
- # Done. (An explicit target — `agentos-auto ticket <KEY>`, i.e. only_keys
2533
- # set — still lets the bot act on a Story: generate its PRD and convert
2534
- # it to a Task.) Once a ticket is a TASK it is coded normally
2535
- # (In Progress → Done), even when it descended from an Epic/Story.
2536
- if ttype == "story" and not only_keys:
2537
- await send(Envelope(
2538
- type="step_update", thread_id=thread_id,
2539
- data={"jira_task_id": tid,
2540
- "current_step": f"({idx}/{total}) {jira_key}: skipped — user Story "
2541
- f"left for the assigned developer"},
2542
- ).model_dump())
2543
- continue
2544
-
2545
2543
  await send(Envelope(
2546
2544
  type="step_update", thread_id=thread_id,
2547
2545
  data={"current_step": f"({idx}/{total}) {jira_key}: {task.get('summary', '')}".rstrip(": "),
@@ -2550,8 +2548,9 @@ async def handle_orchestrate_auto_local_run(
2550
2548
 
2551
2549
  # Staged decomposition — an EPIC and a STORY are transformed, not coded:
2552
2550
  # Epic → decompose into user Stories (To Do), mark Epic Done
2553
- # Story → (optional PRD) + convert to Task (To Do) [only when targeted]
2554
- # Task → implemented by the normal pipeline below
2551
+ # Story → (optional PRD) + ALWAYS convert to Task (To Do); a Story is
2552
+ # never marked In Progress/Done only ever converted
2553
+ # Task → implemented by the normal pipeline below (In Progress → Done)
2555
2554
  # Each is isolated: a failure must not abort the remaining queue.
2556
2555
  if ttype in ("epic", "story"):
2557
2556
  handler = _handle_epic if ttype == "epic" else _handle_story
@@ -11,6 +11,8 @@ Commands:
11
11
  agentos-auto resume alias for run — re-fetches remaining To Do tasks
12
12
  agentos-auto ticket K [K...] run ONLY the named Jira ticket(s), e.g. BBW-81 —
13
13
  any status, in the order given (alias: tickets)
14
+ agentos-auto story [K ...] convert Story(ies) → Task(s) only (with PRD), NO
15
+ coding; no keys = all To Do Stories (alias: stories)
14
16
  agentos-auto stop ask the running loop to pause after the current task
15
17
  agentos-auto status show whether the background loop is running / stop pending
16
18
  agentos-auto update upgrade the SDK to the latest version
@@ -298,7 +300,8 @@ async def _send(env: dict) -> None:
298
300
  print(f" [{etype}] {json.dumps(data)[:200]}")
299
301
 
300
302
 
301
- def _run_loop(workspace_root: str | None, only_keys: list[str] | None = None) -> None:
303
+ def _run_loop(workspace_root: str | None, only_keys: list[str] | None = None,
304
+ stories_only: bool = False) -> None:
302
305
  from sdk import local_exec
303
306
  from sdk.client import BackendClient, register_status_observer
304
307
  from sdk.ipc import handlers
@@ -320,6 +323,9 @@ def _run_loop(workspace_root: str | None, only_keys: list[str] | None = None) ->
320
323
  if only_keys:
321
324
  # Targeted run: only these Jira tickets, in this order, regardless of status.
322
325
  payload["only_keys"] = only_keys
326
+ if stories_only:
327
+ # Story→Task-only run: convert Stories to Tasks (with PRD), no coding.
328
+ payload["stories_only"] = True
323
329
 
324
330
  _clear_stop() # a fresh run/resume clears any stale stop flag
325
331
  _clear_backlogged() # session-scoped: only track Backlog moves from THIS run
@@ -458,17 +464,22 @@ def main() -> None:
458
464
  print("stop pending: yes" if _stop_requested() else "stop pending: no")
459
465
  return
460
466
 
461
- if cmd in ("run", "resume", "ticket", "tickets"):
462
- # Targeted run: `ticket`/`tickets` runs ONLY the named Jira keys (any
463
- # status, in the order given); `run`/`resume` runs the whole To Do queue.
467
+ if cmd in ("run", "resume", "ticket", "tickets", "story", "stories"):
468
+ # Run modes:
469
+ # run/resume the whole To Do queue
470
+ # ticket K... → ONLY the named Jira keys (any status, in order)
471
+ # story [K...] → convert Stories → Tasks only (with PRD); NO coding.
472
+ # With keys: those Stories; without: every To Do Story.
464
473
  only_keys: list[str] | None = None
465
- if cmd in ("ticket", "tickets"):
474
+ stories_only = cmd in ("story", "stories")
475
+ if cmd in ("ticket", "tickets", "story", "stories"):
466
476
  only_keys = _parse_ticket_keys(argv)
467
- if not only_keys:
477
+ if cmd in ("ticket", "tickets") and not only_keys:
468
478
  print("usage: agentos-auto ticket <JIRA-KEY> [<JIRA-KEY> ...]")
469
479
  print(" e.g. agentos-auto ticket BBW-81")
470
480
  print(" agentos-auto ticket BBW-81 BBW-80")
471
481
  sys.exit(1)
482
+ # `story` keys are OPTIONAL — no keys means "all To Do Stories".
472
483
 
473
484
  # Self-update FIRST (before the already-running check) so the installed
474
485
  # package is refreshed even when a daemon is already running — otherwise a
@@ -494,14 +505,18 @@ def main() -> None:
494
505
  # attached (useful for debugging). On platforms without fork we stay attached.
495
506
  foreground = "--foreground" in argv or "-f" in argv
496
507
  if not foreground and hasattr(os, "fork"):
497
- target = f" — tickets: {', '.join(only_keys)}" if only_keys else ""
508
+ if stories_only:
509
+ target = (f" — stories→tasks: {', '.join(only_keys)}" if only_keys
510
+ else " — converting all To Do Stories → Tasks")
511
+ else:
512
+ target = f" — tickets: {', '.join(only_keys)}" if only_keys else ""
498
513
  print(f"▶ agentos-auto started in the background{target} — it keeps running "
499
514
  "after you close this terminal.")
500
515
  print(f" logs: {_LOG_FILE}")
501
516
  print(" status: agentos-auto status")
502
517
  print(" stop: agentos-auto stop")
503
518
  if not foreground and _daemonize():
504
- _run_loop(workspace_root, only_keys) # only the detached daemon child reaches here
519
+ _run_loop(workspace_root, only_keys, stories_only) # detached daemon child only
505
520
  return
506
521
  if not foreground:
507
522
  print("note: background mode unavailable on this platform — running in the "
@@ -510,10 +525,10 @@ def main() -> None:
510
525
  # Foreground run still records a PID so stop/status can find it.
511
526
  _AGENTOS_DIR.mkdir(parents=True, exist_ok=True)
512
527
  _PID_FILE.write_text(str(os.getpid()))
513
- _run_loop(workspace_root, only_keys)
528
+ _run_loop(workspace_root, only_keys, stories_only)
514
529
  return
515
530
 
516
- print(f"unknown command '{cmd}'. use: run | resume | ticket | stop | status | update")
531
+ print(f"unknown command '{cmd}'. use: run | resume | ticket | story | stop | status | update")
517
532
  sys.exit(1)
518
533
 
519
534