nugit-cli 0.0.1 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/api-client.js +10 -23
- package/src/github-device-flow.js +1 -1
- package/src/github-oauth-client-id.js +11 -0
- package/src/github-pr-social.js +42 -0
- package/src/github-rest.js +149 -6
- package/src/nugit-config.js +84 -0
- package/src/nugit-stack.js +40 -257
- package/src/nugit.js +104 -647
- package/src/review-hub/review-autoapprove.js +95 -0
- package/src/review-hub/review-hub-back.js +10 -0
- package/src/review-hub/review-hub-ink.js +169 -0
- package/src/review-hub/run-review-hub.js +131 -0
- package/src/services/repo-branches.js +151 -0
- package/src/services/stack-inference.js +90 -0
- package/src/split-view/run-split.js +14 -76
- package/src/split-view/split-ink.js +2 -2
- package/src/stack-infer-from-prs.js +71 -0
- package/src/stack-view/diff-line-map.js +62 -0
- package/src/stack-view/fetch-pr-data.js +104 -4
- package/src/stack-view/infer-chains-to-pick-stacks.js +80 -0
- package/src/stack-view/ink-app.js +3 -421
- package/src/stack-view/loader.js +19 -93
- package/src/stack-view/loading-ink.js +2 -0
- package/src/stack-view/merge-alternate-pick-stacks.js +245 -0
- package/src/stack-view/patch-preview-merge.js +108 -0
- package/src/stack-view/remote-infer-doc.js +76 -0
- package/src/stack-view/repo-picker-back.js +10 -0
- package/src/stack-view/run-stack-view.js +508 -150
- package/src/stack-view/run-view-entry.js +115 -0
- package/src/stack-view/sgr-mouse.js +56 -0
- package/src/stack-view/stack-branch-graph.js +95 -0
- package/src/stack-view/stack-pick-graph.js +93 -0
- package/src/stack-view/stack-pick-ink.js +308 -0
- package/src/stack-view/stack-pick-layout.js +19 -0
- package/src/stack-view/stack-pick-sort.js +188 -0
- package/src/stack-view/stack-picker-graph-pane.js +118 -0
- package/src/stack-view/terminal-fullscreen.js +7 -0
- package/src/stack-view/tree-ascii.js +73 -0
- package/src/stack-view/view-md-plain.js +23 -0
- package/src/stack-view/view-repo-picker-ink.js +293 -0
- package/src/stack-view/view-tui-sequential.js +126 -0
- package/src/tui/pages/home.js +122 -0
- package/src/tui/pages/repo-actions.js +81 -0
- package/src/tui/pages/repo-branches.js +259 -0
- package/src/tui/pages/viewer.js +2129 -0
- package/src/tui/router.js +40 -0
- package/src/tui/run-tui.js +281 -0
- package/src/utilities/loading.js +37 -0
- package/src/utilities/terminal.js +31 -0
- package/src/cli-output.js +0 -228
- package/src/nugit-start.js +0 -211
- package/src/stack-discover.js +0 -284
- package/src/stack-discovery-config.js +0 -91
- package/src/stack-extra-commands.js +0 -353
- package/src/stack-graph.js +0 -214
- package/src/stack-helpers.js +0 -58
- package/src/stack-propagate.js +0 -422
package/package.json
CHANGED
package/src/api-client.js
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
githubListUserRepos,
|
|
8
8
|
githubRestJson,
|
|
9
9
|
githubSearchIssues,
|
|
10
|
+
githubSearchRepositories,
|
|
10
11
|
githubListOpenPulls
|
|
11
12
|
} from "./github-rest.js";
|
|
12
13
|
import {
|
|
@@ -14,6 +15,7 @@ import {
|
|
|
14
15
|
githubDeviceFlowRequestCode
|
|
15
16
|
} from "./github-device-flow.js";
|
|
16
17
|
import { resolveGithubToken } from "./auth-token.js";
|
|
18
|
+
import { resolveGithubOAuthClientId } from "./github-oauth-client-id.js";
|
|
17
19
|
|
|
18
20
|
/** @returns {string} */
|
|
19
21
|
export function getToken() {
|
|
@@ -29,15 +31,10 @@ export function withAuthHeaders(headers = {}) {
|
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
/**
|
|
32
|
-
* Start GitHub OAuth device flow (
|
|
34
|
+
* Start GitHub OAuth device flow (bundled OAuth App client id; override with GITHUB_OAUTH_CLIENT_ID).
|
|
33
35
|
*/
|
|
34
36
|
export async function startDeviceFlow() {
|
|
35
|
-
const clientId =
|
|
36
|
-
if (!clientId) {
|
|
37
|
-
throw new Error(
|
|
38
|
-
"Set GITHUB_OAUTH_CLIENT_ID to your GitHub OAuth App client ID (Settings → Developer settings → OAuth Apps), or use a PAT with NUGIT_USER_TOKEN."
|
|
39
|
-
);
|
|
40
|
-
}
|
|
37
|
+
const clientId = resolveGithubOAuthClientId();
|
|
41
38
|
return githubDeviceFlowRequestCode(clientId, "repo read:user user:email");
|
|
42
39
|
}
|
|
43
40
|
|
|
@@ -47,10 +44,7 @@ export async function startDeviceFlow() {
|
|
|
47
44
|
* @param {number} [intervalSeconds] minimum wait before next poll (from GitHub or prior slow_down)
|
|
48
45
|
*/
|
|
49
46
|
export async function pollDeviceFlow(deviceCode, intervalSeconds = 5) {
|
|
50
|
-
const clientId =
|
|
51
|
-
if (!clientId) {
|
|
52
|
-
throw new Error("Set GITHUB_OAUTH_CLIENT_ID");
|
|
53
|
-
}
|
|
47
|
+
const clientId = resolveGithubOAuthClientId();
|
|
54
48
|
const payload = await githubDeviceFlowPollAccessToken(clientId, deviceCode);
|
|
55
49
|
if (payload.access_token) {
|
|
56
50
|
return { access_token: payload.access_token, token_type: payload.token_type, scope: payload.scope };
|
|
@@ -145,6 +139,11 @@ export async function getRepoMetadata(owner, repo) {
|
|
|
145
139
|
return githubGetRepoMetadata(owner, repo);
|
|
146
140
|
}
|
|
147
141
|
|
|
142
|
+
/** @param {string} query GitHub `q` syntax for /search/repositories */
|
|
143
|
+
export async function searchRepositories(query, perPage = 20, page = 1) {
|
|
144
|
+
return githubSearchRepositories(query, perPage, page);
|
|
145
|
+
}
|
|
146
|
+
|
|
148
147
|
export async function createPullRequest(owner, repo, fields) {
|
|
149
148
|
return githubCreatePullRequest(owner, repo, fields);
|
|
150
149
|
}
|
|
@@ -160,18 +159,6 @@ export function decodeGithubFileContent(item) {
|
|
|
160
159
|
return Buffer.from(item.content.replace(/\s/g, ""), "base64").toString("utf8");
|
|
161
160
|
}
|
|
162
161
|
|
|
163
|
-
export async function fetchRemoteStackJson(repoFullName, ref) {
|
|
164
|
-
const [owner, repo] = repoFullName.split("/");
|
|
165
|
-
if (!owner || !repo) {
|
|
166
|
-
throw new Error("repoFullName must be owner/repo");
|
|
167
|
-
}
|
|
168
|
-
const item = await getGithubContents(owner, repo, ".nugit/stack.json", ref);
|
|
169
|
-
const text = decodeGithubFileContent(item);
|
|
170
|
-
if (!text) {
|
|
171
|
-
throw new Error("Could not read .nugit/stack.json from GitHub");
|
|
172
|
-
}
|
|
173
|
-
return JSON.parse(text);
|
|
174
|
-
}
|
|
175
162
|
|
|
176
163
|
export async function getPull(owner, repo, number) {
|
|
177
164
|
return githubGetPull(owner, repo, number);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* GitHub OAuth device flow (no backend).
|
|
2
|
+
* GitHub OAuth device flow (no backend). Client id from resolveGithubOAuthClientId() (bundled default or GITHUB_OAUTH_CLIENT_ID).
|
|
3
3
|
* @see https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps#device-flow
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default GitHub OAuth App for `nugit auth login` (device flow).
|
|
3
|
+
* Client IDs are public (not secrets). Override with env for a custom app or fork.
|
|
4
|
+
*/
|
|
5
|
+
export const NUGIT_BUNDLED_GITHUB_OAUTH_CLIENT_ID = "Ov23liMBQqJJvRNiO0qm";
|
|
6
|
+
|
|
7
|
+
/** @returns {string} */
|
|
8
|
+
export function resolveGithubOAuthClientId() {
|
|
9
|
+
const fromEnv = process.env.GITHUB_OAUTH_CLIENT_ID?.trim();
|
|
10
|
+
return fromEnv || NUGIT_BUNDLED_GITHUB_OAUTH_CLIENT_ID;
|
|
11
|
+
}
|
package/src/github-pr-social.js
CHANGED
|
@@ -124,3 +124,45 @@ export async function githubListAssignableUsers(owner, repo) {
|
|
|
124
124
|
const r = encodeURIComponent(repo);
|
|
125
125
|
return githubGetAllPages(`/repos/${o}/${r}/assignees`);
|
|
126
126
|
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* All pull reviews (paginated).
|
|
130
|
+
* @param {string} owner
|
|
131
|
+
* @param {string} repo
|
|
132
|
+
* @param {number} pullNumber
|
|
133
|
+
*/
|
|
134
|
+
export async function githubListPullReviewsAll(owner, repo, pullNumber) {
|
|
135
|
+
const o = encodeURIComponent(owner);
|
|
136
|
+
const r = encodeURIComponent(repo);
|
|
137
|
+
const n = encodeURIComponent(String(pullNumber));
|
|
138
|
+
return githubGetAllPages(`/repos/${o}/${r}/pulls/${n}/reviews`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Post a single line-linked review comment on a PR (creates a new review thread).
|
|
143
|
+
*
|
|
144
|
+
* @param {string} owner
|
|
145
|
+
* @param {string} repo
|
|
146
|
+
* @param {number} pullNumber
|
|
147
|
+
* @param {{ body: string, commit_id: string, path: string, line: number, side?: "LEFT" | "RIGHT" }} payload
|
|
148
|
+
*/
|
|
149
|
+
export async function githubPostPullReviewLineComment(owner, repo, pullNumber, payload) {
|
|
150
|
+
const o = encodeURIComponent(owner);
|
|
151
|
+
const r = encodeURIComponent(repo);
|
|
152
|
+
const n = encodeURIComponent(String(pullNumber));
|
|
153
|
+
return githubRestJson("POST", `/repos/${o}/${r}/pulls/${n}/comments`, payload);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Submit a PR review (approve, comment, request changes).
|
|
158
|
+
* @param {string} owner
|
|
159
|
+
* @param {string} repo
|
|
160
|
+
* @param {number} pullNumber
|
|
161
|
+
* @param {{ event: string, body?: string, commit_id?: string }} payload
|
|
162
|
+
*/
|
|
163
|
+
export async function githubPostPullReview(owner, repo, pullNumber, payload) {
|
|
164
|
+
const o = encodeURIComponent(owner);
|
|
165
|
+
const r = encodeURIComponent(repo);
|
|
166
|
+
const n = encodeURIComponent(String(pullNumber));
|
|
167
|
+
return githubRestJson("POST", `/repos/${o}/${r}/pulls/${n}/reviews`, payload);
|
|
168
|
+
}
|
package/src/github-rest.js
CHANGED
|
@@ -26,16 +26,30 @@ export function useDirectGithub() {
|
|
|
26
26
|
*/
|
|
27
27
|
export async function githubRestJson(method, path, jsonBody, tokenOverride) {
|
|
28
28
|
const token = tokenOverride ?? getGithubPat();
|
|
29
|
-
|
|
29
|
+
const methodUpper = String(method).toUpperCase();
|
|
30
|
+
const unauthDisabled =
|
|
31
|
+
process.env.NUGIT_GITHUB_UNAUTHENTICATED === "0" ||
|
|
32
|
+
process.env.NUGIT_GITHUB_UNAUTHENTICATED === "false";
|
|
33
|
+
const allowUnauthenticatedGet =
|
|
34
|
+
!unauthDisabled &&
|
|
35
|
+
!token &&
|
|
36
|
+
(methodUpper === "GET" || methodUpper === "HEAD");
|
|
37
|
+
|
|
38
|
+
if (!token && !allowUnauthenticatedGet) {
|
|
30
39
|
throw new Error(
|
|
31
|
-
"
|
|
40
|
+
"GitHub authentication required for this request. Run `nugit auth login` or `nugit auth pat --token …`, " +
|
|
41
|
+
"or set NUGIT_USER_TOKEN / STACKPR_USER_TOKEN. " +
|
|
42
|
+
"Some read-only public GETs work without a token (low rate limits); set NUGIT_GITHUB_UNAUTHENTICATED=0 to disable that."
|
|
32
43
|
);
|
|
33
44
|
}
|
|
34
45
|
const headers = {
|
|
35
|
-
Authorization: `Bearer ${token}`,
|
|
36
46
|
Accept: "application/vnd.github+json",
|
|
37
|
-
"X-GitHub-Api-Version": "2022-11-28"
|
|
47
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
48
|
+
"User-Agent": "nugit-cli"
|
|
38
49
|
};
|
|
50
|
+
if (token) {
|
|
51
|
+
headers.Authorization = `Bearer ${token}`;
|
|
52
|
+
}
|
|
39
53
|
if (jsonBody !== undefined) {
|
|
40
54
|
headers["Content-Type"] = "application/json";
|
|
41
55
|
}
|
|
@@ -147,19 +161,125 @@ export async function githubCreatePullRequest(owner, repo, fields) {
|
|
|
147
161
|
/**
|
|
148
162
|
* @param {number} page
|
|
149
163
|
*/
|
|
150
|
-
|
|
164
|
+
/**
|
|
165
|
+
* @param {number} [page]
|
|
166
|
+
* @param {number} [perPage] max 100
|
|
167
|
+
*/
|
|
168
|
+
export async function githubListUserRepos(page = 1, perPage = 100) {
|
|
169
|
+
const pp = Math.min(100, Math.max(1, perPage));
|
|
170
|
+
const p = Math.max(1, page);
|
|
151
171
|
return githubRestJson(
|
|
152
172
|
"GET",
|
|
153
|
-
`/user/repos?page=${encodeURIComponent(String(
|
|
173
|
+
`/user/repos?page=${encodeURIComponent(String(p))}&per_page=${encodeURIComponent(String(pp))}&sort=full_name`
|
|
154
174
|
);
|
|
155
175
|
}
|
|
156
176
|
|
|
177
|
+
/**
|
|
178
|
+
* @returns {Promise<unknown[]>}
|
|
179
|
+
*/
|
|
180
|
+
export async function githubListAllUserRepos() {
|
|
181
|
+
/** @type {unknown[]} */
|
|
182
|
+
const all = [];
|
|
183
|
+
let page = 1;
|
|
184
|
+
for (;;) {
|
|
185
|
+
const chunk = await githubListUserRepos(page, 100);
|
|
186
|
+
if (!Array.isArray(chunk) || chunk.length === 0) {
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
all.push(...chunk);
|
|
190
|
+
if (chunk.length < 100) {
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
page += 1;
|
|
194
|
+
}
|
|
195
|
+
return all;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* @param {string} owner
|
|
200
|
+
* @param {string} repo
|
|
201
|
+
* @param {string} base
|
|
202
|
+
* @param {string} head
|
|
203
|
+
*/
|
|
204
|
+
export async function githubCompareRefs(owner, repo, base, head) {
|
|
205
|
+
const o = encodeURIComponent(owner);
|
|
206
|
+
const r = encodeURIComponent(repo);
|
|
207
|
+
const bh = `${base}...${head}`;
|
|
208
|
+
return githubRestJson("GET", `/repos/${o}/${r}/compare/${encodeURIComponent(bh)}`);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* @param {string} owner
|
|
213
|
+
* @param {string} repo
|
|
214
|
+
* @param {string} treeSha commit or tree sha
|
|
215
|
+
* @param {boolean} [recursive]
|
|
216
|
+
*/
|
|
217
|
+
export async function githubGetGitTree(owner, repo, treeSha, recursive = false) {
|
|
218
|
+
const o = encodeURIComponent(owner);
|
|
219
|
+
const r = encodeURIComponent(repo);
|
|
220
|
+
const sha = encodeURIComponent(treeSha);
|
|
221
|
+
const q = recursive ? "?recursive=1" : "";
|
|
222
|
+
return githubRestJson("GET", `/repos/${o}/${r}/git/trees/${sha}${q}`);
|
|
223
|
+
}
|
|
224
|
+
|
|
157
225
|
/**
|
|
158
226
|
* Search issues (includes PRs). `q` is GitHub search query syntax.
|
|
159
227
|
* @param {string} q
|
|
160
228
|
* @param {number} [perPage] max 100
|
|
161
229
|
* @param {number} [page] 1-based
|
|
162
230
|
*/
|
|
231
|
+
/**
|
|
232
|
+
* GitHub repository search (`q` uses GitHub search syntax, e.g. `todo language:python` or `user:orgname`).
|
|
233
|
+
* @param {string} q
|
|
234
|
+
* @param {number} [perPage]
|
|
235
|
+
* @param {number} [page]
|
|
236
|
+
*/
|
|
237
|
+
export async function githubSearchRepositories(q, perPage = 20, page = 1) {
|
|
238
|
+
const pp = Math.min(100, Math.max(1, perPage));
|
|
239
|
+
const p = Math.max(1, page);
|
|
240
|
+
const query = new URLSearchParams({
|
|
241
|
+
q,
|
|
242
|
+
per_page: String(pp),
|
|
243
|
+
page: String(p)
|
|
244
|
+
});
|
|
245
|
+
return githubRestJson("GET", `/search/repositories?${query.toString()}`);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* List branches in a repository (one page).
|
|
250
|
+
* @param {string} owner
|
|
251
|
+
* @param {string} repo
|
|
252
|
+
* @param {number} [page] 1-based
|
|
253
|
+
* @param {number} [perPage] max 100
|
|
254
|
+
*/
|
|
255
|
+
export async function githubListBranches(owner, repo, page = 1, perPage = 100) {
|
|
256
|
+
const pp = Math.min(100, Math.max(1, perPage));
|
|
257
|
+
const p = Math.max(1, page);
|
|
258
|
+
return githubRestJson(
|
|
259
|
+
"GET",
|
|
260
|
+
`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/branches?page=${p}&per_page=${pp}`
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* @param {string} owner
|
|
266
|
+
* @param {string} repo
|
|
267
|
+
* @returns {Promise<unknown[]>}
|
|
268
|
+
*/
|
|
269
|
+
export async function githubListAllBranches(owner, repo) {
|
|
270
|
+
/** @type {unknown[]} */
|
|
271
|
+
const all = [];
|
|
272
|
+
let page = 1;
|
|
273
|
+
for (;;) {
|
|
274
|
+
const chunk = await githubListBranches(owner, repo, page, 100);
|
|
275
|
+
if (!Array.isArray(chunk) || chunk.length === 0) break;
|
|
276
|
+
all.push(...chunk);
|
|
277
|
+
if (chunk.length < 100) break;
|
|
278
|
+
page += 1;
|
|
279
|
+
}
|
|
280
|
+
return all;
|
|
281
|
+
}
|
|
282
|
+
|
|
163
283
|
export async function githubSearchIssues(q, perPage = 30, page = 1) {
|
|
164
284
|
const pp = Math.min(100, Math.max(1, perPage));
|
|
165
285
|
const p = Math.max(1, page);
|
|
@@ -189,6 +309,29 @@ export async function githubListOpenPulls(owner, repo, page = 1, perPage = 30) {
|
|
|
189
309
|
);
|
|
190
310
|
}
|
|
191
311
|
|
|
312
|
+
/**
|
|
313
|
+
* @param {string} owner
|
|
314
|
+
* @param {string} repo
|
|
315
|
+
* @returns {Promise<unknown[]>}
|
|
316
|
+
*/
|
|
317
|
+
export async function githubListAllOpenPulls(owner, repo) {
|
|
318
|
+
/** @type {unknown[]} */
|
|
319
|
+
const all = [];
|
|
320
|
+
let page = 1;
|
|
321
|
+
for (;;) {
|
|
322
|
+
const chunk = await githubListOpenPulls(owner, repo, page, 100);
|
|
323
|
+
if (!Array.isArray(chunk) || chunk.length === 0) {
|
|
324
|
+
break;
|
|
325
|
+
}
|
|
326
|
+
all.push(...chunk);
|
|
327
|
+
if (chunk.length < 100) {
|
|
328
|
+
break;
|
|
329
|
+
}
|
|
330
|
+
page += 1;
|
|
331
|
+
}
|
|
332
|
+
return all;
|
|
333
|
+
}
|
|
334
|
+
|
|
192
335
|
/**
|
|
193
336
|
* @param {string} owner
|
|
194
337
|
* @param {string} repo
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config-related actions for the `nugit config` and `nugit env` commands.
|
|
3
|
+
* Extracted from the former nugit-start.js.
|
|
4
|
+
*/
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
import path from "path";
|
|
7
|
+
import {
|
|
8
|
+
expandUserPath,
|
|
9
|
+
getConfigPath,
|
|
10
|
+
inferMonorepoRootFromCli,
|
|
11
|
+
readUserConfig,
|
|
12
|
+
writeUserConfig,
|
|
13
|
+
buildStartEnv
|
|
14
|
+
} from "./user-config.js";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @param {object} opts
|
|
18
|
+
* @param {string} [opts.installRoot]
|
|
19
|
+
* @param {string} [opts.envFile]
|
|
20
|
+
* @param {string} [opts.workingDirectory]
|
|
21
|
+
*/
|
|
22
|
+
export function runConfigInit(opts) {
|
|
23
|
+
const root = path.resolve(expandUserPath(opts.installRoot || inferMonorepoRootFromCli()));
|
|
24
|
+
const defaultEnv = path.join(root, ".env");
|
|
25
|
+
const envFile = expandUserPath(opts.envFile || defaultEnv);
|
|
26
|
+
const cfg = { installRoot: root, envFile };
|
|
27
|
+
if (opts.workingDirectory) cfg.workingDirectory = expandUserPath(opts.workingDirectory);
|
|
28
|
+
writeUserConfig(cfg);
|
|
29
|
+
console.error(`Wrote ${getConfigPath()}`);
|
|
30
|
+
console.error(` installRoot: ${root}`);
|
|
31
|
+
console.error(` envFile: ${envFile}`);
|
|
32
|
+
if (!fs.existsSync(envFile)) {
|
|
33
|
+
console.error(" (env file does not exist yet — create it or run: nugit config set env-file <path>)");
|
|
34
|
+
}
|
|
35
|
+
if (cfg.workingDirectory) console.error(` workingDirectory: ${cfg.workingDirectory}`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function runConfigShow() {
|
|
39
|
+
console.log(JSON.stringify(readUserConfig(), null, 2));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @param {string} key
|
|
44
|
+
* @param {string} value
|
|
45
|
+
*/
|
|
46
|
+
export function runConfigSet(key, value) {
|
|
47
|
+
const c = readUserConfig();
|
|
48
|
+
const k = key.toLowerCase().replace(/_/g, "-");
|
|
49
|
+
if (k === "install-root") {
|
|
50
|
+
c.installRoot = path.resolve(expandUserPath(value));
|
|
51
|
+
} else if (k === "env-file") {
|
|
52
|
+
c.envFile = expandUserPath(value);
|
|
53
|
+
} else if (k === "working-directory" || k === "cwd") {
|
|
54
|
+
c.workingDirectory = expandUserPath(value);
|
|
55
|
+
} else {
|
|
56
|
+
throw new Error(`Unknown key "${key}". Use: install-root | env-file | working-directory`);
|
|
57
|
+
}
|
|
58
|
+
writeUserConfig(c);
|
|
59
|
+
console.error(`Updated ${getConfigPath()}`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @param {"bash" | "fish"} shell
|
|
64
|
+
*/
|
|
65
|
+
export function runEnvExport(shell) {
|
|
66
|
+
const cfg = readUserConfig();
|
|
67
|
+
let env;
|
|
68
|
+
try {
|
|
69
|
+
env = buildStartEnv(cfg);
|
|
70
|
+
} catch (e) {
|
|
71
|
+
console.error(String(e?.message || e));
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
const entries = Object.entries(env);
|
|
75
|
+
if (shell === "fish") {
|
|
76
|
+
for (const [k, v] of entries) {
|
|
77
|
+
if (v !== undefined) console.log(`set -gx ${k} ${JSON.stringify(v)};`);
|
|
78
|
+
}
|
|
79
|
+
} else {
|
|
80
|
+
for (const [k, v] of entries) {
|
|
81
|
+
if (v !== undefined) console.log(`export ${k}=${JSON.stringify(v)}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|