taskboy 0.1.1__py3-none-any.whl

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 (95) hide show
  1. taskboy/__init__.py +0 -0
  2. taskboy/adapters/__init__.py +0 -0
  3. taskboy/adapters/_util.py +30 -0
  4. taskboy/adapters/aws_read.py +107 -0
  5. taskboy/adapters/confluence.py +88 -0
  6. taskboy/adapters/github_api.py +564 -0
  7. taskboy/adapters/issues.py +435 -0
  8. taskboy/adapters/jira.py +263 -0
  9. taskboy/adapters/sentry.py +82 -0
  10. taskboy/adapters/slack_history.py +204 -0
  11. taskboy/assets.py +27 -0
  12. taskboy/audit.py +78 -0
  13. taskboy/broker.py +266 -0
  14. taskboy/classifier.py +219 -0
  15. taskboy/cli.py +161 -0
  16. taskboy/config.py +539 -0
  17. taskboy/dashboard/__init__.py +5 -0
  18. taskboy/dashboard/api.py +963 -0
  19. taskboy/dashboard/app.py +78 -0
  20. taskboy/dashboard/auth.py +117 -0
  21. taskboy/dashboard/editors.py +140 -0
  22. taskboy/dashboard/gitops.py +60 -0
  23. taskboy/dashboard/render.py +48 -0
  24. taskboy/debug_feed.py +170 -0
  25. taskboy/deploy/env.example +17 -0
  26. taskboy/deploy/git-cred-helper.py +36 -0
  27. taskboy/deploy/install.sh +101 -0
  28. taskboy/deploy/remote-update.sh +52 -0
  29. taskboy/deploy/taskboy-restart.path +10 -0
  30. taskboy/deploy/taskboy-restart.service +8 -0
  31. taskboy/deploy/taskboy.service +22 -0
  32. taskboy/hooks.py +208 -0
  33. taskboy/issue_runs.py +203 -0
  34. taskboy/llm.py +125 -0
  35. taskboy/main.py +269 -0
  36. taskboy/memory.py +59 -0
  37. taskboy/models.py +103 -0
  38. taskboy/mrkdwn.py +19 -0
  39. taskboy/notify.py +41 -0
  40. taskboy/orchestrator.py +401 -0
  41. taskboy/personality.py +17 -0
  42. taskboy/prompts.py +288 -0
  43. taskboy/quick.py +234 -0
  44. taskboy/redact.py +49 -0
  45. taskboy/repocache.py +117 -0
  46. taskboy/review_requests.py +388 -0
  47. taskboy/router.py +146 -0
  48. taskboy/runner.py +581 -0
  49. taskboy/scheduler.py +301 -0
  50. taskboy/secrets.py +83 -0
  51. taskboy/settings.py +19 -0
  52. taskboy/setup_checks.py +184 -0
  53. taskboy/setup_wizard.py +699 -0
  54. taskboy/skills.py +95 -0
  55. taskboy/slack.py +666 -0
  56. taskboy/slack_users.py +32 -0
  57. taskboy/started_messages.py +33 -0
  58. taskboy/store.py +1393 -0
  59. taskboy/task_actions.py +86 -0
  60. taskboy/templates/config.example.yaml +149 -0
  61. taskboy/templates/conventions.md +60 -0
  62. taskboy/templates/help.example.md +29 -0
  63. taskboy/templates/personality_agent.example.md +5 -0
  64. taskboy/templates/personality_reviewer.example.md +5 -0
  65. taskboy/templates/services/aws.yaml +7 -0
  66. taskboy/templates/services/confluence.yaml +4 -0
  67. taskboy/templates/services/github.yaml +14 -0
  68. taskboy/templates/services/jira.yaml +6 -0
  69. taskboy/templates/services/sentry.yaml +4 -0
  70. taskboy/templates/services/slack.yaml +9 -0
  71. taskboy/templates/skills/README.md +33 -0
  72. taskboy/templates/skills/discoverissues/SKILL.md +46 -0
  73. taskboy/templates/skills/implementapprovedissues/SKILL.md +41 -0
  74. taskboy/templates/skills/jira2pr/SKILL.md +66 -0
  75. taskboy/templates/skills/monitor/SKILL.md +32 -0
  76. taskboy/templates/skills/monitornew/SKILL.md +27 -0
  77. taskboy/templates/skills/refineissue/SKILL.md +35 -0
  78. taskboy/templates/skills/release/SKILL.md +36 -0
  79. taskboy/templates/skills/review/SKILL.md +89 -0
  80. taskboy/templates/skills/reviewandmonitor/SKILL.md +22 -0
  81. taskboy/templates/skills/reviews/SKILL.md +26 -0
  82. taskboy/templates/skills/slack2jira/SKILL.md +47 -0
  83. taskboy/templates/skills/slack2pr/SKILL.md +64 -0
  84. taskboy/templates/skills/spec2pr/SKILL.md +35 -0
  85. taskboy/templates/slack_app_manifest.yaml +48 -0
  86. taskboy/templates/task_started_messages.yaml +14 -0
  87. taskboy/ui_dist/assets/index-DmYxR9Qy.css +1 -0
  88. taskboy/ui_dist/assets/index-LDDO4iT2.js +15 -0
  89. taskboy/ui_dist/index.html +15 -0
  90. taskboy/workspace.py +86 -0
  91. taskboy-0.1.1.dist-info/METADATA +120 -0
  92. taskboy-0.1.1.dist-info/RECORD +95 -0
  93. taskboy-0.1.1.dist-info/WHEEL +5 -0
  94. taskboy-0.1.1.dist-info/entry_points.txt +2 -0
  95. taskboy-0.1.1.dist-info/top_level.txt +1 -0
taskboy/slack_users.py ADDED
@@ -0,0 +1,32 @@
1
+ """shared Slack user-profile cache and refresh logic."""
2
+
3
+ from datetime import datetime, timezone
4
+
5
+ from taskboy.store import Store
6
+
7
+ USER_CACHE_SECONDS = 24 * 60 * 60
8
+
9
+
10
+ async def cached_user_profile(store: Store, client, user_id: str) -> dict | None:
11
+ cached = store.get_slack_user(user_id)
12
+ if cached:
13
+ try:
14
+ age = datetime.now(timezone.utc) - datetime.fromisoformat(cached["updated_at"])
15
+ if age.total_seconds() < USER_CACHE_SECONDS:
16
+ return cached
17
+ except (KeyError, TypeError, ValueError):
18
+ pass
19
+ response = await client.users_info(user=user_id)
20
+ user = response.get("user") or {}
21
+ profile = user.get("profile") or {}
22
+ return store.upsert_slack_user(
23
+ user_id,
24
+ team_id=user.get("team_id"),
25
+ username=user.get("name"),
26
+ real_name=user.get("real_name") or profile.get("real_name"),
27
+ display_name=profile.get("display_name"),
28
+ email=profile.get("email"),
29
+ title=profile.get("title"),
30
+ tz=user.get("tz"),
31
+ is_bot=int(bool(user.get("is_bot"))),
32
+ )
@@ -0,0 +1,33 @@
1
+ """token-free task-started message selection from agent and reviewer yaml pools."""
2
+
3
+ import random
4
+ from pathlib import Path
5
+
6
+ import yaml
7
+
8
+
9
+ def validate_content(content: str) -> dict[str, list[str]]:
10
+ try:
11
+ pools = yaml.safe_load(content)
12
+ except yaml.YAMLError as e:
13
+ raise ValueError(f"task-started messages are not valid yaml: {e}") from e
14
+ if not isinstance(pools, dict) or set(pools) != {"agent", "reviewer"}:
15
+ raise ValueError("task-started messages must be a yaml mapping with 'agent' and 'reviewer' lists")
16
+ for pool, choices in pools.items():
17
+ if not isinstance(choices, list) or not choices or not all(isinstance(item, str) and item.strip() for item in choices):
18
+ raise ValueError(f"task-started messages for {pool!r} must be a non-empty list of non-empty strings")
19
+ return pools
20
+
21
+
22
+ def pick(path: str | None, pool: str | None) -> str | None:
23
+ if not path or not pool:
24
+ return None
25
+ try:
26
+ pools = yaml.safe_load(Path(path).read_text())
27
+ choices = pools.get(pool) if isinstance(pools, dict) else None
28
+ if not isinstance(choices, list):
29
+ return None
30
+ valid = [item.strip() for item in choices if isinstance(item, str) and item.strip()]
31
+ return random.choice(valid) if valid else None
32
+ except (OSError, UnicodeError, yaml.YAMLError):
33
+ return None