@covibes/zeroshot 5.3.0 → 5.4.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.
- package/README.md +94 -14
- package/cli/commands/providers.js +8 -9
- package/cli/index.js +3032 -2409
- package/cli/message-formatters-normal.js +28 -6
- package/cluster-templates/base-templates/debug-workflow.json +1 -1
- package/cluster-templates/base-templates/full-workflow.json +72 -188
- package/cluster-templates/base-templates/worker-validator.json +59 -3
- package/cluster-templates/conductor-bootstrap.json +4 -4
- package/lib/docker-config.js +8 -0
- package/lib/git-remote-utils.js +165 -0
- package/lib/id-detector.js +10 -7
- package/lib/provider-defaults.js +62 -0
- package/lib/provider-names.js +2 -1
- package/lib/settings/claude-auth.js +78 -0
- package/lib/settings.js +161 -63
- package/package.json +7 -2
- package/scripts/setup-merge-queue.sh +170 -0
- package/src/agent/agent-config.js +135 -82
- package/src/agent/agent-context-builder.js +297 -188
- package/src/agent/agent-hook-executor.js +310 -113
- package/src/agent/agent-lifecycle.js +385 -325
- package/src/agent/agent-stuck-detector.js +7 -7
- package/src/agent/agent-task-executor.js +824 -565
- package/src/agent/output-extraction.js +41 -24
- package/src/agent/output-reformatter.js +1 -1
- package/src/agent/schema-utils.js +108 -73
- package/src/agent-wrapper.js +10 -1
- package/src/agents/git-pusher-template.js +285 -0
- package/src/claude-task-runner.js +85 -34
- package/src/config-validator.js +922 -657
- package/src/input-helpers.js +65 -0
- package/src/isolation-manager.js +289 -199
- package/src/issue-providers/README.md +305 -0
- package/src/issue-providers/azure-devops-provider.js +273 -0
- package/src/issue-providers/base-provider.js +232 -0
- package/src/issue-providers/github-provider.js +179 -0
- package/src/issue-providers/gitlab-provider.js +241 -0
- package/src/issue-providers/index.js +196 -0
- package/src/issue-providers/jira-provider.js +239 -0
- package/src/ledger.js +22 -5
- package/src/lib/safe-exec.js +88 -0
- package/src/orchestrator.js +1107 -811
- package/src/preflight.js +313 -159
- package/src/process-metrics.js +98 -56
- package/src/providers/anthropic/cli-builder.js +53 -25
- package/src/providers/anthropic/index.js +72 -2
- package/src/providers/anthropic/output-parser.js +32 -14
- package/src/providers/base-provider.js +70 -0
- package/src/providers/capabilities.js +9 -0
- package/src/providers/google/output-parser.js +47 -38
- package/src/providers/index.js +19 -3
- package/src/providers/openai/cli-builder.js +14 -3
- package/src/providers/openai/index.js +1 -0
- package/src/providers/openai/output-parser.js +44 -30
- package/src/providers/opencode/cli-builder.js +42 -0
- package/src/providers/opencode/index.js +103 -0
- package/src/providers/opencode/models.js +55 -0
- package/src/providers/opencode/output-parser.js +122 -0
- package/src/schemas/sub-cluster.js +68 -39
- package/src/status-footer.js +94 -48
- package/src/sub-cluster-wrapper.js +76 -35
- package/src/template-resolver.js +12 -9
- package/src/tui/data-poller.js +123 -99
- package/src/tui/formatters.js +4 -3
- package/src/tui/keybindings.js +259 -318
- package/src/tui/renderer.js +11 -21
- package/task-lib/attachable-watcher.js +118 -81
- package/task-lib/claude-recovery.js +61 -24
- package/task-lib/commands/episodes.js +105 -0
- package/task-lib/commands/list.js +2 -2
- package/task-lib/commands/logs.js +231 -189
- package/task-lib/commands/schedules.js +111 -61
- package/task-lib/config.js +0 -2
- package/task-lib/runner.js +84 -27
- package/task-lib/scheduler.js +1 -1
- package/task-lib/store.js +467 -168
- package/task-lib/tui/formatters.js +94 -45
- package/task-lib/tui/renderer.js +13 -3
- package/task-lib/tui.js +73 -32
- package/task-lib/watcher.js +128 -90
- package/src/agents/git-pusher-agent.json +0 -20
- package/src/github.js +0 -139
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
# Issue Providers
|
|
2
|
+
|
|
3
|
+
Multi-platform issue support for Zeroshot. Fetch issues from GitHub, GitLab, Jira, and Azure DevOps.
|
|
4
|
+
|
|
5
|
+
## Supported Providers
|
|
6
|
+
|
|
7
|
+
| Provider | CLI Tool | URL Pattern | Issue Key Format |
|
|
8
|
+
| ---------------- | -------- | -------------------------------------------- | --------------------- |
|
|
9
|
+
| **GitHub** | `gh` | `github.com/org/repo/issues/123` | `123`, `org/repo#123` |
|
|
10
|
+
| **GitLab** | `glab` | `gitlab.com/org/repo/-/issues/123` | `123`, `org/repo#123` |
|
|
11
|
+
| **Jira** | `jira` | `*.atlassian.net/browse/KEY-123` | `KEY-123` |
|
|
12
|
+
| **Azure DevOps** | `az` | `dev.azure.com/org/proj/_workitems/edit/123` | `123` |
|
|
13
|
+
|
|
14
|
+
## Quick Start
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# GitHub
|
|
18
|
+
zeroshot run 123
|
|
19
|
+
zeroshot run https://github.com/org/repo/issues/123
|
|
20
|
+
zeroshot run org/repo#123
|
|
21
|
+
|
|
22
|
+
# GitLab
|
|
23
|
+
zeroshot run https://gitlab.com/org/repo/-/issues/123
|
|
24
|
+
zeroshot run 123 --gitlab
|
|
25
|
+
|
|
26
|
+
# Jira
|
|
27
|
+
zeroshot run PROJ-123
|
|
28
|
+
zeroshot run https://company.atlassian.net/browse/PROJ-123
|
|
29
|
+
|
|
30
|
+
# Azure DevOps
|
|
31
|
+
zeroshot run https://dev.azure.com/org/project/_workitems/edit/123
|
|
32
|
+
zeroshot run 123 --devops
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Automatic Git Remote Detection
|
|
36
|
+
|
|
37
|
+
When working in a git repository, zeroshot automatically detects the issue provider from your git remote URL:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# In a GitHub repository
|
|
41
|
+
git remote get-url origin # → https://github.com/org/repo.git
|
|
42
|
+
zeroshot run 123 # Automatically uses GitHub
|
|
43
|
+
|
|
44
|
+
# In a GitLab repository
|
|
45
|
+
git remote get-url origin # → https://gitlab.com/org/repo.git
|
|
46
|
+
zeroshot run 456 # Automatically uses GitLab
|
|
47
|
+
|
|
48
|
+
# In an Azure DevOps repository
|
|
49
|
+
git remote get-url origin # → https://dev.azure.com/org/project/_git/repo
|
|
50
|
+
zeroshot run 789 # Automatically uses Azure DevOps
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Force Flags
|
|
54
|
+
|
|
55
|
+
Override auto-detection with explicit provider flags:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
-G, --github # Force GitHub as issue source
|
|
59
|
+
-L, --gitlab # Force GitLab as issue source
|
|
60
|
+
-J, --jira # Force Jira as issue source
|
|
61
|
+
-D, --devops # Force Azure DevOps as issue source
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Example:**
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# Force GitLab even when in a GitHub repo
|
|
68
|
+
zeroshot run 123 --gitlab
|
|
69
|
+
|
|
70
|
+
# Force Jira for bare number
|
|
71
|
+
zeroshot run 456 --jira
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Detection Priority
|
|
75
|
+
|
|
76
|
+
For bare issue numbers (e.g., `123`), zeroshot uses this priority:
|
|
77
|
+
|
|
78
|
+
1. **Force flag** (`--github`, `--gitlab`, etc.) - Explicit CLI override
|
|
79
|
+
2. **Git remote URL** - Auto-detected from your repository
|
|
80
|
+
3. **Settings** (`defaultIssueSource`) - User preference
|
|
81
|
+
4. **Legacy fallback** - GitHub (when no git context and no settings)
|
|
82
|
+
|
|
83
|
+
For URLs and issue keys (like `PROJ-123`), the provider is detected from the format.
|
|
84
|
+
|
|
85
|
+
## Settings
|
|
86
|
+
|
|
87
|
+
Configure default provider and platform-specific settings:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# Set default provider for bare numbers
|
|
91
|
+
zeroshot settings set defaultIssueSource gitlab
|
|
92
|
+
|
|
93
|
+
# GitLab self-hosted instance
|
|
94
|
+
zeroshot settings set gitlabInstance gitlab.company.com
|
|
95
|
+
|
|
96
|
+
# Jira configuration
|
|
97
|
+
zeroshot settings set jiraInstance jira.company.com
|
|
98
|
+
zeroshot settings set jiraProject MYPROJECT # Default project for bare numbers
|
|
99
|
+
|
|
100
|
+
# Azure DevOps configuration
|
|
101
|
+
zeroshot settings set azureOrg mycompany
|
|
102
|
+
zeroshot settings set azureProject myproject # Default project for bare numbers
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Settings Reference
|
|
106
|
+
|
|
107
|
+
| Setting | Type | Default | Description |
|
|
108
|
+
| -------------------- | ------ | ------- | ------------------------------------------ |
|
|
109
|
+
| `defaultIssueSource` | string | github | Provider for bare numbers (123) |
|
|
110
|
+
| `gitlabInstance` | string | null | Self-hosted GitLab URL |
|
|
111
|
+
| `jiraInstance` | string | null | Self-hosted Jira URL |
|
|
112
|
+
| `jiraProject` | string | null | Default Jira project key for bare numbers |
|
|
113
|
+
| `azureOrg` | string | null | Azure DevOps organization name |
|
|
114
|
+
| `azureProject` | string | null | Azure DevOps project name for bare numbers |
|
|
115
|
+
|
|
116
|
+
## CLI Tool Setup
|
|
117
|
+
|
|
118
|
+
Each provider requires its corresponding CLI tool:
|
|
119
|
+
|
|
120
|
+
### GitHub
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
# Install
|
|
124
|
+
brew install gh # macOS
|
|
125
|
+
apt install gh # Linux
|
|
126
|
+
# Or: https://cli.github.com/
|
|
127
|
+
|
|
128
|
+
# Authenticate
|
|
129
|
+
gh auth login
|
|
130
|
+
gh auth status
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### GitLab
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
# Install
|
|
137
|
+
brew install glab # macOS
|
|
138
|
+
# Or: https://gitlab.com/gitlab-org/cli
|
|
139
|
+
|
|
140
|
+
# Authenticate
|
|
141
|
+
glab auth login
|
|
142
|
+
glab auth status
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Jira
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
# Install go-jira
|
|
149
|
+
brew install go-jira # macOS
|
|
150
|
+
# Or: https://github.com/go-jira/jira
|
|
151
|
+
|
|
152
|
+
# Configure
|
|
153
|
+
jira login
|
|
154
|
+
jira version
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Azure DevOps
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
# Install Azure CLI
|
|
161
|
+
brew install azure-cli # macOS
|
|
162
|
+
# Or: https://docs.microsoft.com/cli/azure/
|
|
163
|
+
|
|
164
|
+
# Configure
|
|
165
|
+
az login
|
|
166
|
+
az devops configure --defaults organization=https://dev.azure.com/yourorg
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Self-Hosted Instances
|
|
170
|
+
|
|
171
|
+
### GitLab Self-Hosted
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
# Configure self-hosted instance
|
|
175
|
+
zeroshot settings set gitlabInstance gitlab.company.com
|
|
176
|
+
|
|
177
|
+
# Now these work
|
|
178
|
+
zeroshot run https://gitlab.company.com/org/repo/-/issues/123
|
|
179
|
+
zeroshot run 123 --gitlab # Uses self-hosted instance
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Jira Self-Hosted (Server/Data Center)
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
# Configure self-hosted instance and default project
|
|
186
|
+
zeroshot settings set jiraInstance jira.company.com
|
|
187
|
+
zeroshot settings set jiraProject MYPROJ
|
|
188
|
+
|
|
189
|
+
# Now these work
|
|
190
|
+
zeroshot run https://jira.company.com/browse/MYPROJ-123
|
|
191
|
+
zeroshot run MYPROJ-123
|
|
192
|
+
zeroshot run 123 --jira # Becomes MYPROJ-123
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Examples
|
|
196
|
+
|
|
197
|
+
### Team Using GitLab
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
# Set once per machine
|
|
201
|
+
zeroshot settings set defaultIssueSource gitlab
|
|
202
|
+
|
|
203
|
+
# Then just use bare numbers
|
|
204
|
+
zeroshot run 456
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Team Using Jira
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
# Set once per machine
|
|
211
|
+
zeroshot settings set defaultIssueSource jira
|
|
212
|
+
zeroshot settings set jiraProject MYTEAM
|
|
213
|
+
|
|
214
|
+
# Then just use bare numbers (becomes MYTEAM-789)
|
|
215
|
+
zeroshot run 789
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Mixed Team
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
# Use explicit URLs or flags when working across platforms
|
|
222
|
+
zeroshot run https://github.com/org/repo/issues/100
|
|
223
|
+
zeroshot run https://gitlab.com/org/repo/-/issues/200
|
|
224
|
+
zeroshot run PROJ-300
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### CI/CD Integration
|
|
228
|
+
|
|
229
|
+
```yaml
|
|
230
|
+
# .github/workflows/zeroshot.yml
|
|
231
|
+
- name: Run Zeroshot on Issue
|
|
232
|
+
run: zeroshot run ${{ github.event.issue.html_url }} --ship
|
|
233
|
+
|
|
234
|
+
# .gitlab-ci.yml
|
|
235
|
+
- script: zeroshot run $CI_MERGE_REQUEST_IID --gitlab --ship
|
|
236
|
+
|
|
237
|
+
# Azure Pipelines
|
|
238
|
+
- script: zeroshot run $(System.WorkItemId) --devops --ship
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Troubleshooting
|
|
242
|
+
|
|
243
|
+
### Provider Not Detected
|
|
244
|
+
|
|
245
|
+
**Problem:** Bare number not detected as expected provider
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
$ zeroshot run 123
|
|
249
|
+
# Fetches from GitHub, but I wanted GitLab
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
**Solution:** Set `defaultIssueSource` or use force flag
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
zeroshot settings set defaultIssueSource gitlab
|
|
256
|
+
# OR
|
|
257
|
+
zeroshot run 123 --gitlab
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### CLI Tool Not Installed
|
|
261
|
+
|
|
262
|
+
**Problem:** Preflight check fails
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
PREFLIGHT CHECK FAILED
|
|
266
|
+
GitLab CLI (glab) not installed
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
**Solution:** Install the required CLI tool
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
brew install glab
|
|
273
|
+
glab auth login
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Self-Hosted Instance Not Recognized
|
|
277
|
+
|
|
278
|
+
**Problem:** Self-hosted URL not detected
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
$ zeroshot run https://gitlab.mycompany.com/org/repo/-/issues/123
|
|
282
|
+
Error: No issue provider matched input
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
**Solution:** Configure the instance setting
|
|
286
|
+
|
|
287
|
+
```bash
|
|
288
|
+
zeroshot settings set gitlabInstance gitlab.mycompany.com
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Jira Bare Numbers Don't Work
|
|
292
|
+
|
|
293
|
+
**Problem:** Bare numbers don't convert to Jira keys
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
$ zeroshot run 123 --jira
|
|
297
|
+
Error: Failed to fetch Jira issue
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
**Solution:** Configure `jiraProject` setting
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
zeroshot settings set jiraProject MYPROJECT
|
|
304
|
+
# Now: 123 becomes MYPROJECT-123
|
|
305
|
+
```
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Azure DevOps Provider - Fetch work items via az CLI
|
|
3
|
+
* Supports Azure DevOps cloud (dev.azure.com)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const IssueProvider = require('./base-provider');
|
|
7
|
+
const { execSync } = require('../lib/safe-exec');
|
|
8
|
+
const { detectGitContext } = require('../../lib/git-remote-utils');
|
|
9
|
+
|
|
10
|
+
class AzureDevOpsProvider extends IssueProvider {
|
|
11
|
+
static id = 'azure-devops';
|
|
12
|
+
static displayName = 'Azure DevOps';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Azure DevOps supports PR creation via az CLI
|
|
16
|
+
*/
|
|
17
|
+
static supportsPR() {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Get PR CLI tool info for preflight checks
|
|
23
|
+
*/
|
|
24
|
+
static getPRTool() {
|
|
25
|
+
return {
|
|
26
|
+
name: 'az',
|
|
27
|
+
checkCmd: 'az --version',
|
|
28
|
+
installHint: 'https://docs.microsoft.com/cli/azure/',
|
|
29
|
+
displayName: 'Azure DevOps',
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Detect Azure DevOps work item URLs
|
|
35
|
+
* Matches:
|
|
36
|
+
* - dev.azure.com URLs
|
|
37
|
+
* - *.visualstudio.com URLs
|
|
38
|
+
* - Bare numbers when git remote is Azure DevOps (auto-detected)
|
|
39
|
+
* - Bare numbers when defaultIssueSource=azure-devops (requires azureOrg setting)
|
|
40
|
+
*
|
|
41
|
+
* @param {string} input - Issue identifier (URL or number)
|
|
42
|
+
* @param {Object} settings - User settings
|
|
43
|
+
* @param {Object|null} gitContext - Auto-detected git remote context
|
|
44
|
+
* @returns {boolean} True if this provider should handle the input
|
|
45
|
+
*/
|
|
46
|
+
static detectIdentifier(input, settings, gitContext = null) {
|
|
47
|
+
// dev.azure.com URLs
|
|
48
|
+
if (/dev\.azure\.com\/.*\/_workitems\/edit\/\d+/.test(input)) {
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Legacy visualstudio.com URLs (e.g., https://org.visualstudio.com/project/_workitems/edit/123)
|
|
53
|
+
if (/https?:\/\/[^.]+\.visualstudio\.com\/[^/]+\/_workitems\/edit\/\d+/.test(input)) {
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Bare numbers - use shared priority cascade logic
|
|
58
|
+
// Azure DevOps requires azureOrg setting when using defaultIssueSource
|
|
59
|
+
return IssueProvider.detectBareNumber(input, settings, gitContext, 'azure-devops', {
|
|
60
|
+
requiredSettings: ['azureOrg'],
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
static getRequiredTool() {
|
|
65
|
+
return {
|
|
66
|
+
name: 'az',
|
|
67
|
+
checkCmd: 'az --version',
|
|
68
|
+
installHint:
|
|
69
|
+
'Install Azure CLI: https://docs.microsoft.com/en-us/cli/azure/install-azure-cli\nThen: az extension add --name azure-devops',
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Check az CLI authentication for Azure DevOps
|
|
75
|
+
*/
|
|
76
|
+
static checkAuth() {
|
|
77
|
+
try {
|
|
78
|
+
// First check Azure login
|
|
79
|
+
execSync('az account show', { encoding: 'utf8', stdio: 'pipe' });
|
|
80
|
+
} catch (err) {
|
|
81
|
+
const stderr = err.stderr || err.message || '';
|
|
82
|
+
|
|
83
|
+
if (stderr.includes('az login') || stderr.includes('not logged in')) {
|
|
84
|
+
return {
|
|
85
|
+
authenticated: false,
|
|
86
|
+
error: 'Azure CLI not authenticated',
|
|
87
|
+
recovery: [
|
|
88
|
+
'Run: az login',
|
|
89
|
+
'Then verify: az account show',
|
|
90
|
+
'Ensure azure-devops extension: az extension add --name azure-devops',
|
|
91
|
+
],
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
authenticated: false,
|
|
97
|
+
error: stderr.trim() || 'Unknown Azure CLI error',
|
|
98
|
+
recovery: ['Run: az login', 'Then verify: az account show'],
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Check azure-devops extension is installed
|
|
103
|
+
try {
|
|
104
|
+
const output = execSync('az extension list --query "[?name==\'azure-devops\']" -o json', {
|
|
105
|
+
encoding: 'utf8',
|
|
106
|
+
stdio: 'pipe',
|
|
107
|
+
});
|
|
108
|
+
const extensions = JSON.parse(output);
|
|
109
|
+
if (extensions.length === 0) {
|
|
110
|
+
return {
|
|
111
|
+
authenticated: false,
|
|
112
|
+
error: 'Azure DevOps extension not installed',
|
|
113
|
+
recovery: [
|
|
114
|
+
'Install the extension: az extension add --name azure-devops',
|
|
115
|
+
'Then verify: az extension list | grep azure-devops',
|
|
116
|
+
],
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
} catch {
|
|
120
|
+
return {
|
|
121
|
+
authenticated: false,
|
|
122
|
+
error: 'Could not verify Azure DevOps extension',
|
|
123
|
+
recovery: ['Install the extension: az extension add --name azure-devops'],
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return { authenticated: true, error: null, recovery: [] };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Azure DevOps-specific settings schema
|
|
132
|
+
*/
|
|
133
|
+
static getSettingsSchema() {
|
|
134
|
+
return {
|
|
135
|
+
azureOrg: {
|
|
136
|
+
type: 'string',
|
|
137
|
+
nullable: true,
|
|
138
|
+
default: null,
|
|
139
|
+
description: "Azure DevOps org URL (e.g., 'https://dev.azure.com/myorg')",
|
|
140
|
+
},
|
|
141
|
+
azureProject: {
|
|
142
|
+
type: 'string',
|
|
143
|
+
nullable: true,
|
|
144
|
+
default: null,
|
|
145
|
+
description: "Azure DevOps project name (e.g., 'MyProject')",
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
fetchIssue(identifier, settings) {
|
|
151
|
+
try {
|
|
152
|
+
const { workItemId, org, project: _project } = this._parseIdentifier(identifier, settings);
|
|
153
|
+
|
|
154
|
+
if (!org) {
|
|
155
|
+
throw new Error(
|
|
156
|
+
'Azure DevOps requires azureOrg setting. Set via: zeroshot settings set azureOrg <url>'
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Fetch work item using az CLI
|
|
161
|
+
// Note: Work item IDs are unique per organization, no --project flag needed
|
|
162
|
+
const cmd = `az boards work-item show --id ${workItemId} --org "${org}" -o json`;
|
|
163
|
+
const output = execSync(cmd, { encoding: 'utf8' });
|
|
164
|
+
const workItem = JSON.parse(output);
|
|
165
|
+
|
|
166
|
+
return this._parseWorkItem(workItem);
|
|
167
|
+
} catch (error) {
|
|
168
|
+
throw new Error(`Failed to fetch Azure DevOps work item: ${error.message}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Parse identifier to extract work item ID, org, and project
|
|
174
|
+
* Uses git context for auto-detection when settings not provided
|
|
175
|
+
* @private
|
|
176
|
+
*/
|
|
177
|
+
_parseIdentifier(identifier, settings) {
|
|
178
|
+
// URL format: https://dev.azure.com/org/project/_workitems/edit/123
|
|
179
|
+
// Use [^/]+ to match any characters except /, supporting spaces and special chars
|
|
180
|
+
const urlMatch = identifier.match(/dev\.azure\.com\/([^/]+)\/([^/]+)\/_workitems\/edit\/(\d+)/);
|
|
181
|
+
if (urlMatch) {
|
|
182
|
+
return {
|
|
183
|
+
org: `https://dev.azure.com/${urlMatch[1]}`,
|
|
184
|
+
project: decodeURIComponent(urlMatch[2]), // Decode URL-encoded project names
|
|
185
|
+
workItemId: urlMatch[3],
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Legacy URL format: https://org.visualstudio.com/project/_workitems/edit/123
|
|
190
|
+
const legacyMatch = identifier.match(
|
|
191
|
+
/https?:\/\/([^.]+)\.visualstudio\.com\/([^/]+)\/_workitems\/edit\/(\d+)/
|
|
192
|
+
);
|
|
193
|
+
if (legacyMatch) {
|
|
194
|
+
return {
|
|
195
|
+
org: `https://${legacyMatch[1]}.visualstudio.com`,
|
|
196
|
+
project: decodeURIComponent(legacyMatch[2]), // Decode URL-encoded project names
|
|
197
|
+
workItemId: legacyMatch[3],
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Bare number: try settings first, then git context
|
|
202
|
+
if (/^\d+$/.test(identifier)) {
|
|
203
|
+
let org = settings.azureOrg;
|
|
204
|
+
let project = settings.azureProject;
|
|
205
|
+
|
|
206
|
+
// If settings don't have org, try git context
|
|
207
|
+
if (!org) {
|
|
208
|
+
const gitContext = detectGitContext();
|
|
209
|
+
if (gitContext?.provider === 'azure-devops') {
|
|
210
|
+
org = gitContext.azureOrg;
|
|
211
|
+
project = gitContext.azureProject;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return {
|
|
216
|
+
org,
|
|
217
|
+
project,
|
|
218
|
+
workItemId: identifier,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
throw new Error(`Could not parse Azure DevOps work item identifier: ${identifier}`);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Parse Azure DevOps work item into standardized InputData format
|
|
227
|
+
* @private
|
|
228
|
+
*/
|
|
229
|
+
_parseWorkItem(workItem) {
|
|
230
|
+
const fields = workItem.fields || {};
|
|
231
|
+
const id = workItem.id;
|
|
232
|
+
const title = fields['System.Title'] || '';
|
|
233
|
+
const description = fields['System.Description'] || '';
|
|
234
|
+
const tags = fields['System.Tags'] ? fields['System.Tags'].split(';').map((t) => t.trim()) : [];
|
|
235
|
+
|
|
236
|
+
let context = `# Azure DevOps Work Item #${id}\n\n`;
|
|
237
|
+
context += `## Title\n${title}\n\n`;
|
|
238
|
+
|
|
239
|
+
if (description) {
|
|
240
|
+
// Strip HTML tags from description (Azure stores as HTML)
|
|
241
|
+
const plainDescription = description.replace(/<[^>]*>/g, '');
|
|
242
|
+
context += `## Description\n${plainDescription}\n\n`;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (tags.length > 0) {
|
|
246
|
+
context += `## Tags\n`;
|
|
247
|
+
context += tags.map((t) => `- ${t}`).join('\n');
|
|
248
|
+
context += '\n\n';
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Map tags to labels format
|
|
252
|
+
const labels = tags.map((name) => ({ name }));
|
|
253
|
+
|
|
254
|
+
// Azure work items don't have comments in the same API call
|
|
255
|
+
// Would need separate call to fetch comments, omitting for now
|
|
256
|
+
const comments = [];
|
|
257
|
+
|
|
258
|
+
// Strip HTML from description for body field
|
|
259
|
+
const plainBody = description.replace(/<[^>]*>/g, '');
|
|
260
|
+
|
|
261
|
+
return {
|
|
262
|
+
number: id,
|
|
263
|
+
title,
|
|
264
|
+
body: plainBody,
|
|
265
|
+
labels,
|
|
266
|
+
comments,
|
|
267
|
+
url: workItem.url || null,
|
|
268
|
+
context,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
module.exports = AzureDevOpsProvider;
|