@sascha384/tic 0.1.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/LICENSE +21 -0
- package/README.md +221 -0
- package/dist/app.d.ts +17 -0
- package/dist/app.js +27 -0
- package/dist/app.js.map +1 -0
- package/dist/backends/factory.d.ts +5 -0
- package/dist/backends/factory.js +42 -0
- package/dist/backends/factory.js.map +1 -0
- package/dist/backends/github/gh.d.ts +2 -0
- package/dist/backends/github/gh.js +17 -0
- package/dist/backends/github/gh.js.map +1 -0
- package/dist/backends/github/index.d.ts +26 -0
- package/dist/backends/github/index.js +188 -0
- package/dist/backends/github/index.js.map +1 -0
- package/dist/backends/github/mappers.d.ts +33 -0
- package/dist/backends/github/mappers.js +26 -0
- package/dist/backends/github/mappers.js.map +1 -0
- package/dist/backends/gitlab/glab.d.ts +2 -0
- package/dist/backends/gitlab/glab.js +17 -0
- package/dist/backends/gitlab/glab.js.map +1 -0
- package/dist/backends/gitlab/group.d.ts +1 -0
- package/dist/backends/gitlab/group.js +33 -0
- package/dist/backends/gitlab/group.js.map +1 -0
- package/dist/backends/gitlab/index.d.ts +29 -0
- package/dist/backends/gitlab/index.js +296 -0
- package/dist/backends/gitlab/index.js.map +1 -0
- package/dist/backends/gitlab/mappers.d.ts +43 -0
- package/dist/backends/gitlab/mappers.js +44 -0
- package/dist/backends/gitlab/mappers.js.map +1 -0
- package/dist/backends/local/config.d.ts +12 -0
- package/dist/backends/local/config.js +28 -0
- package/dist/backends/local/config.js.map +1 -0
- package/dist/backends/local/index.d.ts +26 -0
- package/dist/backends/local/index.js +212 -0
- package/dist/backends/local/index.js.map +1 -0
- package/dist/backends/local/items.d.ts +6 -0
- package/dist/backends/local/items.js +118 -0
- package/dist/backends/local/items.js.map +1 -0
- package/dist/backends/types.d.ts +56 -0
- package/dist/backends/types.js +39 -0
- package/dist/backends/types.js.map +1 -0
- package/dist/cli/commands/config.d.ts +2 -0
- package/dist/cli/commands/config.js +40 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/init.d.ts +6 -0
- package/dist/cli/commands/init.js +12 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/item.d.ts +41 -0
- package/dist/cli/commands/item.js +80 -0
- package/dist/cli/commands/item.js.map +1 -0
- package/dist/cli/commands/iteration.d.ts +7 -0
- package/dist/cli/commands/iteration.js +10 -0
- package/dist/cli/commands/iteration.js.map +1 -0
- package/dist/cli/commands/mcp.d.ts +85 -0
- package/dist/cli/commands/mcp.js +448 -0
- package/dist/cli/commands/mcp.js.map +1 -0
- package/dist/cli/format.d.ts +3 -0
- package/dist/cli/format.js +10 -0
- package/dist/cli/format.js.map +1 -0
- package/dist/cli/index.d.ts +4 -0
- package/dist/cli/index.js +426 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/components/IterationPicker.d.ts +1 -0
- package/dist/components/IterationPicker.js +18 -0
- package/dist/components/IterationPicker.js.map +1 -0
- package/dist/components/Settings.d.ts +1 -0
- package/dist/components/Settings.js +40 -0
- package/dist/components/Settings.js.map +1 -0
- package/dist/components/WorkItemForm.d.ts +1 -0
- package/dist/components/WorkItemForm.js +270 -0
- package/dist/components/WorkItemForm.js.map +1 -0
- package/dist/components/WorkItemList.d.ts +1 -0
- package/dist/components/WorkItemList.js +219 -0
- package/dist/components/WorkItemList.js.map +1 -0
- package/dist/git.d.ts +38 -0
- package/dist/git.js +115 -0
- package/dist/git.js.map +1 -0
- package/dist/implement.d.ts +25 -0
- package/dist/implement.js +130 -0
- package/dist/implement.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +26 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +66 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 fa-krug
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# tic
|
|
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.
|
|
4
|
+
|
|
5
|
+
Built with TypeScript and [Ink](https://github.com/vadimdemedes/ink).
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Keyboard-driven TUI** — browse, create, edit, and manage work items without leaving the terminal
|
|
10
|
+
- **Local markdown storage** — work items stored as markdown files with YAML frontmatter in a `.tic/` directory
|
|
11
|
+
- **Work item types** — organize by epic, issue, and task (configurable)
|
|
12
|
+
- **Iterations** — group work into sprints or milestones
|
|
13
|
+
- **Parent-child relationships** — build hierarchies with tree-indented views
|
|
14
|
+
- **Dependencies** — track which items block others, with warnings on premature completion
|
|
15
|
+
- **Priority & assignee** — track who owns what and what's most important
|
|
16
|
+
- **Comments** — add timestamped comments to any work item
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install -g tic
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
cd your-project
|
|
28
|
+
tic
|
|
29
|
+
```
|
|
30
|
+
|
|
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.
|
|
32
|
+
|
|
33
|
+
## Usage
|
|
34
|
+
|
|
35
|
+
### List View
|
|
36
|
+
|
|
37
|
+
The main screen shows work items filtered by type and iteration, displayed as a tree that reflects parent-child relationships.
|
|
38
|
+
|
|
39
|
+
| Key | Action |
|
|
40
|
+
|-----|--------|
|
|
41
|
+
| `↑` `↓` | Navigate between items |
|
|
42
|
+
| `Enter` | Edit selected item |
|
|
43
|
+
| `c` | Create new work item |
|
|
44
|
+
| `d` | Delete selected item (with confirmation) |
|
|
45
|
+
| `s` | Cycle status forward |
|
|
46
|
+
| `p` | Set parent for selected item |
|
|
47
|
+
| `Tab` | Switch work item type (epic / issue / task) |
|
|
48
|
+
| `i` | Switch iteration |
|
|
49
|
+
| `q` | Quit |
|
|
50
|
+
|
|
51
|
+
The list displays ID, title (tree-indented), status, priority, and assignee. Items with dependencies show a `⧗` indicator.
|
|
52
|
+
|
|
53
|
+
### Editing a Work Item
|
|
54
|
+
|
|
55
|
+
Press `Enter` on an item or `c` to create one. The form has these fields:
|
|
56
|
+
|
|
57
|
+
- **Title** — name of the work item
|
|
58
|
+
- **Type** — epic, issue, or task
|
|
59
|
+
- **Status** — backlog, todo, in-progress, review, done
|
|
60
|
+
- **Iteration** — which sprint/milestone this belongs to
|
|
61
|
+
- **Priority** — low, medium, high, critical
|
|
62
|
+
- **Assignee** — who owns this
|
|
63
|
+
- **Labels** — comma-separated tags
|
|
64
|
+
- **Parent** — ID of the parent item
|
|
65
|
+
- **Depends On** — comma-separated IDs of items this depends on
|
|
66
|
+
- **Description** — full details (multi-line)
|
|
67
|
+
- **Comments** — add new comments; existing comments shown as read-only
|
|
68
|
+
|
|
69
|
+
Navigate fields with `↑` `↓`, press `Enter` to edit a field, and `Esc` to save and return to the list.
|
|
70
|
+
|
|
71
|
+
### Iterations
|
|
72
|
+
|
|
73
|
+
Press `i` in the list view to open the iteration picker. The current iteration filters which items you see. New iterations are created automatically when you assign an item to one that doesn't exist yet.
|
|
74
|
+
|
|
75
|
+
### Relationships
|
|
76
|
+
|
|
77
|
+
**Parent-child:** Set a parent ID on an item to nest it under another. Children appear indented in the tree view. Circular parent chains are prevented.
|
|
78
|
+
|
|
79
|
+
**Dependencies:** Add dependency IDs to indicate that an item is blocked by others. When you try to complete an item that has open children or unresolved dependencies, tic shows a warning so you can decide whether to proceed.
|
|
80
|
+
|
|
81
|
+
Deleting an item automatically cleans up references — children have their parent cleared, and the item is removed from other items' dependency lists.
|
|
82
|
+
|
|
83
|
+
## Storage
|
|
84
|
+
|
|
85
|
+
Work items live in `.tic/` at the root of your project:
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
.tic/
|
|
89
|
+
├── config.yml # Types, statuses, iterations, settings
|
|
90
|
+
└── items/
|
|
91
|
+
├── 1.md # Work item #1
|
|
92
|
+
├── 2.md # Work item #2
|
|
93
|
+
└── ...
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Each item is a markdown file with YAML frontmatter:
|
|
97
|
+
|
|
98
|
+
```markdown
|
|
99
|
+
---
|
|
100
|
+
id: 1
|
|
101
|
+
title: Implement user login
|
|
102
|
+
type: task
|
|
103
|
+
status: in-progress
|
|
104
|
+
iteration: sprint-1
|
|
105
|
+
priority: high
|
|
106
|
+
assignee: alice
|
|
107
|
+
labels: auth, backend
|
|
108
|
+
parent: 3
|
|
109
|
+
depends_on:
|
|
110
|
+
- 2
|
|
111
|
+
created: 2026-01-15T10:00:00.000Z
|
|
112
|
+
updated: 2026-01-20T14:30:00.000Z
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
Full description of the work item goes here.
|
|
116
|
+
|
|
117
|
+
## Comments
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
author: alice
|
|
121
|
+
date: 2026-01-18T09:00:00.000Z
|
|
122
|
+
|
|
123
|
+
Decided to use JWT tokens for this.
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Configuration in `.tic/config.yml`:
|
|
127
|
+
|
|
128
|
+
```yaml
|
|
129
|
+
types:
|
|
130
|
+
- epic
|
|
131
|
+
- issue
|
|
132
|
+
- task
|
|
133
|
+
statuses:
|
|
134
|
+
- backlog
|
|
135
|
+
- todo
|
|
136
|
+
- in-progress
|
|
137
|
+
- review
|
|
138
|
+
- done
|
|
139
|
+
iterations:
|
|
140
|
+
- default
|
|
141
|
+
current_iteration: default
|
|
142
|
+
next_id: 1
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
You can edit these files directly — they're plain text. Customize types, statuses, and iterations by editing `config.yml`.
|
|
146
|
+
|
|
147
|
+
## MCP Server
|
|
148
|
+
|
|
149
|
+
tic includes a built-in [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server that exposes your work items to AI assistants like Claude. This lets AI tools read, create, update, and manage your issues without leaving the conversation.
|
|
150
|
+
|
|
151
|
+
### Starting the Server
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
tic mcp serve
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
The server communicates over stdio using the MCP protocol.
|
|
158
|
+
|
|
159
|
+
### Connecting to Claude Code
|
|
160
|
+
|
|
161
|
+
Add tic as an MCP server in your project:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
claude mcp add --scope project --transport stdio tic -- npx tic mcp serve
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Or create a `.mcp.json` file in your project root:
|
|
168
|
+
|
|
169
|
+
```json
|
|
170
|
+
{
|
|
171
|
+
"mcpServers": {
|
|
172
|
+
"tic": {
|
|
173
|
+
"type": "stdio",
|
|
174
|
+
"command": "npx",
|
|
175
|
+
"args": ["tic", "mcp", "serve"]
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Available Tools
|
|
182
|
+
|
|
183
|
+
The MCP server exposes 14 tools:
|
|
184
|
+
|
|
185
|
+
| Tool | Description |
|
|
186
|
+
|------|-------------|
|
|
187
|
+
| `init_project` | Initialize a new tic project (creates `.tic/` directory) |
|
|
188
|
+
| `get_config` | Get project configuration (statuses, types, iterations) |
|
|
189
|
+
| `list_items` | List work items with optional filters by type, status, or iteration |
|
|
190
|
+
| `show_item` | Show full details of a single work item |
|
|
191
|
+
| `create_item` | Create a new work item with title, type, status, priority, and more |
|
|
192
|
+
| `update_item` | Update any field on an existing work item |
|
|
193
|
+
| `delete_item` | Preview deleting a work item (shows affected children and dependents) |
|
|
194
|
+
| `confirm_delete` | Confirm and execute a previously previewed deletion |
|
|
195
|
+
| `add_comment` | Add a timestamped comment to a work item |
|
|
196
|
+
| `set_iteration` | Set the current active iteration |
|
|
197
|
+
| `search_items` | Search work items by text query across titles and descriptions |
|
|
198
|
+
| `get_children` | Get child items of a specific work item |
|
|
199
|
+
| `get_dependents` | Get items that depend on a specific work item |
|
|
200
|
+
| `get_item_tree` | Get work items as a hierarchical tree structure |
|
|
201
|
+
|
|
202
|
+
### Safety
|
|
203
|
+
|
|
204
|
+
Deletion is a two-step process: `delete_item` returns a preview of what will be affected (children that will be unparented, dependents that will lose a dependency), and `confirm_delete` must be called separately to actually remove the item. This prevents accidental data loss when AI tools are managing your issues.
|
|
205
|
+
|
|
206
|
+
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
|
+
|
|
208
|
+
## Roadmap
|
|
209
|
+
|
|
210
|
+
Planned but not yet implemented:
|
|
211
|
+
|
|
212
|
+
- **Multi-backend support** — GitHub (via `gh`), GitLab (via `glab`), Azure DevOps (via `az`)
|
|
213
|
+
- **Automatic backend detection** — select backend based on git remote
|
|
214
|
+
|
|
215
|
+
## Contributing
|
|
216
|
+
|
|
217
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, architecture, and conventions.
|
|
218
|
+
|
|
219
|
+
## License
|
|
220
|
+
|
|
221
|
+
[MIT](LICENSE)
|
package/dist/app.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Backend } from './backends/types.js';
|
|
2
|
+
type Screen = 'list' | 'form' | 'iteration-picker' | 'settings';
|
|
3
|
+
interface AppState {
|
|
4
|
+
screen: Screen;
|
|
5
|
+
selectedWorkItemId: string | null;
|
|
6
|
+
activeType: string | null;
|
|
7
|
+
backend: Backend;
|
|
8
|
+
navigate: (screen: Screen) => void;
|
|
9
|
+
selectWorkItem: (id: string | null) => void;
|
|
10
|
+
setActiveType: (type: string | null) => void;
|
|
11
|
+
}
|
|
12
|
+
export declare const AppContext: import("react").Context<AppState>;
|
|
13
|
+
export declare function useAppState(): AppState;
|
|
14
|
+
export declare function App({ backend }: {
|
|
15
|
+
backend: Backend;
|
|
16
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
export {};
|
package/dist/app.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, createContext, useContext } from 'react';
|
|
3
|
+
import { Box } from 'ink';
|
|
4
|
+
import { WorkItemList } from './components/WorkItemList.js';
|
|
5
|
+
import { WorkItemForm } from './components/WorkItemForm.js';
|
|
6
|
+
import { IterationPicker } from './components/IterationPicker.js';
|
|
7
|
+
import { Settings } from './components/Settings.js';
|
|
8
|
+
export const AppContext = createContext(null);
|
|
9
|
+
export function useAppState() {
|
|
10
|
+
return useContext(AppContext);
|
|
11
|
+
}
|
|
12
|
+
export function App({ backend }) {
|
|
13
|
+
const [screen, setScreen] = useState('list');
|
|
14
|
+
const [selectedWorkItemId, setSelectedWorkItemId] = useState(null);
|
|
15
|
+
const [activeType, setActiveType] = useState(null);
|
|
16
|
+
const state = {
|
|
17
|
+
screen,
|
|
18
|
+
selectedWorkItemId,
|
|
19
|
+
activeType,
|
|
20
|
+
backend,
|
|
21
|
+
navigate: setScreen,
|
|
22
|
+
selectWorkItem: setSelectedWorkItemId,
|
|
23
|
+
setActiveType,
|
|
24
|
+
};
|
|
25
|
+
return (_jsx(AppContext.Provider, { value: state, children: _jsxs(Box, { flexDirection: "column", children: [screen === 'list' && _jsx(WorkItemList, {}), screen === 'form' && _jsx(WorkItemForm, {}), screen === 'iteration-picker' && _jsx(IterationPicker, {}), screen === 'settings' && _jsx(Settings, {})] }) }));
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=app.js.map
|
package/dist/app.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAC5D,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAC1B,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAepD,MAAM,CAAC,MAAM,UAAU,GAAG,aAAa,CAAW,IAAK,CAAC,CAAC;AAEzD,MAAM,UAAU,WAAW;IACzB,OAAO,UAAU,CAAC,UAAU,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,EAAE,OAAO,EAAwB;IACnD,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAS,MAAM,CAAC,CAAC;IACrD,MAAM,CAAC,kBAAkB,EAAE,qBAAqB,CAAC,GAAG,QAAQ,CAC1D,IAAI,CACL,CAAC;IACF,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IAElE,MAAM,KAAK,GAAa;QACtB,MAAM;QACN,kBAAkB;QAClB,UAAU;QACV,OAAO;QACP,QAAQ,EAAE,SAAS;QACnB,cAAc,EAAE,qBAAqB;QACrC,aAAa;KACd,CAAC;IAEF,OAAO,CACL,KAAC,UAAU,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YAC/B,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,aACxB,MAAM,KAAK,MAAM,IAAI,KAAC,YAAY,KAAG,EACrC,MAAM,KAAK,MAAM,IAAI,KAAC,YAAY,KAAG,EACrC,MAAM,KAAK,kBAAkB,IAAI,KAAC,eAAe,KAAG,EACpD,MAAM,KAAK,UAAU,IAAI,KAAC,QAAQ,KAAG,IAClC,GACc,CACvB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Backend } from './types.js';
|
|
2
|
+
export declare const VALID_BACKENDS: readonly ["local", "github", "gitlab", "azure"];
|
|
3
|
+
export type BackendType = (typeof VALID_BACKENDS)[number];
|
|
4
|
+
export declare function detectBackend(root: string): BackendType;
|
|
5
|
+
export declare function createBackend(root: string): Backend;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import { LocalBackend } from './local/index.js';
|
|
3
|
+
import { GitHubBackend } from './github/index.js';
|
|
4
|
+
import { GitLabBackend } from './gitlab/index.js';
|
|
5
|
+
import { readConfig } from './local/config.js';
|
|
6
|
+
export const VALID_BACKENDS = ['local', 'github', 'gitlab', 'azure'];
|
|
7
|
+
export function detectBackend(root) {
|
|
8
|
+
try {
|
|
9
|
+
const output = execSync('git remote -v', {
|
|
10
|
+
cwd: root,
|
|
11
|
+
encoding: 'utf-8',
|
|
12
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
13
|
+
});
|
|
14
|
+
if (output.includes('github.com'))
|
|
15
|
+
return 'github';
|
|
16
|
+
if (output.includes('gitlab.com'))
|
|
17
|
+
return 'gitlab';
|
|
18
|
+
if (output.includes('dev.azure.com'))
|
|
19
|
+
return 'azure';
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// Not a git repo or git not available
|
|
23
|
+
}
|
|
24
|
+
return 'local';
|
|
25
|
+
}
|
|
26
|
+
export function createBackend(root) {
|
|
27
|
+
const config = readConfig(root);
|
|
28
|
+
const backend = config.backend ?? 'local';
|
|
29
|
+
switch (backend) {
|
|
30
|
+
case 'local':
|
|
31
|
+
return new LocalBackend(root);
|
|
32
|
+
case 'github':
|
|
33
|
+
return new GitHubBackend(root);
|
|
34
|
+
case 'gitlab':
|
|
35
|
+
return new GitLabBackend(root);
|
|
36
|
+
case 'azure':
|
|
37
|
+
throw new Error(`Backend "${backend}" is not yet implemented. Use "local" for now.`);
|
|
38
|
+
default:
|
|
39
|
+
throw new Error(`Unknown backend "${backend}". Valid backends: ${VALID_BACKENDS.join(', ')}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=factory.js.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
export function gh(args, cwd) {
|
|
3
|
+
const result = execFileSync('gh', args, {
|
|
4
|
+
cwd,
|
|
5
|
+
encoding: 'utf-8',
|
|
6
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
7
|
+
});
|
|
8
|
+
return JSON.parse(result);
|
|
9
|
+
}
|
|
10
|
+
export function ghExec(args, cwd) {
|
|
11
|
+
return execFileSync('gh', args, {
|
|
12
|
+
cwd,
|
|
13
|
+
encoding: 'utf-8',
|
|
14
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=gh.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gh.js","sourceRoot":"","sources":["../../../src/backends/github/gh.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,UAAU,EAAE,CAAI,IAAc,EAAE,GAAW;IAC/C,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE;QACtC,GAAG;QACH,QAAQ,EAAE,OAAO;QACjB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;KAChC,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;KAChC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
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 GitHubBackend extends BaseBackend {
|
|
5
|
+
private cwd;
|
|
6
|
+
constructor(cwd: string);
|
|
7
|
+
getCapabilities(): BackendCapabilities;
|
|
8
|
+
getStatuses(): string[];
|
|
9
|
+
getWorkItemTypes(): string[];
|
|
10
|
+
getIterations(): string[];
|
|
11
|
+
getCurrentIteration(): string;
|
|
12
|
+
setCurrentIteration(_name: string): void;
|
|
13
|
+
listWorkItems(iteration?: string): WorkItem[];
|
|
14
|
+
getWorkItem(id: string): WorkItem;
|
|
15
|
+
createWorkItem(data: NewWorkItem): WorkItem;
|
|
16
|
+
updateWorkItem(id: string, data: Partial<WorkItem>): WorkItem;
|
|
17
|
+
deleteWorkItem(id: string): void;
|
|
18
|
+
addComment(workItemId: string, comment: NewComment): Comment;
|
|
19
|
+
getChildren(id: string): WorkItem[];
|
|
20
|
+
getDependents(id: string): WorkItem[];
|
|
21
|
+
getItemUrl(id: string): string;
|
|
22
|
+
openItem(id: string): void;
|
|
23
|
+
private fetchMilestones;
|
|
24
|
+
private fetchOpenMilestones;
|
|
25
|
+
private getRepoNwo;
|
|
26
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { BaseBackend } from '../types.js';
|
|
2
|
+
import { gh, ghExec } from './gh.js';
|
|
3
|
+
import { mapIssueToWorkItem } from './mappers.js';
|
|
4
|
+
const ISSUE_FIELDS = 'number,title,body,state,assignees,labels,milestone,createdAt,updatedAt,comments';
|
|
5
|
+
export class GitHubBackend extends BaseBackend {
|
|
6
|
+
cwd;
|
|
7
|
+
constructor(cwd) {
|
|
8
|
+
super();
|
|
9
|
+
this.cwd = cwd;
|
|
10
|
+
ghExec(['auth', 'status'], cwd);
|
|
11
|
+
}
|
|
12
|
+
getCapabilities() {
|
|
13
|
+
return {
|
|
14
|
+
relationships: false,
|
|
15
|
+
customTypes: false,
|
|
16
|
+
customStatuses: false,
|
|
17
|
+
iterations: true,
|
|
18
|
+
comments: true,
|
|
19
|
+
fields: {
|
|
20
|
+
priority: false,
|
|
21
|
+
assignee: true,
|
|
22
|
+
labels: true,
|
|
23
|
+
parent: false,
|
|
24
|
+
dependsOn: false,
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
getStatuses() {
|
|
29
|
+
return ['open', 'closed'];
|
|
30
|
+
}
|
|
31
|
+
getWorkItemTypes() {
|
|
32
|
+
return ['issue'];
|
|
33
|
+
}
|
|
34
|
+
getIterations() {
|
|
35
|
+
const milestones = this.fetchMilestones();
|
|
36
|
+
return milestones.map((m) => m.title);
|
|
37
|
+
}
|
|
38
|
+
getCurrentIteration() {
|
|
39
|
+
const milestones = this.fetchOpenMilestones();
|
|
40
|
+
if (milestones.length === 0)
|
|
41
|
+
return '';
|
|
42
|
+
return milestones[0].title;
|
|
43
|
+
}
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
45
|
+
setCurrentIteration(_name) {
|
|
46
|
+
// No-op — current iteration is always first open milestone
|
|
47
|
+
}
|
|
48
|
+
listWorkItems(iteration) {
|
|
49
|
+
const args = [
|
|
50
|
+
'issue',
|
|
51
|
+
'list',
|
|
52
|
+
'--state',
|
|
53
|
+
'all',
|
|
54
|
+
'--json',
|
|
55
|
+
ISSUE_FIELDS,
|
|
56
|
+
'--limit',
|
|
57
|
+
'500',
|
|
58
|
+
];
|
|
59
|
+
if (iteration) {
|
|
60
|
+
args.push('--milestone', iteration);
|
|
61
|
+
}
|
|
62
|
+
const issues = gh(args, this.cwd);
|
|
63
|
+
return issues.map(mapIssueToWorkItem);
|
|
64
|
+
}
|
|
65
|
+
getWorkItem(id) {
|
|
66
|
+
const issue = gh(['issue', 'view', id, '--json', ISSUE_FIELDS], this.cwd);
|
|
67
|
+
return mapIssueToWorkItem(issue);
|
|
68
|
+
}
|
|
69
|
+
createWorkItem(data) {
|
|
70
|
+
this.validateFields(data);
|
|
71
|
+
const args = ['issue', 'create', '--title', data.title];
|
|
72
|
+
if (data.description) {
|
|
73
|
+
args.push('--body', data.description);
|
|
74
|
+
}
|
|
75
|
+
if (data.assignee) {
|
|
76
|
+
args.push('--assignee', data.assignee);
|
|
77
|
+
}
|
|
78
|
+
if (data.iteration) {
|
|
79
|
+
args.push('--milestone', data.iteration);
|
|
80
|
+
}
|
|
81
|
+
for (const label of data.labels) {
|
|
82
|
+
args.push('--label', label);
|
|
83
|
+
}
|
|
84
|
+
const output = ghExec(args, this.cwd);
|
|
85
|
+
// gh issue create prints the URL: https://github.com/owner/repo/issues/123
|
|
86
|
+
const match = output.match(/\/issues\/(\d+)/);
|
|
87
|
+
if (!match) {
|
|
88
|
+
throw new Error('Failed to parse issue number from gh output');
|
|
89
|
+
}
|
|
90
|
+
const id = match[1];
|
|
91
|
+
return this.getWorkItem(id);
|
|
92
|
+
}
|
|
93
|
+
updateWorkItem(id, data) {
|
|
94
|
+
this.validateFields(data);
|
|
95
|
+
// Handle status changes via close/reopen
|
|
96
|
+
if (data.status === 'closed') {
|
|
97
|
+
ghExec(['issue', 'close', id], this.cwd);
|
|
98
|
+
}
|
|
99
|
+
else if (data.status === 'open') {
|
|
100
|
+
ghExec(['issue', 'reopen', id], this.cwd);
|
|
101
|
+
}
|
|
102
|
+
// Handle field edits
|
|
103
|
+
const editArgs = ['issue', 'edit', id];
|
|
104
|
+
let hasEdits = false;
|
|
105
|
+
if (data.title !== undefined) {
|
|
106
|
+
editArgs.push('--title', data.title);
|
|
107
|
+
hasEdits = true;
|
|
108
|
+
}
|
|
109
|
+
if (data.description !== undefined) {
|
|
110
|
+
editArgs.push('--body', data.description);
|
|
111
|
+
hasEdits = true;
|
|
112
|
+
}
|
|
113
|
+
if (data.iteration !== undefined) {
|
|
114
|
+
if (data.iteration) {
|
|
115
|
+
editArgs.push('--milestone', data.iteration);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
editArgs.push('--remove-milestone');
|
|
119
|
+
}
|
|
120
|
+
hasEdits = true;
|
|
121
|
+
}
|
|
122
|
+
if (data.assignee !== undefined) {
|
|
123
|
+
if (data.assignee) {
|
|
124
|
+
editArgs.push('--add-assignee', data.assignee);
|
|
125
|
+
}
|
|
126
|
+
hasEdits = true;
|
|
127
|
+
}
|
|
128
|
+
if (data.labels !== undefined) {
|
|
129
|
+
for (const label of data.labels) {
|
|
130
|
+
editArgs.push('--add-label', label);
|
|
131
|
+
}
|
|
132
|
+
hasEdits = true;
|
|
133
|
+
}
|
|
134
|
+
if (hasEdits) {
|
|
135
|
+
ghExec(editArgs, this.cwd);
|
|
136
|
+
}
|
|
137
|
+
return this.getWorkItem(id);
|
|
138
|
+
}
|
|
139
|
+
deleteWorkItem(id) {
|
|
140
|
+
ghExec(['issue', 'delete', id, '--yes'], this.cwd);
|
|
141
|
+
}
|
|
142
|
+
addComment(workItemId, comment) {
|
|
143
|
+
ghExec(['issue', 'comment', workItemId, '--body', comment.body], this.cwd);
|
|
144
|
+
return {
|
|
145
|
+
author: comment.author,
|
|
146
|
+
date: new Date().toISOString(),
|
|
147
|
+
body: comment.body,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
getChildren(id) {
|
|
151
|
+
this.assertSupported(this.getCapabilities().relationships, 'relationships');
|
|
152
|
+
return this.listWorkItems().filter((item) => item.parent === id);
|
|
153
|
+
}
|
|
154
|
+
getDependents(id) {
|
|
155
|
+
this.assertSupported(this.getCapabilities().relationships, 'relationships');
|
|
156
|
+
return this.listWorkItems().filter((item) => item.dependsOn.includes(id));
|
|
157
|
+
}
|
|
158
|
+
getItemUrl(id) {
|
|
159
|
+
const result = gh(['issue', 'view', id, '--json', 'url'], this.cwd);
|
|
160
|
+
return result.url;
|
|
161
|
+
}
|
|
162
|
+
openItem(id) {
|
|
163
|
+
ghExec(['issue', 'view', id, '--web'], this.cwd);
|
|
164
|
+
}
|
|
165
|
+
fetchMilestones() {
|
|
166
|
+
const owner = this.getRepoNwo();
|
|
167
|
+
return gh(['api', `repos/${owner}/milestones`, '--jq', '.'], this.cwd);
|
|
168
|
+
}
|
|
169
|
+
fetchOpenMilestones() {
|
|
170
|
+
const milestones = this.fetchMilestones();
|
|
171
|
+
return milestones
|
|
172
|
+
.filter((m) => m.state === 'open')
|
|
173
|
+
.sort((a, b) => {
|
|
174
|
+
if (!a.due_on && !b.due_on)
|
|
175
|
+
return 0;
|
|
176
|
+
if (!a.due_on)
|
|
177
|
+
return 1;
|
|
178
|
+
if (!b.due_on)
|
|
179
|
+
return -1;
|
|
180
|
+
return a.due_on.localeCompare(b.due_on);
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
getRepoNwo() {
|
|
184
|
+
const result = gh(['repo', 'view', '--json', 'nameWithOwner'], this.cwd);
|
|
185
|
+
return result.nameWithOwner;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/backends/github/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { WorkItem, Comment } from '../../types.js';
|
|
2
|
+
export interface GhIssue {
|
|
3
|
+
number: number;
|
|
4
|
+
title: string;
|
|
5
|
+
body: string | null;
|
|
6
|
+
state: string;
|
|
7
|
+
assignees: {
|
|
8
|
+
login: string;
|
|
9
|
+
}[];
|
|
10
|
+
labels: {
|
|
11
|
+
name: string;
|
|
12
|
+
}[];
|
|
13
|
+
milestone: {
|
|
14
|
+
title: string;
|
|
15
|
+
} | null;
|
|
16
|
+
createdAt: string;
|
|
17
|
+
updatedAt: string;
|
|
18
|
+
comments?: GhComment[];
|
|
19
|
+
}
|
|
20
|
+
export interface GhComment {
|
|
21
|
+
author: {
|
|
22
|
+
login: string;
|
|
23
|
+
};
|
|
24
|
+
createdAt: string;
|
|
25
|
+
body: string;
|
|
26
|
+
}
|
|
27
|
+
export interface GhMilestone {
|
|
28
|
+
title: string;
|
|
29
|
+
state: string;
|
|
30
|
+
due_on: string | null;
|
|
31
|
+
}
|
|
32
|
+
export declare function mapCommentToComment(ghComment: GhComment): Comment;
|
|
33
|
+
export declare function mapIssueToWorkItem(ghIssue: GhIssue): WorkItem;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function mapCommentToComment(ghComment) {
|
|
2
|
+
return {
|
|
3
|
+
author: ghComment.author.login,
|
|
4
|
+
date: ghComment.createdAt,
|
|
5
|
+
body: ghComment.body,
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
export function mapIssueToWorkItem(ghIssue) {
|
|
9
|
+
return {
|
|
10
|
+
id: String(ghIssue.number),
|
|
11
|
+
title: ghIssue.title,
|
|
12
|
+
description: ghIssue.body ?? '',
|
|
13
|
+
status: ghIssue.state === 'OPEN' ? 'open' : 'closed',
|
|
14
|
+
type: 'issue',
|
|
15
|
+
assignee: ghIssue.assignees[0]?.login ?? '',
|
|
16
|
+
labels: ghIssue.labels.map((l) => l.name),
|
|
17
|
+
iteration: ghIssue.milestone?.title ?? '',
|
|
18
|
+
priority: 'medium',
|
|
19
|
+
created: ghIssue.createdAt,
|
|
20
|
+
updated: ghIssue.updatedAt,
|
|
21
|
+
parent: null,
|
|
22
|
+
dependsOn: [],
|
|
23
|
+
comments: (ghIssue.comments ?? []).map(mapCommentToComment),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=mappers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mappers.js","sourceRoot":"","sources":["../../../src/backends/github/mappers.ts"],"names":[],"mappings":"AA2BA,MAAM,UAAU,mBAAmB,CAAC,SAAoB;IACtD,OAAO;QACL,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,KAAK;QAC9B,IAAI,EAAE,SAAS,CAAC,SAAS;QACzB,IAAI,EAAE,SAAS,CAAC,IAAI;KACrB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,OAAgB;IACjD,OAAO;QACL,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;QAC1B,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,WAAW,EAAE,OAAO,CAAC,IAAI,IAAI,EAAE;QAC/B,MAAM,EAAE,OAAO,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ;QACpD,IAAI,EAAE,OAAO;QACb,QAAQ,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,EAAE;QAC3C,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QACzC,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,KAAK,IAAI,EAAE;QACzC,QAAQ,EAAE,QAAQ;QAClB,OAAO,EAAE,OAAO,CAAC,SAAS;QAC1B,OAAO,EAAE,OAAO,CAAC,SAAS;QAC1B,MAAM,EAAE,IAAI;QACZ,SAAS,EAAE,EAAE;QACb,QAAQ,EAAE,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,mBAAmB,CAAC;KAC5D,CAAC;AACJ,CAAC"}
|