job-apply-plugin 0.2.0 → 0.3.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.
@@ -9,7 +9,7 @@
9
9
  "name": "job-apply",
10
10
  "source": "./plugin",
11
11
  "description": "Prepare and submit individual job applications using a résumé and profile saved on your device.",
12
- "version": "0.2.0",
12
+ "version": "0.3.0",
13
13
  "category": "productivity"
14
14
  }
15
15
  ]
package/README.md CHANGED
@@ -74,24 +74,45 @@ npm install -g job-apply-plugin && job-apply-plugin install
74
74
  The installer never overwrites a folder it didn't create unless you pass `--force`.
75
75
  When you add Job Apply to an existing `marketplace.json`, the other entries in that file are kept.
76
76
 
77
+ ## Uninstall
78
+
79
+ ```bash
80
+ npx job-apply-plugin uninstall
81
+ ```
82
+
83
+ It asks which app to remove Job Apply from, and shows where it's currently installed:
84
+
85
+ ```
86
+ Remove Job Apply from which app?
87
+ 1) Claude Code (installed v0.3.0)
88
+ 2) ChatGPT desktop / Codex (not installed)
89
+ 3) Both
90
+ Choose 1, 2 or 3 [1]:
91
+ ```
92
+
93
+ To skip the menu, use `--claude`, `--chatgpt` or `--all`. `remove` works as another name for `uninstall`.
94
+ For ChatGPT, also click **Uninstall** on its Plugins page, then restart the app.
95
+
96
+ Uninstalling keeps your résumé, profile and application history in `~/Documents/Job Apply`.
97
+ To erase those too, ask the assistant to "clear my Job Apply profile" before you uninstall,
98
+ or delete that folder.
99
+
100
+ > `npm uninstall -g job-apply-plugin` only removes the installer, not the plugin.
101
+ > Run `uninstall` first.
102
+
77
103
  ## Other commands
78
104
 
79
105
  ```bash
80
106
  npx job-apply-plugin status # where it's installed and whether Python is OK
81
107
  npx job-apply-plugin install # run again to update
82
- npx job-apply-plugin uninstall # removes it from both apps and keeps your profile and history
83
- npx job-apply-plugin uninstall --chatgpt # or from just one app (--claude / --chatgpt)
84
108
  ```
85
109
 
86
- To erase your saved profile and history, ask the assistant to "clear my Job Apply profile",
87
- or delete `~/Documents/Job Apply`.
88
-
89
110
  ## Installing from the Git repository (alternative)
90
111
 
91
112
  The repository is also a plugin marketplace for both apps.
92
113
 
93
- - Claude Code: `claude plugin marketplace add <owner>/<repo>` then `claude plugin install job-apply@job-apply`
94
- - Codex: `codex plugin marketplace add <path-or-repo>`
114
+ - Claude Code: `claude plugin marketplace add filipelinemburger/job-apply` then `claude plugin install job-apply@job-apply`
115
+ - Codex: `codex plugin marketplace add https://github.com/filipelinemburger/job-apply.git`
95
116
 
96
117
  ## Privacy
97
118
 
package/bin/job-apply.js CHANGED
@@ -232,7 +232,8 @@ Usage
232
232
 
233
233
  Commands
234
234
  install Install (or update) the plugin
235
- uninstall Remove the plugin (your profile and history in ~/Documents/Job Apply are kept)
235
+ uninstall Remove the plugin from Claude Code, ChatGPT or both (alias: remove).
236
+ Your profile and history in ~/Documents/Job Apply are kept.
236
237
  status Show where the plugin is installed and check Python
237
238
 
238
239
  Options
@@ -246,7 +247,7 @@ Options
246
247
 
247
248
  Run "install" with no app flag to pick from a menu: Claude Code, ChatGPT, or both.
248
249
  Without a terminal to ask in (CI, scripts), it installs into the apps found on this computer.
249
- "uninstall" with no app flag removes the plugin from both apps.`;
250
+ "uninstall" with no app flag shows the same menu (without a terminal, it removes from both).`;
250
251
 
251
252
  const CHOICES = {
252
253
  1: ["claude"],
@@ -271,16 +272,16 @@ function flagTargets(values) {
271
272
  return chosen;
272
273
  }
273
274
 
274
- async function askTargets(found) {
275
- const defaultChoice = found.length === 1 ? (found[0] === claude ? "1" : "2") : "3";
276
- const note = (t) => (found.includes(t) ? " (found on this computer)" : "");
277
- log("Where should Job Apply be installed?");
278
- log(` 1) Claude Code${note(claude)}`);
279
- log(` 2) ChatGPT desktop / Codex${note(chatgpt)}`);
275
+ // Shows the 1/2/3 menu. `notes` maps each target to a hint shown next to it; `verb` is "installed" or "removed".
276
+ async function askTargets({ title, notes, defaultChoice, verb }) {
277
+ const cancelled = `Cancelled. Nothing was ${verb}.`;
278
+ log(title);
279
+ log(` 1) Claude Code${notes(claude)}`);
280
+ log(` 2) ChatGPT desktop / Codex${notes(chatgpt)}`);
280
281
  log(` 3) Both`);
281
282
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
282
283
  rl.on("SIGINT", () => {
283
- log("\nCancelled. Nothing was installed.");
284
+ log(`\n${cancelled}`);
284
285
  process.exit(130);
285
286
  });
286
287
  try {
@@ -290,7 +291,7 @@ async function askTargets(found) {
290
291
  answer = await rl.question(`Choose 1, 2 or 3 [${defaultChoice}]: `);
291
292
  } catch (err) {
292
293
  // Ctrl+D or a closed terminal
293
- if (err?.name === "AbortError" || err?.code === "ABORT_ERR") throw new CliError("Cancelled. Nothing was installed.");
294
+ if (err?.name === "AbortError" || err?.code === "ABORT_ERR") throw new CliError(cancelled);
294
295
  throw err;
295
296
  }
296
297
  const ids = parseChoice(answer, defaultChoice);
@@ -303,20 +304,49 @@ async function askTargets(found) {
303
304
  } finally {
304
305
  rl.close();
305
306
  }
306
- throw new CliError("No choice made. Nothing was installed.");
307
+ throw new CliError(`No choice made. Nothing was ${verb}.`);
307
308
  }
308
309
 
310
+ const interactive = () => Boolean(process.stdin.isTTY && process.stdout.isTTY);
311
+ const choiceFor = (list) => (list.length === 1 ? (list[0] === claude ? "1" : "2") : "3");
312
+
309
313
  async function pickInstallTargets(values) {
310
314
  const flagged = flagTargets(values);
311
315
  if (flagged.length) return flagged;
312
316
  const found = Object.values(TARGETS).filter((t) => t.detect());
313
- if (process.stdin.isTTY && process.stdout.isTTY) return askTargets(found);
317
+ if (interactive()) {
318
+ return askTargets({
319
+ title: "Where should Job Apply be installed?",
320
+ notes: (t) => (found.includes(t) ? " (found on this computer)" : ""),
321
+ defaultChoice: choiceFor(found),
322
+ verb: "installed",
323
+ });
324
+ }
314
325
  // No terminal to ask in: fall back to the apps found here (both if none).
315
326
  const targets = found.length ? found : Object.values(TARGETS);
316
327
  log(`No app chosen; installing for ${targets.map((t) => t.label).join(" and ")}. Use --claude, --chatgpt or --all to choose.`);
317
328
  return targets;
318
329
  }
319
330
 
331
+ // Returns [] when there is nothing to remove.
332
+ async function pickUninstallTargets(values, opts) {
333
+ const flagged = flagTargets(values);
334
+ if (flagged.length) return flagged;
335
+ const statuses = new Map(Object.values(TARGETS).map((t) => [t, t.status(opts)]));
336
+ const installed = [...statuses].filter(([, s]) => s !== "not installed").map(([t]) => t);
337
+ if (installed.length === 0) {
338
+ log("Job Apply isn't installed in Claude Code or ChatGPT on this computer. Nothing to remove.");
339
+ return [];
340
+ }
341
+ if (!interactive()) return Object.values(TARGETS);
342
+ return askTargets({
343
+ title: "Remove Job Apply from which app?",
344
+ notes: (t) => ` (${statuses.get(t).replace(/ \(.*$/, "").replace(/ — .*$/, "")})`,
345
+ defaultChoice: choiceFor(installed),
346
+ verb: "removed",
347
+ });
348
+ }
349
+
320
350
  export async function main(argv = process.argv.slice(2)) {
321
351
  const { values, positionals } = parseArgs({
322
352
  args: argv,
@@ -348,10 +378,13 @@ export async function main(argv = process.argv.slice(2)) {
348
378
  }
349
379
  log();
350
380
  log(`Your résumé, profile and application history are stored locally in ~/Documents/Job Apply.`);
351
- } else if (command === "uninstall") {
352
- const flagged = flagTargets(values);
353
- for (const t of flagged.length ? flagged : Object.values(TARGETS)) t.uninstall(opts);
354
- log(`Your local profile in ~/Documents/Job Apply was not touched.`);
381
+ } else if (command === "uninstall" || command === "remove") {
382
+ const targets = await pickUninstallTargets(values, opts);
383
+ if (targets.length === 0) return;
384
+ for (const t of targets) t.uninstall(opts);
385
+ log();
386
+ log(`Your résumé, profile and history in ~/Documents/Job Apply were kept.`);
387
+ log(`To erase them too, delete that folder or ask the assistant to "clear my Job Apply profile" before uninstalling.`);
355
388
  } else if (command === "status") {
356
389
  for (const t of Object.values(TARGETS)) log(`${t.label.padEnd(24)} ${t.status(opts)}`);
357
390
  const py = pythonCheck();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "job-apply-plugin",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Job Apply plugin for Claude Code and ChatGPT/Codex: prepare and submit job applications from a résumé and profile saved on your device.",
5
5
  "keywords": [
6
6
  "claude",
@@ -14,6 +14,14 @@
14
14
  "resume"
15
15
  ],
16
16
  "author": "Filipe Linemburger",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/filipelinemburger/job-apply.git"
20
+ },
21
+ "homepage": "https://github.com/filipelinemburger/job-apply#readme",
22
+ "bugs": {
23
+ "url": "https://github.com/filipelinemburger/job-apply/issues"
24
+ },
17
25
  "type": "module",
18
26
  "bin": {
19
27
  "job-apply-plugin": "bin/job-apply.js"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "job-apply",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Prepare and submit individual job applications using a résumé and profile saved on your device.",
5
5
  "author": {
6
6
  "name": "Filipe Linemburger"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
3
3
  "name": "job-apply",
4
- "version": "0.2.0",
4
+ "version": "0.3.0",
5
5
  "description": "Prepare and submit individual job applications using a résumé and profile saved on your device.",
6
6
  "keywords": [
7
7
  "jobs",