@sascha384/tic 0.1.0 → 1.0.1

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 CHANGED
@@ -1,34 +1,46 @@
1
1
  # tic
2
2
 
3
- A terminal UI for issue tracking, built for developers who live in the terminal. Track work items as markdown files stored right in your repository.
3
+ A terminal UI for issue tracking, built for developers who live in the terminal. Track work items across multiple backends — local markdown files, GitHub Issues, GitLab Issues, and Azure DevOps Work Items.
4
4
 
5
5
  Built with TypeScript and [Ink](https://github.com/vadimdemedes/ink).
6
6
 
7
7
  ## Features
8
8
 
9
9
  - **Keyboard-driven TUI** — browse, create, edit, and manage work items without leaving the terminal
10
+ - **Multiple backends** — local markdown, GitHub (via `gh`), GitLab (via `glab`), Azure DevOps (via `az`)
11
+ - **Automatic backend detection** — selects backend based on git remote, or configure manually
10
12
  - **Local markdown storage** — work items stored as markdown files with YAML frontmatter in a `.tic/` directory
13
+ - **CLI commands** — scriptable commands for all operations (`tic item list`, `tic item create`, etc.)
11
14
  - **Work item types** — organize by epic, issue, and task (configurable)
12
15
  - **Iterations** — group work into sprints or milestones
13
16
  - **Parent-child relationships** — build hierarchies with tree-indented views
14
17
  - **Dependencies** — track which items block others, with warnings on premature completion
15
18
  - **Priority & assignee** — track who owns what and what's most important
16
19
  - **Comments** — add timestamped comments to any work item
20
+ - **MCP server** — expose work items to AI assistants via the Model Context Protocol
17
21
 
18
22
  ## Installation
19
23
 
20
24
  ```bash
21
- npm install -g tic
25
+ npm install -g @sascha384/tic
22
26
  ```
23
27
 
24
28
  ## Quick Start
25
29
 
26
30
  ```bash
27
31
  cd your-project
28
- tic
32
+ tic init # Initialize (auto-detects backend from git remote)
33
+ tic # Launch the TUI
29
34
  ```
30
35
 
31
- This launches the TUI. On first run, tic automatically creates a `.tic/` directory to store your work items. No setup or init command needed.
36
+ For local storage, `tic init` creates a `.tic/` directory to store your work items. For GitHub, GitLab, or Azure DevOps projects, it detects the backend from the git remote automatically. You can also specify a backend explicitly:
37
+
38
+ ```bash
39
+ tic init --backend github
40
+ tic init --backend gitlab
41
+ tic init --backend azure
42
+ tic init --backend local
43
+ ```
32
44
 
33
45
  ## Usage
34
46
 
@@ -205,12 +217,38 @@ Deletion is a two-step process: `delete_item` returns a preview of what will be
205
217
 
206
218
  If the server is started in a directory without a `.tic/` project, all tools except `init_project` will return an error asking you to initialize first.
207
219
 
208
- ## Roadmap
220
+ ## CLI Commands
221
+
222
+ tic also provides a full CLI for scripting and automation:
223
+
224
+ ```bash
225
+ tic item list # List work items
226
+ tic item list --status in-progress # Filter by status
227
+ tic item list --type task # Filter by type
228
+ tic item show 42 # Show item details
229
+ tic item create --title "Fix bug" # Create an item
230
+ tic item update 42 --status done # Update an item
231
+ tic item delete 42 # Delete an item
232
+ tic item comment 42 --body "Done" # Add a comment
233
+ tic item open 42 # Open in editor/browser
234
+ tic iteration list # List iterations
235
+ tic iteration set sprint-2 # Set current iteration
236
+ tic config get backend # Get config value
237
+ tic config set backend github # Set config value
238
+ ```
239
+
240
+ Add `--json` to any command for machine-readable output, or `--quiet` to suppress non-essential output.
241
+
242
+ ## Backends
209
243
 
210
- Planned but not yet implemented:
244
+ | Backend | CLI Tool | Detection |
245
+ |---------|----------|-----------|
246
+ | Local markdown | — | Default fallback |
247
+ | GitHub Issues | [`gh`](https://cli.github.com/) | `github.com` in git remote |
248
+ | GitLab Issues | [`glab`](https://gitlab.com/gitlab-org/cli) | `gitlab.com` in git remote |
249
+ | Azure DevOps Work Items | [`az`](https://learn.microsoft.com/en-us/cli/azure/) | `dev.azure.com` or `visualstudio.com` in git remote |
211
250
 
212
- - **Multi-backend support** GitHub (via `gh`), GitLab (via `glab`), Azure DevOps (via `az`)
213
- - **Automatic backend detection** — select backend based on git remote
251
+ Each backend supports a different set of capabilities (types, statuses, iterations, relationships, etc.). The TUI and CLI automatically adapt to show only what the active backend supports.
214
252
 
215
253
  ## Contributing
216
254
 
@@ -0,0 +1,11 @@
1
+ export declare function az<T>(args: string[], cwd: string): T;
2
+ export declare function azExec(args: string[], cwd: string): string;
3
+ export interface AzInvokeOptions {
4
+ area: string;
5
+ resource: string;
6
+ httpMethod?: string;
7
+ routeParameters?: string;
8
+ body?: unknown;
9
+ apiVersion?: string;
10
+ }
11
+ export declare function azInvoke<T>(options: AzInvokeOptions, cwd: string): T;
@@ -0,0 +1,62 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import fs from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ const AZ_TIMEOUT_MS = 15_000;
6
+ export function az(args, cwd) {
7
+ const result = execFileSync('az', [...args, '-o', 'json'], {
8
+ cwd,
9
+ encoding: 'utf-8',
10
+ stdio: ['pipe', 'pipe', 'pipe'],
11
+ timeout: AZ_TIMEOUT_MS,
12
+ });
13
+ return JSON.parse(result);
14
+ }
15
+ export function azExec(args, cwd) {
16
+ return execFileSync('az', args, {
17
+ cwd,
18
+ encoding: 'utf-8',
19
+ stdio: ['pipe', 'pipe', 'pipe'],
20
+ timeout: AZ_TIMEOUT_MS,
21
+ });
22
+ }
23
+ export function azInvoke(options, cwd) {
24
+ const args = [
25
+ 'devops',
26
+ 'invoke',
27
+ '--area',
28
+ options.area,
29
+ '--resource',
30
+ options.resource,
31
+ ];
32
+ if (options.httpMethod) {
33
+ args.push('--http-method', options.httpMethod);
34
+ }
35
+ if (options.routeParameters) {
36
+ args.push('--route-parameters', options.routeParameters);
37
+ }
38
+ if (options.apiVersion) {
39
+ args.push('--api-version', options.apiVersion);
40
+ }
41
+ let tmpFile;
42
+ if (options.body) {
43
+ tmpFile = path.join(os.tmpdir(), `tic-az-${Date.now()}.json`);
44
+ fs.writeFileSync(tmpFile, JSON.stringify(options.body));
45
+ args.push('--in-file', tmpFile);
46
+ }
47
+ try {
48
+ const result = execFileSync('az', [...args, '-o', 'json'], {
49
+ cwd,
50
+ encoding: 'utf-8',
51
+ stdio: ['pipe', 'pipe', 'pipe'],
52
+ timeout: AZ_TIMEOUT_MS,
53
+ });
54
+ return JSON.parse(result);
55
+ }
56
+ finally {
57
+ if (tmpFile) {
58
+ fs.unlinkSync(tmpFile);
59
+ }
60
+ }
61
+ }
62
+ //# sourceMappingURL=az.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"az.js","sourceRoot":"","sources":["../../../src/backends/ado/az.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,aAAa,GAAG,MAAM,CAAC;AAE7B,MAAM,UAAU,EAAE,CAAI,IAAc,EAAE,GAAW;IAC/C,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE;QACzD,GAAG;QACH,QAAQ,EAAE,OAAO;QACjB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;QAC/B,OAAO,EAAE,aAAa;KACvB,CAAC,CAAC;IACH,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAM,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,IAAc,EAAE,GAAW;IAChD,OAAO,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE;QAC9B,GAAG;QACH,QAAQ,EAAE,OAAO;QACjB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;QAC/B,OAAO,EAAE,aAAa;KACvB,CAAC,CAAC;AACL,CAAC;AAWD,MAAM,UAAU,QAAQ,CAAI,OAAwB,EAAE,GAAW;IAC/D,MAAM,IAAI,GAAG;QACX,QAAQ;QACR,QAAQ;QACR,QAAQ;QACR,OAAO,CAAC,IAAI;QACZ,YAAY;QACZ,OAAO,CAAC,QAAQ;KACjB,CAAC;IAEF,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;QACvB,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IACjD,CAAC;IACD,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;QAC5B,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;IAC3D,CAAC;IACD,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;QACvB,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IACjD,CAAC;IAED,IAAI,OAA2B,CAAC;IAChC,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,UAAU,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAC9D,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QACxD,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAClC,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE;YACzD,GAAG;YACH,QAAQ,EAAE,OAAO;YACjB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;YAC/B,OAAO,EAAE,aAAa;SACvB,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAM,CAAC;IACjC,CAAC;YAAS,CAAC;QACT,IAAI,OAAO,EAAE,CAAC;YACZ,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,28 @@
1
+ import { BaseBackend } from '../types.js';
2
+ import type { BackendCapabilities } from '../types.js';
3
+ import type { WorkItem, NewWorkItem, NewComment, Comment } from '../../types.js';
4
+ export declare class AzureDevOpsBackend extends BaseBackend {
5
+ private cwd;
6
+ private org;
7
+ private project;
8
+ private types;
9
+ constructor(cwd: string);
10
+ getCapabilities(): BackendCapabilities;
11
+ getStatuses(): string[];
12
+ getWorkItemTypes(): string[];
13
+ getIterations(): string[];
14
+ getCurrentIteration(): string;
15
+ setCurrentIteration(_name: string): void;
16
+ private escapeWiql;
17
+ private batchFetchWorkItems;
18
+ listWorkItems(iteration?: string): WorkItem[];
19
+ getWorkItem(id: string): WorkItem;
20
+ createWorkItem(data: NewWorkItem): WorkItem;
21
+ updateWorkItem(id: string, data: Partial<WorkItem>): WorkItem;
22
+ deleteWorkItem(id: string): void;
23
+ addComment(workItemId: string, comment: NewComment): Comment;
24
+ getChildren(id: string): WorkItem[];
25
+ getDependents(id: string): WorkItem[];
26
+ getItemUrl(id: string): string;
27
+ openItem(id: string): void;
28
+ }
@@ -0,0 +1,457 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import { BaseBackend } from '../types.js';
3
+ import { az, azExec, azInvoke } from './az.js';
4
+ import { parseAdoRemote } from './remote.js';
5
+ import { mapWorkItemToWorkItem, mapCommentToComment, mapPriorityToAdo, formatTags, extractParent, extractPredecessors, } from './mappers.js';
6
+ const BATCH_FIELDS = [
7
+ 'System.Id',
8
+ 'System.Title',
9
+ 'System.WorkItemType',
10
+ 'System.State',
11
+ 'System.IterationPath',
12
+ 'Microsoft.VSTS.Common.Priority',
13
+ 'System.AssignedTo',
14
+ 'System.Tags',
15
+ 'System.Description',
16
+ 'System.CreatedDate',
17
+ 'System.ChangedDate',
18
+ ];
19
+ export class AzureDevOpsBackend extends BaseBackend {
20
+ cwd;
21
+ org;
22
+ project;
23
+ types;
24
+ constructor(cwd) {
25
+ super();
26
+ this.cwd = cwd;
27
+ azExec(['account', 'show'], cwd);
28
+ const remote = parseAdoRemote(cwd);
29
+ this.org = remote.org;
30
+ this.project = remote.project;
31
+ this.types = azInvoke({
32
+ area: 'wit',
33
+ resource: 'workitemtypes',
34
+ routeParameters: `project=${this.project}`,
35
+ apiVersion: '7.1',
36
+ }, cwd).value;
37
+ }
38
+ getCapabilities() {
39
+ return {
40
+ relationships: true,
41
+ customTypes: false,
42
+ customStatuses: false,
43
+ iterations: true,
44
+ comments: true,
45
+ fields: {
46
+ priority: true,
47
+ assignee: true,
48
+ labels: true,
49
+ parent: true,
50
+ dependsOn: true,
51
+ },
52
+ };
53
+ }
54
+ getStatuses() {
55
+ const allStates = new Set();
56
+ for (const type of this.types) {
57
+ for (const state of type.states) {
58
+ allStates.add(state.name);
59
+ }
60
+ }
61
+ return [...allStates];
62
+ }
63
+ getWorkItemTypes() {
64
+ return this.types.map((t) => t.name);
65
+ }
66
+ getIterations() {
67
+ const iterations = az([
68
+ 'boards',
69
+ 'iteration',
70
+ 'team',
71
+ 'list',
72
+ '--team',
73
+ `${this.project} Team`,
74
+ '--org',
75
+ `https://dev.azure.com/${this.org}`,
76
+ '--project',
77
+ this.project,
78
+ ], this.cwd);
79
+ return iterations.map((i) => i.path);
80
+ }
81
+ getCurrentIteration() {
82
+ const iterations = az([
83
+ 'boards',
84
+ 'iteration',
85
+ 'team',
86
+ 'list',
87
+ '--team',
88
+ `${this.project} Team`,
89
+ '--timeframe',
90
+ 'current',
91
+ '--org',
92
+ `https://dev.azure.com/${this.org}`,
93
+ '--project',
94
+ this.project,
95
+ ], this.cwd);
96
+ return iterations[0]?.path ?? '';
97
+ }
98
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
99
+ setCurrentIteration(_name) {
100
+ // No-op — current iteration is determined by date range in ADO
101
+ }
102
+ escapeWiql(value) {
103
+ return value.replace(/'/g, "''");
104
+ }
105
+ batchFetchWorkItems(ids) {
106
+ const CHUNK_SIZE = 200;
107
+ const items = [];
108
+ for (let i = 0; i < ids.length; i += CHUNK_SIZE) {
109
+ const chunk = ids.slice(i, i + CHUNK_SIZE);
110
+ const batchResult = azInvoke({
111
+ area: 'wit',
112
+ resource: 'workitemsbatch',
113
+ httpMethod: 'POST',
114
+ body: { ids: chunk, fields: BATCH_FIELDS, $expand: 4 },
115
+ apiVersion: '7.1',
116
+ }, this.cwd);
117
+ items.push(...batchResult.value.map(mapWorkItemToWorkItem));
118
+ }
119
+ return items;
120
+ }
121
+ listWorkItems(iteration) {
122
+ let wiql = `SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = '${this.escapeWiql(this.project)}'`;
123
+ if (iteration) {
124
+ wiql += ` AND [System.IterationPath] = '${this.escapeWiql(iteration)}'`;
125
+ }
126
+ const queryResult = az([
127
+ 'boards',
128
+ 'query',
129
+ '--wiql',
130
+ wiql,
131
+ '--org',
132
+ `https://dev.azure.com/${this.org}`,
133
+ '--project',
134
+ this.project,
135
+ ], this.cwd);
136
+ const ids = queryResult.workItems.map((w) => w.id);
137
+ if (ids.length === 0)
138
+ return [];
139
+ const items = this.batchFetchWorkItems(ids);
140
+ items.sort((a, b) => b.updated.localeCompare(a.updated));
141
+ return items;
142
+ }
143
+ getWorkItem(id) {
144
+ const ado = az([
145
+ 'boards',
146
+ 'work-item',
147
+ 'show',
148
+ '--id',
149
+ id,
150
+ '--expand',
151
+ 'relations',
152
+ '--org',
153
+ `https://dev.azure.com/${this.org}`,
154
+ '--project',
155
+ this.project,
156
+ ], this.cwd);
157
+ const item = mapWorkItemToWorkItem(ado);
158
+ // Fetch comments
159
+ const commentResult = azInvoke({
160
+ area: 'wit',
161
+ resource: 'comments',
162
+ routeParameters: `workItemId=${id}`,
163
+ apiVersion: '7.1',
164
+ }, this.cwd);
165
+ item.comments = (commentResult.comments ?? []).map(mapCommentToComment);
166
+ return item;
167
+ }
168
+ createWorkItem(data) {
169
+ this.validateFields(data);
170
+ const args = [
171
+ 'boards',
172
+ 'work-item',
173
+ 'create',
174
+ '--type',
175
+ data.type,
176
+ '--title',
177
+ data.title,
178
+ '--org',
179
+ `https://dev.azure.com/${this.org}`,
180
+ '--project',
181
+ this.project,
182
+ ];
183
+ const fields = [];
184
+ if (data.status)
185
+ fields.push(`System.State=${data.status}`);
186
+ if (data.iteration)
187
+ fields.push(`System.IterationPath=${data.iteration}`);
188
+ if (data.priority)
189
+ fields.push(`Microsoft.VSTS.Common.Priority=${mapPriorityToAdo(data.priority)}`);
190
+ if (data.assignee)
191
+ fields.push(`System.AssignedTo=${data.assignee}`);
192
+ if (data.labels.length > 0)
193
+ fields.push(`System.Tags=${formatTags(data.labels)}`);
194
+ if (data.description)
195
+ fields.push(`System.Description=${data.description}`);
196
+ for (const field of fields) {
197
+ args.push('--fields', field);
198
+ }
199
+ const created = az(args, this.cwd);
200
+ const createdId = String(created.id);
201
+ // Add parent relation if specified
202
+ if (data.parent) {
203
+ azExec([
204
+ 'boards',
205
+ 'work-item',
206
+ 'relation',
207
+ 'add',
208
+ '--id',
209
+ createdId,
210
+ '--relation-type',
211
+ 'System.LinkTypes.Hierarchy-Reverse',
212
+ '--target-id',
213
+ data.parent,
214
+ '--org',
215
+ `https://dev.azure.com/${this.org}`,
216
+ ], this.cwd);
217
+ }
218
+ // Add dependency relations
219
+ for (const depId of data.dependsOn) {
220
+ azExec([
221
+ 'boards',
222
+ 'work-item',
223
+ 'relation',
224
+ 'add',
225
+ '--id',
226
+ createdId,
227
+ '--relation-type',
228
+ 'System.LinkTypes.Dependency-Reverse',
229
+ '--target-id',
230
+ depId,
231
+ '--org',
232
+ `https://dev.azure.com/${this.org}`,
233
+ ], this.cwd);
234
+ }
235
+ return this.getWorkItem(createdId);
236
+ }
237
+ updateWorkItem(id, data) {
238
+ this.validateFields(data);
239
+ const args = [
240
+ 'boards',
241
+ 'work-item',
242
+ 'update',
243
+ '--id',
244
+ id,
245
+ '--org',
246
+ `https://dev.azure.com/${this.org}`,
247
+ '--project',
248
+ this.project,
249
+ ];
250
+ const fields = [];
251
+ if (data.title !== undefined)
252
+ fields.push(`System.Title=${data.title}`);
253
+ if (data.status !== undefined)
254
+ fields.push(`System.State=${data.status}`);
255
+ if (data.iteration !== undefined)
256
+ fields.push(`System.IterationPath=${data.iteration}`);
257
+ if (data.priority !== undefined)
258
+ fields.push(`Microsoft.VSTS.Common.Priority=${mapPriorityToAdo(data.priority)}`);
259
+ if (data.assignee !== undefined)
260
+ fields.push(`System.AssignedTo=${data.assignee}`);
261
+ if (data.labels !== undefined)
262
+ fields.push(`System.Tags=${formatTags(data.labels)}`);
263
+ if (data.description !== undefined)
264
+ fields.push(`System.Description=${data.description}`);
265
+ for (const field of fields) {
266
+ args.push('--fields', field);
267
+ }
268
+ if (fields.length > 0) {
269
+ az(args, this.cwd);
270
+ }
271
+ // Handle parent relation changes
272
+ if (data.parent !== undefined) {
273
+ const current = az([
274
+ 'boards',
275
+ 'work-item',
276
+ 'show',
277
+ '--id',
278
+ id,
279
+ '--expand',
280
+ 'relations',
281
+ '--org',
282
+ `https://dev.azure.com/${this.org}`,
283
+ '--project',
284
+ this.project,
285
+ ], this.cwd);
286
+ const currentParent = extractParent(current.relations);
287
+ if (currentParent && currentParent !== data.parent) {
288
+ azExec([
289
+ 'boards',
290
+ 'work-item',
291
+ 'relation',
292
+ 'remove',
293
+ '--id',
294
+ id,
295
+ '--relation-type',
296
+ 'System.LinkTypes.Hierarchy-Reverse',
297
+ '--target-id',
298
+ currentParent,
299
+ '--org',
300
+ `https://dev.azure.com/${this.org}`,
301
+ '--yes',
302
+ ], this.cwd);
303
+ }
304
+ if (data.parent && data.parent !== currentParent) {
305
+ azExec([
306
+ 'boards',
307
+ 'work-item',
308
+ 'relation',
309
+ 'add',
310
+ '--id',
311
+ id,
312
+ '--relation-type',
313
+ 'System.LinkTypes.Hierarchy-Reverse',
314
+ '--target-id',
315
+ data.parent,
316
+ '--org',
317
+ `https://dev.azure.com/${this.org}`,
318
+ ], this.cwd);
319
+ }
320
+ }
321
+ // Handle dependency relation changes
322
+ if (data.dependsOn !== undefined) {
323
+ const current = az([
324
+ 'boards',
325
+ 'work-item',
326
+ 'show',
327
+ '--id',
328
+ id,
329
+ '--expand',
330
+ 'relations',
331
+ '--org',
332
+ `https://dev.azure.com/${this.org}`,
333
+ '--project',
334
+ this.project,
335
+ ], this.cwd);
336
+ const currentDeps = new Set(extractPredecessors(current.relations));
337
+ const newDeps = new Set(data.dependsOn);
338
+ // Remove deps that are no longer in the list
339
+ for (const dep of currentDeps) {
340
+ if (!newDeps.has(dep)) {
341
+ azExec([
342
+ 'boards',
343
+ 'work-item',
344
+ 'relation',
345
+ 'remove',
346
+ '--id',
347
+ id,
348
+ '--relation-type',
349
+ 'System.LinkTypes.Dependency-Reverse',
350
+ '--target-id',
351
+ dep,
352
+ '--org',
353
+ `https://dev.azure.com/${this.org}`,
354
+ '--yes',
355
+ ], this.cwd);
356
+ }
357
+ }
358
+ // Add deps that are new
359
+ for (const dep of newDeps) {
360
+ if (!currentDeps.has(dep)) {
361
+ azExec([
362
+ 'boards',
363
+ 'work-item',
364
+ 'relation',
365
+ 'add',
366
+ '--id',
367
+ id,
368
+ '--relation-type',
369
+ 'System.LinkTypes.Dependency-Reverse',
370
+ '--target-id',
371
+ dep,
372
+ '--org',
373
+ `https://dev.azure.com/${this.org}`,
374
+ ], this.cwd);
375
+ }
376
+ }
377
+ }
378
+ return this.getWorkItem(id);
379
+ }
380
+ deleteWorkItem(id) {
381
+ azExec([
382
+ 'boards',
383
+ 'work-item',
384
+ 'delete',
385
+ '--id',
386
+ id,
387
+ '--yes',
388
+ '--org',
389
+ `https://dev.azure.com/${this.org}`,
390
+ '--project',
391
+ this.project,
392
+ ], this.cwd);
393
+ }
394
+ addComment(workItemId, comment) {
395
+ azInvoke({
396
+ area: 'wit',
397
+ resource: 'comments',
398
+ routeParameters: `workItemId=${workItemId}`,
399
+ httpMethod: 'POST',
400
+ body: { text: comment.body },
401
+ apiVersion: '7.1',
402
+ }, this.cwd);
403
+ return {
404
+ author: comment.author,
405
+ date: new Date().toISOString(),
406
+ body: comment.body,
407
+ };
408
+ }
409
+ getChildren(id) {
410
+ const numericId = parseInt(id, 10);
411
+ if (isNaN(numericId))
412
+ throw new Error(`Invalid work item ID: "${id}"`);
413
+ const wiql = `SELECT [System.Id] FROM WorkItemLinks WHERE [Source].[System.Id] = ${numericId} AND [System.Links.LinkType] = 'System.LinkTypes.Hierarchy-Forward' MODE (MustContain)`;
414
+ const queryResult = az([
415
+ 'boards',
416
+ 'query',
417
+ '--wiql',
418
+ wiql,
419
+ '--org',
420
+ `https://dev.azure.com/${this.org}`,
421
+ '--project',
422
+ this.project,
423
+ ], this.cwd);
424
+ const ids = queryResult.workItems?.map((w) => w.id) ?? [];
425
+ if (ids.length === 0)
426
+ return [];
427
+ return this.batchFetchWorkItems(ids);
428
+ }
429
+ getDependents(id) {
430
+ const numericId = parseInt(id, 10);
431
+ if (isNaN(numericId))
432
+ throw new Error(`Invalid work item ID: "${id}"`);
433
+ const wiql = `SELECT [System.Id] FROM WorkItemLinks WHERE [Source].[System.Id] = ${numericId} AND [System.Links.LinkType] = 'System.LinkTypes.Dependency-Forward' MODE (MustContain)`;
434
+ const queryResult = az([
435
+ 'boards',
436
+ 'query',
437
+ '--wiql',
438
+ wiql,
439
+ '--org',
440
+ `https://dev.azure.com/${this.org}`,
441
+ '--project',
442
+ this.project,
443
+ ], this.cwd);
444
+ const ids = queryResult.workItems?.map((w) => w.id) ?? [];
445
+ if (ids.length === 0)
446
+ return [];
447
+ return this.batchFetchWorkItems(ids);
448
+ }
449
+ getItemUrl(id) {
450
+ return `https://dev.azure.com/${this.org}/${encodeURIComponent(this.project)}/_workitems/edit/${id}`;
451
+ }
452
+ openItem(id) {
453
+ const url = this.getItemUrl(id);
454
+ execFileSync('open', [url]);
455
+ }
456
+ }
457
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/backends/ado/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import type { WorkItem, Comment } from '../../types.js';
2
+ export interface AdoWorkItem {
3
+ id: number;
4
+ fields: Record<string, unknown>;
5
+ relations?: AdoRelation[];
6
+ }
7
+ export interface AdoRelation {
8
+ rel: string;
9
+ url: string;
10
+ attributes: Record<string, unknown>;
11
+ }
12
+ export interface AdoComment {
13
+ createdBy: {
14
+ displayName: string;
15
+ };
16
+ createdDate: string;
17
+ text: string;
18
+ }
19
+ export interface AdoIteration {
20
+ name: string;
21
+ path: string;
22
+ attributes: {
23
+ startDate?: string;
24
+ finishDate?: string;
25
+ };
26
+ }
27
+ export interface AdoWorkItemType {
28
+ name: string;
29
+ states: {
30
+ name: string;
31
+ }[];
32
+ }
33
+ export declare function mapPriorityToTic(priority: number | undefined): 'critical' | 'high' | 'medium' | 'low';
34
+ export declare function mapPriorityToAdo(priority: string): number;
35
+ export declare function parseTags(tags: string | undefined): string[];
36
+ export declare function formatTags(tags: string[]): string;
37
+ export declare function extractParent(relations: AdoRelation[] | undefined): string | null;
38
+ export declare function extractPredecessors(relations: AdoRelation[] | undefined): string[];
39
+ export declare function mapWorkItemToWorkItem(ado: AdoWorkItem): WorkItem;
40
+ export declare function mapCommentToComment(ado: AdoComment): Comment;
@@ -0,0 +1,92 @@
1
+ import TurndownService from 'turndown';
2
+ const turndown = new TurndownService();
3
+ export function mapPriorityToTic(priority) {
4
+ switch (priority) {
5
+ case 1:
6
+ return 'critical';
7
+ case 2:
8
+ return 'high';
9
+ case 3:
10
+ return 'medium';
11
+ case 4:
12
+ return 'low';
13
+ default:
14
+ return 'medium';
15
+ }
16
+ }
17
+ export function mapPriorityToAdo(priority) {
18
+ switch (priority) {
19
+ case 'critical':
20
+ return 1;
21
+ case 'high':
22
+ return 2;
23
+ case 'medium':
24
+ return 3;
25
+ case 'low':
26
+ return 4;
27
+ default:
28
+ return 3;
29
+ }
30
+ }
31
+ export function parseTags(tags) {
32
+ if (!tags)
33
+ return [];
34
+ return tags
35
+ .split(';')
36
+ .map((t) => t.trim())
37
+ .filter((t) => t.length > 0);
38
+ }
39
+ export function formatTags(tags) {
40
+ return tags.join('; ');
41
+ }
42
+ function extractIdFromUrl(url) {
43
+ const match = url.match(/\/workItems\/(\d+)$/);
44
+ return match ? match[1] : '';
45
+ }
46
+ export function extractParent(relations) {
47
+ if (!relations)
48
+ return null;
49
+ const parent = relations.find((r) => r.rel === 'System.LinkTypes.Hierarchy-Reverse');
50
+ return parent ? extractIdFromUrl(parent.url) || null : null;
51
+ }
52
+ export function extractPredecessors(relations) {
53
+ if (!relations)
54
+ return [];
55
+ return relations
56
+ .filter((r) => r.rel === 'System.LinkTypes.Dependency-Reverse')
57
+ .map((r) => extractIdFromUrl(r.url))
58
+ .filter((id) => id !== '');
59
+ }
60
+ function htmlToMarkdown(html) {
61
+ if (!html)
62
+ return '';
63
+ return turndown.turndown(html);
64
+ }
65
+ export function mapWorkItemToWorkItem(ado) {
66
+ const fields = ado.fields;
67
+ const assignedTo = fields['System.AssignedTo'];
68
+ return {
69
+ id: String(ado.id),
70
+ title: fields['System.Title'] ?? '',
71
+ type: fields['System.WorkItemType'] ?? '',
72
+ status: fields['System.State'] ?? '',
73
+ iteration: fields['System.IterationPath'] ?? '',
74
+ priority: mapPriorityToTic(fields['Microsoft.VSTS.Common.Priority']),
75
+ assignee: assignedTo?.displayName ?? '',
76
+ labels: parseTags(fields['System.Tags']),
77
+ description: htmlToMarkdown(fields['System.Description']),
78
+ created: fields['System.CreatedDate'] ?? '',
79
+ updated: fields['System.ChangedDate'] ?? '',
80
+ parent: extractParent(ado.relations),
81
+ dependsOn: extractPredecessors(ado.relations),
82
+ comments: [],
83
+ };
84
+ }
85
+ export function mapCommentToComment(ado) {
86
+ return {
87
+ author: ado.createdBy.displayName,
88
+ date: ado.createdDate,
89
+ body: htmlToMarkdown(ado.text),
90
+ };
91
+ }
92
+ //# sourceMappingURL=mappers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mappers.js","sourceRoot":"","sources":["../../../src/backends/ado/mappers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export interface AdoRemoteInfo {
2
+ org: string;
3
+ project: string;
4
+ }
5
+ export declare function parseAdoRemote(cwd: string): AdoRemoteInfo;
@@ -0,0 +1,37 @@
1
+ import { execSync } from 'node:child_process';
2
+ export function parseAdoRemote(cwd) {
3
+ const output = execSync('git remote -v', {
4
+ cwd,
5
+ encoding: 'utf-8',
6
+ stdio: ['pipe', 'pipe', 'pipe'],
7
+ });
8
+ const lines = output.split('\n');
9
+ for (const line of lines) {
10
+ // HTTPS: https://dev.azure.com/{org}/{project}/_git/{repo}
11
+ const httpsMatch = line.match(/dev\.azure\.com\/([^/]+)\/([^/]+)\/_git\//);
12
+ if (httpsMatch) {
13
+ return {
14
+ org: httpsMatch[1],
15
+ project: decodeURIComponent(httpsMatch[2]),
16
+ };
17
+ }
18
+ // SSH: git@ssh.dev.azure.com:v3/{org}/{project}/{repo}
19
+ const sshMatch = line.match(/ssh\.dev\.azure\.com:v3\/([^/]+)\/([^/]+)\/[^/]+/);
20
+ if (sshMatch) {
21
+ return {
22
+ org: sshMatch[1],
23
+ project: decodeURIComponent(sshMatch[2]),
24
+ };
25
+ }
26
+ // Legacy: https://{org}.visualstudio.com/{project}/_git/{repo}
27
+ const legacyMatch = line.match(/([^/.]+)\.visualstudio\.com\/([^/]+)\/_git\//);
28
+ if (legacyMatch) {
29
+ return {
30
+ org: legacyMatch[1],
31
+ project: decodeURIComponent(legacyMatch[2]),
32
+ };
33
+ }
34
+ }
35
+ throw new Error('No Azure DevOps remote found in git remotes');
36
+ }
37
+ //# sourceMappingURL=remote.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote.js","sourceRoot":"","sources":["../../../src/backends/ado/remote.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAO9C,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,MAAM,MAAM,GAAG,QAAQ,CAAC,eAAe,EAAE;QACvC,GAAG;QACH,QAAQ,EAAE,OAAO;QACjB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;KAChC,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACjC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,2DAA2D;QAC3D,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,2CAA2C,CAAC,CAAC;QAC3E,IAAI,UAAU,EAAE,CAAC;YACf,OAAO;gBACL,GAAG,EAAE,UAAU,CAAC,CAAC,CAAE;gBACnB,OAAO,EAAE,kBAAkB,CAAC,UAAU,CAAC,CAAC,CAAE,CAAC;aAC5C,CAAC;QACJ,CAAC;QAED,uDAAuD;QACvD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CACzB,kDAAkD,CACnD,CAAC;QACF,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO;gBACL,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAE;gBACjB,OAAO,EAAE,kBAAkB,CAAC,QAAQ,CAAC,CAAC,CAAE,CAAC;aAC1C,CAAC;QACJ,CAAC;QAED,+DAA+D;QAC/D,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAC5B,8CAA8C,CAC/C,CAAC;QACF,IAAI,WAAW,EAAE,CAAC;YAChB,OAAO;gBACL,GAAG,EAAE,WAAW,CAAC,CAAC,CAAE;gBACpB,OAAO,EAAE,kBAAkB,CAAC,WAAW,CAAC,CAAC,CAAE,CAAC;aAC7C,CAAC;QACJ,CAAC;IACH,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;AACjE,CAAC"}
@@ -2,6 +2,7 @@ import { execSync } from 'node:child_process';
2
2
  import { LocalBackend } from './local/index.js';
3
3
  import { GitHubBackend } from './github/index.js';
4
4
  import { GitLabBackend } from './gitlab/index.js';
5
+ import { AzureDevOpsBackend } from './ado/index.js';
5
6
  import { readConfig } from './local/config.js';
6
7
  export const VALID_BACKENDS = ['local', 'github', 'gitlab', 'azure'];
7
8
  export function detectBackend(root) {
@@ -15,7 +16,9 @@ export function detectBackend(root) {
15
16
  return 'github';
16
17
  if (output.includes('gitlab.com'))
17
18
  return 'gitlab';
18
- if (output.includes('dev.azure.com'))
19
+ if (output.includes('dev.azure.com') ||
20
+ output.includes('ssh.dev.azure.com') ||
21
+ /\w+\.visualstudio\.com/.test(output))
19
22
  return 'azure';
20
23
  }
21
24
  catch {
@@ -34,7 +37,7 @@ export function createBackend(root) {
34
37
  case 'gitlab':
35
38
  return new GitLabBackend(root);
36
39
  case 'azure':
37
- throw new Error(`Backend "${backend}" is not yet implemented. Use "local" for now.`);
40
+ return new AzureDevOpsBackend(root);
38
41
  default:
39
42
  throw new Error(`Unknown backend "${backend}". Valid backends: ${VALID_BACKENDS.join(', ')}`);
40
43
  }
@@ -1 +1 @@
1
- {"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/backends/factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/C,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,CAAU,CAAC;AAG9E,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,QAAQ,CAAC,eAAe,EAAE;YACvC,GAAG,EAAE,IAAI;YACT,QAAQ,EAAE,OAAO;YACjB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;SAChC,CAAC,CAAC;QACH,IAAI,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC;YAAE,OAAO,QAAQ,CAAC;QACnD,IAAI,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC;YAAE,OAAO,QAAQ,CAAC;QACnD,IAAI,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC;YAAE,OAAO,OAAO,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACP,sCAAsC;IACxC,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAChC,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,OAAO,CAAC;IAE1C,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,OAAO;YACV,OAAO,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC;QAChC,KAAK,QAAQ;YACX,OAAO,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC;QACjC,KAAK,QAAQ;YACX,OAAO,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC;QACjC,KAAK,OAAO;YACV,MAAM,IAAI,KAAK,CACb,YAAY,OAAO,gDAAgD,CACpE,CAAC;QACJ;YACE,MAAM,IAAI,KAAK,CACb,oBAAoB,OAAO,sBAAsB,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC7E,CAAC;IACN,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/backends/factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/C,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,CAAU,CAAC;AAG9E,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,QAAQ,CAAC,eAAe,EAAE;YACvC,GAAG,EAAE,IAAI;YACT,QAAQ,EAAE,OAAO;YACjB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;SAChC,CAAC,CAAC;QACH,IAAI,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC;YAAE,OAAO,QAAQ,CAAC;QACnD,IAAI,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC;YAAE,OAAO,QAAQ,CAAC;QACnD,IACE,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC;YAChC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAC;YACpC,wBAAwB,CAAC,IAAI,CAAC,MAAM,CAAC;YAErC,OAAO,OAAO,CAAC;IACnB,CAAC;IAAC,MAAM,CAAC;QACP,sCAAsC;IACxC,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAChC,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,OAAO,CAAC;IAE1C,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,OAAO;YACV,OAAO,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC;QAChC,KAAK,QAAQ;YACX,OAAO,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC;QACjC,KAAK,QAAQ;YACX,OAAO,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC;QACjC,KAAK,OAAO;YACV,OAAO,IAAI,kBAAkB,CAAC,IAAI,CAAC,CAAC;QACtC;YACE,MAAM,IAAI,KAAK,CACb,oBAAoB,OAAO,sBAAsB,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC7E,CAAC;IACN,CAAC;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sascha384/tic",
3
- "version": "0.1.0",
3
+ "version": "1.0.1",
4
4
  "description": "Terminal UI for issue tracking",
5
5
  "type": "module",
6
6
  "bin": {
@@ -37,6 +37,7 @@
37
37
  "ink-select-input": "^6.2.0",
38
38
  "ink-text-input": "^6.0.0",
39
39
  "react": "^19.2.4",
40
+ "turndown": "^7.2.2",
40
41
  "yaml": "^2.8.2",
41
42
  "zod": "^4.3.6"
42
43
  },
@@ -45,6 +46,7 @@
45
46
  "@sindresorhus/tsconfig": "^8.1.0",
46
47
  "@types/node": "^25.1.0",
47
48
  "@types/react": "^19.2.10",
49
+ "@types/turndown": "^5.0.6",
48
50
  "eslint": "^9.39.2",
49
51
  "eslint-config-prettier": "^10.1.8",
50
52
  "husky": "^9.1.7",
@@ -55,7 +57,9 @@
55
57
  "vitest": "^4.0.18"
56
58
  },
57
59
  "release": {
58
- "branches": ["main"],
60
+ "branches": [
61
+ "main"
62
+ ],
59
63
  "plugins": [
60
64
  "@semantic-release/commit-analyzer",
61
65
  "@semantic-release/release-notes-generator",