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/task_actions.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""privileged task controls shared by the dashboard and command-line operator."""
|
|
2
|
+
|
|
3
|
+
import time
|
|
4
|
+
|
|
5
|
+
from taskboy.config import Config
|
|
6
|
+
from taskboy.models import BLOCKED, CANCELLED, FAILED, QUEUED, TERMINAL_STATES, Task, utcnow
|
|
7
|
+
from taskboy.orchestrator import accept_task, reopen_issue_and_cancel
|
|
8
|
+
from taskboy.store import Store, TransitionRaced
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def cancel_task(store: Store, task_id: str, actor: str) -> tuple[Task | None, str]:
|
|
12
|
+
task = store.get_task(task_id)
|
|
13
|
+
if task is None:
|
|
14
|
+
return None, "not found"
|
|
15
|
+
if task.state in TERMINAL_STATES:
|
|
16
|
+
store.add_event(task_id, "operator_action", {"actor": actor, "action": "cancel", "outcome": "noop", "state": task.state})
|
|
17
|
+
return task, f"already {task.state}"
|
|
18
|
+
try:
|
|
19
|
+
cancelled = store.transition(task_id, task.state, CANCELLED, f"cancelled via dashboard by {actor}", finished_at=utcnow())
|
|
20
|
+
except TransitionRaced:
|
|
21
|
+
current = store.get_task(task_id)
|
|
22
|
+
if current is not None:
|
|
23
|
+
store.add_event(task_id, "operator_action", {"actor": actor, "action": "cancel", "outcome": "raced", "state": current.state})
|
|
24
|
+
return current, f"already {current.state}" if current else "not found"
|
|
25
|
+
store.add_event(task_id, "operator_action", {"actor": actor, "action": "cancel", "outcome": "cancelled", "previous_state": task.state})
|
|
26
|
+
return cancelled, "cancelled"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
async def retry_task(store: Store, config: Config, notifier, source_task_id: str, actor: str) -> tuple[Task | None, str]:
|
|
30
|
+
source = store.get_task(source_task_id)
|
|
31
|
+
if source is None:
|
|
32
|
+
return None, "not found"
|
|
33
|
+
if source.state not in (FAILED, CANCELLED):
|
|
34
|
+
store.add_event(source_task_id, "operator_action", {"actor": actor, "action": "retry", "outcome": "rejected", "state": source.state})
|
|
35
|
+
return source, f"cannot retry {source.state}"
|
|
36
|
+
if (source.request_text or "").startswith("/spec2pr "):
|
|
37
|
+
# an issue-backed task's issue is already back to `proposed` with no spec by the time it's terminal (#76)
|
|
38
|
+
store.add_event(source_task_id, "operator_action", {"actor": actor, "action": "retry", "outcome": "rejected", "state": source.state, "reason": "issue_backed"})
|
|
39
|
+
return source, "cannot retry an issue-backed task — re-approve its issue instead"
|
|
40
|
+
retried, status = await accept_task(
|
|
41
|
+
store,
|
|
42
|
+
config,
|
|
43
|
+
notifier,
|
|
44
|
+
team_id=source.slack_team_id,
|
|
45
|
+
channel_id=source.slack_channel_id,
|
|
46
|
+
thread_ts=source.slack_thread_ts,
|
|
47
|
+
message_ts=str(time.time_ns()),
|
|
48
|
+
user_id=actor,
|
|
49
|
+
text=source.request_text,
|
|
50
|
+
parent_task_id=source.task_id,
|
|
51
|
+
model_override=source.model_override,
|
|
52
|
+
effort_override=source.effort_override,
|
|
53
|
+
thread_context=source.thread_context,
|
|
54
|
+
debug_thread_ts=source.debug_thread_ts,
|
|
55
|
+
debug_permalink=source.debug_permalink,
|
|
56
|
+
)
|
|
57
|
+
store.add_event(source_task_id, "operator_action", {"actor": actor, "action": "retry", "outcome": status, "new_task_id": retried.task_id if retried else None})
|
|
58
|
+
return retried, status
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
async def decide_permission(store: Store, notifier, task_id: str, kind: str, target: str, decision: str, actor: str) -> tuple[Task | None, str]:
|
|
62
|
+
"""grant or deny a sub-agent's permission request: granting a blocked task resumes it, denying reopens and cancels its issue-backed task instead of stranding it (#76)."""
|
|
63
|
+
if decision not in ("granted", "denied"):
|
|
64
|
+
return None, "decision must be granted or denied"
|
|
65
|
+
task = store.get_task(task_id)
|
|
66
|
+
if task is None:
|
|
67
|
+
return None, "not found"
|
|
68
|
+
row = store.decide_permission_request(task_id, kind, target, decision, actor)
|
|
69
|
+
if row is None:
|
|
70
|
+
store.add_event(task_id, "operator_action", {"actor": actor, "action": f"permission_{decision}", "outcome": "no pending request", "kind": kind, "target": target})
|
|
71
|
+
return task, "no pending request"
|
|
72
|
+
store.add_event(task_id, "permission_decision", {"actor": actor, "decision": decision, "kind": kind, "target": target})
|
|
73
|
+
# re-read: the task may have settled into BLOCKED since we first fetched it. if it is still RUNNING, the
|
|
74
|
+
# orchestrator will pick up this grant when the session ends (run-start grant snapshot diff), so we leave it be here.
|
|
75
|
+
task = store.get_task(task_id) or task
|
|
76
|
+
if task.state == BLOCKED:
|
|
77
|
+
if decision == "granted":
|
|
78
|
+
try:
|
|
79
|
+
task = store.transition(task_id, BLOCKED, QUEUED, f"resumed after permission granted by {actor}", resume_session_id=task.session_id)
|
|
80
|
+
except TransitionRaced:
|
|
81
|
+
pass
|
|
82
|
+
elif not store.has_pending_permission_request(task_id):
|
|
83
|
+
# no other request is still pending on this task; an issue-backed task must not strand its issue in_progress forever (#76)
|
|
84
|
+
if await reopen_issue_and_cancel(store, notifier, task):
|
|
85
|
+
task = store.get_task(task_id) or task
|
|
86
|
+
return task, decision
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# taskboy operator config. copy to config.yaml (local dev) or /etc/taskboy/config.yaml (host).
|
|
2
|
+
# config.yaml is gitignored in this repo template — commit it to your own private fork/repo if you want it version-controlled.
|
|
3
|
+
# apply changes with a service restart — restarts are safe, running tasks are requeued and resumed.
|
|
4
|
+
# tip: run `taskboy setup` for a guided walkthrough that fills this file in for you.
|
|
5
|
+
#
|
|
6
|
+
# connected services (slack, github, jira, confluence, sentry, aws) each live in their own file
|
|
7
|
+
# under services/<name>.yaml next to this file, with an explicit enabled flag — turn a service
|
|
8
|
+
# on or off there without touching this file.
|
|
9
|
+
|
|
10
|
+
orchestrator:
|
|
11
|
+
max_concurrency: 3 # max sub-agents running at once (ORC-005)
|
|
12
|
+
queue_max: 20 # tasks waiting beyond this are refused with a clear reply (ORC-006)
|
|
13
|
+
max_retries: 2 # restart/requeue attempts before a task is failed (REL-003)
|
|
14
|
+
progress_min_interval_seconds: 60 # per-task floor between slack progress posts (SLK-006)
|
|
15
|
+
runner: echo # echo (dev, no model calls) | claude (real sub-agent sessions; needs CLAUDE_CODE_OAUTH_TOKEN)
|
|
16
|
+
|
|
17
|
+
agent: # the main agent's identity
|
|
18
|
+
name: Agent # display name used in slack, prompts, PR/Jira text, and the dashboard
|
|
19
|
+
personality_file: "" # e.g. personality_agent.md, relative to this config file; empty = neutral voice; configured files must exist at startup
|
|
20
|
+
|
|
21
|
+
reviewer: # optional second GitHub-only persona that reviews the main agent's PRs (needs its own GitHub App)
|
|
22
|
+
enabled: false
|
|
23
|
+
name: Reviewer
|
|
24
|
+
personality_file: "" # e.g. personality_reviewer.md; configured files must exist at startup
|
|
25
|
+
review_agent_prs: true # auto-review PRs authored by the main agent
|
|
26
|
+
commit_name: "" # git author on the reviewer's commits; empty = defaults to reviewer name
|
|
27
|
+
# GitHub derives the commit avatar from this email; verify it on a GitHub account to give the reviewer a custom avatar (operator-side setup)
|
|
28
|
+
commit_email: "" # required when enabled
|
|
29
|
+
|
|
30
|
+
classifier:
|
|
31
|
+
tier: haiku # model alias used for the single classification call
|
|
32
|
+
|
|
33
|
+
cli_update: # off-peak upgrade of the claude-agent-sdk package (bundles the claude cli) + service restart, ungated by ci
|
|
34
|
+
enabled: false # the service self-restarts after a pip upgrade via the flag-file + taskboy-restart.path unit from the packaged install.sh
|
|
35
|
+
at_time: "02:00" # HH:MM, local to tzname
|
|
36
|
+
tzname: America/Los_Angeles
|
|
37
|
+
|
|
38
|
+
issues: # multi-repo issue discovery, refinement, discussion, and implementation
|
|
39
|
+
notify_channel: "" # optional slack channel id for lifecycle posts on manual issue runs
|
|
40
|
+
uploads_bucket: "" # empty = comment attachments disabled (local dev)
|
|
41
|
+
# recurring discovery/implementation runs are seeded as editable schedules on the dashboard Scheduler page
|
|
42
|
+
|
|
43
|
+
skills: # model + execution profile for /skill-name invocations; tier must be in the models catalog
|
|
44
|
+
tier: opus # change here to move all skill runs
|
|
45
|
+
profile: standard
|
|
46
|
+
|
|
47
|
+
roles: # use members: ["*"] on at most one role to allow anyone in an allowed channel
|
|
48
|
+
admin:
|
|
49
|
+
members: [YOUR_SLACK_USER_ID, cli]
|
|
50
|
+
allowed_profiles: [read_only, standard, deep]
|
|
51
|
+
model_override: true
|
|
52
|
+
max_budget_usd: null
|
|
53
|
+
developer:
|
|
54
|
+
members: []
|
|
55
|
+
allowed_profiles: [read_only, standard, deep]
|
|
56
|
+
model_override: false
|
|
57
|
+
max_budget_usd: 12.0
|
|
58
|
+
readonly:
|
|
59
|
+
members: []
|
|
60
|
+
allowed_profiles: [read_only]
|
|
61
|
+
model_override: false
|
|
62
|
+
max_budget_usd: 2.0
|
|
63
|
+
system:
|
|
64
|
+
members: [github]
|
|
65
|
+
allowed_profiles: [read_only, standard]
|
|
66
|
+
model_override: false
|
|
67
|
+
max_budget_usd: 12.0
|
|
68
|
+
|
|
69
|
+
models: # catalog aliases -> model ids; swap here, never in code (MOD-007)
|
|
70
|
+
# the cli resolves haiku/sonnet/opus to current models; pin full ids here if you need version stability
|
|
71
|
+
haiku: { id: haiku, fallbacks: [sonnet] }
|
|
72
|
+
sonnet: { id: sonnet, fallbacks: [opus] }
|
|
73
|
+
opus: { id: opus, fallbacks: [] }
|
|
74
|
+
fable: { id: claude-fable-5, fallbacks: [opus] }
|
|
75
|
+
|
|
76
|
+
routing: # first match wins; fields ANDed, values within a field ORed (MOD-002)
|
|
77
|
+
rules:
|
|
78
|
+
- { name: cheap-questions, match: { task_type: [question, investigation], complexity: [trivial] }, tier: haiku, profile: read_only }
|
|
79
|
+
- { name: critical-work, match: { complexity: [critical] }, tier: fable, profile: deep }
|
|
80
|
+
- { name: complex-work, match: { complexity: [complex] }, tier: opus, profile: deep }
|
|
81
|
+
- { name: standard-eng, match: { task_type: [bug_fix, feature, pr_review, incident_diagnosis, jira_ops] }, tier: sonnet, profile: standard }
|
|
82
|
+
default: { tier: sonnet, profile: read_only }
|
|
83
|
+
|
|
84
|
+
profiles: # budgets + tool allowlists per risk level (ORC-009, §11)
|
|
85
|
+
read_only:
|
|
86
|
+
# bash is needed for investigation (clone, grep, run tests); hooks police it and read_only sessions get read-only github tokens
|
|
87
|
+
allowed_tools: [Read, Grep, Glob, Bash, WebFetch, ToolSearch, mcp__harness__report_progress, mcp__harness__report_blocked, mcp__harness__request_permission, mcp__harness__ask_questions, mcp__github__get_pull_request, mcp__github__list_pull_requests, mcp__github__list_pr_files, mcp__github__list_pr_comments, mcp__jira__search_issues, mcp__jira__search_users, mcp__jira__get_issue, mcp__jira__list_boards, mcp__jira__list_sprints, mcp__sentry__list_issues, mcp__sentry__get_issue, mcp__sentry__get_latest_event, mcp__aws__aws_read, mcp__slack__channel_history, mcp__slack__thread_replies, mcp__slack__user_info, mcp__slack__get_file, mcp__confluence__search_pages, mcp__confluence__get_page]
|
|
88
|
+
max_budget_usd: 2.0
|
|
89
|
+
max_turns: 60
|
|
90
|
+
max_runtime_minutes: 30
|
|
91
|
+
effort: low
|
|
92
|
+
standard:
|
|
93
|
+
allowed_tools: [Read, Grep, Glob, Bash, WebFetch, ToolSearch, Write, Edit, mcp__harness__report_progress, mcp__harness__report_blocked, mcp__harness__request_permission, mcp__harness__ask_questions, mcp__github__get_pull_request, mcp__github__list_pull_requests, mcp__github__list_pr_files, mcp__github__list_pr_comments, mcp__github__create_pull_request, mcp__github__comment_on_pull_request, mcp__github__create_pr_review, mcp__github__reply_to_pr_comment, mcp__github__resolve_pr_thread, mcp__github__close_pull_request, mcp__github__delete_branch, mcp__github__create_release, mcp__jira__search_issues, mcp__jira__search_users, mcp__jira__get_issue, mcp__jira__list_boards, mcp__jira__list_sprints, mcp__sentry__list_issues, mcp__sentry__get_issue, mcp__sentry__get_latest_event, mcp__aws__aws_read, mcp__jira__create_issue, mcp__jira__add_comment, mcp__jira__assign_issue, mcp__jira__transition_issue, mcp__jira__move_to_sprint, mcp__jira__set_epic, mcp__jira__set_story_points, mcp__jira__link_pr, mcp__slack__channel_history, mcp__slack__thread_replies, mcp__slack__user_info, mcp__slack__get_file, mcp__slack__send_dm, mcp__confluence__search_pages, mcp__confluence__get_page]
|
|
94
|
+
max_budget_usd: 12.0
|
|
95
|
+
max_turns: 400
|
|
96
|
+
max_runtime_minutes: 240
|
|
97
|
+
deep:
|
|
98
|
+
allowed_tools: [Read, Grep, Glob, Bash, WebFetch, ToolSearch, Write, Edit, mcp__harness__report_progress, mcp__harness__report_blocked, mcp__harness__request_permission, mcp__harness__ask_questions, mcp__github__get_pull_request, mcp__github__list_pull_requests, mcp__github__list_pr_files, mcp__github__list_pr_comments, mcp__github__create_pull_request, mcp__github__comment_on_pull_request, mcp__github__create_pr_review, mcp__github__reply_to_pr_comment, mcp__github__resolve_pr_thread, mcp__github__close_pull_request, mcp__github__delete_branch, mcp__github__create_release, mcp__jira__search_issues, mcp__jira__search_users, mcp__jira__get_issue, mcp__jira__list_boards, mcp__jira__list_sprints, mcp__sentry__list_issues, mcp__sentry__get_issue, mcp__sentry__get_latest_event, mcp__aws__aws_read, mcp__jira__create_issue, mcp__jira__add_comment, mcp__jira__assign_issue, mcp__jira__transition_issue, mcp__jira__move_to_sprint, mcp__jira__set_epic, mcp__jira__set_story_points, mcp__jira__link_pr, mcp__slack__channel_history, mcp__slack__thread_replies, mcp__slack__user_info, mcp__slack__get_file, mcp__slack__send_dm, mcp__confluence__search_pages, mcp__confluence__get_page]
|
|
99
|
+
max_budget_usd: 20.0
|
|
100
|
+
max_turns: 400
|
|
101
|
+
max_runtime_minutes: 240
|
|
102
|
+
effort: high
|
|
103
|
+
thinking: { type: adaptive }
|
|
104
|
+
|
|
105
|
+
quick_answer:
|
|
106
|
+
enabled: true
|
|
107
|
+
tier: haiku
|
|
108
|
+
timeout_seconds: 20
|
|
109
|
+
max_per_user_per_hour: 30
|
|
110
|
+
|
|
111
|
+
usage_limits: # total-token ceilings for dashboard remaining-capacity meters; zero disables a meter
|
|
112
|
+
five_hour_tokens: 0
|
|
113
|
+
weekly_tokens: 0
|
|
114
|
+
fable_weekly_tokens: 0
|
|
115
|
+
|
|
116
|
+
conventions: # engineering conventions doc injected into repo-targeting task workspaces as CONVENTIONS.md
|
|
117
|
+
file: "" # e.g. conventions.md, relative to this config file's directory; empty = disabled
|
|
118
|
+
|
|
119
|
+
help: # curated `/help` reply text; re-read on every request, editable in the dashboard
|
|
120
|
+
file: "" # e.g. help.md, relative to this config file's directory; empty disables the /help fast path entirely
|
|
121
|
+
|
|
122
|
+
dashboard: # web ui; served behind an alb doing sso in deployed envs
|
|
123
|
+
enabled: false
|
|
124
|
+
bind: 127.0.0.1 # 0.0.0.0 on the host (the security group only admits the alb)
|
|
125
|
+
port: 8787
|
|
126
|
+
allowed_email_domain: "" # required when enabled; read-only access for any account under this domain, e.g. example.com
|
|
127
|
+
public_url: "" # e.g. https://agent.example.com; empty disables dashboard links in slack posts
|
|
128
|
+
admin_emails: [] # management surface: editing, cancel/retry
|
|
129
|
+
dev_user_email: dev@example.com # local-only stand-in identity when no alb header is present
|
|
130
|
+
expected_alb_arn: "" # required when enabled: true; must match the oidc header's "signer" claim
|
|
131
|
+
auto_commit: # empty repo disables committing dashboard edits back to git
|
|
132
|
+
repo: "" # org/repo that holds this config
|
|
133
|
+
branch: main
|
|
134
|
+
committer_name: "" # git committer identity on dashboard auto-commits; empty = "<agent name> Dashboard"
|
|
135
|
+
committer_email: "" # required when repo is set
|
|
136
|
+
|
|
137
|
+
audit: # hourly jsonl shipping of the hash-chained event trail (§10); empty bucket = disabled (local dev)
|
|
138
|
+
bucket: ""
|
|
139
|
+
|
|
140
|
+
retention: # days (MEM-012, §12)
|
|
141
|
+
workspace_completed_days: 3
|
|
142
|
+
workspace_failed_days: 7
|
|
143
|
+
memory_days: 90
|
|
144
|
+
audit_days: 365
|
|
145
|
+
slack_events_days: 7
|
|
146
|
+
errors_days: 30 # errors.traceback/context_json rows can be multi-KB; not hash-chained, safe to purge
|
|
147
|
+
# usage, admin_events, and task_events are kept forever
|
|
148
|
+
blocked_task_max_days: 10 # a task stuck BLOCKED (unanswered questions/permission request) this long auto-fails
|
|
149
|
+
blocked_task_reminder_days: 5 # thread reminder posted partway through the blocked_task_max_days window
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Engineering Conventions
|
|
2
|
+
|
|
3
|
+
<!--
|
|
4
|
+
This file is your organization's ground truth for how code should be written, tested,
|
|
5
|
+
and shipped. When a task targets one of your repositories, the harness copies this file
|
|
6
|
+
into the agent's workspace as CONVENTIONS.md and instructs the agent to read and follow
|
|
7
|
+
it before writing, changing, or reviewing any code. The /review skill judges pull
|
|
8
|
+
requests against it.
|
|
9
|
+
|
|
10
|
+
How to set it up:
|
|
11
|
+
1. Copy this file to config/conventions.md (or any path you like).
|
|
12
|
+
2. Point conventions.file at it in config.yaml (path is relative to config.yaml).
|
|
13
|
+
3. Fill in the sections below. Delete any section that doesn't apply.
|
|
14
|
+
|
|
15
|
+
Writing tips:
|
|
16
|
+
- Be concrete. "Use pytest, tests live in tests/, mirror the source layout" beats
|
|
17
|
+
"write good tests".
|
|
18
|
+
- State the *why* for rules that would otherwise be argued with.
|
|
19
|
+
- Keep it under ~250 lines — the agent reads this on every repo task.
|
|
20
|
+
-->
|
|
21
|
+
|
|
22
|
+
## Stack & shared libraries
|
|
23
|
+
|
|
24
|
+
<!-- The languages, frameworks, and internal libraries your services are built on,
|
|
25
|
+
with pinned versions where they matter. Example:
|
|
26
|
+
"Services are Python 3.12 + FastAPI. All services use our shared `acme-lib`
|
|
27
|
+
(pin the latest tag, currently 2.4.1) for auth, logging, and DB sessions." -->
|
|
28
|
+
|
|
29
|
+
## Code style & values
|
|
30
|
+
|
|
31
|
+
<!-- The taste rules that make code reviewable in your org. Example:
|
|
32
|
+
"Keep it simple: write logic out longhand rather than clever or DRY.
|
|
33
|
+
Comments are short, lowercase, and explain why — not what the line does." -->
|
|
34
|
+
|
|
35
|
+
## Project layout
|
|
36
|
+
|
|
37
|
+
<!-- What a well-formed repo looks like: the directory skeleton, what goes where,
|
|
38
|
+
and when to add (or not add) new folders. -->
|
|
39
|
+
|
|
40
|
+
## Testing
|
|
41
|
+
|
|
42
|
+
<!-- Which test tiers exist, which are required, how they run, and what a good test
|
|
43
|
+
looks like. Example: "Every service ships end-to-end tests under
|
|
44
|
+
tests/e2e/ that hit a live deployed instance; unit tests only for pure logic." -->
|
|
45
|
+
|
|
46
|
+
## Checks & CI
|
|
47
|
+
|
|
48
|
+
<!-- The commands that must pass before pushing (e.g. `make check`), and what CI
|
|
49
|
+
enforces on pull requests. -->
|
|
50
|
+
|
|
51
|
+
## Review bar
|
|
52
|
+
|
|
53
|
+
<!-- What a reviewer (human or agent) should block on vs. let slide.
|
|
54
|
+
Example: "Block on correctness, security, and missing tests. Don't block on
|
|
55
|
+
style that the formatter accepts." -->
|
|
56
|
+
|
|
57
|
+
## Branching, releases & deployment
|
|
58
|
+
|
|
59
|
+
<!-- Branch naming, protected branches, how releases are cut, and where things deploy.
|
|
60
|
+
Example: "PRs target develop; a v*.*.* tag on main deploys to production." -->
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# curated `/help` reply, answered instantly without creating a task.
|
|
2
|
+
# copy to help.md (the setup wizard fills in the placeholders and drops these comment lines),
|
|
3
|
+
# point help.file at it in config.yaml, and trim it to the skills you actually installed.
|
|
4
|
+
|
|
5
|
+
Here's how to work with {{agent_name}}:
|
|
6
|
+
|
|
7
|
+
• Mention `@{{agent_name}}` in an allowlisted channel, or in a DM, to start a task. A DM without a mention gets a quick chat reply instead — it won't start a task.
|
|
8
|
+
• Plain-English requests are classified and routed to the right skill automatically — no slash needed.
|
|
9
|
+
• A slash invocation runs one skill directly, skipping classification, e.g. `@{{agent_name}} /review {pr_url}`.
|
|
10
|
+
• Mentioning `@{{agent_name}}` again inside an existing task's thread continues that same task as a follow-up. A plain reply (no mention) is ignored unless the task is waiting on your answer to a question.
|
|
11
|
+
• If {{agent_name}} needs more information it will ask; if it can't proceed it will say why instead of guessing.
|
|
12
|
+
|
|
13
|
+
Dashboard: {{dashboard_url}}
|
|
14
|
+
|
|
15
|
+
Slash skills:
|
|
16
|
+
• `/discoverissues {owner/repo}` — investigate a repo and record ranked issues for review.
|
|
17
|
+
• `/refineissue {issue_id}` — reconcile an issue's discussion with the current repo and sharpen it for implementation.
|
|
18
|
+
• `/jira2pr {ticket_id} {context}` — implement a Jira ticket and open a PR.
|
|
19
|
+
• `/slack2jira {slack_thread_url} {context}` — turn a Slack thread into a Jira ticket.
|
|
20
|
+
• `/slack2pr {slack_thread_url} {context}` — turn a Slack thread directly into a PR, no ticket in between.
|
|
21
|
+
• `/review {pr_url}` — review one GitHub PR for simplicity and correctness.
|
|
22
|
+
• `/reviews {pr_url ...|repo}` — run /review across many PRs at once.
|
|
23
|
+
• `/monitor {pr_url}` — watch a PR for changes for up to 3 hours and re-review on each one.
|
|
24
|
+
• `/monitornew {repo}` — watch a repo for its next new PR and review it when it appears.
|
|
25
|
+
• `/reviewandmonitor {pr_url}` — review a PR now, then keep watching it.
|
|
26
|
+
• `/release` — cut a new release of the agent's own repo with notes sized to the changes since the last tag.
|
|
27
|
+
• `/implementapprovedissues` — turn the day's approved issue batch into specs and enqueue one PR task per issue.
|
|
28
|
+
|
|
29
|
+
That's not the full skill list — ask an admin, or check the `SKILL.md` files under `skills/`, for anything not covered here.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# the main agent's voice, used only for requester-facing replies (never internal reports).
|
|
2
|
+
# copy to personality_agent.md, point agent.personality_file at it in config.yaml, and make it yours.
|
|
3
|
+
# keep it short — a few sentences of tone guidance beats a page of rules.
|
|
4
|
+
|
|
5
|
+
Plainspoken and direct. Answer first, context second. No filler, no corporate cheer.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# the reviewer persona's voice, used in PR review comments.
|
|
2
|
+
# copy to personality_reviewer.md, point reviewer.personality_file at it in config.yaml, and make it yours.
|
|
3
|
+
|
|
4
|
+
Courteous and exact. Every finding names the file and line, states the failure it causes,
|
|
5
|
+
and suggests a concrete fix. Approve promptly when the code is sound — silence is not a review.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# aws service — read-only diagnostics; writes are denied by IAM and by the adapter (AWS-004/005).
|
|
2
|
+
enabled: false # true requires at least one entry in allowed_services
|
|
3
|
+
allowed_services: [] # e.g. [logs, cloudwatch, lambda, ecs, s3]
|
|
4
|
+
allowed_regions: [us-east-1]
|
|
5
|
+
# diagnostics_role_arns: # per-environment roles the agent assumes for reads; comment out for local-dev default creds
|
|
6
|
+
# staging: arn:aws:iam::YOUR_ACCOUNT_ID:role/your-staging-diagnostics-role
|
|
7
|
+
# production: arn:aws:iam::YOUR_ACCOUNT_ID:role/your-production-diagnostics-role
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
# confluence service — read-only page search/fetch (uses the jira service account credentials).
|
|
2
|
+
enabled: false # true requires site below plus JIRA_EMAIL / JIRA_API_TOKEN in the environment
|
|
3
|
+
site: "" # e.g. https://your-org.atlassian.net
|
|
4
|
+
spaces: [] # empty = any space the service account can read
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# github service — git push auth, pull request tools, review-request polling, and the issues pipeline.
|
|
2
|
+
enabled: false # true requires the GitHub App credentials (GITHUB_APP_ID / GITHUB_INSTALLATION_ID / GITHUB_APP_PRIVATE_KEY)
|
|
3
|
+
approved_repos: [] # org/repo
|
|
4
|
+
self_repo: "" # org/repo of this agent's own source; must also be in approved_repos; empty = no self-repo hint
|
|
5
|
+
protected_branch_patterns: [main, develop]
|
|
6
|
+
commit_name: "" # git author/committer on commits the agent pushes; empty = defaults to agent.name
|
|
7
|
+
# GitHub derives the commit avatar from this email; verify it on a GitHub account to give the agent a custom avatar (operator-side setup)
|
|
8
|
+
commit_email: ""
|
|
9
|
+
review_requests: # poll approved repos for PRs where the agent is a requested reviewer -> run /review
|
|
10
|
+
enabled: false
|
|
11
|
+
poll_interval_seconds: 60
|
|
12
|
+
notify_channel: "" # optional slack channel id for lifecycle updates on github-triggered reviews
|
|
13
|
+
auto_address_agent_prs: false # when true, an unresolved (non-APPROVE) reviewer review on an agent-authored PR auto-spawns the agent to address it
|
|
14
|
+
round_cap: 10 # max agent<->reviewer rounds per PR before the loop stops and escalates instead of continuing
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# jira service — issue reads and writes through the jira mcp tools.
|
|
2
|
+
enabled: false # true requires site below plus JIRA_EMAIL / JIRA_API_TOKEN in the environment
|
|
3
|
+
site: "" # e.g. https://your-org.atlassian.net
|
|
4
|
+
projects: [] # project keys the agent may touch, e.g. [ENG]
|
|
5
|
+
issue_types: [Story, Bug, Task]
|
|
6
|
+
story_points_field: "" # e.g. customfield_10016; empty = story points not set
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# slack service — socket-mode intake and notifications.
|
|
2
|
+
# each connected service lives in its own file under services/ so it can be turned on or off here
|
|
3
|
+
# without touching config.yaml. apply changes with a service restart.
|
|
4
|
+
enabled: false # true requires team_id below plus SLACK_BOT_TOKEN / SLACK_APP_TOKEN in the environment
|
|
5
|
+
team_id: "" # workspace id (T…)
|
|
6
|
+
debug_channel: "" # set to a private debug channel id; empty disables the debug feed
|
|
7
|
+
task_started_messages_file: task_started_messages.yaml # relative to config.yaml; missing/invalid uses legacy text
|
|
8
|
+
allowed_channels: [] # channel ids, e.g. [C0123ABCDEF]; empty = any channel the bot is invited to
|
|
9
|
+
ack_reaction: true # 👀 reaction instead of the "On it" message; false restores the text ack
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Skill templates
|
|
2
|
+
|
|
3
|
+
These directories are **templates**, not installed skills. The agent never loads them directly — they are instantiated into the top-level `skills/` directory, either by the `taskboy setup` wizard (the skills picker step fills in every variable from your answers) or by hand:
|
|
4
|
+
|
|
5
|
+
1. Copy the template directory into `skills/` (keep the `<name>/SKILL.md` layout).
|
|
6
|
+
2. Replace every `{{variable}}` placeholder in the copied `SKILL.md` with your value. No placeholder may remain — the loader does not substitute variables at runtime.
|
|
7
|
+
|
|
8
|
+
## Variables
|
|
9
|
+
|
|
10
|
+
| Variable | Meaning | Example |
|
|
11
|
+
| --- | --- | --- |
|
|
12
|
+
| `{{agent_name}}` | Main agent display name | `Scout` |
|
|
13
|
+
| `{{reviewer_name}}` | Reviewer persona display name | `Critic` |
|
|
14
|
+
| `{{github_org}}` | GitHub org bare repo names resolve against | `example-org` |
|
|
15
|
+
| `{{repo_list}}` | Comma-separated backtick list of repos the agent may work in | `` `svc-a`, `svc-b` `` |
|
|
16
|
+
| `{{self_repo}}` | The agent's own repository (`owner/repo`) | `example-org/taskboy` |
|
|
17
|
+
| `{{pr_target_branch}}` | Default PR target / integration branch | `main` |
|
|
18
|
+
| `{{jira_project}}` | Jira project key | `ENG` |
|
|
19
|
+
| `{{jira_site}}` | Jira site host | `example.atlassian.net` |
|
|
20
|
+
| `{{conventions_file}}` | Repo-relative path to the engineering conventions doc (`conventions.file` in config.yaml) | `config/conventions.md` |
|
|
21
|
+
|
|
22
|
+
Not every template uses every variable — replacing all of them is always safe.
|
|
23
|
+
|
|
24
|
+
## Frontmatter
|
|
25
|
+
|
|
26
|
+
Each `SKILL.md` starts with YAML frontmatter:
|
|
27
|
+
|
|
28
|
+
- `name` (required) — must equal the directory name exactly; the loader rejects a mismatch.
|
|
29
|
+
- `description` (required) — one line shown in skill listings and used for routing.
|
|
30
|
+
- `requires` (optional) — list of other skill names whose bodies get inlined when this skill runs; the required skills must be installed too.
|
|
31
|
+
- `model` (optional) — model alias the skill runs on; must be an alias defined in the model catalog in config.yaml (e.g. `fable`, `opus`, `sonnet`).
|
|
32
|
+
- `profile` (optional) — execution profile override.
|
|
33
|
+
- `internal_tools` (optional) — in-process capability servers the skill opts into (currently `issues`, `enqueue`).
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: discoverissues
|
|
3
|
+
description: Investigate one approved repository and record ranked issues for operator review. Invoked as `@{{agent_name}} /discoverissues {owner/repo}`.
|
|
4
|
+
model: fable
|
|
5
|
+
profile: standard
|
|
6
|
+
internal_tools: [issues]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# discoverissues
|
|
10
|
+
|
|
11
|
+
Discover concrete, code-grounded issues in one repository. Record proposals only; never open a PR here.
|
|
12
|
+
|
|
13
|
+
The first argument is the required `owner/repo`. If it is missing, call `report_blocked` asking for it and stop. Call `list_existing_issues` with that repo before investigating; if the tool rejects the repo as unapproved, call `report_blocked` with the allowed repositories from the error and stop.
|
|
14
|
+
|
|
15
|
+
## Step 1 — Gather evidence
|
|
16
|
+
|
|
17
|
+
Always inspect existing issues for this repo so you do not duplicate proposed, approved, denied, active, or completed work.
|
|
18
|
+
|
|
19
|
+
For `{{self_repo}}`, also use `list_task_feedback`, `list_failed_tasks`, and `list_recent_errors` (limit 200) to study real task failures and recurring internal errors. For every other repo, ground findings in its current code, open pull requests/issues context available through GitHub tools, and recent `git log` history.
|
|
20
|
+
|
|
21
|
+
Every tool response is capped around 4000 characters and truncates silently past that. `list_existing_issues` takes `offset` and `status` to page through the full table (add `keys_only: true` for compact id/dedupe_key/status rows when you just need to check for duplicates), and `list_recent_errors` takes `offset`, `component`, and `kind` to page to a specific recurring error and control the traceback tail length with `traceback_chars`. `list_failed_tasks` takes `offset`, `task_type`, and `query`. `list_task_feedback` takes `offset`. Make repeated calls with `offset` until a call returns fewer rows than `limit` to see the whole backlog.
|
|
22
|
+
|
|
23
|
+
## Step 2 — Read the repository
|
|
24
|
+
|
|
25
|
+
Clone the requested repo and read enough source, tests, conventions, and recent history to verify every finding. Prefer a small number of high-impact findings over speculation. Look for user-facing gaps, bugs, security weaknesses, reliability failures, performance problems, and code that is needlessly difficult to change.
|
|
26
|
+
|
|
27
|
+
## Step 3 — Record issues
|
|
28
|
+
|
|
29
|
+
Call `record_issue` once per distinct finding with:
|
|
30
|
+
|
|
31
|
+
- `repo`: the exact requested `owner/repo`.
|
|
32
|
+
- `summary`: one concise title.
|
|
33
|
+
- `issue_type`: use `feature_request`, `bug`, `security`, `user_experience`, `reliability`, `performance`, `token_efficiency`, or `organization` where applicable.
|
|
34
|
+
- `details`: markdown explaining what should change, where, why, evidence, acceptance checks, and risks.
|
|
35
|
+
- `dedupe_key`: a stable repo-qualified kebab-case slug. Reuse an existing proposed issue's exact key only when refreshing it.
|
|
36
|
+
- `priority`: 1–100 based on impact and frequency.
|
|
37
|
+
|
|
38
|
+
Do not re-propose denied or completed work. Recording the same key only refreshes a proposed row.
|
|
39
|
+
|
|
40
|
+
## Reply
|
|
41
|
+
|
|
42
|
+
In 1–3 sentences, say how many issues you recorded, name the top one or two, and say they await dashboard review.
|
|
43
|
+
|
|
44
|
+
## Final Report
|
|
45
|
+
|
|
46
|
+
List the repo, every recorded id/key/type/priority, the main evidence, anything you could not verify, and one short line per tool failure.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: implementapprovedissues
|
|
3
|
+
description: Turn the reserved batch of approved multi-repo issues into grounded specs and enqueue one PR task per issue. Invoked as `@{{agent_name}} /implementapprovedissues`.
|
|
4
|
+
model: fable
|
|
5
|
+
profile: standard
|
|
6
|
+
internal_tools: [issues, enqueue]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# implementapprovedissues
|
|
10
|
+
|
|
11
|
+
Turn the current reserved issue batch into implementation specs, then hand each spec to `/spec2pr` with `enqueue_spec_pr`. Do not open PRs yourself.
|
|
12
|
+
|
|
13
|
+
## Step 1 — Load the batch
|
|
14
|
+
|
|
15
|
+
Call `list_accepted_issues`. It returns up to five issues reserved for this coordinator, including each issue's target `repo`. If there are none, reply that nothing was approved and stop — do not call `report_blocked`; an empty queue is a normal daily outcome.
|
|
16
|
+
|
|
17
|
+
Clone each distinct target repo as needed. A batch may span repositories; never assume an issue targets `{{self_repo}}`.
|
|
18
|
+
|
|
19
|
+
## Step 2 — Write repo-specific specs
|
|
20
|
+
|
|
21
|
+
For every issue in priority order, verify its description against the current target repo. Skip work that is already complete or no longer applies and explain why in the final report.
|
|
22
|
+
|
|
23
|
+
Each markdown spec must explicitly name the target repo and include:
|
|
24
|
+
|
|
25
|
+
- Goal and rationale tied to the issue.
|
|
26
|
+
- Concrete files/areas and changes.
|
|
27
|
+
- Tests and validation commands.
|
|
28
|
+
- Existing repository conventions and compatibility constraints.
|
|
29
|
+
- The smallest acceptable scope and explicit non-goals.
|
|
30
|
+
|
|
31
|
+
## Step 3 — Enqueue PR work
|
|
32
|
+
|
|
33
|
+
Call `enqueue_spec_pr` once per verified issue with its id and full spec. The child `/spec2pr` task reads the issue's repo, implements the spec there, opens a PR, and records the result. Do not wait for child tasks.
|
|
34
|
+
|
|
35
|
+
## Reply
|
|
36
|
+
|
|
37
|
+
In 1–3 sentences, report how many specs were enqueued and which issue ids/repos they target.
|
|
38
|
+
|
|
39
|
+
## Final Report
|
|
40
|
+
|
|
41
|
+
List each issue and child handoff, skipped issues and reasons, and one short line per tool failure.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: jira2pr
|
|
3
|
+
description: Read a {{jira_project}} Jira ticket (and its linked Slack thread, if any) and open a PR that implements it. Invoked as `@{{agent_name}} /jira2pr {ticket_id} {context}` — ticket_id is the bare number (e.g. `514`); context may name the repo. Solutions must be the simplest thing that works (KISS + YAGNI).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# jira2pr
|
|
7
|
+
|
|
8
|
+
Turn a **{{jira_project}}** ticket into a pull request.
|
|
9
|
+
|
|
10
|
+
- The first argument is the ticket id, a **bare number** like `514`. Prepend `{{jira_project}}-` to get the key (`{{jira_project}}-514`). Tolerate the full key or its lowercase form too.
|
|
11
|
+
- The rest of the arguments are free-form context. Parse for intent — most importantly a **repo hint**, plus any implementation notes.
|
|
12
|
+
|
|
13
|
+
If the ticket id is missing, call `report_blocked` asking for it, then stop.
|
|
14
|
+
|
|
15
|
+
Don't pre-check auth or tool availability — just call the tool. If it fails, note it in the Final Report.
|
|
16
|
+
|
|
17
|
+
## Keep it simple
|
|
18
|
+
|
|
19
|
+
Simplest thing that works — KISS + YAGNI:
|
|
20
|
+
|
|
21
|
+
- **Do the least** — fewest lines, files, concepts. No new abstractions, files, folders, config, or options "for later" unless the ticket genuinely can't be done without them.
|
|
22
|
+
- **No fluff** — no comments restating the code, no docstrings on obvious methods, no defensive/speculative scaffolding.
|
|
23
|
+
- **Match neighboring code** and the repo's existing conventions (see the workspace `CONVENTIONS.md` when present): longhand over clever/DRY. Mirror idioms; don't invent.
|
|
24
|
+
- **Err hard toward under-building** — too-simple gets flagged on review, not the reverse.
|
|
25
|
+
|
|
26
|
+
## Step 1 — Read the ticket
|
|
27
|
+
|
|
28
|
+
Fetch `{{jira_project}}-{id}` with `mcp__jira__get_issue`. Pull: summary, description, status, assignee, and any linked URLs. Use `mcp__jira__search_issues` if you need the parent epic for context.
|
|
29
|
+
|
|
30
|
+
If the description contains a **Slack thread link**, read that thread with `mcp__slack__thread_replies` (resolve the channel id and thread timestamp from the URL — `p1234567890123456` becomes `1234567890.123456`). This only works for channels {{agent_name}} is allowed in; if it fails, note it and continue with the ticket alone.
|
|
31
|
+
|
|
32
|
+
Boil it down to: what needs to change, and the smallest change that satisfies it.
|
|
33
|
+
|
|
34
|
+
## Step 2 — Decide the repo
|
|
35
|
+
|
|
36
|
+
The work happens in one of the repos in the `{{github_org}}` org: {{repo_list}}.
|
|
37
|
+
|
|
38
|
+
- If the **context names a repo**, use it.
|
|
39
|
+
- Otherwise infer from the ticket (which service/domain the work clearly belongs to).
|
|
40
|
+
- **If you're not confident, call `report_blocked` asking which repo, then stop.** Don't guess on this.
|
|
41
|
+
|
|
42
|
+
The repo may already be cloned in your workspace; otherwise `git clone https://github.com/{{github_org}}/{repo}` (auth is handled).
|
|
43
|
+
|
|
44
|
+
## Step 3 — Implement (simplest possible)
|
|
45
|
+
|
|
46
|
+
1. Branch off **`{{pr_target_branch}}`** (the integration branch and the PR target). Name the branch per {{agent_name}}'s convention with the ticket key: `agent/{task_id}-{lowercase_ticket_key}-short-slug`.
|
|
47
|
+
2. Make the **smallest change that satisfies the ticket.** Re-read "keep it simple" before you write a line. Prefer editing existing files; prefer a few lines over many.
|
|
48
|
+
3. Follow the repo's existing patterns exactly. No new deps, no version bumps, no reformatting unrelated lines, no drive-by cleanups.
|
|
49
|
+
4. Add a test **only** if the ticket's logic warrants it, following the repo's testing conventions.
|
|
50
|
+
|
|
51
|
+
## Step 4 — Open the PR
|
|
52
|
+
|
|
53
|
+
Commit, push the branch, and open a PR **targeting `{{pr_target_branch}}`** with `mcp__github__create_pull_request`.
|
|
54
|
+
|
|
55
|
+
- **Title**: short — the ticket key + a terse description, e.g. `{{jira_project}}-514: fix pagination on the exports list`.
|
|
56
|
+
- **Body**: the required Summary, Testing performed, and Known limitations sections — each terse — plus the ticket link (`https://{{jira_site}}/browse/{{jira_project}}-{id}`).
|
|
57
|
+
|
|
58
|
+
Opening the PR automatically triggers a review by {{reviewer_name}}, the reviewer persona (when enabled) — no action is needed, and don't wait for it.
|
|
59
|
+
|
|
60
|
+
Then attach the PR to the ticket with `mcp__jira__link_pr`.
|
|
61
|
+
|
|
62
|
+
Open it directly when the repo and scope are clear. Call `report_blocked` only if the ticket is too underspecified to implement.
|
|
63
|
+
|
|
64
|
+
## Final Report
|
|
65
|
+
|
|
66
|
+
The PR URL on its own line. One short line per failure. Nothing else.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: monitor
|
|
3
|
+
description: Watch a GitHub PR for up to 3 hours, checking every 5 minutes for new pushes or new comments from the PR author; on any change, re-run the /review procedure on it. Invoked as `@{{agent_name}} /monitor {pr_url}`.
|
|
4
|
+
requires: [review]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# monitor
|
|
8
|
+
|
|
9
|
+
Watch a GitHub PR and re-review it every time it changes, for up to 3 hours (your session has a hard runtime limit — never plan past it).
|
|
10
|
+
|
|
11
|
+
The first GitHub PR URL in the arguments is the PR to watch. If it is missing, call `report_blocked` asking for it, then stop.
|
|
12
|
+
|
|
13
|
+
A "change" is either a **new push** (the PR head SHA moved) or a **new comment from the PR author** (issue or review comment authored by the PR author). Comments you ({{agent_name}}) leave don't count — only the author's.
|
|
14
|
+
|
|
15
|
+
An author comment counts **even when it's just "done"** with no new push. Never skip the re-review as redundant — a "done" with the code unchanged is exactly what the review's reconciliation step exists for: it confirms whether "done" is actually done, and re-flags if it isn't.
|
|
16
|
+
|
|
17
|
+
## The loop
|
|
18
|
+
|
|
19
|
+
Keep watch state in a file at `../notes/monitor.json` (your workspace's notes directory): `end_time` (epoch seconds), `head_sha`, `last_author_comment_ts`.
|
|
20
|
+
|
|
21
|
+
1. **Baseline** — fetch the PR with `mcp__github__get_pull_request` (note `head_sha`) and the latest author comment timestamp from `mcp__github__list_pr_comments` called with `author` set to the PR author's login. Compute `end_time` = now + 10800 (3 hours). Write the state file. Post one `report_progress`: `monitoring {repo}#{n} — checking every 5 min for up to 3h`. Do **not** review at baseline.
|
|
22
|
+
2. **Tick** — wait 5 minutes using Bash sleeps in chunks no longer than 100 seconds (`sleep 100` three times); a single long sleep will hit the command timeout. Then:
|
|
23
|
+
- If now ≥ `end_time`, or the PR is closed/merged: stop the loop and write the Final Report.
|
|
24
|
+
- Fetch the current head SHA, and call `mcp__github__list_pr_comments` with `author` set to the PR author's login to check cheaply for anything new from them — the newest author comment is always the last line. It counts as new only if its `created_at` is **strictly greater than** `last_author_comment_ts`, not merely if the result is non-empty.
|
|
25
|
+
- **Changed** (head moved, or a newer author comment, per the strictly-greater check above): run the included `/review` procedure end to end, then update the state file and post `report_progress`: `reviewed — left N comments`.
|
|
26
|
+
- **Not changed**: do nothing (no progress post) and continue.
|
|
27
|
+
|
|
28
|
+
Keep per-tick work minimal — two tool calls and a comparison.
|
|
29
|
+
|
|
30
|
+
## Final Report
|
|
31
|
+
|
|
32
|
+
How long you watched, how many ticks ran, and each re-review with its comment count. Nothing else.
|