devops-bot-sdk 1.4.81__tar.gz → 1.4.83__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.81 → devops_bot_sdk-1.4.83}/PKG-INFO +1 -1
  2. {devops_bot_sdk-1.4.81 → devops_bot_sdk-1.4.83}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
  3. {devops_bot_sdk-1.4.81 → devops_bot_sdk-1.4.83}/pyproject.toml +1 -1
  4. {devops_bot_sdk-1.4.81 → devops_bot_sdk-1.4.83}/sdk/__init__.py +2 -2
  5. {devops_bot_sdk-1.4.81 → devops_bot_sdk-1.4.83}/sdk/client.py +1 -1
  6. {devops_bot_sdk-1.4.81 → devops_bot_sdk-1.4.83}/sdk/ipc/handlers.py +65 -11
  7. {devops_bot_sdk-1.4.81 → devops_bot_sdk-1.4.83}/README.md +0 -0
  8. {devops_bot_sdk-1.4.81 → devops_bot_sdk-1.4.83}/devops_bot_sdk.egg-info/SOURCES.txt +0 -0
  9. {devops_bot_sdk-1.4.81 → devops_bot_sdk-1.4.83}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
  10. {devops_bot_sdk-1.4.81 → devops_bot_sdk-1.4.83}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
  11. {devops_bot_sdk-1.4.81 → devops_bot_sdk-1.4.83}/devops_bot_sdk.egg-info/requires.txt +0 -0
  12. {devops_bot_sdk-1.4.81 → devops_bot_sdk-1.4.83}/devops_bot_sdk.egg-info/top_level.txt +0 -0
  13. {devops_bot_sdk-1.4.81 → devops_bot_sdk-1.4.83}/sdk/collectors/__init__.py +0 -0
  14. {devops_bot_sdk-1.4.81 → devops_bot_sdk-1.4.83}/sdk/collectors/files.py +0 -0
  15. {devops_bot_sdk-1.4.81 → devops_bot_sdk-1.4.83}/sdk/collectors/process.py +0 -0
  16. {devops_bot_sdk-1.4.81 → devops_bot_sdk-1.4.83}/sdk/collectors/screenshot.py +0 -0
  17. {devops_bot_sdk-1.4.81 → devops_bot_sdk-1.4.83}/sdk/config.py +0 -0
  18. {devops_bot_sdk-1.4.81 → devops_bot_sdk-1.4.83}/sdk/crucial.py +0 -0
  19. {devops_bot_sdk-1.4.81 → devops_bot_sdk-1.4.83}/sdk/exceptions.py +0 -0
  20. {devops_bot_sdk-1.4.81 → devops_bot_sdk-1.4.83}/sdk/git_ops.py +0 -0
  21. {devops_bot_sdk-1.4.81 → devops_bot_sdk-1.4.83}/sdk/graphify.py +0 -0
  22. {devops_bot_sdk-1.4.81 → devops_bot_sdk-1.4.83}/sdk/hooks/__init__.py +0 -0
  23. {devops_bot_sdk-1.4.81 → devops_bot_sdk-1.4.83}/sdk/hooks/crucial_guard.py +0 -0
  24. {devops_bot_sdk-1.4.81 → devops_bot_sdk-1.4.83}/sdk/ipc/__init__.py +0 -0
  25. {devops_bot_sdk-1.4.81 → devops_bot_sdk-1.4.83}/sdk/ipc/electron_bridge.py +0 -0
  26. {devops_bot_sdk-1.4.81 → devops_bot_sdk-1.4.83}/sdk/local_exec.py +0 -0
  27. {devops_bot_sdk-1.4.81 → devops_bot_sdk-1.4.83}/sdk/models/__init__.py +0 -0
  28. {devops_bot_sdk-1.4.81 → devops_bot_sdk-1.4.83}/sdk/models/envelope.py +0 -0
  29. {devops_bot_sdk-1.4.81 → devops_bot_sdk-1.4.83}/sdk/models/requests.py +0 -0
  30. {devops_bot_sdk-1.4.81 → devops_bot_sdk-1.4.83}/sdk/models/responses.py +0 -0
  31. {devops_bot_sdk-1.4.81 → devops_bot_sdk-1.4.83}/sdk/models/snapshots.py +0 -0
  32. {devops_bot_sdk-1.4.81 → devops_bot_sdk-1.4.83}/sdk/py.typed +0 -0
  33. {devops_bot_sdk-1.4.81 → devops_bot_sdk-1.4.83}/sdk/run_auto.py +0 -0
  34. {devops_bot_sdk-1.4.81 → devops_bot_sdk-1.4.83}/sdk/sse.py +0 -0
  35. {devops_bot_sdk-1.4.81 → devops_bot_sdk-1.4.83}/sdk/test.py +0 -0
  36. {devops_bot_sdk-1.4.81 → devops_bot_sdk-1.4.83}/sdk/test_pipeline.py +0 -0
  37. {devops_bot_sdk-1.4.81 → devops_bot_sdk-1.4.83}/sdk/updater.py +0 -0
  38. {devops_bot_sdk-1.4.81 → devops_bot_sdk-1.4.83}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devops-bot-sdk
3
- Version: 1.4.81
3
+ Version: 1.4.83
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.81
3
+ Version: 1.4.83
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.81"
7
+ version = "1.4.83"
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.81
3
+ Version: 1.4.83
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.81"
33
+ __version__ = "1.4.83"
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.81"
61
+ SDK_VERSION = "1.4.83"
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
@@ -531,7 +531,8 @@ _PRD_FORMAT = (
531
531
 
532
532
 
533
533
  def _build_epic_prompt(jira_key: str, summary: str, description: str | None,
534
- human_instructions: str | None, story_count: int | None = None) -> str:
534
+ human_instructions: str | None, story_count: int | None = None,
535
+ story_titles: list[str] | None = None) -> str:
535
536
  """Read-only prompt that turns an Epic into a decomposition JSON."""
536
537
  parts: list[str] = [
537
538
  "You are decomposing a JIRA EPIC into implementable user stories. This is "
@@ -578,11 +579,21 @@ def _build_epic_prompt(jira_key: str, summary: str, description: str | None,
578
579
  "}\n\n"
579
580
  "Do NOT produce humanInstructions for the stories — created stories are clean "
580
581
  "user-story artifacts; implementation instructions are added later.\n"
581
- + (f"Generate EXACTLY {story_count} user stor{'y' if story_count == 1 else 'ies'} — "
582
- "the epic explicitly asks for this many; do not produce more or fewer.\n"
583
- if story_count else
584
- "Create as many stories as the epic genuinely needs (do not pad or truncate "
585
- "artificially); each story must be independently implementable.\n")
582
+ + (
583
+ # The epic ENUMERATES its stories reproduce them EXACTLY (verbatim titles).
584
+ ("The epic already lists its user stories. Produce EXACTLY these "
585
+ f"{len(story_titles)} stor{'y' if len(story_titles) == 1 else 'ies'}, in this "
586
+ "order, using each title VERBATIM as the story's summary do NOT invent, "
587
+ "rename, merge, add, drop, or reorder any. Only expand each into the USER "
588
+ "STORY FORMAT for its description:\n"
589
+ + "\n".join(f" {i}. {t}" for i, t in enumerate(story_titles, 1)) + "\n")
590
+ if story_titles else
591
+ (f"Generate EXACTLY {story_count} user stor{'y' if story_count == 1 else 'ies'} — "
592
+ "the epic explicitly asks for this many; do not produce more or fewer.\n"
593
+ if story_count else
594
+ "Create as many stories as the epic genuinely needs (do not pad or truncate "
595
+ "artificially); each story must be independently implementable.\n")
596
+ )
586
597
  + "assigneeEmail is OPTIONAL per story: set it ONLY when the epic explicitly "
587
598
  "assigns that specific work to a named person/email (e.g. 'assign the API to "
588
599
  "alice@x.com'); otherwise omit it.\n\n"
@@ -684,6 +695,42 @@ def _extract_story_count(text: str | None) -> int | None:
684
695
  return min(n, 50) if n and n >= 1 else None
685
696
 
686
697
 
698
+ _STORY_LIST_ITEM_RE = re.compile(r"^\s*(?:\d+[.)]|[-*•])\s+(.+?)\s*$")
699
+
700
+
701
+ def _extract_story_titles(text: str | None) -> list[str]:
702
+ """Titles of the user stories the epic ENUMERATES under a 'User Stories' heading.
703
+
704
+ When the epic already lists its stories ("1. Login — …", "- Logout"), those are
705
+ the authoritative stories — the bot must reproduce EXACTLY them (same count, same
706
+ titles), not invent its own. Returns the list of titles (the part before an
707
+ em/en-dash or colon separator), or [] when the epic gives no explicit list.
708
+ """
709
+ if not text:
710
+ return []
711
+ lines = text.splitlines()
712
+ start = None
713
+ for i, ln in enumerate(lines):
714
+ if re.match(r"\s*#{0,6}\s*user\s+stor(?:y|ies)\b\s*:?\s*$", ln, re.I):
715
+ start = i + 1
716
+ break
717
+ if start is None:
718
+ return []
719
+ titles: list[str] = []
720
+ for ln in lines[start:]:
721
+ if re.match(r"\s*#{1,6}\s+\S", ln): # next heading → section ends
722
+ break
723
+ m = _STORY_LIST_ITEM_RE.match(ln)
724
+ if not m:
725
+ continue
726
+ item = m.group(1).strip()
727
+ title = re.split(r"\s+[—–-]\s+|:\s+", item, maxsplit=1)[0]
728
+ title = title.strip(" *`_").strip()
729
+ if title:
730
+ titles.append(title[:250])
731
+ return titles[:50]
732
+
733
+
687
734
  def _extract_stories_dir(text: str | None) -> str | None:
688
735
  """A directory the instructions ask the user stories to be WRITTEN into, or None.
689
736
 
@@ -879,16 +926,22 @@ async def _handle_epic(
879
926
  ).model_dump())
880
927
 
881
928
  # 1. Generate the decomposition, grounded in the codebase where one exists.
882
- # If the description/instructions ask for a specific number of stories, honor
883
- # it exactly; otherwise the model decides (bounded by _MAX_EPIC_STORIES).
884
- story_count = _extract_story_count(description) or _extract_story_count(human)
929
+ # Priority for how many stories + their titles:
930
+ # (a) the epic ENUMERATES its stories (a "User Stories" list) → use those
931
+ # exact titles and count, verbatim;
932
+ # (b) else an explicit "build N stories" count → exactly N;
933
+ # (c) else the model decides (bounded by _MAX_EPIC_STORIES).
934
+ story_titles = _extract_story_titles(description) or _extract_story_titles(human)
935
+ story_count = (len(story_titles) if story_titles
936
+ else (_extract_story_count(description) or _extract_story_count(human)))
885
937
  limit = story_count or _MAX_EPIC_STORIES
886
938
  try:
887
939
  await graphify.prepare(project_path)
888
940
  except Exception: # noqa: BLE001
889
941
  pass
890
942
  gen = await _run_generation(
891
- _build_epic_prompt(jira_key, summary, description, human, story_count=story_count),
943
+ _build_epic_prompt(jira_key, summary, description, human,
944
+ story_count=story_count, story_titles=story_titles or None),
892
945
  project_path, send=send, thread_id=thread_id,
893
946
  label=f"Epic {jira_key}: generating user stories",
894
947
  github_token=await client.github_token(),
@@ -976,7 +1029,8 @@ async def _handle_epic(
976
1029
  # the instructions (e.g. "create /home/me/new_tasks and write the user stories
977
1030
  # there"). This is IN ADDITION to the Story tickets. Best-effort — a file-write
978
1031
  # failure never fails the Epic.
979
- stories_dir = _extract_stories_dir(human)
1032
+ # Folder may be named in the humanInstructions OR the description — check both.
1033
+ stories_dir = _extract_stories_dir(human) or _extract_stories_dir(description)
980
1034
  if stories_dir:
981
1035
  from pathlib import Path
982
1036
  try: