@openhands/extensions 0.31.0 → 0.32.0

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.
@@ -97,6 +97,7 @@
97
97
  "source": "./skills/software-factory",
98
98
  "description": "Set up a GitHub software factory fast: ask only for the repositories and the ticket source (GitHub issues, Jira or Linear), create a working issue-to-PR and PR-review pipeline from catalog automations with defaults, then let the user stop or extend it step by step.",
99
99
  "category": "automations",
100
+ "defaultEnabled": true,
100
101
  "keywords": [
101
102
  "onboarding",
102
103
  "software-factory",
@@ -97,6 +97,7 @@
97
97
  "source": "./skills/software-factory",
98
98
  "description": "Set up a GitHub software factory fast: ask only for the repositories and the ticket source (GitHub issues, Jira or Linear), create a working issue-to-PR and PR-review pipeline from catalog automations with defaults, then let the user stop or extend it step by step.",
99
99
  "category": "automations",
100
+ "defaultEnabled": true,
100
101
  "keywords": [
101
102
  "onboarding",
102
103
  "software-factory",
@@ -97,6 +97,7 @@
97
97
  "source": "./skills/software-factory",
98
98
  "description": "Set up a GitHub software factory fast: ask only for the repositories and the ticket source (GitHub issues, Jira or Linear), create a working issue-to-PR and PR-review pipeline from catalog automations with defaults, then let the user stop or extend it step by step.",
99
99
  "category": "automations",
100
+ "defaultEnabled": true,
100
101
  "keywords": [
101
102
  "onboarding",
102
103
  "software-factory",
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "0.31.0"
2
+ ".": "0.32.0"
3
3
  }
@@ -42,7 +42,7 @@ two records of the same fact from drifting apart, and it is why the file is as s
42
42
  | The setup route | `/automations/new/<id>` |
43
43
  | The integrations the card lists | `requires.integrations`, which every entry carries |
44
44
  | The capabilities endpoint | The host queries it for every automation |
45
- | The trigger variants a deployment can offer | The keys of `setup.form.triggers`; an entry with multiple keys is usable when at least one variant is supported |
45
+ | The trigger variants a deployment can offer | The keys of `setup.form.triggers`; an entry with multiple keys is usable when at least one variant is supported. An entry without a setup form states its kind in `triggers` instead |
46
46
  | Schedule limits, timezone list, event choices, and model profiles | The `cron`, `timezone`, `event-source`, `event-type`, and `llm-profile` field types |
47
47
  | Local validation rules | The `required` flag and `constraints` on each field, applied to the selected trigger variant |
48
48
  | The preflight call | `POST /v1/validate` with the entry id, the create endpoint, and the rendered payload |
@@ -101,6 +101,12 @@ That way a skill can rename its trigger without leaving a stale copy behind in t
101
101
  `setup` is optional. Entries that carry one never repeat `id`, `name`, `category`, or `description`.
102
102
  `setup.version` selects how the block is interpreted; a future format ships a new constant.
103
103
 
104
+ `triggers` is stated only by an entry whose setup does not declare `form.triggers`, which otherwise already
105
+ names the variants: `cron`, `event`, or both, describing how the automation runs once it has been set up.
106
+ An entry lists both when its skill can set up either, for example an event trigger on a deployment that can
107
+ receive events and cron polling elsewhere. Agent Canvas renders it as the card's trigger badge and filter.
108
+ The schema rejects an entry that states it both ways, so the fact stays in one place.
109
+
104
110
  ### Triggers and args
105
111
 
106
112
  `form` separates the two things a user is configuring, and both are keyed by field name:
@@ -115,7 +121,8 @@ That way a skill can rename its trigger without leaving a stale copy behind in t
115
121
  the endpoint implied by the action key: `prompt` -> `/v1/preset/prompt`, `plugin` -> `/v1/preset/plugin`,
116
122
  and `upload` -> `/v1/uploads` followed by `/v1`.
117
123
 
118
- An assisted entry declares no triggers, because the trigger is settled during the conversation.
124
+ An assisted entry declares no `form.triggers`, because the trigger is settled during the conversation. It may
125
+ still list the kinds that conversation can set up in the top-level `triggers`.
119
126
 
120
127
  ### What the form produces
121
128
 
@@ -23,6 +23,10 @@
23
23
  },
24
24
  "popularityRank": 78,
25
25
  "estimatedSetupMinutes": 8,
26
+ "triggers": [
27
+ "cron",
28
+ "event"
29
+ ],
26
30
  "skill": "incident-retrospective",
27
31
  "exampleImplementation": "Trigger: manual, cron, or incident label added\nRequired MCPs: Slack, Linear, Notion\n\n1. Gather incident messages, timestamps, and linked issue references.\n2. Pull Linear follow-up tickets, owners, and status.\n3. TODO: define the incident identifier format and approved Notion template.\n4. Build a timeline, impact summary, root-cause hypotheses, and action items.\n5. Publish a draft retrospective and notify the incident owner.",
28
32
  "setup": {
@@ -18,6 +18,9 @@
18
18
  },
19
19
  "popularityRank": 83,
20
20
  "estimatedSetupMinutes": 6,
21
+ "triggers": [
22
+ "cron"
23
+ ],
21
24
  "skill": "jira-issue-to-pr",
22
25
  "exampleImplementation": "Trigger: cron polling (e.g. every 5 minutes)\nRequired integrations: Atlassian Rovo MCP for Jira access and a Bitbucket connection. On OpenHands Cloud and Enterprise the poller reads and comments on Jira through the connected Atlassian Rovo MCP server, so it needs no Jira API token; the conversation it starts pushes with the connected Bitbucket integration, or commits through the Bitbucket tools of the same Atlassian Rovo MCP server when it has no token. A local Agent Canvas needs a Jira API token and a Bitbucket token as secrets instead.\n\n1. Connect Atlassian Rovo MCP and Bitbucket, then collect the Jira site, label to watch, and cron schedule from the user, and set git_provider to bitbucket. A local Agent Canvas also needs the Jira base URL, email, and API token secret name. No Bitbucket repo is needed at deploy time - each ticket must include the target repo (workspace/repo on bitbucket.org) in its body.\n2. Search the Jira Cloud instance for open issues carrying the configured label.\n3. Deduplicate against a KV-store-backed set of already-processed issue keys so re-runs never create duplicate PRs.\n4. For each new issue, start an independent OpenHands agent conversation that extracts the Bitbucket repo from the ticket body, clones it, creates a branch named after the Jira key, implements or scaffolds the requested change, and opens a pull request.\n5. Immediately after the conversation is created, post a Jira comment on the issue: 'I'm on it: <conversation URL>'.\n6. Persist the processed issue key immediately after dispatching so the next poll skips it."
23
26
  }
@@ -18,6 +18,9 @@
18
18
  },
19
19
  "popularityRank": 84,
20
20
  "estimatedSetupMinutes": 6,
21
+ "triggers": [
22
+ "cron"
23
+ ],
21
24
  "skill": "jira-issue-to-pr",
22
25
  "exampleImplementation": "Trigger: cron polling (e.g. every 5 minutes)\nRequired integrations: Atlassian Rovo MCP for Jira access and a GitLab connection. On OpenHands Cloud and Enterprise the poller reads and comments on Jira through the connected Atlassian Rovo MCP server, so it needs no Jira API token; the conversation it starts pushes with the connected GitLab integration, or commits through the connected GitLab MCP server when it has no token. Calling GitLab MCP directly needs GitLab 19.4 or later. On an older GitLab, sign in with GitLab or connect it under Settings -> Integrations; add a GITLAB_TOKEN secret only if you can do neither. A local Agent Canvas needs a Jira API token and a GitLab token (api scope) as secrets instead.\n\n1. Connect Atlassian Rovo MCP and GitLab, then collect the Jira site, label to watch, and cron schedule from the user, and set git_provider to gitlab. A local Agent Canvas also needs the Jira base URL, email, and API token secret name. No GitLab project is needed at deploy time - each ticket must include the target project (group/project, subgroups included, or its URL on a self-managed host) in its body.\n2. Search the Jira Cloud instance for open issues carrying the configured label.\n3. Deduplicate against a KV-store-backed set of already-processed issue keys so re-runs never create duplicate merge requests.\n4. For each new issue, start an independent OpenHands agent conversation that extracts the GitLab project from the ticket body, clones it, creates a branch named after the Jira key, implements or scaffolds the requested change, and opens a merge request.\n5. Immediately after the conversation is created, post a Jira comment on the issue: 'I'm on it: <conversation URL>'.\n6. Persist the processed issue key immediately after dispatching so the next poll skips it."
23
26
  }
@@ -15,5 +15,8 @@
15
15
  },
16
16
  "popularityRank": 85,
17
17
  "estimatedSetupMinutes": 5,
18
+ "triggers": [
19
+ "cron"
20
+ ],
18
21
  "exampleImplementation": "Trigger: cron polling (e.g. every 5 minutes)\nRequired integrations: Atlassian Rovo MCP for Jira access and a GitHub connection. On OpenHands Cloud and Enterprise the poller reads and comments on Jira through the connected Atlassian Rovo MCP server, so it needs no Jira API token. A local Agent Canvas needs a Jira API token and a GitHub personal access token (repo + workflow scope) as secrets instead.\n\n1. Connect Atlassian Rovo MCP and GitHub, then collect the Jira site, label to watch, and cron schedule from the user. A local Agent Canvas also needs the Jira base URL, email, and API token secret name. No GitHub repo is needed at deploy time - each ticket must include the target repo (owner/repo) in its body.\n2. Search the Jira Cloud instance for open issues carrying the configured label.\n3. Deduplicate against a KV-store-backed set of already-processed issue keys so re-runs never create duplicate PRs.\n4. For each new issue, start an independent OpenHands agent conversation that extracts the GitHub repo from the ticket body, clones it, creates a branch named after the Jira key, implements or scaffolds the requested change, and opens a pull request.\n5. Immediately after the conversation is created, post a Jira comment on the issue: 'I'm on it: <conversation URL>'.\n6. Persist the processed issue key immediately after dispatching so the next poll skips it."
19
22
  }
@@ -18,6 +18,10 @@
18
18
  },
19
19
  "popularityRank": 87,
20
20
  "estimatedSetupMinutes": 6,
21
+ "triggers": [
22
+ "cron",
23
+ "event"
24
+ ],
21
25
  "skill": "ticket-to-code-change",
22
26
  "exampleImplementation": "Trigger: Linear issue marked implementation-ready\nRequired integrations: Linear, Bitbucket\n\n1. Watch the selected Linear team or project for issues carrying a configurable implementation-ready label or state.\n2. Deduplicate issue IDs so each transition dispatches once.\n3. Read the issue, linked dependencies, acceptance criteria, and target repository.\n4. Start an independent OpenHands conversation that clones the Bitbucket repository, creates an issue-keyed branch, implements and tests the change, and opens a pull request.\n5. Post the conversation and pull request links back to the Linear issue and update its status according to the team's workflow."
23
27
  }
@@ -18,6 +18,10 @@
18
18
  },
19
19
  "popularityRank": 89,
20
20
  "estimatedSetupMinutes": 6,
21
+ "triggers": [
22
+ "cron",
23
+ "event"
24
+ ],
21
25
  "skill": "ticket-to-code-change",
22
26
  "exampleImplementation": "Trigger: Linear issue marked implementation-ready\nRequired integrations: Linear, GitHub\n\n1. Watch the selected Linear team or project for issues carrying a configurable implementation-ready label or state.\n2. Deduplicate issue IDs so each transition dispatches once.\n3. Read the issue, linked dependencies, acceptance criteria, and target repository.\n4. Start an independent OpenHands conversation that clones the GitHub repository, creates an issue-keyed branch, implements and tests the change, and opens a pull request.\n5. Post the conversation and pull request links back to the Linear issue and update its status according to the team's workflow."
23
27
  }
@@ -18,6 +18,10 @@
18
18
  },
19
19
  "popularityRank": 88,
20
20
  "estimatedSetupMinutes": 6,
21
+ "triggers": [
22
+ "cron",
23
+ "event"
24
+ ],
21
25
  "skill": "ticket-to-code-change",
22
26
  "exampleImplementation": "Trigger: Linear issue marked implementation-ready\nRequired integrations: Linear, GitLab\n\n1. Watch the selected Linear team or project for issues carrying a configurable implementation-ready label or state.\n2. Deduplicate issue IDs so each transition dispatches once.\n3. Read the issue, linked dependencies, acceptance criteria, and target repository.\n4. Start an independent OpenHands conversation that clones the GitLab repository, creates an issue-keyed branch, implements and tests the change, and opens a merge request.\n5. Post the conversation and merge request links back to the Linear issue and update its status according to the team's workflow."
23
27
  }
@@ -12,6 +12,10 @@
12
12
  },
13
13
  "popularityRank": 90,
14
14
  "estimatedSetupMinutes": 3,
15
+ "triggers": [
16
+ "cron",
17
+ "event"
18
+ ],
15
19
  "skill": "linear-triage",
16
20
  "exampleImplementation": "Trigger: linear Issue create\nRequired MCP: Linear\n\n1. Load the new issue, team context, project, labels, and recent similar issues.\n2. Score priority and classify the request as bug, feature, support, or chore.\n3. TODO: map local team labels and priority conventions.\n4. Add a triage comment with rationale and suggested next actions.\n5. Optionally update labels, priority, and assignee."
17
21
  }
@@ -15,6 +15,9 @@
15
15
  },
16
16
  "popularityRank": 84,
17
17
  "estimatedSetupMinutes": 7,
18
+ "triggers": [
19
+ "cron"
20
+ ],
18
21
  "skill": "research-brief",
19
22
  "exampleImplementation": "Trigger: cron, weekly or daily\nRequired MCPs: Tavily, Notion\n\n1. Run focused Tavily searches for configured topics and competitors.\n2. Deduplicate sources and rank them by freshness and authority.\n3. TODO: confirm citation format and destination Notion database schema.\n4. Write an executive summary, implications, and recommended actions.\n5. Create or update a Notion page with citations and source links."
20
23
  }
@@ -12,5 +12,8 @@
12
12
  },
13
13
  "popularityRank": 92,
14
14
  "estimatedSetupMinutes": 7,
15
+ "triggers": [
16
+ "cron"
17
+ ],
15
18
  "exampleImplementation": "Trigger: cron, every minute\nRequired secret: SLACK_BOT_TOKEN or SLACK_USER_TOKEN\n\n1. Poll up to 10 configured Slack channels for new messages since the last run.\n2. Match messages containing the trigger phrase (default: @openhands).\n3. React with 👀 and start an OpenHands conversation with the message and recent channel context.\n4. Post a thread reply with a link to the conversation, then post the agent response when done.\n5. Keep the thread in a short follow-up watch window and forward only replies that repeat the trigger phrase to the same conversation, with per-thread backoff for quiet threads."
16
19
  }
@@ -12,5 +12,8 @@
12
12
  },
13
13
  "popularityRank": 94,
14
14
  "estimatedSetupMinutes": 5,
15
+ "triggers": [
16
+ "cron"
17
+ ],
15
18
  "exampleImplementation": "Trigger: cron, weekday mornings\nRequired MCP: Slack\n\n1. Search configured channels for messages since the last digest window.\n2. Cluster updates into shipped work, active work, blockers, and decisions.\n3. TODO: choose a safe allowlist for channels and whether DMs are excluded.\n4. Draft a Slack message with owners, links, and unanswered questions.\n5. Optionally post to the configured standup channel."
16
19
  }
@@ -64,8 +64,25 @@
64
64
  }
65
65
  }
66
66
  },
67
- "setup": { "$ref": "#/$defs/setup" }
67
+ "setup": { "$ref": "#/$defs/setup" },
68
+ "triggers": {
69
+ "description": "How the automation runs once created, for an entry without a setup form: `cron` for a schedule, `event` for an event-driven trigger, or both. An entry whose `setup.form.triggers` declares the variants states it there instead, so the two never coexist. Informational: Agent Canvas renders it as the card's trigger badge and filter.",
70
+ "type": "array",
71
+ "minItems": 1,
72
+ "uniqueItems": true,
73
+ "items": { "enum": ["cron", "event"] }
74
+ }
75
+ },
76
+ "if": {
77
+ "required": ["setup"],
78
+ "properties": {
79
+ "setup": {
80
+ "required": ["form"],
81
+ "properties": { "form": { "required": ["triggers"] } }
82
+ }
83
+ }
68
84
  },
85
+ "then": { "not": { "required": ["triggers"] } },
69
86
  "$defs": {
70
87
  "impactCopy": {
71
88
  "description": "One value-statement phrase. Markup-free, and the only placeholder it may open is {{count}}, which the host substitutes with the formatted run count.",
@@ -42,6 +42,14 @@ export interface RecommendedAutomation {
42
42
  impact?: AutomationImpact;
43
43
  /** Present when this automation ships an extension-owned setup experience. */
44
44
  setup?: AutomationSetup;
45
+ /**
46
+ * How the automation runs once created, for an entry without a setup form:
47
+ * `cron` for a schedule, `event` for an event-driven trigger, or both. An
48
+ * entry whose `setup.form.triggers` declares the variants states it there
49
+ * instead. Informational: the host renders it as the card's trigger badge
50
+ * and filter.
51
+ */
52
+ triggers?: AutomationTriggerKind[];
45
53
  }
46
54
 
47
55
  export interface AutomationImpact {
@@ -97,6 +97,7 @@
97
97
  "source": "./skills/software-factory",
98
98
  "description": "Set up a GitHub software factory fast: ask only for the repositories and the ticket source (GitHub issues, Jira or Linear), create a working issue-to-PR and PR-review pipeline from catalog automations with defaults, then let the user stop or extend it step by step.",
99
99
  "category": "automations",
100
+ "defaultEnabled": true,
100
101
  "keywords": [
101
102
  "onboarding",
102
103
  "software-factory",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openhands/extensions",
3
- "version": "0.31.0",
3
+ "version": "0.32.0",
4
4
  "description": "Public OpenHands extension catalogs for skills, plugins, integrations, and automation templates.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "openhands-extensions"
3
- version = "0.31.0"
3
+ version = "0.32.0"
4
4
  description = "OpenHands extensions, plugins, and skills (Python bindings for the integration catalog)"
5
5
  requires-python = ">=3.12"
6
6
  dependencies = [
@@ -18,7 +18,7 @@ from importlib.metadata import PackageNotFoundError, version
18
18
  #: Kept in lock-step with pyproject.toml/package.json by the version test.
19
19
  #: release-please only rewrites a semver found on the annotated line itself, so
20
20
  #: the annotation must stay on the assignment; on its own line it is a no-op.
21
- _FALLBACK_VERSION = "0.31.0" # x-release-please-version
21
+ _FALLBACK_VERSION = "0.32.0" # x-release-please-version
22
22
 
23
23
  try:
24
24
  __version__: str = version("openhands-extensions")
@@ -6,9 +6,11 @@ description: Bitbucket Data Center (self-hosted Bitbucket Server) specifics —
6
6
  Use authenticated Bitbucket Data Center MCP tools first when they are available in the agent's environment. Detect this by tool availability, without depending on a particular MCP server name. Use the token and direct REST API instructions below only when those tools are unavailable or when raw API or `curl` access is explicitly needed.
7
7
 
8
8
  You are working with **Bitbucket Data Center** (self-hosted Bitbucket Server). You have
9
- access to an environment variable, `BITBUCKET_DATA_CENTER_TOKEN`, which contains a basic
10
- auth token in the format `username:your-token` that allows you to interact with the git
11
- repository and the REST API.
9
+ access to an environment variable, `BITBUCKET_DATA_CENTER_TOKEN`, which holds your Bitbucket
10
+ Data Center credential in one of two forms:
11
+
12
+ - An access or OAuth token: the value has no colon, or starts with `x-token-auth:`.
13
+ - A `username:token` pair.
12
14
 
13
15
  > Environment variable names are case-sensitive. If `BITBUCKET_DATA_CENTER_TOKEN` is not
14
16
  > present, use whichever case variant actually exists (for example
@@ -17,19 +19,34 @@ repository and the REST API.
17
19
  - REST API base URL: `https://{domain}/rest/api/1.0`
18
20
  - Repository identifier format: `PROJECT/repo_slug` (project key, slash, repo slug)
19
21
 
20
- You can use this token to interact with the Bitbucket Data Center REST API:
21
- ```bash
22
- curl -u "${BITBUCKET_DATA_CENTER_TOKEN}" https://{domain}/rest/api/1.0/...
23
- ```
22
+ Use it with the Bitbucket Data Center REST API as follows:
23
+
24
+ - An access or OAuth token goes in a Bearer header, without any `x-token-auth:` prefix:
25
+ ```bash
26
+ curl -H "Authorization: Bearer ${BITBUCKET_DATA_CENTER_TOKEN#x-token-auth:}" https://{domain}/rest/api/1.0/...
27
+ ```
28
+ - A `username:token` pair uses HTTP Basic auth:
29
+ ```bash
30
+ curl -u "${BITBUCKET_DATA_CENTER_TOKEN}" https://{domain}/rest/api/1.0/...
31
+ ```
24
32
 
25
33
  <IMPORTANT>
26
34
  ALWAYS use the Bitbucket Data Center API for operations instead of a web browser.
27
35
  ALWAYS use the `create_bitbucket_data_center_pr` tool to open a pull request
28
36
  </IMPORTANT>
29
37
 
30
- If you encounter authentication issues when pushing to Bitbucket Data Center (such as password prompts or permission errors), the old token may have expired. In such case, update the remote URL to include the current token: `git remote set-url origin https://${BITBUCKET_DATA_CENTER_TOKEN}@{domain}/scm/{project_lower}/{repo}.git`
38
+ If you encounter authentication issues when pushing to Bitbucket Data Center (such as password prompts or permission errors), the token in the remote URL may have expired. Update the remote URL to include the current token:
39
+
40
+ - For a `username:token` pair: `git remote set-url origin https://${BITBUCKET_DATA_CENTER_TOKEN}@{domain}/scm/{project_lower}/{repo}.git`
41
+ - For an access or OAuth token, keep the username that is already in the remote URL and replace only the token. The git username these tokens need depends on the server, so don't choose one yourself. If the remote URL has no username, tell the user instead.
42
+ ```bash
43
+ GIT_USER=$(git remote get-url origin | sed -nE 's#^https://([^:@/]+):[^@]*@.*#\1#p') && \
44
+ [ -n "$GIT_USER" ] && \
45
+ NEW_TOKEN=$(python3 -c 'import sys, urllib.parse; print(urllib.parse.quote(sys.argv[1], safe=""))' "${BITBUCKET_DATA_CENTER_TOKEN#x-token-auth:}") && \
46
+ git remote set-url origin "https://${GIT_USER}:${NEW_TOKEN}@{domain}/scm/{project_lower}/{repo}.git"
47
+ ```
31
48
 
32
- The token is a `username:token` pair, so if the username or token contains characters that are reserved in URLs (such as `@`), split on the first `:` and URL-encode each part before embedding it in a remote:
49
+ If the username or token contains characters that are reserved in URLs (such as `@`), split the pair on the first `:` and URL-encode each part before embedding it in a remote:
33
50
 
34
51
  ```bash
35
52
  BB_USER="${BITBUCKET_DATA_CENTER_TOKEN%%:*}" && \
package/skills/index.js CHANGED
@@ -98,7 +98,7 @@ export const SKILLS_CATALOG = [
98
98
  "name": "bitbucket-data-center",
99
99
  "description": "Bitbucket Data Center (self-hosted Bitbucket Server) specifics — authenticate with BITBUCKET_DATA_CENTER_TOKEN, use the REST API 1.0, PROJECT/repo_slug repositories, scm/ git remotes, and the create_bitbucket_data_center_pr tool. Loaded on demand by the bitbucket skill once a Data Center environment is detected.",
100
100
  "triggers": [],
101
- "content": "Use authenticated Bitbucket Data Center MCP tools first when they are available in the agent's environment. Detect this by tool availability, without depending on a particular MCP server name. Use the token and direct REST API instructions below only when those tools are unavailable or when raw API or `curl` access is explicitly needed.\n\nYou are working with **Bitbucket Data Center** (self-hosted Bitbucket Server). You have\naccess to an environment variable, `BITBUCKET_DATA_CENTER_TOKEN`, which contains a basic\nauth token in the format `username:your-token` that allows you to interact with the git\nrepository and the REST API.\n\n> Environment variable names are case-sensitive. If `BITBUCKET_DATA_CENTER_TOKEN` is not\n> present, use whichever case variant actually exists (for example\n> `bitbucket_data_center_token`). Run `env | grep -i 'bitbucket_data_center'` to find it.\n\n- REST API base URL: `https://{domain}/rest/api/1.0`\n- Repository identifier format: `PROJECT/repo_slug` (project key, slash, repo slug)\n\nYou can use this token to interact with the Bitbucket Data Center REST API:\n```bash\ncurl -u \"${BITBUCKET_DATA_CENTER_TOKEN}\" https://{domain}/rest/api/1.0/...\n```\n\n<IMPORTANT>\nALWAYS use the Bitbucket Data Center API for operations instead of a web browser.\nALWAYS use the `create_bitbucket_data_center_pr` tool to open a pull request\n</IMPORTANT>\n\nIf you encounter authentication issues when pushing to Bitbucket Data Center (such as password prompts or permission errors), the old token may have expired. In such case, update the remote URL to include the current token: `git remote set-url origin https://${BITBUCKET_DATA_CENTER_TOKEN}@{domain}/scm/{project_lower}/{repo}.git`\n\nThe token is a `username:token` pair, so if the username or token contains characters that are reserved in URLs (such as `@`), split on the first `:` and URL-encode each part before embedding it in a remote:\n\n```bash\nBB_USER=\"${BITBUCKET_DATA_CENTER_TOKEN%%:*}\" && \\\nBB_PASS=\"${BITBUCKET_DATA_CENTER_TOKEN#*:}\" && \\\nENCODED_USER=$(python3 -c 'import sys, urllib.parse; print(urllib.parse.quote(sys.argv[1], safe=\"\"))' \"$BB_USER\") && \\\nENCODED_PASS=$(python3 -c 'import sys, urllib.parse; print(urllib.parse.quote(sys.argv[1], safe=\"\"))' \"$BB_PASS\") && \\\ngit remote set-url origin \"https://${ENCODED_USER}:${ENCODED_PASS}@{domain}/scm/{project_lower}/{repo}.git\"\n```\n\nHere are some instructions for pushing, but ONLY do this if the user asks you to:\n* NEVER push directly to the `main` or `master` branch\n* Git config (username and email) is pre-set. Do not modify.\n* You may already be on a branch starting with `openhands-workspace`. Create a new branch with a better name before pushing.\n* Use the `create_bitbucket_data_center_pr` tool to create a pull request, if you haven't already\n* Once you've created your own branch or a pull request, continue to update it. Do NOT create a new one unless you are explicitly asked to. Update the PR title and description as necessary, but don't change the branch name.\n* Use the main branch as the base branch, unless the user requests otherwise\n* After opening or updating a pull request, send the user a short message with a link to the pull request.\n* Do NOT mark a pull request as ready to review unless the user explicitly says so\n* Do all of the above in as few steps as possible. E.g. you could push changes with one step by running the following bash commands:\n```bash\ngit remote -v && git branch # to find the current org, repo and branch\ngit checkout -b create-widget && git add . && git commit -m \"Create widget\" && git push -u origin create-widget\n```",
101
+ "content": "Use authenticated Bitbucket Data Center MCP tools first when they are available in the agent's environment. Detect this by tool availability, without depending on a particular MCP server name. Use the token and direct REST API instructions below only when those tools are unavailable or when raw API or `curl` access is explicitly needed.\n\nYou are working with **Bitbucket Data Center** (self-hosted Bitbucket Server). You have\naccess to an environment variable, `BITBUCKET_DATA_CENTER_TOKEN`, which holds your Bitbucket\nData Center credential in one of two forms:\n\n- An access or OAuth token: the value has no colon, or starts with `x-token-auth:`.\n- A `username:token` pair.\n\n> Environment variable names are case-sensitive. If `BITBUCKET_DATA_CENTER_TOKEN` is not\n> present, use whichever case variant actually exists (for example\n> `bitbucket_data_center_token`). Run `env | grep -i 'bitbucket_data_center'` to find it.\n\n- REST API base URL: `https://{domain}/rest/api/1.0`\n- Repository identifier format: `PROJECT/repo_slug` (project key, slash, repo slug)\n\nUse it with the Bitbucket Data Center REST API as follows:\n\n- An access or OAuth token goes in a Bearer header, without any `x-token-auth:` prefix:\n ```bash\n curl -H \"Authorization: Bearer ${BITBUCKET_DATA_CENTER_TOKEN#x-token-auth:}\" https://{domain}/rest/api/1.0/...\n ```\n- A `username:token` pair uses HTTP Basic auth:\n ```bash\n curl -u \"${BITBUCKET_DATA_CENTER_TOKEN}\" https://{domain}/rest/api/1.0/...\n ```\n\n<IMPORTANT>\nALWAYS use the Bitbucket Data Center API for operations instead of a web browser.\nALWAYS use the `create_bitbucket_data_center_pr` tool to open a pull request\n</IMPORTANT>\n\nIf you encounter authentication issues when pushing to Bitbucket Data Center (such as password prompts or permission errors), the token in the remote URL may have expired. Update the remote URL to include the current token:\n\n- For a `username:token` pair: `git remote set-url origin https://${BITBUCKET_DATA_CENTER_TOKEN}@{domain}/scm/{project_lower}/{repo}.git`\n- For an access or OAuth token, keep the username that is already in the remote URL and replace only the token. The git username these tokens need depends on the server, so don't choose one yourself. If the remote URL has no username, tell the user instead.\n ```bash\n GIT_USER=$(git remote get-url origin | sed -nE 's#^https://([^:@/]+):[^@]*@.*#\\1#p') && \\\n [ -n \"$GIT_USER\" ] && \\\n NEW_TOKEN=$(python3 -c 'import sys, urllib.parse; print(urllib.parse.quote(sys.argv[1], safe=\"\"))' \"${BITBUCKET_DATA_CENTER_TOKEN#x-token-auth:}\") && \\\n git remote set-url origin \"https://${GIT_USER}:${NEW_TOKEN}@{domain}/scm/{project_lower}/{repo}.git\"\n ```\n\nIf the username or token contains characters that are reserved in URLs (such as `@`), split the pair on the first `:` and URL-encode each part before embedding it in a remote:\n\n```bash\nBB_USER=\"${BITBUCKET_DATA_CENTER_TOKEN%%:*}\" && \\\nBB_PASS=\"${BITBUCKET_DATA_CENTER_TOKEN#*:}\" && \\\nENCODED_USER=$(python3 -c 'import sys, urllib.parse; print(urllib.parse.quote(sys.argv[1], safe=\"\"))' \"$BB_USER\") && \\\nENCODED_PASS=$(python3 -c 'import sys, urllib.parse; print(urllib.parse.quote(sys.argv[1], safe=\"\"))' \"$BB_PASS\") && \\\ngit remote set-url origin \"https://${ENCODED_USER}:${ENCODED_PASS}@{domain}/scm/{project_lower}/{repo}.git\"\n```\n\nHere are some instructions for pushing, but ONLY do this if the user asks you to:\n* NEVER push directly to the `main` or `master` branch\n* Git config (username and email) is pre-set. Do not modify.\n* You may already be on a branch starting with `openhands-workspace`. Create a new branch with a better name before pushing.\n* Use the `create_bitbucket_data_center_pr` tool to create a pull request, if you haven't already\n* Once you've created your own branch or a pull request, continue to update it. Do NOT create a new one unless you are explicitly asked to. Update the PR title and description as necessary, but don't change the branch name.\n* Use the main branch as the base branch, unless the user requests otherwise\n* After opening or updating a pull request, send the user a short message with a link to the pull request.\n* Do NOT mark a pull request as ready to review unless the user explicitly says so\n* Do all of the above in as few steps as possible. E.g. you could push changes with one step by running the following bash commands:\n```bash\ngit remote -v && git branch # to find the current org, repo and branch\ngit checkout -b create-widget && git add . && git commit -m \"Create widget\" && git push -u origin create-widget\n```",
102
102
  "category": "code-hosting"
103
103
  },
104
104
  {
@@ -616,7 +616,8 @@ export const SKILLS_CATALOG = [
616
616
  "software factory"
617
617
  ],
618
618
  "content": "# Software Factory\n\nBuild a GitHub software factory from unmodified automation catalog bundles\n(`automations/catalog/<id>/manifest.json`); a Jira or Linear station is created\nby its own skill. This skill adds no automation logic.\n\nTwo phases: **Phase 1** gets a working factory after one question. **Phase 2**\nextends it in short rounds, only as far as the user wants. After Phase 1 and\nafter every round, the **Checkpoint** shows what exists and lets the user stop.\n\nCode lives on GitHub. Tickets can come from GitHub issues (default), Jira or\nLinear: only the issue to PR station changes (see \"Ticket source\"). GitLab and\nBitbucket code hosts are not part of this flow.\n\n## Stations\n\n| Station | Catalog id | Phase | Needs agent profile |\n|---|---|---|---|\n| Issue to PR | `github-issue-to-pr`, or the Jira or Linear station | 1 | yes (GitHub issues only) |\n| PR reviewer | `github-pr-reviewer` | 1 | yes |\n| AGENTS.md maintainer | `github-agents-md-maintainer` | 1 | no |\n| Issue triage | `github-issue-triage`, or `linear-triage-assistant` | add-on | yes (GitHub issues only) |\n| Auto-merge watchdog | `github-delivery-watchdog` | add-on | no |\n| Stale-CI PR closer | `github-stale-ci-pr-closer` | add-on | no |\n| QA changes | `qa-changes` | add-on | no |\n\n## Helper script\n\n`scripts/factory.py` in this skill's directory does every catalog and API step\nand prints one JSON document; it exits non-zero with `{\"error\": ...}` on\nfailure. Its usage is below: do not read its source, and do not load the\n`openhands-automation` skill for Phase 1.\n\nSet `OPENHANDS_HOST` to the **Ingress** URL in `<RUNTIME_SERVICES>` on a local\nAgent Canvas (it serves both `/api/automation` and `/api`; the automation\nbackend URL does not), or to `<HOST>` on OpenHands Cloud. Run every command as:\n\n```bash\nGITHUB_PERSONAL_ACCESS_TOKEN=\"$GITHUB_PERSONAL_ACCESS_TOKEN\" \\\nOPENHANDS_AUTOMATION_API_KEY=\"$OPENHANDS_AUTOMATION_API_KEY\" \\\nOPENHANDS_API_KEY=\"$OPENHANDS_API_KEY\" \\\npython3 <this skill's directory>/scripts/factory.py <command> ...\n```\n\n`$F` below stands for that whole line. Keep the secret names in it: the agent\nserver passes a saved secret into a command only when the command names it.\nThe script reads the token and never prints it.\n\n| Command | Does |\n|---|---|\n| `$F preflight --repos a/b,c/d` | Which stations this deployment supports, the agent profile, the GitHub login, push access per repository. |\n| `$F create --repos a/b --ids ID,ID [--set FIELD=VALUE] [--set ID.FIELD=VALUE]` | Fills each station's form (answers, then derived values, then manifest defaults), uploads the bundle and creates it disabled. `FIELD=VALUE` applies to every listed station with that field. Stations that already exist are reported under `existing`, not recreated. |\n| `$F tune --repos a/b --ids ID,ID [--set FIELD=VALUE] [--set ID.FIELD=VALUE]` | Applies new settings to an existing station by updating it in place. Use this to change a value that is fixed at creation. |\n| `$F verify ID... [--no-wait ID...]` | Dispatches once, enables the ones whose run `COMPLETED` and disables the rest, reporting their `error_detail`. `--no-wait` enables without waiting. |\n| `$F status ID...` | The latest run of each. |\n| `$F prs --repos a/b [--branch-prefix P]` | Open pull requests whose branch starts with `P` (default `openhands/agents-md`, the AGENTS.md maintainer's). |\n| `$F reset-token` | Deletes the saved `GITHUB_PERSONAL_ACCESS_TOKEN` (local Agent Canvas), so `request_secret` opens its form again. |\n\n`create` and `verify` return each automation's `url`, its page in Agent Canvas.\nTrust the script's output: do not open pages in the browser to check them, and\ndo not use the task tracker for this flow.\n\nAdd `--profile NAME` to `preflight` or `create` to use a profile other than the\nactive one.\n\n## Rules\n\n- **Supported stations**: create only those `preflight` reports `ok`; say why\n the others are skipped.\n- **Derived values** (the script fills them; never ask): every\n `githubTokenSecret` is `GITHUB_PERSONAL_ACCESS_TOKEN`. The watchdog's\n `branchPrefix` must equal issue to PR's: when it was changed, pass\n `--set github-delivery-watchdog.branchPrefix=<same value>`.\n- **Agent profile**: if `preflight` reports a profile error, skip the stations\n that need one and point the user to the station's catalog card in Agent\n Canvas (Automations), which has a profile picker.\n- **Token**: never ask for its value in chat, echo it, or put it in a tool\n argument or file.\n- **Failures**: if `create` fails, its output lists the stations it already\n created; report them with the error and ask whether to retry or delete them\n (`DELETE ${OPENHANDS_HOST}/api/automation/v1/{id}`).\n- **Existing stations**: a station `create` reports under `existing` was already\n set up by an earlier run and was left unchanged. Say so rather than presenting\n it as new, and change it with `$F tune` if the user wants different settings.\n\n## Phase 1 - Working factory\n\n1. Ask, in one message: \"Which GitHub repositories (`owner/repo`)? Where do\n tickets live: GitHub issues (default), Jira or Linear?\" Say that everything\n else uses defaults that can be changed afterwards. If the answer names only\n repositories, use GitHub issues; do not ask again.\n2. Run `$F preflight --repos <repos>`. Drop repositories without `push`, and\n say which.\n3. If `github_login` is null, act on `token_error`:\n - `not_set`, and the `request_secret` tool is available: call it once with\n `name: \"GITHUB_PERSONAL_ACCESS_TOKEN\"` and a `reason` asking for Contents,\n Issues, Pull requests and Workflows read and write (classic PAT: `repo`,\n `workflow`). End your turn, wait for the `[secret-request]` message,\n then rerun `preflight`.\n - `rejected`: GitHub refuses the saved token (invalid or expired), and\n `request_secret` cannot replace it: it answers `saved` at once for a name\n that exists. Say so in one sentence and ask \"Shall I remove it and open\n the form for a new one?\" On yes, run `$F reset-token`, then handle it as\n `not_set`. If `reset-token` fails, tell the user to replace it in\n Settings -> Secrets and say when done.\n - `not_set` without the tool: tell the user to add it in Settings ->\n Secrets and stop.\n4. `$F create --repos <repos> --ids github-pr-reviewer,github-agents-md-maintainer`,\n adding `github-issue-to-pr` for GitHub issues. For Jira or Linear, also set\n up the issue to PR station as \"Ticket source\" describes.\n5. `$F verify <reviewer id> [<issue to PR id>] --no-wait <maintainer id>`. The\n maintainer's run opens one `AGENTS.md` pull request per repository.\n6. Go to the Checkpoint.\n\n## Checkpoint\n\nWrite one short message, in this order:\n\n1. One plain sentence on what runs and how to use it: \"Your factory is running\n on `<repos>`. Add the `<trigger label>` label to a ticket to get a draft pull\n request; the reviewer reviews it and you merge it.\" (On Jira, add that the\n ticket body must name the `owner/repo`.)\n2. The `AGENTS.md` pull requests from `$F prs --repos <repos>`, asking the user\n to merge them: they give the build agent the repository's build, test and\n lint commands. One opened before this setup started (`created_at`) is\n \"still open from an earlier run\", not new; the maintainer works once a week\n per repository. If there are none yet, say they are on their way.\n3. A small table of the automations, each linked to its `url`:\n\n | Automation | Schedule | Status |\n |---|---|---|\n | [Issue to PR](<url>) | every 15 minutes | on |\n\n Status is `on`, or `off` with the reason in a few words. No automation ids.\n4. The settings in effect, as a short list (the Tune table's current values).\n Mention that the reviewer also reviews any open, non-draft, green pull\n request with no review yet (at most 2 new per scan).\n5. End with only a list of possible next steps, one line each, and nothing\n after it. Mark the first one not done yet as recommended:\n - **Try it**: I open a small test issue with the `<trigger label>` label in\n `<repo>`, only if you say so, and report the pull request.\n - **Tune**: change a setting from the list above.\n - **Add a station**: triage, auto-merge watchdog, stale-CI closer (QA only\n if `preflight` reports it `ok`).\n - **Stop here**: the factory keeps running as it is.\n\n## Phase 2 - Extend, one round at a time\n\nDo only what the user picked. Ask only the questions for it, in one message,\neach with its current value. Apply, then return to the Checkpoint.\n\n### Tune\n\n| Setting | Default | `--set` |\n|---|---|---|\n| Issue trigger label (GitHub issues) | `openhands` | `github-issue-to-pr.triggerLabel` |\n| Draft or ready pull requests | `draft` | `pullRequestMode` (issue to PR, AGENTS.md maintainer) |\n| Branch prefix (GitHub issues) | `openhands/issue` | `github-issue-to-pr.branchPrefix` (and the watchdog's, if present) |\n| Which pull requests get reviewed | label `openhands-review`, reviewer `all-hands-bot` | `github-pr-reviewer.triggerLabel`, `triggerReviewer` |\n| Review tone: concise, thorough, friendly | `concise` | `reviewTone` |\n| Human reviewers requested after agent approval (two or more logins) | none | `maintainers` |\n| Schedules and timezone | issue to PR `*/15 * * * *`, reviewer `*/5 * * * *`, AGENTS.md maintainer `0 9 * * 1`, add-ons per manifest; `UTC` | `ID.schedule`, `timezone` |\n| Agent profile | active profile | `--profile NAME` |\n\nApply:\n\n- Schedule or timezone only:\n `PATCH ${OPENHANDS_HOST}/api/automation/v1/{id}` with `{\"trigger\": {...}}`.\n- Jira or Linear station settings: change them as its skill describes.\n- Anything else is fixed at creation: `$F tune --repos <repos> --ids <id>` with\n the new `--set` values (repeat every earlier non-default value), then\n `$F verify <id>`. `tune` updates the station in place, so there is no\n replacement to delete and no template-id collision.\n\n### Add a station\n\nAsk only the question below, then `$F create` it and `$F verify` it.\n\n- **Auto-merge watchdog** (`github-delivery-watchdog`, GitHub issues only,\n since it merges only branches with the issue to PR prefix): confirm the user\n wants the agent to merge pull requests itself once review, tests and CI are\n green.\n- **Stale-CI closer** (`github-stale-ci-pr-closer`): confirm it may warn on,\n then close, pull requests whose CI keeps failing.\n- **Triage** (`github-issue-triage`): nothing. For Linear, follow the\n `linear-triage` skill instead; for Jira there is none.\n- **QA changes**: which base branch (default `main`). Offer QA only if\n `preflight` reports it `ok` and the `openhands-automation` webhook\n reachability check passes. It has no bundle, so the script cannot create it:\n create it as the `openhands-automation` skill describes for an\n event-triggered prompt preset, with its manifest `prompt` and `filter`, one\n per repository (it takes a single `repository`). It runs only on pull request\n events, so leave it enabled and skip `verify`.\n\n## Ticket source\n\n- **GitHub issues**: `github-issue-to-pr`, created with the others in Phase 1.\n- **Jira**: follow the `jira-issue-to-pr` skill with `git_provider` `github`\n and its defaults (label `create-pr`, `*/5 * * * *`). Ask only what it cannot\n look up (the Jira site if several, or the API-token details when Atlassian\n Rovo is not connected). Tell the user each ticket body must name its\n `owner/repo`.\n- **Linear**: follow the `ticket-to-code-change` skill with GitHub as the\n provider. Ask its questions in one message, proposing the repositories above\n and `*/15 * * * *` as defaults.\n\nOnce its skill has created the station, run `$F verify <id>` on it.\n\n## Troubleshooting\n\n| Symptom | Cause and fix |\n|---|---|\n| Create returns 422 | A value does not match the manifest `setup` block; fix the field at the path the error names. |\n| Issue to PR fails at push | The token lacks Contents or Workflows write, or does not cover that repository. |\n| Reviewer never reviews | The pull request has neither the trigger label nor a request for the trigger reviewer, and is not green. |\n| Watchdog never merges | Its branch prefix differs from issue to PR's, or the pull request lacks an independent approval or green CI. |",
619
- "category": "automations"
619
+ "category": "automations",
620
+ "defaultEnabled": true
620
621
  },
621
622
  {
622
623
  "name": "spark-version-upgrade",
@@ -724,5 +725,5 @@ export const SKILLS_CATALOG = [
724
725
  "category": "integrations"
725
726
  }
726
727
  ];
727
- export const DEFAULT_ENABLED_SKILL_NAMES = ["add-skill","agent-canvas-environment","agent-memory","agent-sdk-builder","canvas-extension-api","code-review","docker","github","openhands-api","openhands-automation","openhands-sdk","skill-creator"];
728
+ export const DEFAULT_ENABLED_SKILL_NAMES = ["add-skill","agent-canvas-environment","agent-memory","agent-sdk-builder","canvas-extension-api","code-review","docker","github","openhands-api","openhands-automation","openhands-sdk","skill-creator","software-factory"];
728
729
  export default SKILLS_CATALOG;