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.
- taskboy/__init__.py +0 -0
- taskboy/adapters/__init__.py +0 -0
- taskboy/adapters/_util.py +30 -0
- taskboy/adapters/aws_read.py +107 -0
- taskboy/adapters/confluence.py +88 -0
- taskboy/adapters/github_api.py +564 -0
- taskboy/adapters/issues.py +435 -0
- taskboy/adapters/jira.py +263 -0
- taskboy/adapters/sentry.py +82 -0
- taskboy/adapters/slack_history.py +204 -0
- taskboy/assets.py +27 -0
- taskboy/audit.py +78 -0
- taskboy/broker.py +266 -0
- taskboy/classifier.py +219 -0
- taskboy/cli.py +161 -0
- taskboy/config.py +539 -0
- taskboy/dashboard/__init__.py +5 -0
- taskboy/dashboard/api.py +963 -0
- taskboy/dashboard/app.py +78 -0
- taskboy/dashboard/auth.py +117 -0
- taskboy/dashboard/editors.py +140 -0
- taskboy/dashboard/gitops.py +60 -0
- taskboy/dashboard/render.py +48 -0
- taskboy/debug_feed.py +170 -0
- taskboy/deploy/env.example +17 -0
- taskboy/deploy/git-cred-helper.py +36 -0
- taskboy/deploy/install.sh +101 -0
- taskboy/deploy/remote-update.sh +52 -0
- taskboy/deploy/taskboy-restart.path +10 -0
- taskboy/deploy/taskboy-restart.service +8 -0
- taskboy/deploy/taskboy.service +22 -0
- taskboy/hooks.py +208 -0
- taskboy/issue_runs.py +203 -0
- taskboy/llm.py +125 -0
- taskboy/main.py +269 -0
- taskboy/memory.py +59 -0
- taskboy/models.py +103 -0
- taskboy/mrkdwn.py +19 -0
- taskboy/notify.py +41 -0
- taskboy/orchestrator.py +401 -0
- taskboy/personality.py +17 -0
- taskboy/prompts.py +288 -0
- taskboy/quick.py +234 -0
- taskboy/redact.py +49 -0
- taskboy/repocache.py +117 -0
- taskboy/review_requests.py +388 -0
- taskboy/router.py +146 -0
- taskboy/runner.py +581 -0
- taskboy/scheduler.py +301 -0
- taskboy/secrets.py +83 -0
- taskboy/settings.py +19 -0
- taskboy/setup_checks.py +184 -0
- taskboy/setup_wizard.py +699 -0
- taskboy/skills.py +95 -0
- taskboy/slack.py +666 -0
- taskboy/slack_users.py +32 -0
- taskboy/started_messages.py +33 -0
- taskboy/store.py +1393 -0
- taskboy/task_actions.py +86 -0
- taskboy/templates/config.example.yaml +149 -0
- taskboy/templates/conventions.md +60 -0
- taskboy/templates/help.example.md +29 -0
- taskboy/templates/personality_agent.example.md +5 -0
- taskboy/templates/personality_reviewer.example.md +5 -0
- taskboy/templates/services/aws.yaml +7 -0
- taskboy/templates/services/confluence.yaml +4 -0
- taskboy/templates/services/github.yaml +14 -0
- taskboy/templates/services/jira.yaml +6 -0
- taskboy/templates/services/sentry.yaml +4 -0
- taskboy/templates/services/slack.yaml +9 -0
- taskboy/templates/skills/README.md +33 -0
- taskboy/templates/skills/discoverissues/SKILL.md +46 -0
- taskboy/templates/skills/implementapprovedissues/SKILL.md +41 -0
- taskboy/templates/skills/jira2pr/SKILL.md +66 -0
- taskboy/templates/skills/monitor/SKILL.md +32 -0
- taskboy/templates/skills/monitornew/SKILL.md +27 -0
- taskboy/templates/skills/refineissue/SKILL.md +35 -0
- taskboy/templates/skills/release/SKILL.md +36 -0
- taskboy/templates/skills/review/SKILL.md +89 -0
- taskboy/templates/skills/reviewandmonitor/SKILL.md +22 -0
- taskboy/templates/skills/reviews/SKILL.md +26 -0
- taskboy/templates/skills/slack2jira/SKILL.md +47 -0
- taskboy/templates/skills/slack2pr/SKILL.md +64 -0
- taskboy/templates/skills/spec2pr/SKILL.md +35 -0
- taskboy/templates/slack_app_manifest.yaml +48 -0
- taskboy/templates/task_started_messages.yaml +14 -0
- taskboy/ui_dist/assets/index-DmYxR9Qy.css +1 -0
- taskboy/ui_dist/assets/index-LDDO4iT2.js +15 -0
- taskboy/ui_dist/index.html +15 -0
- taskboy/workspace.py +86 -0
- taskboy-0.1.1.dist-info/METADATA +120 -0
- taskboy-0.1.1.dist-info/RECORD +95 -0
- taskboy-0.1.1.dist-info/WHEEL +5 -0
- taskboy-0.1.1.dist-info/entry_points.txt +2 -0
- taskboy-0.1.1.dist-info/top_level.txt +1 -0
taskboy/skills.py
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""repository-shipped skill discovery, parsing, and prompt rendering."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import yaml
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SkillError(ValueError):
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# in-process capability servers a skill may opt into via `internal_tools:` frontmatter (wired by the runner)
|
|
15
|
+
KNOWN_INTERNAL_TOOLS = {"issues", "enqueue"}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class Skill:
|
|
20
|
+
name: str
|
|
21
|
+
description: str
|
|
22
|
+
body: str
|
|
23
|
+
requires: list[str]
|
|
24
|
+
model: str | None = None # model alias this skill runs on (e.g. fable); None uses the config skills.tier default
|
|
25
|
+
profile: str | None = None # execution profile override; None uses the config skills.profile default
|
|
26
|
+
internal_tools: list[str] = None # type: ignore[assignment] # normalized to a list in load()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def parse_invocation(text: str) -> tuple[str, str] | None:
|
|
30
|
+
match = re.match(r"^/([a-z0-9_-]+)\s*(.*)$", text, flags=re.DOTALL)
|
|
31
|
+
if not match:
|
|
32
|
+
return None
|
|
33
|
+
return match.group(1), match.group(2)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def available(root: str | Path) -> list[str]:
|
|
37
|
+
path = Path(root)
|
|
38
|
+
if not path.is_dir():
|
|
39
|
+
return []
|
|
40
|
+
return sorted(child.name for child in path.iterdir() if child.is_dir() and (child / "SKILL.md").is_file())
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def load(root: str | Path, name: str) -> Skill:
|
|
44
|
+
path = Path(root) / name / "SKILL.md"
|
|
45
|
+
try:
|
|
46
|
+
text = path.read_text()
|
|
47
|
+
except FileNotFoundError as e:
|
|
48
|
+
raise SkillError(f"skill /{name} is not installed") from e
|
|
49
|
+
match = re.match(r"\A---\s*\n(.*?)\n---\s*\n?(.*)\Z", text, flags=re.DOTALL)
|
|
50
|
+
if not match:
|
|
51
|
+
raise SkillError(f"skill /{name} has invalid frontmatter")
|
|
52
|
+
try:
|
|
53
|
+
metadata = yaml.safe_load(match.group(1))
|
|
54
|
+
except yaml.YAMLError as e:
|
|
55
|
+
raise SkillError(f"skill /{name} has invalid frontmatter: {e}") from e
|
|
56
|
+
if not isinstance(metadata, dict):
|
|
57
|
+
raise SkillError(f"skill /{name} has invalid frontmatter")
|
|
58
|
+
skill_name = metadata.get("name")
|
|
59
|
+
description = metadata.get("description")
|
|
60
|
+
requires = metadata.get("requires") or []
|
|
61
|
+
model = metadata.get("model")
|
|
62
|
+
profile = metadata.get("profile")
|
|
63
|
+
internal_tools = metadata.get("internal_tools") or []
|
|
64
|
+
if not isinstance(skill_name, str) or not isinstance(description, str):
|
|
65
|
+
raise SkillError(f"skill /{name} frontmatter needs name and description")
|
|
66
|
+
if skill_name != name:
|
|
67
|
+
raise SkillError(f"skill /{name} frontmatter name does not match its directory")
|
|
68
|
+
if not isinstance(requires, list) or not all(isinstance(item, str) for item in requires):
|
|
69
|
+
raise SkillError(f"skill /{name} requires must be a list of skill names")
|
|
70
|
+
if model is not None and not isinstance(model, str):
|
|
71
|
+
raise SkillError(f"skill /{name} model must be a model alias string")
|
|
72
|
+
if profile is not None and not isinstance(profile, str):
|
|
73
|
+
raise SkillError(f"skill /{name} profile must be a profile name string")
|
|
74
|
+
if not isinstance(internal_tools, list) or not all(isinstance(item, str) for item in internal_tools):
|
|
75
|
+
raise SkillError(f"skill /{name} internal_tools must be a list of strings")
|
|
76
|
+
unknown = set(internal_tools) - KNOWN_INTERNAL_TOOLS
|
|
77
|
+
if unknown:
|
|
78
|
+
raise SkillError(f"skill /{name} internal_tools contains unknown entries: {sorted(unknown)}")
|
|
79
|
+
return Skill(name=skill_name, description=description, body=match.group(2).rstrip(), requires=requires, model=model or None, profile=profile or None, internal_tools=internal_tools)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def render(root: str | Path, name: str) -> str:
|
|
83
|
+
first = load(root, name)
|
|
84
|
+
rendered = [first.body]
|
|
85
|
+
seen = {name}
|
|
86
|
+
queue = list(first.requires)
|
|
87
|
+
while queue:
|
|
88
|
+
required_name = queue.pop(0)
|
|
89
|
+
if required_name in seen:
|
|
90
|
+
continue
|
|
91
|
+
required = load(root, required_name)
|
|
92
|
+
seen.add(required_name)
|
|
93
|
+
rendered.append(f"### Included skill: /{required_name}\n{required.body}")
|
|
94
|
+
queue.extend(required.requires)
|
|
95
|
+
return "\n\n".join(rendered)
|