@bretwardjames/ghp-cli 0.1.4 → 0.1.7
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 +63 -11
- package/dist/branch-linker.d.ts +32 -21
- package/dist/branch-linker.d.ts.map +1 -1
- package/dist/branch-linker.js +94 -64
- package/dist/branch-linker.js.map +1 -1
- package/dist/commands/config.d.ts +7 -1
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +164 -23
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/link-branch.d.ts.map +1 -1
- package/dist/commands/link-branch.js +7 -3
- package/dist/commands/link-branch.js.map +1 -1
- package/dist/commands/open.d.ts.map +1 -1
- package/dist/commands/open.js +12 -2
- package/dist/commands/open.js.map +1 -1
- package/dist/commands/plan.d.ts.map +1 -1
- package/dist/commands/plan.js +8 -4
- package/dist/commands/plan.js.map +1 -1
- package/dist/commands/pr.d.ts.map +1 -1
- package/dist/commands/pr.js +9 -6
- package/dist/commands/pr.js.map +1 -1
- package/dist/commands/start.d.ts.map +1 -1
- package/dist/commands/start.js +8 -3
- package/dist/commands/start.js.map +1 -1
- package/dist/commands/switch.d.ts.map +1 -1
- package/dist/commands/switch.js +20 -17
- package/dist/commands/switch.js.map +1 -1
- package/dist/commands/sync.js +2 -2
- package/dist/commands/sync.js.map +1 -1
- package/dist/commands/unlink-branch.d.ts.map +1 -1
- package/dist/commands/unlink-branch.js +14 -3
- package/dist/commands/unlink-branch.js.map +1 -1
- package/dist/commands/work.d.ts.map +1 -1
- package/dist/commands/work.js +28 -1
- package/dist/commands/work.js.map +1 -1
- package/dist/config.d.ts +77 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +396 -20
- package/dist/config.js.map +1 -1
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/table.d.ts +1 -1
- package/dist/table.d.ts.map +1 -1
- package/dist/table.js +8 -1
- package/dist/table.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,10 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
GitHub Projects CLI - manage project boards from your terminal.
|
|
4
4
|
|
|
5
|
+
Part of the [GHP Tools](https://github.com/bretwardjames/ghp-core) suite. Works alongside the [VS Code/Cursor extension](https://github.com/bretwardjames/vscode-gh-projects) for a complete GitHub Projects workflow.
|
|
6
|
+
|
|
5
7
|
## Installation
|
|
6
8
|
|
|
9
|
+
**Quick install (CLI + VS Code extension):**
|
|
10
|
+
```bash
|
|
11
|
+
curl -fsSL https://raw.githubusercontent.com/bretwardjames/ghp-core/main/install.sh | bash
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
**CLI only:**
|
|
7
15
|
```bash
|
|
8
|
-
npm install -g ghp-cli
|
|
16
|
+
npm install -g @bretwardjames/ghp-cli
|
|
9
17
|
```
|
|
10
18
|
|
|
11
19
|
## Quick Start
|
|
@@ -33,6 +41,7 @@ ghp add "Fix login bug"
|
|
|
33
41
|
ghp work
|
|
34
42
|
ghp work --status "In Progress"
|
|
35
43
|
ghp work --hide-done
|
|
44
|
+
ghp work --group priority # Group by field
|
|
36
45
|
|
|
37
46
|
# Project board
|
|
38
47
|
ghp plan
|
|
@@ -54,6 +63,9 @@ ghp add --list-templates # List available templates
|
|
|
54
63
|
ghp open 123
|
|
55
64
|
ghp open 123 --browser # Open in browser
|
|
56
65
|
|
|
66
|
+
# Edit issue description
|
|
67
|
+
ghp edit 123 # Opens in $EDITOR
|
|
68
|
+
|
|
57
69
|
# Add comment
|
|
58
70
|
ghp comment 123 -m "Fixed in latest commit"
|
|
59
71
|
ghp comment 123 # Opens editor
|
|
@@ -87,24 +99,45 @@ ghp link-branch 123
|
|
|
87
99
|
|
|
88
100
|
# Unlink branch
|
|
89
101
|
ghp unlink-branch 123
|
|
102
|
+
|
|
103
|
+
# Sync active label with current branch
|
|
104
|
+
ghp sync
|
|
90
105
|
```
|
|
91
106
|
|
|
92
|
-
|
|
107
|
+
## Configuration
|
|
108
|
+
|
|
109
|
+
ghp-cli uses a layered config system (like VS Code):
|
|
110
|
+
|
|
111
|
+
| Layer | Path | Purpose |
|
|
112
|
+
|-------|------|---------|
|
|
113
|
+
| **Workspace** | `.ghp/config.json` | Team settings (commit this) |
|
|
114
|
+
| **User** | `~/.config/ghp-cli/config.json` | Personal overrides |
|
|
115
|
+
|
|
116
|
+
Settings merge: defaults → workspace → user
|
|
117
|
+
|
|
118
|
+
### Config Commands
|
|
93
119
|
|
|
94
120
|
```bash
|
|
95
|
-
# View config
|
|
96
|
-
ghp config --
|
|
121
|
+
# View merged config with sources
|
|
122
|
+
ghp config --show
|
|
97
123
|
|
|
98
|
-
# Edit config
|
|
99
|
-
ghp config
|
|
124
|
+
# Edit user config (opens $EDITOR)
|
|
125
|
+
ghp config
|
|
100
126
|
|
|
101
|
-
#
|
|
102
|
-
ghp config
|
|
103
|
-
```
|
|
127
|
+
# Edit workspace config (shared with team)
|
|
128
|
+
ghp config -w
|
|
104
129
|
|
|
105
|
-
|
|
130
|
+
# Get/set individual values
|
|
131
|
+
ghp config mainBranch
|
|
132
|
+
ghp config mainBranch develop
|
|
133
|
+
ghp config mainBranch develop -w # Set in workspace config
|
|
134
|
+
|
|
135
|
+
# Sync from VS Code/Cursor settings
|
|
136
|
+
ghp config sync
|
|
137
|
+
ghp config sync -w # Sync to workspace config
|
|
138
|
+
```
|
|
106
139
|
|
|
107
|
-
Config
|
|
140
|
+
### Config File Format
|
|
108
141
|
|
|
109
142
|
```json
|
|
110
143
|
{
|
|
@@ -157,6 +190,20 @@ Filter by any field:
|
|
|
157
190
|
--slice Size=Small # Custom project fields
|
|
158
191
|
```
|
|
159
192
|
|
|
193
|
+
### Syncing with VS Code
|
|
194
|
+
|
|
195
|
+
If you use the VS Code extension, you can sync shared settings:
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
ghp config sync
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
This imports `ghProjects.*` settings from your Cursor or VS Code configuration:
|
|
202
|
+
- `ghProjects.mainBranch` → `mainBranch`
|
|
203
|
+
- `ghProjects.branchNamePattern` → `branchPattern`
|
|
204
|
+
- `ghProjects.startWorkingStatus` → `startWorkingStatus`
|
|
205
|
+
- `ghProjects.prMergedStatus` → `doneStatus`
|
|
206
|
+
|
|
160
207
|
## Issue Templates
|
|
161
208
|
|
|
162
209
|
Place templates in `.github/ISSUE_TEMPLATE/` in your repo. When creating issues, you can:
|
|
@@ -171,6 +218,11 @@ Place templates in `.github/ISSUE_TEMPLATE/` in your repo. When creating issues,
|
|
|
171
218
|
- GitHub account with Projects access
|
|
172
219
|
- `gh` CLI recommended (for auth token)
|
|
173
220
|
|
|
221
|
+
## Related
|
|
222
|
+
|
|
223
|
+
- [ghp-core](https://github.com/bretwardjames/ghp-core) - Shared library and install script
|
|
224
|
+
- [vscode-gh-projects](https://github.com/bretwardjames/vscode-gh-projects) - VS Code/Cursor extension
|
|
225
|
+
|
|
174
226
|
## License
|
|
175
227
|
|
|
176
228
|
MIT
|
package/dist/branch-linker.d.ts
CHANGED
|
@@ -1,37 +1,48 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* CLI-specific branch linker
|
|
2
|
+
* CLI-specific branch linker that uses GitHub issue bodies for storage.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Links are stored as hidden HTML comments in issue bodies:
|
|
5
|
+
* <!-- ghp-branch: feature/my-branch -->
|
|
6
|
+
*
|
|
7
|
+
* This allows links to be shared between CLI and VSCode extension.
|
|
6
8
|
*/
|
|
7
|
-
import {
|
|
9
|
+
import { type RepoInfo } from '@bretwardjames/ghp-core';
|
|
8
10
|
/**
|
|
9
|
-
*
|
|
11
|
+
* Link a branch to an issue by storing the link in the issue body.
|
|
10
12
|
*/
|
|
11
|
-
declare
|
|
12
|
-
declare const linker: BranchLinker;
|
|
13
|
+
export declare function linkBranch(repo: RepoInfo, issueNumber: number, branch: string): Promise<boolean>;
|
|
13
14
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
15
|
+
* Remove the branch link from an issue.
|
|
16
|
+
* @returns true if a link was removed, false if no link existed
|
|
16
17
|
*/
|
|
17
|
-
export declare function
|
|
18
|
+
export declare function unlinkBranch(repo: RepoInfo, issueNumber: number): Promise<boolean>;
|
|
18
19
|
/**
|
|
19
|
-
*
|
|
20
|
-
* @returns true if a link was removed, false if no link existed
|
|
20
|
+
* Get the branch linked to an issue by reading the issue body.
|
|
21
21
|
*/
|
|
22
|
-
export declare function
|
|
22
|
+
export declare function getBranchForIssue(repo: RepoInfo, issueNumber: number): Promise<string | null>;
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
24
|
+
* Extract issue number from a branch name.
|
|
25
|
+
* Supports common patterns:
|
|
26
|
+
* - user/123-feature-name
|
|
27
|
+
* - feature/123-something
|
|
28
|
+
* - 123-fix-bug
|
|
29
|
+
* - fix-123-something
|
|
25
30
|
*/
|
|
26
|
-
export declare function
|
|
31
|
+
export declare function extractIssueNumberFromBranch(branchName: string): number | null;
|
|
27
32
|
/**
|
|
28
|
-
*
|
|
33
|
+
* Result of finding an issue for a branch.
|
|
29
34
|
*/
|
|
30
|
-
export
|
|
35
|
+
export interface BranchIssueLink {
|
|
36
|
+
issueNumber: number;
|
|
37
|
+
issueTitle: string;
|
|
38
|
+
branch: string;
|
|
39
|
+
}
|
|
31
40
|
/**
|
|
32
|
-
*
|
|
41
|
+
* Find the issue linked to a branch.
|
|
42
|
+
* This first extracts the issue number from the branch name pattern,
|
|
43
|
+
* then verifies the link exists in the issue body.
|
|
44
|
+
*
|
|
45
|
+
* @returns Issue info if found and verified, null otherwise
|
|
33
46
|
*/
|
|
34
|
-
export declare function
|
|
35
|
-
export type { BranchLink } from '@bretwardjames/ghp-core';
|
|
36
|
-
export { linker, fileStorageAdapter };
|
|
47
|
+
export declare function getIssueForBranch(repo: RepoInfo, branchName: string): Promise<BranchIssueLink | null>;
|
|
37
48
|
//# sourceMappingURL=branch-linker.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"branch-linker.d.ts","sourceRoot":"","sources":["../src/branch-linker.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"branch-linker.d.ts","sourceRoot":"","sources":["../src/branch-linker.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAIH,KAAK,QAAQ,EAChB,MAAM,yBAAyB,CAAC;AAGjC;;GAEG;AACH,wBAAsB,UAAU,CAC5B,IAAI,EAAE,QAAQ,EACd,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,GACf,OAAO,CAAC,OAAO,CAAC,CAUlB;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAC9B,IAAI,EAAE,QAAQ,EACd,WAAW,EAAE,MAAM,GACpB,OAAO,CAAC,OAAO,CAAC,CAgBlB;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACnC,IAAI,EAAE,QAAQ,EACd,WAAW,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAQxB;AAED;;;;;;;GAOG;AACH,wBAAgB,4BAA4B,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAgB9E;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACnC,IAAI,EAAE,QAAQ,EACd,UAAU,EAAE,MAAM,GACnB,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CA4BjC"}
|
package/dist/branch-linker.js
CHANGED
|
@@ -1,86 +1,116 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* CLI-specific branch linker
|
|
2
|
+
* CLI-specific branch linker that uses GitHub issue bodies for storage.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import { homedir } from 'os';
|
|
9
|
-
import { join } from 'path';
|
|
10
|
-
import { BranchLinker, } from '@bretwardjames/ghp-core';
|
|
11
|
-
const DATA_DIR = join(homedir(), '.config', 'ghp-cli');
|
|
12
|
-
const LINKS_FILE = join(DATA_DIR, 'branch-links.json');
|
|
13
|
-
/**
|
|
14
|
-
* File-based storage adapter for CLI usage
|
|
4
|
+
* Links are stored as hidden HTML comments in issue bodies:
|
|
5
|
+
* <!-- ghp-branch: feature/my-branch -->
|
|
6
|
+
*
|
|
7
|
+
* This allows links to be shared between CLI and VSCode extension.
|
|
15
8
|
*/
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
try {
|
|
19
|
-
if (existsSync(LINKS_FILE)) {
|
|
20
|
-
const data = readFileSync(LINKS_FILE, 'utf-8');
|
|
21
|
-
return JSON.parse(data);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
catch {
|
|
25
|
-
// Ignore errors, return empty array
|
|
26
|
-
}
|
|
27
|
-
return [];
|
|
28
|
-
},
|
|
29
|
-
save(links) {
|
|
30
|
-
if (!existsSync(DATA_DIR)) {
|
|
31
|
-
mkdirSync(DATA_DIR, { recursive: true });
|
|
32
|
-
}
|
|
33
|
-
writeFileSync(LINKS_FILE, JSON.stringify(links, null, 2));
|
|
34
|
-
},
|
|
35
|
-
};
|
|
36
|
-
// Create singleton linker instance
|
|
37
|
-
const linker = new BranchLinker(fileStorageAdapter);
|
|
9
|
+
import { parseBranchLink, setBranchLinkInBody, removeBranchLinkFromBody, } from '@bretwardjames/ghp-core';
|
|
10
|
+
import { api } from './github-api.js';
|
|
38
11
|
/**
|
|
39
|
-
*
|
|
40
|
-
* Backwards-compatible function signature for existing CLI code.
|
|
12
|
+
* Link a branch to an issue by storing the link in the issue body.
|
|
41
13
|
*/
|
|
42
|
-
export function linkBranch(
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
14
|
+
export async function linkBranch(repo, issueNumber, branch) {
|
|
15
|
+
try {
|
|
16
|
+
const details = await api.getIssueDetails(repo, issueNumber);
|
|
17
|
+
const currentBody = details?.body ?? '';
|
|
18
|
+
const newBody = setBranchLinkInBody(currentBody, branch);
|
|
19
|
+
return await api.updateIssueBody(repo, issueNumber, newBody);
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
console.error('Failed to link branch:', error);
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
46
25
|
}
|
|
47
26
|
/**
|
|
48
|
-
* Remove the link
|
|
27
|
+
* Remove the branch link from an issue.
|
|
49
28
|
* @returns true if a link was removed, false if no link existed
|
|
50
29
|
*/
|
|
51
|
-
export function unlinkBranch(repo, issueNumber) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
30
|
+
export async function unlinkBranch(repo, issueNumber) {
|
|
31
|
+
try {
|
|
32
|
+
const details = await api.getIssueDetails(repo, issueNumber);
|
|
33
|
+
const currentBody = details?.body ?? '';
|
|
34
|
+
// Check if there's a link to remove
|
|
35
|
+
if (!parseBranchLink(currentBody)) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
const newBody = removeBranchLinkFromBody(currentBody);
|
|
39
|
+
return await api.updateIssueBody(repo, issueNumber, newBody);
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
console.error('Failed to unlink branch:', error);
|
|
57
43
|
return false;
|
|
58
44
|
}
|
|
59
|
-
adapter.save(filtered);
|
|
60
|
-
return true;
|
|
61
45
|
}
|
|
62
46
|
/**
|
|
63
|
-
* Get the branch linked to an issue.
|
|
47
|
+
* Get the branch linked to an issue by reading the issue body.
|
|
64
48
|
*/
|
|
65
|
-
export function getBranchForIssue(repo, issueNumber) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
49
|
+
export async function getBranchForIssue(repo, issueNumber) {
|
|
50
|
+
try {
|
|
51
|
+
const details = await api.getIssueDetails(repo, issueNumber);
|
|
52
|
+
return parseBranchLink(details?.body);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
console.error('Failed to get branch for issue:', error);
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
69
58
|
}
|
|
70
59
|
/**
|
|
71
|
-
*
|
|
60
|
+
* Extract issue number from a branch name.
|
|
61
|
+
* Supports common patterns:
|
|
62
|
+
* - user/123-feature-name
|
|
63
|
+
* - feature/123-something
|
|
64
|
+
* - 123-fix-bug
|
|
65
|
+
* - fix-123-something
|
|
72
66
|
*/
|
|
73
|
-
export function
|
|
74
|
-
const
|
|
75
|
-
|
|
67
|
+
export function extractIssueNumberFromBranch(branchName) {
|
|
68
|
+
const patterns = [
|
|
69
|
+
/\/(\d+)-/, // user/123-title
|
|
70
|
+
/^(\d+)-/, // 123-title
|
|
71
|
+
/-(\d+)-/, // feature-123-title
|
|
72
|
+
/[/#](\d+)$/, // ends with #123 or /123
|
|
73
|
+
];
|
|
74
|
+
for (const pattern of patterns) {
|
|
75
|
+
const match = branchName.match(pattern);
|
|
76
|
+
if (match) {
|
|
77
|
+
return parseInt(match[1], 10);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return null;
|
|
76
81
|
}
|
|
77
82
|
/**
|
|
78
|
-
*
|
|
83
|
+
* Find the issue linked to a branch.
|
|
84
|
+
* This first extracts the issue number from the branch name pattern,
|
|
85
|
+
* then verifies the link exists in the issue body.
|
|
86
|
+
*
|
|
87
|
+
* @returns Issue info if found and verified, null otherwise
|
|
79
88
|
*/
|
|
80
|
-
export function
|
|
81
|
-
|
|
82
|
-
|
|
89
|
+
export async function getIssueForBranch(repo, branchName) {
|
|
90
|
+
// First, try to extract issue number from branch name
|
|
91
|
+
const issueNumber = extractIssueNumberFromBranch(branchName);
|
|
92
|
+
if (!issueNumber) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
// Get issue details to verify and get title
|
|
97
|
+
const details = await api.getIssueDetails(repo, issueNumber);
|
|
98
|
+
if (!details) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
// Check if this issue has a link to this branch
|
|
102
|
+
const linkedBranch = parseBranchLink(details.body);
|
|
103
|
+
// If not explicitly linked, still return info based on branch naming convention
|
|
104
|
+
// This allows workflows that rely on branch naming patterns to work
|
|
105
|
+
return {
|
|
106
|
+
issueNumber,
|
|
107
|
+
issueTitle: details.title,
|
|
108
|
+
branch: linkedBranch === branchName ? branchName : branchName,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
console.error('Failed to verify issue for branch:', error);
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
83
115
|
}
|
|
84
|
-
// Also export the linker instance and adapter for advanced usage
|
|
85
|
-
export { linker, fileStorageAdapter };
|
|
86
116
|
//# sourceMappingURL=branch-linker.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"branch-linker.js","sourceRoot":"","sources":["../src/branch-linker.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"branch-linker.js","sourceRoot":"","sources":["../src/branch-linker.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
+
export declare function configSyncCommand(options?: {
|
|
2
|
+
workspace?: boolean;
|
|
3
|
+
user?: boolean;
|
|
4
|
+
}): Promise<void>;
|
|
1
5
|
export declare function configCommand(key?: string, value?: string, options?: {
|
|
2
|
-
|
|
6
|
+
show?: boolean;
|
|
3
7
|
edit?: boolean;
|
|
8
|
+
workspace?: boolean;
|
|
9
|
+
user?: boolean;
|
|
4
10
|
}): Promise<void>;
|
|
5
11
|
//# sourceMappingURL=config.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AA4FA,wBAAsB,iBAAiB,CACnC,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAO,GACtD,OAAO,CAAC,IAAI,CAAC,CAiDf;AAED,wBAAsB,aAAa,CAC/B,GAAG,CAAC,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,MAAM,EACd,OAAO,GAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAO,GACtF,OAAO,CAAC,IAAI,CAAC,CA+Gf"}
|
package/dist/commands/config.js
CHANGED
|
@@ -1,10 +1,47 @@
|
|
|
1
1
|
import { existsSync, writeFileSync, mkdirSync } from 'fs';
|
|
2
2
|
import { spawn } from 'child_process';
|
|
3
3
|
import { dirname } from 'path';
|
|
4
|
-
import
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import { getConfig, setConfig, getFullConfigWithSources, CONFIG_KEYS, getConfigPath, getWorkspaceConfigPath, getUserConfigPath, syncFromVSCode, getVSCodeSettingsPaths } from '../config.js';
|
|
6
|
+
const SOURCE_LABELS = {
|
|
7
|
+
'default': chalk.dim('(default)'),
|
|
8
|
+
'workspace': chalk.cyan('(workspace)'),
|
|
9
|
+
'user': chalk.green('(user)'),
|
|
10
|
+
};
|
|
5
11
|
function isValidKey(key) {
|
|
6
12
|
return CONFIG_KEYS.includes(key);
|
|
7
13
|
}
|
|
14
|
+
function resolveScope(options) {
|
|
15
|
+
if (options.workspace)
|
|
16
|
+
return 'workspace';
|
|
17
|
+
return 'user';
|
|
18
|
+
}
|
|
19
|
+
function formatShortcut(shortcut, indent = '') {
|
|
20
|
+
if (shortcut.status) {
|
|
21
|
+
const statusVal = Array.isArray(shortcut.status)
|
|
22
|
+
? shortcut.status.join(', ')
|
|
23
|
+
: shortcut.status;
|
|
24
|
+
console.log(`${indent}status: ${statusVal}`);
|
|
25
|
+
}
|
|
26
|
+
if (shortcut.mine)
|
|
27
|
+
console.log(`${indent}mine: true`);
|
|
28
|
+
if (shortcut.unassigned)
|
|
29
|
+
console.log(`${indent}unassigned: true`);
|
|
30
|
+
if (shortcut.project)
|
|
31
|
+
console.log(`${indent}project: ${shortcut.project}`);
|
|
32
|
+
if (shortcut.sort)
|
|
33
|
+
console.log(`${indent}sort: ${shortcut.sort}`);
|
|
34
|
+
if (shortcut.slice && shortcut.slice.length > 0) {
|
|
35
|
+
console.log(`${indent}slice: ${shortcut.slice.join(', ')}`);
|
|
36
|
+
}
|
|
37
|
+
// Show other properties that might exist (like list, all, group)
|
|
38
|
+
const knownKeys = ['status', 'mine', 'unassigned', 'project', 'sort', 'slice'];
|
|
39
|
+
for (const [key, value] of Object.entries(shortcut)) {
|
|
40
|
+
if (!knownKeys.includes(key) && value !== undefined) {
|
|
41
|
+
console.log(`${indent}${key}: ${value}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
8
45
|
const CONFIG_TEMPLATE = `{
|
|
9
46
|
"_comment": "ghp-cli configuration - see https://github.com/your/ghp-cli for docs",
|
|
10
47
|
|
|
@@ -49,34 +86,119 @@ function openInEditor(filePath) {
|
|
|
49
86
|
console.log(`Config file is at: ${filePath}`);
|
|
50
87
|
});
|
|
51
88
|
}
|
|
52
|
-
export async function
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
89
|
+
export async function configSyncCommand(options = {}) {
|
|
90
|
+
const scope = resolveScope(options);
|
|
91
|
+
console.log(chalk.bold('Syncing from VS Code/Cursor settings...'));
|
|
92
|
+
console.log();
|
|
93
|
+
const paths = getVSCodeSettingsPaths();
|
|
94
|
+
console.log(chalk.dim('Looking for settings in:'));
|
|
95
|
+
console.log(chalk.dim(` Workspace: ${paths.workspace || '(not in git repo)'}`));
|
|
96
|
+
console.log(chalk.dim(` Cursor: ${paths.cursorUser}`));
|
|
97
|
+
console.log(chalk.dim(` VS Code: ${paths.codeUser}`));
|
|
98
|
+
console.log();
|
|
99
|
+
const result = syncFromVSCode(scope);
|
|
100
|
+
// Report any parse errors
|
|
101
|
+
if (result.errors.length > 0) {
|
|
102
|
+
for (const error of result.errors) {
|
|
103
|
+
console.log(chalk.yellow(`Warning: ${error}`));
|
|
60
104
|
}
|
|
61
|
-
|
|
62
|
-
return;
|
|
105
|
+
console.log();
|
|
63
106
|
}
|
|
64
|
-
if (
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
console.log(
|
|
107
|
+
if (result.synced.length === 0) {
|
|
108
|
+
if (result.skipped.length > 0) {
|
|
109
|
+
console.log(chalk.yellow('No syncable settings found.'));
|
|
110
|
+
console.log(chalk.dim(`Found ${result.skipped.length} extension-only setting(s): ${result.skipped.join(', ')}`));
|
|
111
|
+
console.log();
|
|
112
|
+
console.log(chalk.dim('Syncable settings: mainBranch, branchNamePattern, startWorkingStatus, prMergedStatus'));
|
|
70
113
|
}
|
|
71
|
-
|
|
114
|
+
else {
|
|
115
|
+
console.log(chalk.yellow('No ghProjects.* settings found to sync.'));
|
|
116
|
+
console.log(chalk.dim('Make sure you have settings like ghProjects.mainBranch in your editor.'));
|
|
117
|
+
}
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
console.log(chalk.green(`Synced ${result.synced.length} setting(s) from ${result.editor}:`));
|
|
121
|
+
for (const { key, value, source } of result.synced) {
|
|
122
|
+
const sourceLabel = source === 'workspace' ? chalk.cyan('(workspace)') : chalk.green('(user)');
|
|
123
|
+
console.log(` ${key}: ${value} ${sourceLabel}`);
|
|
124
|
+
}
|
|
125
|
+
if (result.skipped.length > 0) {
|
|
126
|
+
console.log();
|
|
127
|
+
console.log(chalk.dim(`Skipped ${result.skipped.length} extension-only setting(s): ${result.skipped.join(', ')}`));
|
|
128
|
+
}
|
|
129
|
+
console.log();
|
|
130
|
+
console.log(chalk.dim(`Saved to ${scope} config: ${getConfigPath(scope)}`));
|
|
131
|
+
}
|
|
132
|
+
export async function configCommand(key, value, options = {}) {
|
|
133
|
+
const scope = resolveScope(options);
|
|
134
|
+
// Handle 'sync' as first argument
|
|
135
|
+
if (key === 'sync') {
|
|
136
|
+
await configSyncCommand(options);
|
|
72
137
|
return;
|
|
73
138
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
139
|
+
// --show: display merged config from all sources with source indicators
|
|
140
|
+
if (options.show) {
|
|
141
|
+
const fullConfig = getFullConfigWithSources();
|
|
142
|
+
console.log('\n' + chalk.bold('Settings:'));
|
|
143
|
+
console.log('─'.repeat(60));
|
|
144
|
+
for (const [key, { value, source }] of Object.entries(fullConfig.settings)) {
|
|
145
|
+
const sourceLabel = SOURCE_LABELS[source];
|
|
146
|
+
console.log(` ${key}: ${value || chalk.dim('(not set)')} ${sourceLabel}`);
|
|
147
|
+
}
|
|
148
|
+
console.log('\n' + chalk.bold('Defaults:'));
|
|
149
|
+
console.log('─'.repeat(60));
|
|
150
|
+
// Plan defaults
|
|
151
|
+
const planDefaults = fullConfig.defaults.plan;
|
|
152
|
+
const planSourceLabel = SOURCE_LABELS[planDefaults.source];
|
|
153
|
+
if (Object.keys(planDefaults.value).length > 0) {
|
|
154
|
+
console.log(` ${chalk.cyan('plan')} ${planSourceLabel}`);
|
|
155
|
+
formatShortcut(planDefaults.value, ' ');
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
console.log(` ${chalk.cyan('plan')}: ${chalk.dim('(none)')} ${planSourceLabel}`);
|
|
159
|
+
}
|
|
160
|
+
// AddIssue defaults
|
|
161
|
+
const addIssueDefaults = fullConfig.defaults.addIssue;
|
|
162
|
+
const addIssueSourceLabel = SOURCE_LABELS[addIssueDefaults.source];
|
|
163
|
+
if (Object.keys(addIssueDefaults.value).length > 0) {
|
|
164
|
+
console.log(` ${chalk.cyan('addIssue')} ${addIssueSourceLabel}`);
|
|
165
|
+
for (const [k, v] of Object.entries(addIssueDefaults.value)) {
|
|
166
|
+
if (v)
|
|
167
|
+
console.log(` ${k}: ${v}`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
console.log(` ${chalk.cyan('addIssue')}: ${chalk.dim('(none)')} ${addIssueSourceLabel}`);
|
|
172
|
+
}
|
|
173
|
+
console.log('\n' + chalk.bold('Shortcuts:'));
|
|
174
|
+
console.log('─'.repeat(60));
|
|
175
|
+
const shortcuts = fullConfig.shortcuts;
|
|
176
|
+
if (Object.keys(shortcuts).length > 0) {
|
|
177
|
+
for (const [name, { value, source }] of Object.entries(shortcuts)) {
|
|
178
|
+
const sourceLabel = SOURCE_LABELS[source];
|
|
179
|
+
console.log(` ${chalk.cyan(name)} ${sourceLabel}`);
|
|
180
|
+
formatShortcut(value, ' ');
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
console.log(` ${chalk.dim('(none)')}`);
|
|
185
|
+
}
|
|
186
|
+
console.log('\n' + chalk.bold('Config files:'));
|
|
187
|
+
console.log('─'.repeat(60));
|
|
188
|
+
console.log(` User: ${getUserConfigPath()}`);
|
|
189
|
+
const workspacePath = getWorkspaceConfigPath();
|
|
190
|
+
console.log(` Workspace: ${workspacePath || '(not in a git repository)'}`);
|
|
191
|
+
console.log('\nUse "ghp config" to edit user config');
|
|
192
|
+
console.log('Use "ghp config -w" to edit workspace config (shared with team)');
|
|
77
193
|
return;
|
|
78
194
|
}
|
|
195
|
+
// Get/set specific key
|
|
79
196
|
if (key && !value) {
|
|
197
|
+
if (!isValidKey(key)) {
|
|
198
|
+
console.log(`Unknown config key: "${key}"`);
|
|
199
|
+
console.log('Available keys:', CONFIG_KEYS.join(', '));
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
80
202
|
const val = getConfig(key);
|
|
81
203
|
if (val !== undefined) {
|
|
82
204
|
console.log(val);
|
|
@@ -87,8 +209,27 @@ export async function configCommand(key, value, options = {}) {
|
|
|
87
209
|
return;
|
|
88
210
|
}
|
|
89
211
|
if (key && value) {
|
|
90
|
-
|
|
91
|
-
|
|
212
|
+
if (!isValidKey(key)) {
|
|
213
|
+
console.log(`Unknown config key: "${key}"`);
|
|
214
|
+
console.log('Available keys:', CONFIG_KEYS.join(', '));
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
setConfig(key, value, scope);
|
|
218
|
+
console.log(`Set ${key} = ${value} (in ${scope} config)`);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
// Default: open editor (when no key/value provided)
|
|
222
|
+
const configPath = getConfigPath(scope);
|
|
223
|
+
if (scope === 'workspace' && configPath === '(not in a git repository)') {
|
|
224
|
+
console.error('Error: Not in a git repository. Cannot edit workspace config.');
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
// Create config with template if it doesn't exist
|
|
228
|
+
if (!existsSync(configPath)) {
|
|
229
|
+
mkdirSync(dirname(configPath), { recursive: true });
|
|
230
|
+
writeFileSync(configPath, CONFIG_TEMPLATE);
|
|
231
|
+
console.log(`Created config file: ${configPath}`);
|
|
92
232
|
}
|
|
233
|
+
openInEditor(configPath);
|
|
93
234
|
}
|
|
94
235
|
//# sourceMappingURL=config.js.map
|