devops-bot-sdk 1.4.79__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.
- {devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/PKG-INFO +1 -1
- {devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
- {devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/pyproject.toml +1 -1
- {devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/sdk/__init__.py +2 -2
- {devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/sdk/client.py +1 -1
- {devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/sdk/ipc/handlers.py +70 -9
- {devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/README.md +0 -0
- {devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/devops_bot_sdk.egg-info/SOURCES.txt +0 -0
- {devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
- {devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
- {devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/devops_bot_sdk.egg-info/requires.txt +0 -0
- {devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/devops_bot_sdk.egg-info/top_level.txt +0 -0
- {devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/sdk/collectors/__init__.py +0 -0
- {devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/sdk/collectors/files.py +0 -0
- {devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/sdk/collectors/process.py +0 -0
- {devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/sdk/collectors/screenshot.py +0 -0
- {devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/sdk/config.py +0 -0
- {devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/sdk/crucial.py +0 -0
- {devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/sdk/exceptions.py +0 -0
- {devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/sdk/git_ops.py +0 -0
- {devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/sdk/graphify.py +0 -0
- {devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/sdk/hooks/__init__.py +0 -0
- {devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/sdk/hooks/crucial_guard.py +0 -0
- {devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/sdk/ipc/__init__.py +0 -0
- {devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/sdk/ipc/electron_bridge.py +0 -0
- {devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/sdk/local_exec.py +0 -0
- {devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/sdk/models/__init__.py +0 -0
- {devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/sdk/models/envelope.py +0 -0
- {devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/sdk/models/requests.py +0 -0
- {devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/sdk/models/responses.py +0 -0
- {devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/sdk/models/snapshots.py +0 -0
- {devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/sdk/py.typed +0 -0
- {devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/sdk/run_auto.py +0 -0
- {devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/sdk/sse.py +0 -0
- {devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/sdk/test.py +0 -0
- {devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/sdk/test_pipeline.py +0 -0
- {devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/sdk/updater.py +0 -0
- {devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/setup.cfg +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "devops-bot-sdk"
|
|
7
|
-
version = "1.4.
|
|
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.
|
|
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.
|
|
33
|
+
__version__ = "1.4.83"
|
|
34
34
|
__author__ = "AgentOS"
|
|
35
35
|
|
|
36
36
|
from sdk.client import BackendClient
|
|
@@ -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
|
|
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,9 +579,22 @@ 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
|
-
|
|
582
|
-
|
|
583
|
-
|
|
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
|
+
)
|
|
597
|
+
+ "assigneeEmail is OPTIONAL per story: set it ONLY when the epic explicitly "
|
|
584
598
|
"assigns that specific work to a named person/email (e.g. 'assign the API to "
|
|
585
599
|
"alice@x.com'); otherwise omit it.\n\n"
|
|
586
600
|
"=== EPIC FORMAT (epic_description MUST follow this exactly) ===\n"
|
|
@@ -681,6 +695,42 @@ def _extract_story_count(text: str | None) -> int | None:
|
|
|
681
695
|
return min(n, 50) if n and n >= 1 else None
|
|
682
696
|
|
|
683
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
|
+
|
|
684
734
|
def _extract_stories_dir(text: str | None) -> str | None:
|
|
685
735
|
"""A directory the instructions ask the user stories to be WRITTEN into, or None.
|
|
686
736
|
|
|
@@ -876,12 +926,22 @@ async def _handle_epic(
|
|
|
876
926
|
).model_dump())
|
|
877
927
|
|
|
878
928
|
# 1. Generate the decomposition, grounded in the codebase where one exists.
|
|
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)))
|
|
937
|
+
limit = story_count or _MAX_EPIC_STORIES
|
|
879
938
|
try:
|
|
880
939
|
await graphify.prepare(project_path)
|
|
881
940
|
except Exception: # noqa: BLE001
|
|
882
941
|
pass
|
|
883
942
|
gen = await _run_generation(
|
|
884
|
-
_build_epic_prompt(jira_key, summary, description, human
|
|
943
|
+
_build_epic_prompt(jira_key, summary, description, human,
|
|
944
|
+
story_count=story_count, story_titles=story_titles or None),
|
|
885
945
|
project_path, send=send, thread_id=thread_id,
|
|
886
946
|
label=f"Epic {jira_key}: generating user stories",
|
|
887
947
|
github_token=await client.github_token(),
|
|
@@ -924,7 +984,7 @@ async def _handle_epic(
|
|
|
924
984
|
await send(Envelope(
|
|
925
985
|
type="step_update", thread_id=thread_id,
|
|
926
986
|
data={"jira_task_id": tid, "current_step": f"Epic {jira_key}: marked Done — "
|
|
927
|
-
f"creating {min(len(stories),
|
|
987
|
+
f"creating {min(len(stories), limit)} user Story(ies)"},
|
|
928
988
|
).model_dump())
|
|
929
989
|
|
|
930
990
|
# 3. Create a To Do child Task per story, inheriting the Epic's fields.
|
|
@@ -933,7 +993,7 @@ async def _handle_epic(
|
|
|
933
993
|
# inherited assignee — but ONLY on the CHILD task, never on the parent Epic.
|
|
934
994
|
desc_email = _extract_email(description) or _extract_email(human)
|
|
935
995
|
created: list[str] = []
|
|
936
|
-
for st in stories[:
|
|
996
|
+
for st in stories[:limit]:
|
|
937
997
|
if not isinstance(st, dict) or not (st.get("summary") or "").strip():
|
|
938
998
|
continue
|
|
939
999
|
# Precedence: a per-story email the model extracted → an email in the
|
|
@@ -969,14 +1029,15 @@ async def _handle_epic(
|
|
|
969
1029
|
# the instructions (e.g. "create /home/me/new_tasks and write the user stories
|
|
970
1030
|
# there"). This is IN ADDITION to the Story tickets. Best-effort — a file-write
|
|
971
1031
|
# failure never fails the Epic.
|
|
972
|
-
|
|
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)
|
|
973
1034
|
if stories_dir:
|
|
974
1035
|
from pathlib import Path
|
|
975
1036
|
try:
|
|
976
1037
|
d = Path(stories_dir).expanduser()
|
|
977
1038
|
d.mkdir(parents=True, exist_ok=True) # create from scratch
|
|
978
1039
|
written = 0
|
|
979
|
-
for i, st in enumerate(stories[:
|
|
1040
|
+
for i, st in enumerate(stories[:limit], 1):
|
|
980
1041
|
if not isinstance(st, dict) or not (st.get("summary") or "").strip():
|
|
981
1042
|
continue
|
|
982
1043
|
title = str(st.get("summary", "")).strip()
|
|
File without changes
|
|
File without changes
|
{devops_bot_sdk-1.4.79 → devops_bot_sdk-1.4.83}/devops_bot_sdk.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|