@evanpurkhiser/tooling-personal 1.47.0 → 1.49.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/dist/assignees.js CHANGED
@@ -13,8 +13,8 @@ const fzf_1 = require("./fzf");
13
13
  const graphql_1 = require("./graphql");
14
14
  var AssigneeType;
15
15
  (function (AssigneeType) {
16
- AssigneeType[AssigneeType["User"] = 0] = "User";
17
- AssigneeType[AssigneeType["Team"] = 1] = "Team";
16
+ AssigneeType["User"] = "user";
17
+ AssigneeType["Team"] = "team";
18
18
  })(AssigneeType || (exports.AssigneeType = AssigneeType = {}));
19
19
  function isUser(obj) {
20
20
  return obj.repository !== undefined;
package/dist/blame.js CHANGED
@@ -111,7 +111,7 @@ async function blameFile(file, hunks, rev) {
111
111
  continue;
112
112
  }
113
113
  if (line.startsWith('author-mail ')) {
114
- const email = line.slice('author-mail '.length).replace(/^<|>$/g, '');
114
+ const email = line.slice('author-mail '.length).replaceAll(/^<|>$/g, '');
115
115
  if (!shaToEmail.has(last.sha)) {
116
116
  shaToEmail.set(last.sha, email);
117
117
  }
@@ -33,7 +33,7 @@ async function cherryPickOnto(sha, base) {
33
33
  ])).trim();
34
34
  }
35
35
  catch (error) {
36
- throw new Error(`Conflicts applying ${sha.slice(0, 8)} onto ${base}. Rebase locally and retry.\n${error}`);
36
+ throw new Error(`Conflicts applying ${sha.slice(0, 8)} onto ${base}. Rebase locally and retry.\n${error}`, { cause: error });
37
37
  }
38
38
  const info = await git.raw(['show', '-s', '--format=%an%n%ae%n%aI%n%B', sha]);
39
39
  const lines = info.split('\n');
@@ -50,9 +50,6 @@ async function resolveReviewers(repo, slugs) {
50
50
  return found;
51
51
  }
52
52
  async function prCreate(argv) {
53
- if (!argv.title && !argv.updateOnly) {
54
- throw new Error('--title is required when creating a PR');
55
- }
56
53
  const username = await (0, utils_1.getEmailUsername)();
57
54
  const repo = await (0, utils_1.getRepoKey)();
58
55
  const { head, origin } = await (0, utils_1.getBranchNames)();
@@ -78,22 +75,15 @@ async function prCreate(argv) {
78
75
  }
79
76
  const branchName = (0, utils_1.branchFromMessage)(username, commit.message);
80
77
  const existingPr = prs.find(p => p.headRefName === branchName);
81
- if (argv.updateOnly && !existingPr) {
82
- throw new Error(`No existing PR for branch ${branchName}`);
78
+ if (existingPr) {
79
+ throw new Error(`PR already exists for branch ${branchName} (#${existingPr.number}). Use 'pr-update' to push changes.`);
83
80
  }
84
81
  const reviewerSlugs = parseSlugs(argv.reviewer);
85
- // Reviewers only apply on PR creation (matches `pt pr` behavior).
86
- const reviewers = existingPr ? [] : await resolveReviewers(repo, reviewerSlugs);
82
+ const reviewers = await resolveReviewers(repo, reviewerSlugs);
87
83
  console.error(chalk_1.default.gray(`Cherry-picking ${commit.hash.slice(0, 8)} onto ${origin}`));
88
84
  const newSha = await (0, cherry_pick_1.cherryPickOnto)(commit.hash, origin);
89
85
  console.error(chalk_1.default.gray(`Pushing ${branchName}`));
90
86
  await (0, simple_git_1.default)().push(['--force', 'origin', `${newSha}:refs/heads/${branchName}`]);
91
- if (existingPr) {
92
- const url = `https://github.com/${repo.fullName}/pull/${existingPr.number}`;
93
- console.error(chalk_1.default.green(`Updated existing PR #${existingPr.number}: ${existingPr.title}`));
94
- console.log(url);
95
- return;
96
- }
97
87
  console.error(chalk_1.default.gray('Creating Pull Request'));
98
88
  const { createPullRequest } = await (0, pulls_1.createPull)({
99
89
  baseRefName: repoDetails.defaultBranch,
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.prUpdate = prUpdate;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const simple_git_1 = __importDefault(require("simple-git"));
9
+ const cherry_pick_1 = require("../cherry-pick");
10
+ const pulls_1 = require("../pulls");
11
+ const utils_1 = require("../utils");
12
+ async function prUpdate(argv) {
13
+ const username = await (0, utils_1.getEmailUsername)();
14
+ const repo = await (0, utils_1.getRepoKey)();
15
+ const { head, origin } = await (0, utils_1.getBranchNames)();
16
+ if (head === null) {
17
+ throw new Error('Cannot determine HEAD branch name');
18
+ }
19
+ if (origin === null) {
20
+ throw new Error('Cannot determine upstream HEAD branch name');
21
+ }
22
+ const unpublished = (0, simple_git_1.default)().log({ from: 'HEAD', to: origin });
23
+ const [commits, prs] = await Promise.all([unpublished, (0, pulls_1.getPulls)(repo)]);
24
+ const commit = commits.all.find(c => c.hash.startsWith(argv.sha));
25
+ if (!commit) {
26
+ throw new Error(`Commit ${argv.sha} not found in unpublished commits`);
27
+ }
28
+ const branchName = (0, utils_1.branchFromMessage)(username, commit.message);
29
+ const existingPr = prs.find(p => p.headRefName === branchName);
30
+ if (!existingPr) {
31
+ throw new Error(`No existing PR for branch ${branchName}. Use 'pr-create' to open one.`);
32
+ }
33
+ console.error(chalk_1.default.gray(`Cherry-picking ${commit.hash.slice(0, 8)} onto ${origin}`));
34
+ const newSha = await (0, cherry_pick_1.cherryPickOnto)(commit.hash, origin);
35
+ console.error(chalk_1.default.gray(`Pushing ${branchName}`));
36
+ await (0, simple_git_1.default)().push(['--force', 'origin', `${newSha}:refs/heads/${branchName}`]);
37
+ const url = `https://github.com/${repo.fullName}/pull/${existingPr.number}`;
38
+ console.error(chalk_1.default.green(`Updated existing PR #${existingPr.number}: ${existingPr.title}`));
39
+ console.log(url);
40
+ }
package/dist/cmd/pr.js CHANGED
@@ -149,7 +149,7 @@ async function pr(argv) {
149
149
  });
150
150
  // 05-b. Enable auto merge
151
151
  prTasks.add({
152
- enabled: !!argv.autoMerge,
152
+ enabled: Boolean(argv.autoMerge),
153
153
  title: 'Enabling auto merge',
154
154
  task: setAutoMergeTask,
155
155
  });
package/dist/config.js CHANGED
@@ -5,9 +5,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.config = void 0;
7
7
  const convict_1 = __importDefault(require("convict"));
8
- const js_yaml_1 = __importDefault(require("js-yaml"));
9
- const path_1 = require("path");
10
- convict_1.default.addParser({ extension: ['yml', 'yaml'], parse: js_yaml_1.default.load });
8
+ const js_yaml_1 = require("js-yaml");
9
+ const node_path_1 = require("node:path");
10
+ convict_1.default.addParser({ extension: ['yml', 'yaml'], parse: js_yaml_1.load });
11
11
  const config = (0, convict_1.default)({
12
12
  ignoreAssignees: {
13
13
  doc: 'Assignee names / teams to ignore. Should be a regex expression.',
@@ -17,6 +17,6 @@ const config = (0, convict_1.default)({
17
17
  });
18
18
  exports.config = config;
19
19
  const home = process.env.HOME;
20
- const configDir = process.env.XDG_CONFIG_HOME || (0, path_1.join)(home, '.config');
21
- config.loadFile((0, path_1.join)(configDir, 'pt', 'config.yml'));
20
+ const configDir = process.env.XDG_CONFIG_HOME || (0, node_path_1.join)(home, '.config');
21
+ config.loadFile((0, node_path_1.join)(configDir, 'pt', 'config.yml'));
22
22
  config.validate();
package/dist/editor.js CHANGED
@@ -4,17 +4,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.editPullRequest = editPullRequest;
7
- const child_process_1 = require("child_process");
8
- const promises_1 = __importDefault(require("fs/promises"));
9
- const path_1 = __importDefault(require("path"));
7
+ const node_child_process_1 = require("node:child_process");
8
+ const promises_1 = __importDefault(require("node:fs/promises"));
9
+ const node_path_1 = __importDefault(require("node:path"));
10
10
  const utils_1 = require("./utils");
11
11
  async function editPullRequest(commit) {
12
12
  const messageBody = commit.body;
13
13
  const split = messageBody.length > 0 ? '\n\n' : '';
14
14
  const prTemplate = `${commit.message}${split}${messageBody}`;
15
- const pullEditFile = path_1.default.join(await (0, utils_1.getRepoPath)(), '.git', 'PULLREQ_EDITMSG');
15
+ const pullEditFile = node_path_1.default.join(await (0, utils_1.getRepoPath)(), '.git', 'PULLREQ_EDITMSG');
16
16
  await promises_1.default.writeFile(pullEditFile, prTemplate);
17
- const editor = (0, child_process_1.spawn)(process.env.EDITOR ?? 'vim', [pullEditFile], {
17
+ const editor = (0, node_child_process_1.spawn)(process.env.EDITOR ?? 'vim', [pullEditFile], {
18
18
  shell: true,
19
19
  stdio: 'inherit',
20
20
  });
package/dist/fzf.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.fzfSelect = fzfSelect;
4
- const child_process_1 = require("child_process");
4
+ const node_child_process_1 = require("node:child_process");
5
5
  async function fzfSelect({ prompt, multi = true, genValues, }) {
6
6
  const fzfArgs = [
7
7
  '--ansi',
@@ -13,7 +13,7 @@ async function fzfSelect({ prompt, multi = true, genValues, }) {
13
13
  if (multi) {
14
14
  fzfArgs.push('-m');
15
15
  }
16
- const fzf = (0, child_process_1.spawn)('fzf', fzfArgs, { shell: true, stdio: ['pipe', 'pipe', 'inherit'] });
16
+ const fzf = (0, node_child_process_1.spawn)('fzf', fzfArgs, { shell: true, stdio: ['pipe', 'pipe', 'inherit'] });
17
17
  fzf.stdin.setDefaultEncoding('utf-8');
18
18
  const options = {};
19
19
  const valuesDone = genValues(option => {
package/dist/identity.js CHANGED
@@ -6,12 +6,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.loadAssignableUsers = loadAssignableUsers;
7
7
  exports.buildResolver = buildResolver;
8
8
  const graphql_request_1 = require("graphql-request");
9
- const promises_1 = __importDefault(require("fs/promises"));
10
- const path_1 = require("path");
9
+ const promises_1 = __importDefault(require("node:fs/promises"));
10
+ const node_path_1 = require("node:path");
11
11
  const graphql_1 = require("./graphql");
12
12
  function cachePath(repo) {
13
- const base = process.env.XDG_CACHE_HOME || (0, path_1.join)(process.env.HOME ?? '', '.cache');
14
- return (0, path_1.join)(base, 'pt', `assignees-${repo.owner}-${repo.repo}.json`);
13
+ const base = process.env.XDG_CACHE_HOME || (0, node_path_1.join)(process.env.HOME ?? '', '.cache');
14
+ return (0, node_path_1.join)(base, 'pt', `assignees-${repo.owner}-${repo.repo}.json`);
15
15
  }
16
16
  async function readCache(repo) {
17
17
  try {
@@ -24,7 +24,7 @@ async function readCache(repo) {
24
24
  }
25
25
  async function writeCache(repo, users) {
26
26
  const path = cachePath(repo);
27
- await promises_1.default.mkdir((0, path_1.join)(path, '..'), { recursive: true });
27
+ await promises_1.default.mkdir((0, node_path_1.join)(path, '..'), { recursive: true });
28
28
  await promises_1.default.writeFile(path, JSON.stringify(users));
29
29
  }
30
30
  /**
@@ -85,8 +85,8 @@ function parseNoreplyLogin(email) {
85
85
  */
86
86
  function normalizeName(name) {
87
87
  return name
88
- .replace(/[^\p{L}\p{N}\s-]/gu, ' ')
89
- .replace(/\s+/g, ' ')
88
+ .replaceAll(/[^\p{L}\p{N}\s-]/gu, ' ')
89
+ .replaceAll(/\s+/g, ' ')
90
90
  .trim()
91
91
  .toLowerCase();
92
92
  }
package/dist/index.js CHANGED
@@ -8,6 +8,7 @@ const chalk_1 = __importDefault(require("chalk"));
8
8
  const yargs_1 = __importDefault(require("yargs"));
9
9
  const pr_1 = require("./cmd/pr");
10
10
  const pr_create_1 = require("./cmd/pr-create");
11
+ const pr_update_1 = require("./cmd/pr-update");
11
12
  const select_commit_1 = require("./cmd/select-commit");
12
13
  const suggest_assignees_1 = require("./cmd/suggest-assignees");
13
14
  (0, yargs_1.default)(process.argv.slice(2))
@@ -31,7 +32,7 @@ const suggest_assignees_1 = require("./cmd/suggest-assignees");
31
32
  boolean: true,
32
33
  desc: 'Enable auto merge for the PR',
33
34
  }), pr_1.pr)
34
- .command('pr-create <sha>', 'Non-interactively create or update a PR for a single commit. Body is read from stdin.', y => y
35
+ .command('pr-create <sha>', 'Non-interactively create a PR for a single commit. Body is read from stdin.', y => y
35
36
  .positional('sha', {
36
37
  type: 'string',
37
38
  demandOption: true,
@@ -55,15 +56,16 @@ const suggest_assignees_1 = require("./cmd/suggest-assignees");
55
56
  alias: 'm',
56
57
  boolean: true,
57
58
  desc: 'Enable auto merge for the PR',
58
- })
59
- .option('updateOnly', {
60
- boolean: true,
61
- desc: 'Only update an existing PR; fail if none exists for the generated branch',
62
59
  })
63
60
  .option('noOpen', {
64
61
  boolean: true,
65
62
  desc: 'Do not open the PR in the browser after creation',
66
63
  }), pr_create_1.prCreate)
64
+ .command('pr-update <sha>', 'Non-interactively push an amended commit to its existing PR branch.', y => y.positional('sha', {
65
+ type: 'string',
66
+ demandOption: true,
67
+ desc: 'Commit SHA (full or prefix) whose PR branch should be updated',
68
+ }), pr_update_1.prUpdate)
67
69
  .command('suggest-assignees', 'Suggest reviewers for current diff based on blame ownership', y => y
68
70
  .option('commit', {
69
71
  type: 'string',
package/dist/utils.js CHANGED
@@ -11,7 +11,7 @@ exports.getAccessToken = getAccessToken;
11
11
  exports.branchFromMessage = branchFromMessage;
12
12
  const git_url_parse_1 = __importDefault(require("git-url-parse"));
13
13
  const simple_git_1 = __importDefault(require("simple-git"));
14
- const child_process_1 = require("child_process");
14
+ const node_child_process_1 = require("node:child_process");
15
15
  /**
16
16
  * Get's the current repo information
17
17
  */
@@ -64,7 +64,7 @@ async function getBranchNames() {
64
64
  */
65
65
  function getAccessToken() {
66
66
  try {
67
- return (0, child_process_1.execSync)('gh auth token').toString().trim();
67
+ return (0, node_child_process_1.execSync)('gh auth token').toString().trim();
68
68
  }
69
69
  catch {
70
70
  throw new Error('Cannot get token from `gh auth token`');
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@evanpurkhiser/tooling-personal",
4
- "version": "1.47.0",
4
+ "version": "1.49.0",
5
5
  "description": "Evan Purkhiser's personal tooling",
6
6
  "license": "MIT",
7
7
  "author": "Evan Purkhiser",
@@ -16,7 +16,7 @@
16
16
  "scripts": {
17
17
  "build": "tsc && chmod +x ./dist/index.js",
18
18
  "start": "ts-node ./src/index.ts",
19
- "lint": "oxlint src",
19
+ "lint": "oxlint src/",
20
20
  "format": "oxfmt .",
21
21
  "format:check": "oxfmt --check ."
22
22
  },
@@ -40,10 +40,10 @@
40
40
  "yargs": "^17.0.1"
41
41
  },
42
42
  "devDependencies": {
43
- "@evanpurkhiser/oxc-config": "^0.5.0",
43
+ "@evanpurkhiser/oxc-config": "^0.8.0",
44
44
  "@tsconfig/node16": "^1.0.1",
45
- "oxfmt": "^0.41.0",
46
- "oxlint": "^1.56.0",
45
+ "oxfmt": "^0.47.0",
46
+ "oxlint": "^1.62.0",
47
47
  "ts-node": "^10.9.2",
48
48
  "typescript": "^5.8.3"
49
49
  },