@bretwardjames/ghp-core 0.1.2 → 0.1.4
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 +123 -0
- package/dist/index.cjs +50 -117
- package/dist/index.d.cts +30 -116
- package/dist/index.d.ts +30 -116
- package/dist/index.js +46 -115
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# GHP Tools
|
|
2
|
+
|
|
3
|
+
A suite of tools for managing GitHub Projects from your terminal and editor.
|
|
4
|
+
|
|
5
|
+
## What's Included
|
|
6
|
+
|
|
7
|
+
| Package | Description |
|
|
8
|
+
|---------|-------------|
|
|
9
|
+
| **[@bretwardjames/ghp-cli](https://github.com/bretwardjames/ghp-cli)** | Command-line interface for GitHub Projects |
|
|
10
|
+
| **[vscode-gh-projects](https://github.com/bretwardjames/vscode-gh-projects)** | VS Code / Cursor extension with visual boards |
|
|
11
|
+
| **@bretwardjames/ghp-core** | Shared library (this package) |
|
|
12
|
+
|
|
13
|
+
Both the CLI and extension share the same underlying library and are designed to work together.
|
|
14
|
+
|
|
15
|
+
## Quick Install
|
|
16
|
+
|
|
17
|
+
Install both the CLI and VS Code/Cursor extension with a single command:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
curl -fsSL https://raw.githubusercontent.com/bretwardjames/ghp-core/main/install.sh | bash
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
This will:
|
|
24
|
+
1. Install the `ghp` CLI globally via npm
|
|
25
|
+
2. Install the VS Code/Cursor extension from the latest release
|
|
26
|
+
|
|
27
|
+
### Manual Installation
|
|
28
|
+
|
|
29
|
+
**CLI only:**
|
|
30
|
+
```bash
|
|
31
|
+
npm install -g @bretwardjames/ghp-cli
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Extension only:**
|
|
35
|
+
Download the `.vsix` from [releases](https://github.com/bretwardjames/vscode-gh-projects/releases) and install:
|
|
36
|
+
```bash
|
|
37
|
+
code --install-extension gh-projects-*.vsix
|
|
38
|
+
# or for Cursor:
|
|
39
|
+
cursor --install-extension gh-projects-*.vsix
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Getting Started
|
|
43
|
+
|
|
44
|
+
1. Authenticate with GitHub:
|
|
45
|
+
```bash
|
|
46
|
+
ghp auth
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
2. View your assigned work:
|
|
50
|
+
```bash
|
|
51
|
+
ghp work
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
3. Open VS Code/Cursor and find the GitHub Projects panel in the sidebar
|
|
55
|
+
|
|
56
|
+
## Features
|
|
57
|
+
|
|
58
|
+
### Shared Across Both Tools
|
|
59
|
+
|
|
60
|
+
- **Branch Linking** - Link branches to issues, track which issues have active work
|
|
61
|
+
- **Workflow Automation** - "Start Working" creates branches and updates status
|
|
62
|
+
- **Project Board Views** - See your boards exactly as configured on GitHub
|
|
63
|
+
- **Issue Templates** - Create issues using your repo's templates
|
|
64
|
+
|
|
65
|
+
### CLI-Specific
|
|
66
|
+
|
|
67
|
+
- **Shortcuts** - Define named filter combinations (`ghp plan bugs`)
|
|
68
|
+
- **Slice Filters** - Filter by any field (`--slice Priority=High`)
|
|
69
|
+
- **Workspace Config** - Share settings with your team via `.ghp/config.json`
|
|
70
|
+
- **Simple List Output** - Integration with fzf, rofi, and other pickers
|
|
71
|
+
|
|
72
|
+
### Extension-Specific
|
|
73
|
+
|
|
74
|
+
- **Drag and Drop** - Move issues between columns visually
|
|
75
|
+
- **Planning Board** - Full-screen kanban view
|
|
76
|
+
- **Multi-Select** - Bulk operations on multiple items
|
|
77
|
+
- **Real-Time Sync** - Stay in sync with GitHub
|
|
78
|
+
|
|
79
|
+
## Configuration
|
|
80
|
+
|
|
81
|
+
Both tools share the same configuration concepts. The CLI uses JSON files, the extension uses VS Code settings.
|
|
82
|
+
|
|
83
|
+
### CLI Configuration (ghp-cli)
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
# View all settings with their sources
|
|
87
|
+
ghp config --show
|
|
88
|
+
|
|
89
|
+
# Edit user config (opens $EDITOR)
|
|
90
|
+
ghp config
|
|
91
|
+
|
|
92
|
+
# Edit workspace config (shared with team)
|
|
93
|
+
ghp config -w
|
|
94
|
+
|
|
95
|
+
# Set individual value
|
|
96
|
+
ghp config mainBranch develop
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Config files:**
|
|
100
|
+
- User: `~/.config/ghp-cli/config.json` (personal overrides)
|
|
101
|
+
- Workspace: `.ghp/config.json` (committed, shared with team)
|
|
102
|
+
|
|
103
|
+
Merge order: defaults -> workspace -> user
|
|
104
|
+
|
|
105
|
+
### Extension Configuration (VS Code)
|
|
106
|
+
|
|
107
|
+
Settings are in VS Code's settings UI under "GitHub Projects", or in your workspace `.vscode/settings.json`.
|
|
108
|
+
|
|
109
|
+
## Links
|
|
110
|
+
|
|
111
|
+
- [ghp-cli Documentation](https://github.com/bretwardjames/ghp-cli)
|
|
112
|
+
- [VS Code Extension Documentation](https://github.com/bretwardjames/vscode-gh-projects)
|
|
113
|
+
- [Report Issues](https://github.com/bretwardjames/ghp-core/issues)
|
|
114
|
+
|
|
115
|
+
## Requirements
|
|
116
|
+
|
|
117
|
+
- Node.js >= 18
|
|
118
|
+
- GitHub account with Projects access
|
|
119
|
+
- VS Code 1.85+ or Cursor (for extension)
|
|
120
|
+
|
|
121
|
+
## License
|
|
122
|
+
|
|
123
|
+
MIT
|
package/dist/index.cjs
CHANGED
|
@@ -30,7 +30,6 @@ __export(index_exports, {
|
|
|
30
30
|
buildRepoUrl: () => buildRepoUrl,
|
|
31
31
|
checkoutBranch: () => checkoutBranch,
|
|
32
32
|
createBranch: () => createBranch,
|
|
33
|
-
createInMemoryAdapter: () => createInMemoryAdapter,
|
|
34
33
|
detectRepository: () => detectRepository,
|
|
35
34
|
fetchOrigin: () => fetchOrigin,
|
|
36
35
|
generateBranchName: () => generateBranchName,
|
|
@@ -41,11 +40,14 @@ __export(index_exports, {
|
|
|
41
40
|
getRepositoryRoot: () => getRepositoryRoot,
|
|
42
41
|
hasUncommittedChanges: () => hasUncommittedChanges,
|
|
43
42
|
isGitRepository: () => isGitRepository,
|
|
43
|
+
parseBranchLink: () => parseBranchLink,
|
|
44
44
|
parseGitHubUrl: () => parseGitHubUrl,
|
|
45
45
|
parseIssueUrl: () => parseIssueUrl,
|
|
46
46
|
pullLatest: () => pullLatest,
|
|
47
47
|
queries: () => queries_exports,
|
|
48
|
-
|
|
48
|
+
removeBranchLinkFromBody: () => removeBranchLinkFromBody,
|
|
49
|
+
sanitizeForBranchName: () => sanitizeForBranchName,
|
|
50
|
+
setBranchLinkInBody: () => setBranchLinkInBody
|
|
49
51
|
});
|
|
50
52
|
module.exports = __toCommonJS(index_exports);
|
|
51
53
|
|
|
@@ -1005,138 +1007,67 @@ var GitHubAPI = class {
|
|
|
1005
1007
|
};
|
|
1006
1008
|
|
|
1007
1009
|
// src/branch-linker.ts
|
|
1008
|
-
var
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
return
|
|
1010
|
+
var BRANCH_LINK_PATTERN = /<!--\s*ghp-branch:\s*(.+?)\s*-->/;
|
|
1011
|
+
function parseBranchLink(body) {
|
|
1012
|
+
if (!body) return null;
|
|
1013
|
+
const match = body.match(BRANCH_LINK_PATTERN);
|
|
1014
|
+
return match ? match[1].trim() : null;
|
|
1015
|
+
}
|
|
1016
|
+
function setBranchLinkInBody(body, branch) {
|
|
1017
|
+
const currentBody = body || "";
|
|
1018
|
+
const tag = `<!-- ghp-branch: ${branch} -->`;
|
|
1019
|
+
if (BRANCH_LINK_PATTERN.test(currentBody)) {
|
|
1020
|
+
return currentBody.replace(BRANCH_LINK_PATTERN, tag);
|
|
1021
|
+
} else {
|
|
1022
|
+
return currentBody.trim() + "\n\n" + tag;
|
|
1019
1023
|
}
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1024
|
+
}
|
|
1025
|
+
function removeBranchLinkFromBody(body) {
|
|
1026
|
+
if (!body) return "";
|
|
1027
|
+
return body.replace(BRANCH_LINK_PATTERN, "").trim();
|
|
1028
|
+
}
|
|
1029
|
+
var BranchLinker = class {
|
|
1030
|
+
api;
|
|
1031
|
+
constructor(api) {
|
|
1032
|
+
this.api = api;
|
|
1028
1033
|
}
|
|
1029
1034
|
/**
|
|
1030
1035
|
* Create a link between a branch and an issue.
|
|
1031
|
-
*
|
|
1036
|
+
* Stores the link as a hidden comment in the issue body.
|
|
1032
1037
|
*/
|
|
1033
|
-
async link(
|
|
1034
|
-
const
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
);
|
|
1038
|
-
filtered.push({
|
|
1039
|
-
branch,
|
|
1040
|
-
issueNumber,
|
|
1041
|
-
issueTitle,
|
|
1042
|
-
itemId,
|
|
1043
|
-
repo,
|
|
1044
|
-
linkedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1045
|
-
});
|
|
1046
|
-
await this.saveLinks(filtered);
|
|
1038
|
+
async link(repo, issueNumber, branch) {
|
|
1039
|
+
const details = await this.api.getIssueDetails(repo, issueNumber);
|
|
1040
|
+
if (!details) return false;
|
|
1041
|
+
const newBody = setBranchLinkInBody(details.body, branch);
|
|
1042
|
+
return this.api.updateIssueBody(repo, issueNumber, newBody);
|
|
1047
1043
|
}
|
|
1048
1044
|
/**
|
|
1049
|
-
* Remove the link
|
|
1050
|
-
* @returns true if a link was removed, false if no link existed
|
|
1045
|
+
* Remove the branch link from an issue.
|
|
1051
1046
|
*/
|
|
1052
1047
|
async unlink(repo, issueNumber) {
|
|
1053
|
-
const
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
);
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
}
|
|
1060
|
-
await this.saveLinks(filtered);
|
|
1061
|
-
return true;
|
|
1062
|
-
}
|
|
1063
|
-
/**
|
|
1064
|
-
* Remove the link for a branch.
|
|
1065
|
-
* @returns true if a link was removed, false if no link existed
|
|
1066
|
-
*/
|
|
1067
|
-
async unlinkBranch(repo, branch) {
|
|
1068
|
-
const links = await this.loadLinks();
|
|
1069
|
-
const filtered = links.filter(
|
|
1070
|
-
(l) => !(l.repo === repo && l.branch === branch)
|
|
1071
|
-
);
|
|
1072
|
-
if (filtered.length === links.length) {
|
|
1073
|
-
return false;
|
|
1074
|
-
}
|
|
1075
|
-
await this.saveLinks(filtered);
|
|
1076
|
-
return true;
|
|
1048
|
+
const details = await this.api.getIssueDetails(repo, issueNumber);
|
|
1049
|
+
if (!details) return false;
|
|
1050
|
+
const currentBranch = parseBranchLink(details.body);
|
|
1051
|
+
if (!currentBranch) return false;
|
|
1052
|
+
const newBody = removeBranchLinkFromBody(details.body);
|
|
1053
|
+
return this.api.updateIssueBody(repo, issueNumber, newBody);
|
|
1077
1054
|
}
|
|
1078
1055
|
/**
|
|
1079
1056
|
* Get the branch linked to an issue.
|
|
1080
1057
|
*/
|
|
1081
1058
|
async getBranchForIssue(repo, issueNumber) {
|
|
1082
|
-
const
|
|
1083
|
-
|
|
1084
|
-
return
|
|
1085
|
-
}
|
|
1086
|
-
/**
|
|
1087
|
-
* Get the full link info for a branch.
|
|
1088
|
-
*/
|
|
1089
|
-
async getLinkForBranch(repo, branch) {
|
|
1090
|
-
const links = await this.loadLinks();
|
|
1091
|
-
return links.find((l) => l.repo === repo && l.branch === branch) || null;
|
|
1092
|
-
}
|
|
1093
|
-
/**
|
|
1094
|
-
* Get the full link info for an issue.
|
|
1095
|
-
*/
|
|
1096
|
-
async getLinkForIssue(repo, issueNumber) {
|
|
1097
|
-
const links = await this.loadLinks();
|
|
1098
|
-
return links.find((l) => l.repo === repo && l.issueNumber === issueNumber) || null;
|
|
1099
|
-
}
|
|
1100
|
-
/**
|
|
1101
|
-
* Get all links for a repository.
|
|
1102
|
-
*/
|
|
1103
|
-
async getLinksForRepo(repo) {
|
|
1104
|
-
const links = await this.loadLinks();
|
|
1105
|
-
return links.filter((l) => l.repo === repo);
|
|
1106
|
-
}
|
|
1107
|
-
/**
|
|
1108
|
-
* Get all links.
|
|
1109
|
-
*/
|
|
1110
|
-
async getAllLinks() {
|
|
1111
|
-
return this.loadLinks();
|
|
1059
|
+
const details = await this.api.getIssueDetails(repo, issueNumber);
|
|
1060
|
+
if (!details) return null;
|
|
1061
|
+
return parseBranchLink(details.body);
|
|
1112
1062
|
}
|
|
1113
1063
|
/**
|
|
1114
|
-
* Check if
|
|
1064
|
+
* Check if an issue has a branch link.
|
|
1115
1065
|
*/
|
|
1116
|
-
async
|
|
1117
|
-
const
|
|
1118
|
-
return
|
|
1119
|
-
}
|
|
1120
|
-
/**
|
|
1121
|
-
* Check if an issue has a link.
|
|
1122
|
-
*/
|
|
1123
|
-
async hasLinkForIssue(repo, issueNumber) {
|
|
1124
|
-
const link = await this.getLinkForIssue(repo, issueNumber);
|
|
1125
|
-
return link !== null;
|
|
1066
|
+
async hasLink(repo, issueNumber) {
|
|
1067
|
+
const branch = await this.getBranchForIssue(repo, issueNumber);
|
|
1068
|
+
return branch !== null;
|
|
1126
1069
|
}
|
|
1127
1070
|
};
|
|
1128
|
-
function createInMemoryAdapter() {
|
|
1129
|
-
const adapter = {
|
|
1130
|
-
links: [],
|
|
1131
|
-
load() {
|
|
1132
|
-
return [...this.links];
|
|
1133
|
-
},
|
|
1134
|
-
save(links) {
|
|
1135
|
-
this.links = [...links];
|
|
1136
|
-
}
|
|
1137
|
-
};
|
|
1138
|
-
return adapter;
|
|
1139
|
-
}
|
|
1140
1071
|
|
|
1141
1072
|
// src/git-utils.ts
|
|
1142
1073
|
var import_child_process = require("child_process");
|
|
@@ -1324,7 +1255,6 @@ async function getDefaultBranch(options = {}) {
|
|
|
1324
1255
|
buildRepoUrl,
|
|
1325
1256
|
checkoutBranch,
|
|
1326
1257
|
createBranch,
|
|
1327
|
-
createInMemoryAdapter,
|
|
1328
1258
|
detectRepository,
|
|
1329
1259
|
fetchOrigin,
|
|
1330
1260
|
generateBranchName,
|
|
@@ -1335,9 +1265,12 @@ async function getDefaultBranch(options = {}) {
|
|
|
1335
1265
|
getRepositoryRoot,
|
|
1336
1266
|
hasUncommittedChanges,
|
|
1337
1267
|
isGitRepository,
|
|
1268
|
+
parseBranchLink,
|
|
1338
1269
|
parseGitHubUrl,
|
|
1339
1270
|
parseIssueUrl,
|
|
1340
1271
|
pullLatest,
|
|
1341
1272
|
queries,
|
|
1342
|
-
|
|
1273
|
+
removeBranchLinkFromBody,
|
|
1274
|
+
sanitizeForBranchName,
|
|
1275
|
+
setBranchLinkInBody
|
|
1343
1276
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -29,25 +29,6 @@ interface AuthError extends Error {
|
|
|
29
29
|
requiredScopes?: string[];
|
|
30
30
|
ssoUrl?: string;
|
|
31
31
|
}
|
|
32
|
-
/**
|
|
33
|
-
* Interface for persisting branch-issue links.
|
|
34
|
-
* Implement this for different storage backends (file system, VSCode state, etc.)
|
|
35
|
-
*/
|
|
36
|
-
interface StorageAdapter {
|
|
37
|
-
load(): BranchLink[] | Promise<BranchLink[]>;
|
|
38
|
-
save(links: BranchLink[]): void | Promise<void>;
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* A link between a git branch and a GitHub issue/item
|
|
42
|
-
*/
|
|
43
|
-
interface BranchLink {
|
|
44
|
-
branch: string;
|
|
45
|
-
issueNumber: number;
|
|
46
|
-
issueTitle: string;
|
|
47
|
-
itemId: string;
|
|
48
|
-
repo: string;
|
|
49
|
-
linkedAt: string;
|
|
50
|
-
}
|
|
51
32
|
/**
|
|
52
33
|
* Options for git operations
|
|
53
34
|
*/
|
|
@@ -527,120 +508,53 @@ declare class GitHubAPI {
|
|
|
527
508
|
}
|
|
528
509
|
|
|
529
510
|
/**
|
|
530
|
-
* Branch-issue linking
|
|
531
|
-
*
|
|
532
|
-
* The BranchLinker class manages associations between git branches and GitHub issues.
|
|
533
|
-
* Storage is abstracted via the StorageAdapter interface, allowing different backends:
|
|
534
|
-
* - File system (for CLI)
|
|
535
|
-
* - VSCode workspaceState (for extensions)
|
|
536
|
-
* - In-memory (for testing)
|
|
537
|
-
*
|
|
538
|
-
* @example CLI usage with file storage:
|
|
539
|
-
* ```typescript
|
|
540
|
-
* import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
541
|
-
* import { homedir } from 'os';
|
|
542
|
-
* import { join } from 'path';
|
|
511
|
+
* Branch-issue linking stored directly in GitHub issue bodies.
|
|
543
512
|
*
|
|
544
|
-
*
|
|
545
|
-
*
|
|
513
|
+
* Links are stored as hidden HTML comments in the issue body:
|
|
514
|
+
* <!-- ghp-branch: feature/my-branch -->
|
|
546
515
|
*
|
|
547
|
-
*
|
|
548
|
-
*
|
|
549
|
-
* if (existsSync(LINKS_FILE)) {
|
|
550
|
-
* return JSON.parse(readFileSync(LINKS_FILE, 'utf-8'));
|
|
551
|
-
* }
|
|
552
|
-
* return [];
|
|
553
|
-
* },
|
|
554
|
-
* save(links) {
|
|
555
|
-
* if (!existsSync(DATA_DIR)) {
|
|
556
|
-
* mkdirSync(DATA_DIR, { recursive: true });
|
|
557
|
-
* }
|
|
558
|
-
* writeFileSync(LINKS_FILE, JSON.stringify(links, null, 2));
|
|
559
|
-
* }
|
|
560
|
-
* };
|
|
561
|
-
*
|
|
562
|
-
* const linker = new BranchLinker(fileAdapter);
|
|
563
|
-
* ```
|
|
564
|
-
*
|
|
565
|
-
* @example VSCode usage with workspaceState:
|
|
566
|
-
* ```typescript
|
|
567
|
-
* const vscodeAdapter: StorageAdapter = {
|
|
568
|
-
* load() {
|
|
569
|
-
* return context.workspaceState.get<BranchLink[]>('branchLinks', []);
|
|
570
|
-
* },
|
|
571
|
-
* save(links) {
|
|
572
|
-
* context.workspaceState.update('branchLinks', links);
|
|
573
|
-
* }
|
|
574
|
-
* };
|
|
575
|
-
*
|
|
576
|
-
* const linker = new BranchLinker(vscodeAdapter);
|
|
577
|
-
* ```
|
|
516
|
+
* This allows branch links to be shared across all consumers (CLI, VSCode, etc.)
|
|
517
|
+
* since they're stored on GitHub itself.
|
|
578
518
|
*/
|
|
579
519
|
|
|
580
520
|
/**
|
|
581
|
-
*
|
|
521
|
+
* Parse the linked branch from an issue body.
|
|
522
|
+
*/
|
|
523
|
+
declare function parseBranchLink(body: string | null | undefined): string | null;
|
|
524
|
+
/**
|
|
525
|
+
* Set or update the branch link in an issue body.
|
|
526
|
+
* Returns the updated body string.
|
|
527
|
+
*/
|
|
528
|
+
declare function setBranchLinkInBody(body: string | null | undefined, branch: string): string;
|
|
529
|
+
/**
|
|
530
|
+
* Remove the branch link from an issue body.
|
|
531
|
+
* Returns the updated body string.
|
|
532
|
+
*/
|
|
533
|
+
declare function removeBranchLinkFromBody(body: string | null | undefined): string;
|
|
534
|
+
/**
|
|
535
|
+
* Manages branch-issue links stored in GitHub issue bodies.
|
|
582
536
|
*/
|
|
583
537
|
declare class BranchLinker {
|
|
584
|
-
private
|
|
585
|
-
constructor(
|
|
586
|
-
/**
|
|
587
|
-
* Load links from storage (handles both sync and async adapters)
|
|
588
|
-
*/
|
|
589
|
-
private loadLinks;
|
|
590
|
-
/**
|
|
591
|
-
* Save links to storage (handles both sync and async adapters)
|
|
592
|
-
*/
|
|
593
|
-
private saveLinks;
|
|
538
|
+
private api;
|
|
539
|
+
constructor(api: GitHubAPI);
|
|
594
540
|
/**
|
|
595
541
|
* Create a link between a branch and an issue.
|
|
596
|
-
*
|
|
542
|
+
* Stores the link as a hidden comment in the issue body.
|
|
597
543
|
*/
|
|
598
|
-
link(
|
|
544
|
+
link(repo: RepoInfo, issueNumber: number, branch: string): Promise<boolean>;
|
|
599
545
|
/**
|
|
600
|
-
* Remove the link
|
|
601
|
-
* @returns true if a link was removed, false if no link existed
|
|
546
|
+
* Remove the branch link from an issue.
|
|
602
547
|
*/
|
|
603
|
-
unlink(repo:
|
|
604
|
-
/**
|
|
605
|
-
* Remove the link for a branch.
|
|
606
|
-
* @returns true if a link was removed, false if no link existed
|
|
607
|
-
*/
|
|
608
|
-
unlinkBranch(repo: string, branch: string): Promise<boolean>;
|
|
548
|
+
unlink(repo: RepoInfo, issueNumber: number): Promise<boolean>;
|
|
609
549
|
/**
|
|
610
550
|
* Get the branch linked to an issue.
|
|
611
551
|
*/
|
|
612
|
-
getBranchForIssue(repo:
|
|
552
|
+
getBranchForIssue(repo: RepoInfo, issueNumber: number): Promise<string | null>;
|
|
613
553
|
/**
|
|
614
|
-
*
|
|
554
|
+
* Check if an issue has a branch link.
|
|
615
555
|
*/
|
|
616
|
-
|
|
617
|
-
/**
|
|
618
|
-
* Get the full link info for an issue.
|
|
619
|
-
*/
|
|
620
|
-
getLinkForIssue(repo: string, issueNumber: number): Promise<BranchLink | null>;
|
|
621
|
-
/**
|
|
622
|
-
* Get all links for a repository.
|
|
623
|
-
*/
|
|
624
|
-
getLinksForRepo(repo: string): Promise<BranchLink[]>;
|
|
625
|
-
/**
|
|
626
|
-
* Get all links.
|
|
627
|
-
*/
|
|
628
|
-
getAllLinks(): Promise<BranchLink[]>;
|
|
629
|
-
/**
|
|
630
|
-
* Check if a branch has a link.
|
|
631
|
-
*/
|
|
632
|
-
hasLinkForBranch(repo: string, branch: string): Promise<boolean>;
|
|
633
|
-
/**
|
|
634
|
-
* Check if an issue has a link.
|
|
635
|
-
*/
|
|
636
|
-
hasLinkForIssue(repo: string, issueNumber: number): Promise<boolean>;
|
|
556
|
+
hasLink(repo: RepoInfo, issueNumber: number): Promise<boolean>;
|
|
637
557
|
}
|
|
638
|
-
/**
|
|
639
|
-
* Create an in-memory storage adapter for testing.
|
|
640
|
-
*/
|
|
641
|
-
declare function createInMemoryAdapter(): StorageAdapter & {
|
|
642
|
-
links: BranchLink[];
|
|
643
|
-
};
|
|
644
558
|
|
|
645
559
|
/**
|
|
646
560
|
* Git utility functions for working with local repositories.
|
|
@@ -903,4 +817,4 @@ declare namespace queries {
|
|
|
903
817
|
export { queries_ADD_COMMENT_MUTATION as ADD_COMMENT_MUTATION, queries_ADD_LABELS_MUTATION as ADD_LABELS_MUTATION, queries_ADD_TO_PROJECT_MUTATION as ADD_TO_PROJECT_MUTATION, queries_COLLABORATORS_QUERY as COLLABORATORS_QUERY, queries_CREATE_ISSUE_MUTATION as CREATE_ISSUE_MUTATION, queries_ISSUES_WITH_LABEL_QUERY as ISSUES_WITH_LABEL_QUERY, queries_ISSUE_AND_LABEL_QUERY as ISSUE_AND_LABEL_QUERY, queries_ISSUE_DETAILS_QUERY as ISSUE_DETAILS_QUERY, queries_ISSUE_FOR_UPDATE_QUERY as ISSUE_FOR_UPDATE_QUERY, queries_ISSUE_NODE_ID_QUERY as ISSUE_NODE_ID_QUERY, queries_ISSUE_TYPES_QUERY as ISSUE_TYPES_QUERY, queries_LABEL_EXISTS_QUERY as LABEL_EXISTS_QUERY, queries_PROJECT_FIELDS_QUERY as PROJECT_FIELDS_QUERY, queries_PROJECT_ITEMS_QUERY as PROJECT_ITEMS_QUERY, queries_PROJECT_VIEWS_QUERY as PROJECT_VIEWS_QUERY, queries_RECENT_ISSUES_QUERY as RECENT_ISSUES_QUERY, queries_REMOVE_LABELS_MUTATION as REMOVE_LABELS_MUTATION, queries_REPOSITORY_ID_QUERY as REPOSITORY_ID_QUERY, queries_REPOSITORY_PROJECTS_QUERY as REPOSITORY_PROJECTS_QUERY, queries_UPDATE_ISSUE_BODY_MUTATION as UPDATE_ISSUE_BODY_MUTATION, queries_UPDATE_ISSUE_MUTATION as UPDATE_ISSUE_MUTATION, queries_UPDATE_ISSUE_TYPE_MUTATION as UPDATE_ISSUE_TYPE_MUTATION, queries_UPDATE_ITEM_FIELD_MUTATION as UPDATE_ITEM_FIELD_MUTATION, queries_UPDATE_ITEM_STATUS_MUTATION as UPDATE_ITEM_STATUS_MUTATION, queries_VIEWER_QUERY as VIEWER_QUERY };
|
|
904
818
|
}
|
|
905
819
|
|
|
906
|
-
export { type AssigneeInfo, type AuthError,
|
|
820
|
+
export { type AssigneeInfo, type AuthError, BranchLinker, type Collaborator, type DateFieldValue, type FieldInfo, type FieldValue, type FieldValueConnection, GitHubAPI, type GitHubAPIOptions, type GitOptions, type IssueDetails, type IssueReference, type IterationFieldValue, type LabelInfo, type NumberFieldValue, type Project, type ProjectConfig, type ProjectItem, type ProjectItemContent, type ProjectItemsQueryResponse, type ProjectV2, type ProjectV2Field, type ProjectV2Item, type ProjectV2View, type ProjectWithViews, type ProjectsQueryResponse, type RepoInfo, type SingleSelectFieldValue, type StatusField, type TextFieldValue, type TokenProvider, branchExists, buildIssueUrl, buildOrgProjectUrl, buildProjectUrl, buildPullRequestUrl, buildRepoUrl, checkoutBranch, createBranch, detectRepository, fetchOrigin, generateBranchName, getCommitsAhead, getCommitsBehind, getCurrentBranch, getDefaultBranch, getRepositoryRoot, hasUncommittedChanges, isGitRepository, parseBranchLink, parseGitHubUrl, parseIssueUrl, pullLatest, queries, removeBranchLinkFromBody, sanitizeForBranchName, setBranchLinkInBody };
|
package/dist/index.d.ts
CHANGED
|
@@ -29,25 +29,6 @@ interface AuthError extends Error {
|
|
|
29
29
|
requiredScopes?: string[];
|
|
30
30
|
ssoUrl?: string;
|
|
31
31
|
}
|
|
32
|
-
/**
|
|
33
|
-
* Interface for persisting branch-issue links.
|
|
34
|
-
* Implement this for different storage backends (file system, VSCode state, etc.)
|
|
35
|
-
*/
|
|
36
|
-
interface StorageAdapter {
|
|
37
|
-
load(): BranchLink[] | Promise<BranchLink[]>;
|
|
38
|
-
save(links: BranchLink[]): void | Promise<void>;
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* A link between a git branch and a GitHub issue/item
|
|
42
|
-
*/
|
|
43
|
-
interface BranchLink {
|
|
44
|
-
branch: string;
|
|
45
|
-
issueNumber: number;
|
|
46
|
-
issueTitle: string;
|
|
47
|
-
itemId: string;
|
|
48
|
-
repo: string;
|
|
49
|
-
linkedAt: string;
|
|
50
|
-
}
|
|
51
32
|
/**
|
|
52
33
|
* Options for git operations
|
|
53
34
|
*/
|
|
@@ -527,120 +508,53 @@ declare class GitHubAPI {
|
|
|
527
508
|
}
|
|
528
509
|
|
|
529
510
|
/**
|
|
530
|
-
* Branch-issue linking
|
|
531
|
-
*
|
|
532
|
-
* The BranchLinker class manages associations between git branches and GitHub issues.
|
|
533
|
-
* Storage is abstracted via the StorageAdapter interface, allowing different backends:
|
|
534
|
-
* - File system (for CLI)
|
|
535
|
-
* - VSCode workspaceState (for extensions)
|
|
536
|
-
* - In-memory (for testing)
|
|
537
|
-
*
|
|
538
|
-
* @example CLI usage with file storage:
|
|
539
|
-
* ```typescript
|
|
540
|
-
* import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
541
|
-
* import { homedir } from 'os';
|
|
542
|
-
* import { join } from 'path';
|
|
511
|
+
* Branch-issue linking stored directly in GitHub issue bodies.
|
|
543
512
|
*
|
|
544
|
-
*
|
|
545
|
-
*
|
|
513
|
+
* Links are stored as hidden HTML comments in the issue body:
|
|
514
|
+
* <!-- ghp-branch: feature/my-branch -->
|
|
546
515
|
*
|
|
547
|
-
*
|
|
548
|
-
*
|
|
549
|
-
* if (existsSync(LINKS_FILE)) {
|
|
550
|
-
* return JSON.parse(readFileSync(LINKS_FILE, 'utf-8'));
|
|
551
|
-
* }
|
|
552
|
-
* return [];
|
|
553
|
-
* },
|
|
554
|
-
* save(links) {
|
|
555
|
-
* if (!existsSync(DATA_DIR)) {
|
|
556
|
-
* mkdirSync(DATA_DIR, { recursive: true });
|
|
557
|
-
* }
|
|
558
|
-
* writeFileSync(LINKS_FILE, JSON.stringify(links, null, 2));
|
|
559
|
-
* }
|
|
560
|
-
* };
|
|
561
|
-
*
|
|
562
|
-
* const linker = new BranchLinker(fileAdapter);
|
|
563
|
-
* ```
|
|
564
|
-
*
|
|
565
|
-
* @example VSCode usage with workspaceState:
|
|
566
|
-
* ```typescript
|
|
567
|
-
* const vscodeAdapter: StorageAdapter = {
|
|
568
|
-
* load() {
|
|
569
|
-
* return context.workspaceState.get<BranchLink[]>('branchLinks', []);
|
|
570
|
-
* },
|
|
571
|
-
* save(links) {
|
|
572
|
-
* context.workspaceState.update('branchLinks', links);
|
|
573
|
-
* }
|
|
574
|
-
* };
|
|
575
|
-
*
|
|
576
|
-
* const linker = new BranchLinker(vscodeAdapter);
|
|
577
|
-
* ```
|
|
516
|
+
* This allows branch links to be shared across all consumers (CLI, VSCode, etc.)
|
|
517
|
+
* since they're stored on GitHub itself.
|
|
578
518
|
*/
|
|
579
519
|
|
|
580
520
|
/**
|
|
581
|
-
*
|
|
521
|
+
* Parse the linked branch from an issue body.
|
|
522
|
+
*/
|
|
523
|
+
declare function parseBranchLink(body: string | null | undefined): string | null;
|
|
524
|
+
/**
|
|
525
|
+
* Set or update the branch link in an issue body.
|
|
526
|
+
* Returns the updated body string.
|
|
527
|
+
*/
|
|
528
|
+
declare function setBranchLinkInBody(body: string | null | undefined, branch: string): string;
|
|
529
|
+
/**
|
|
530
|
+
* Remove the branch link from an issue body.
|
|
531
|
+
* Returns the updated body string.
|
|
532
|
+
*/
|
|
533
|
+
declare function removeBranchLinkFromBody(body: string | null | undefined): string;
|
|
534
|
+
/**
|
|
535
|
+
* Manages branch-issue links stored in GitHub issue bodies.
|
|
582
536
|
*/
|
|
583
537
|
declare class BranchLinker {
|
|
584
|
-
private
|
|
585
|
-
constructor(
|
|
586
|
-
/**
|
|
587
|
-
* Load links from storage (handles both sync and async adapters)
|
|
588
|
-
*/
|
|
589
|
-
private loadLinks;
|
|
590
|
-
/**
|
|
591
|
-
* Save links to storage (handles both sync and async adapters)
|
|
592
|
-
*/
|
|
593
|
-
private saveLinks;
|
|
538
|
+
private api;
|
|
539
|
+
constructor(api: GitHubAPI);
|
|
594
540
|
/**
|
|
595
541
|
* Create a link between a branch and an issue.
|
|
596
|
-
*
|
|
542
|
+
* Stores the link as a hidden comment in the issue body.
|
|
597
543
|
*/
|
|
598
|
-
link(
|
|
544
|
+
link(repo: RepoInfo, issueNumber: number, branch: string): Promise<boolean>;
|
|
599
545
|
/**
|
|
600
|
-
* Remove the link
|
|
601
|
-
* @returns true if a link was removed, false if no link existed
|
|
546
|
+
* Remove the branch link from an issue.
|
|
602
547
|
*/
|
|
603
|
-
unlink(repo:
|
|
604
|
-
/**
|
|
605
|
-
* Remove the link for a branch.
|
|
606
|
-
* @returns true if a link was removed, false if no link existed
|
|
607
|
-
*/
|
|
608
|
-
unlinkBranch(repo: string, branch: string): Promise<boolean>;
|
|
548
|
+
unlink(repo: RepoInfo, issueNumber: number): Promise<boolean>;
|
|
609
549
|
/**
|
|
610
550
|
* Get the branch linked to an issue.
|
|
611
551
|
*/
|
|
612
|
-
getBranchForIssue(repo:
|
|
552
|
+
getBranchForIssue(repo: RepoInfo, issueNumber: number): Promise<string | null>;
|
|
613
553
|
/**
|
|
614
|
-
*
|
|
554
|
+
* Check if an issue has a branch link.
|
|
615
555
|
*/
|
|
616
|
-
|
|
617
|
-
/**
|
|
618
|
-
* Get the full link info for an issue.
|
|
619
|
-
*/
|
|
620
|
-
getLinkForIssue(repo: string, issueNumber: number): Promise<BranchLink | null>;
|
|
621
|
-
/**
|
|
622
|
-
* Get all links for a repository.
|
|
623
|
-
*/
|
|
624
|
-
getLinksForRepo(repo: string): Promise<BranchLink[]>;
|
|
625
|
-
/**
|
|
626
|
-
* Get all links.
|
|
627
|
-
*/
|
|
628
|
-
getAllLinks(): Promise<BranchLink[]>;
|
|
629
|
-
/**
|
|
630
|
-
* Check if a branch has a link.
|
|
631
|
-
*/
|
|
632
|
-
hasLinkForBranch(repo: string, branch: string): Promise<boolean>;
|
|
633
|
-
/**
|
|
634
|
-
* Check if an issue has a link.
|
|
635
|
-
*/
|
|
636
|
-
hasLinkForIssue(repo: string, issueNumber: number): Promise<boolean>;
|
|
556
|
+
hasLink(repo: RepoInfo, issueNumber: number): Promise<boolean>;
|
|
637
557
|
}
|
|
638
|
-
/**
|
|
639
|
-
* Create an in-memory storage adapter for testing.
|
|
640
|
-
*/
|
|
641
|
-
declare function createInMemoryAdapter(): StorageAdapter & {
|
|
642
|
-
links: BranchLink[];
|
|
643
|
-
};
|
|
644
558
|
|
|
645
559
|
/**
|
|
646
560
|
* Git utility functions for working with local repositories.
|
|
@@ -903,4 +817,4 @@ declare namespace queries {
|
|
|
903
817
|
export { queries_ADD_COMMENT_MUTATION as ADD_COMMENT_MUTATION, queries_ADD_LABELS_MUTATION as ADD_LABELS_MUTATION, queries_ADD_TO_PROJECT_MUTATION as ADD_TO_PROJECT_MUTATION, queries_COLLABORATORS_QUERY as COLLABORATORS_QUERY, queries_CREATE_ISSUE_MUTATION as CREATE_ISSUE_MUTATION, queries_ISSUES_WITH_LABEL_QUERY as ISSUES_WITH_LABEL_QUERY, queries_ISSUE_AND_LABEL_QUERY as ISSUE_AND_LABEL_QUERY, queries_ISSUE_DETAILS_QUERY as ISSUE_DETAILS_QUERY, queries_ISSUE_FOR_UPDATE_QUERY as ISSUE_FOR_UPDATE_QUERY, queries_ISSUE_NODE_ID_QUERY as ISSUE_NODE_ID_QUERY, queries_ISSUE_TYPES_QUERY as ISSUE_TYPES_QUERY, queries_LABEL_EXISTS_QUERY as LABEL_EXISTS_QUERY, queries_PROJECT_FIELDS_QUERY as PROJECT_FIELDS_QUERY, queries_PROJECT_ITEMS_QUERY as PROJECT_ITEMS_QUERY, queries_PROJECT_VIEWS_QUERY as PROJECT_VIEWS_QUERY, queries_RECENT_ISSUES_QUERY as RECENT_ISSUES_QUERY, queries_REMOVE_LABELS_MUTATION as REMOVE_LABELS_MUTATION, queries_REPOSITORY_ID_QUERY as REPOSITORY_ID_QUERY, queries_REPOSITORY_PROJECTS_QUERY as REPOSITORY_PROJECTS_QUERY, queries_UPDATE_ISSUE_BODY_MUTATION as UPDATE_ISSUE_BODY_MUTATION, queries_UPDATE_ISSUE_MUTATION as UPDATE_ISSUE_MUTATION, queries_UPDATE_ISSUE_TYPE_MUTATION as UPDATE_ISSUE_TYPE_MUTATION, queries_UPDATE_ITEM_FIELD_MUTATION as UPDATE_ITEM_FIELD_MUTATION, queries_UPDATE_ITEM_STATUS_MUTATION as UPDATE_ITEM_STATUS_MUTATION, queries_VIEWER_QUERY as VIEWER_QUERY };
|
|
904
818
|
}
|
|
905
819
|
|
|
906
|
-
export { type AssigneeInfo, type AuthError,
|
|
820
|
+
export { type AssigneeInfo, type AuthError, BranchLinker, type Collaborator, type DateFieldValue, type FieldInfo, type FieldValue, type FieldValueConnection, GitHubAPI, type GitHubAPIOptions, type GitOptions, type IssueDetails, type IssueReference, type IterationFieldValue, type LabelInfo, type NumberFieldValue, type Project, type ProjectConfig, type ProjectItem, type ProjectItemContent, type ProjectItemsQueryResponse, type ProjectV2, type ProjectV2Field, type ProjectV2Item, type ProjectV2View, type ProjectWithViews, type ProjectsQueryResponse, type RepoInfo, type SingleSelectFieldValue, type StatusField, type TextFieldValue, type TokenProvider, branchExists, buildIssueUrl, buildOrgProjectUrl, buildProjectUrl, buildPullRequestUrl, buildRepoUrl, checkoutBranch, createBranch, detectRepository, fetchOrigin, generateBranchName, getCommitsAhead, getCommitsBehind, getCurrentBranch, getDefaultBranch, getRepositoryRoot, hasUncommittedChanges, isGitRepository, parseBranchLink, parseGitHubUrl, parseIssueUrl, pullLatest, queries, removeBranchLinkFromBody, sanitizeForBranchName, setBranchLinkInBody };
|
package/dist/index.js
CHANGED
|
@@ -960,138 +960,67 @@ var GitHubAPI = class {
|
|
|
960
960
|
};
|
|
961
961
|
|
|
962
962
|
// src/branch-linker.ts
|
|
963
|
-
var
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
return
|
|
963
|
+
var BRANCH_LINK_PATTERN = /<!--\s*ghp-branch:\s*(.+?)\s*-->/;
|
|
964
|
+
function parseBranchLink(body) {
|
|
965
|
+
if (!body) return null;
|
|
966
|
+
const match = body.match(BRANCH_LINK_PATTERN);
|
|
967
|
+
return match ? match[1].trim() : null;
|
|
968
|
+
}
|
|
969
|
+
function setBranchLinkInBody(body, branch) {
|
|
970
|
+
const currentBody = body || "";
|
|
971
|
+
const tag = `<!-- ghp-branch: ${branch} -->`;
|
|
972
|
+
if (BRANCH_LINK_PATTERN.test(currentBody)) {
|
|
973
|
+
return currentBody.replace(BRANCH_LINK_PATTERN, tag);
|
|
974
|
+
} else {
|
|
975
|
+
return currentBody.trim() + "\n\n" + tag;
|
|
974
976
|
}
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
977
|
+
}
|
|
978
|
+
function removeBranchLinkFromBody(body) {
|
|
979
|
+
if (!body) return "";
|
|
980
|
+
return body.replace(BRANCH_LINK_PATTERN, "").trim();
|
|
981
|
+
}
|
|
982
|
+
var BranchLinker = class {
|
|
983
|
+
api;
|
|
984
|
+
constructor(api) {
|
|
985
|
+
this.api = api;
|
|
983
986
|
}
|
|
984
987
|
/**
|
|
985
988
|
* Create a link between a branch and an issue.
|
|
986
|
-
*
|
|
989
|
+
* Stores the link as a hidden comment in the issue body.
|
|
987
990
|
*/
|
|
988
|
-
async link(
|
|
989
|
-
const
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
);
|
|
993
|
-
filtered.push({
|
|
994
|
-
branch,
|
|
995
|
-
issueNumber,
|
|
996
|
-
issueTitle,
|
|
997
|
-
itemId,
|
|
998
|
-
repo,
|
|
999
|
-
linkedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1000
|
-
});
|
|
1001
|
-
await this.saveLinks(filtered);
|
|
991
|
+
async link(repo, issueNumber, branch) {
|
|
992
|
+
const details = await this.api.getIssueDetails(repo, issueNumber);
|
|
993
|
+
if (!details) return false;
|
|
994
|
+
const newBody = setBranchLinkInBody(details.body, branch);
|
|
995
|
+
return this.api.updateIssueBody(repo, issueNumber, newBody);
|
|
1002
996
|
}
|
|
1003
997
|
/**
|
|
1004
|
-
* Remove the link
|
|
1005
|
-
* @returns true if a link was removed, false if no link existed
|
|
998
|
+
* Remove the branch link from an issue.
|
|
1006
999
|
*/
|
|
1007
1000
|
async unlink(repo, issueNumber) {
|
|
1008
|
-
const
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
);
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
}
|
|
1015
|
-
await this.saveLinks(filtered);
|
|
1016
|
-
return true;
|
|
1017
|
-
}
|
|
1018
|
-
/**
|
|
1019
|
-
* Remove the link for a branch.
|
|
1020
|
-
* @returns true if a link was removed, false if no link existed
|
|
1021
|
-
*/
|
|
1022
|
-
async unlinkBranch(repo, branch) {
|
|
1023
|
-
const links = await this.loadLinks();
|
|
1024
|
-
const filtered = links.filter(
|
|
1025
|
-
(l) => !(l.repo === repo && l.branch === branch)
|
|
1026
|
-
);
|
|
1027
|
-
if (filtered.length === links.length) {
|
|
1028
|
-
return false;
|
|
1029
|
-
}
|
|
1030
|
-
await this.saveLinks(filtered);
|
|
1031
|
-
return true;
|
|
1001
|
+
const details = await this.api.getIssueDetails(repo, issueNumber);
|
|
1002
|
+
if (!details) return false;
|
|
1003
|
+
const currentBranch = parseBranchLink(details.body);
|
|
1004
|
+
if (!currentBranch) return false;
|
|
1005
|
+
const newBody = removeBranchLinkFromBody(details.body);
|
|
1006
|
+
return this.api.updateIssueBody(repo, issueNumber, newBody);
|
|
1032
1007
|
}
|
|
1033
1008
|
/**
|
|
1034
1009
|
* Get the branch linked to an issue.
|
|
1035
1010
|
*/
|
|
1036
1011
|
async getBranchForIssue(repo, issueNumber) {
|
|
1037
|
-
const
|
|
1038
|
-
|
|
1039
|
-
return
|
|
1040
|
-
}
|
|
1041
|
-
/**
|
|
1042
|
-
* Get the full link info for a branch.
|
|
1043
|
-
*/
|
|
1044
|
-
async getLinkForBranch(repo, branch) {
|
|
1045
|
-
const links = await this.loadLinks();
|
|
1046
|
-
return links.find((l) => l.repo === repo && l.branch === branch) || null;
|
|
1047
|
-
}
|
|
1048
|
-
/**
|
|
1049
|
-
* Get the full link info for an issue.
|
|
1050
|
-
*/
|
|
1051
|
-
async getLinkForIssue(repo, issueNumber) {
|
|
1052
|
-
const links = await this.loadLinks();
|
|
1053
|
-
return links.find((l) => l.repo === repo && l.issueNumber === issueNumber) || null;
|
|
1054
|
-
}
|
|
1055
|
-
/**
|
|
1056
|
-
* Get all links for a repository.
|
|
1057
|
-
*/
|
|
1058
|
-
async getLinksForRepo(repo) {
|
|
1059
|
-
const links = await this.loadLinks();
|
|
1060
|
-
return links.filter((l) => l.repo === repo);
|
|
1061
|
-
}
|
|
1062
|
-
/**
|
|
1063
|
-
* Get all links.
|
|
1064
|
-
*/
|
|
1065
|
-
async getAllLinks() {
|
|
1066
|
-
return this.loadLinks();
|
|
1012
|
+
const details = await this.api.getIssueDetails(repo, issueNumber);
|
|
1013
|
+
if (!details) return null;
|
|
1014
|
+
return parseBranchLink(details.body);
|
|
1067
1015
|
}
|
|
1068
1016
|
/**
|
|
1069
|
-
* Check if
|
|
1017
|
+
* Check if an issue has a branch link.
|
|
1070
1018
|
*/
|
|
1071
|
-
async
|
|
1072
|
-
const
|
|
1073
|
-
return
|
|
1074
|
-
}
|
|
1075
|
-
/**
|
|
1076
|
-
* Check if an issue has a link.
|
|
1077
|
-
*/
|
|
1078
|
-
async hasLinkForIssue(repo, issueNumber) {
|
|
1079
|
-
const link = await this.getLinkForIssue(repo, issueNumber);
|
|
1080
|
-
return link !== null;
|
|
1019
|
+
async hasLink(repo, issueNumber) {
|
|
1020
|
+
const branch = await this.getBranchForIssue(repo, issueNumber);
|
|
1021
|
+
return branch !== null;
|
|
1081
1022
|
}
|
|
1082
1023
|
};
|
|
1083
|
-
function createInMemoryAdapter() {
|
|
1084
|
-
const adapter = {
|
|
1085
|
-
links: [],
|
|
1086
|
-
load() {
|
|
1087
|
-
return [...this.links];
|
|
1088
|
-
},
|
|
1089
|
-
save(links) {
|
|
1090
|
-
this.links = [...links];
|
|
1091
|
-
}
|
|
1092
|
-
};
|
|
1093
|
-
return adapter;
|
|
1094
|
-
}
|
|
1095
1024
|
|
|
1096
1025
|
// src/git-utils.ts
|
|
1097
1026
|
import { exec } from "child_process";
|
|
@@ -1278,7 +1207,6 @@ export {
|
|
|
1278
1207
|
buildRepoUrl,
|
|
1279
1208
|
checkoutBranch,
|
|
1280
1209
|
createBranch,
|
|
1281
|
-
createInMemoryAdapter,
|
|
1282
1210
|
detectRepository,
|
|
1283
1211
|
fetchOrigin,
|
|
1284
1212
|
generateBranchName,
|
|
@@ -1289,9 +1217,12 @@ export {
|
|
|
1289
1217
|
getRepositoryRoot,
|
|
1290
1218
|
hasUncommittedChanges,
|
|
1291
1219
|
isGitRepository,
|
|
1220
|
+
parseBranchLink,
|
|
1292
1221
|
parseGitHubUrl,
|
|
1293
1222
|
parseIssueUrl,
|
|
1294
1223
|
pullLatest,
|
|
1295
1224
|
queries_exports as queries,
|
|
1296
|
-
|
|
1225
|
+
removeBranchLinkFromBody,
|
|
1226
|
+
sanitizeForBranchName,
|
|
1227
|
+
setBranchLinkInBody
|
|
1297
1228
|
};
|