issy 0.3.0 → 0.4.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/bin/issy CHANGED
@@ -10,6 +10,11 @@ import {
10
10
  import { join, resolve } from 'node:path'
11
11
  import { fileURLToPath } from 'node:url'
12
12
  import updateNotifier from 'update-notifier'
13
+ import {
14
+ detectPackageManager,
15
+ isGlobalInstall,
16
+ getUpdateCommand
17
+ } from '../dist/update-checker.js'
13
18
 
14
19
  const args = process.argv.slice(2)
15
20
 
@@ -17,7 +22,21 @@ const args = process.argv.slice(2)
17
22
  const here = resolve(fileURLToPath(import.meta.url), '..')
18
23
  const pkgPath = resolve(here, '..', 'package.json')
19
24
  const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'))
20
- updateNotifier({ pkg, updateCheckInterval: 1000 * 60 * 60 }).notify() // 1 hour
25
+
26
+ const notifier = updateNotifier({ pkg, updateCheckInterval: 1000 * 60 }) // 1 minute
27
+
28
+ if (notifier.update) {
29
+ const scriptPath = resolve(fileURLToPath(import.meta.url))
30
+ const pm = detectPackageManager(scriptPath)
31
+ const isGlobal = isGlobalInstall(scriptPath)
32
+ const updateCmd = getUpdateCommand(pm, isGlobal)
33
+
34
+ notifier.notify({
35
+ message: `Update available: ${notifier.update.current} → ${notifier.update.latest}\nRun: ${updateCmd}`,
36
+ defer: true,
37
+ boxenOptions: { padding: 1, margin: 1, borderStyle: 'round', borderColor: 'yellow' }
38
+ })
39
+ }
21
40
 
22
41
  const cliCommands = new Set([
23
42
  'list',
@@ -0,0 +1,54 @@
1
+ // src/install-info.ts
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { homedir } from "node:os";
4
+ import { join } from "node:path";
5
+ var CONFIG_DIR = join(homedir(), ".config", "issy");
6
+ var INFO_FILE = join(CONFIG_DIR, "install-info.json");
7
+ function saveInstallInfo(info) {
8
+ try {
9
+ if (!existsSync(CONFIG_DIR)) {
10
+ mkdirSync(CONFIG_DIR, { recursive: true });
11
+ }
12
+ writeFileSync(INFO_FILE, JSON.stringify(info, null, 2));
13
+ } catch {}
14
+ }
15
+ function loadInstallInfo() {
16
+ try {
17
+ if (!existsSync(INFO_FILE)) {
18
+ return null;
19
+ }
20
+ const data = readFileSync(INFO_FILE, "utf-8");
21
+ return JSON.parse(data);
22
+ } catch {
23
+ return null;
24
+ }
25
+ }
26
+ function detectPackageManagerFromEnv() {
27
+ const userAgent = process.env.npm_config_user_agent || "";
28
+ if (userAgent.includes("bun/"))
29
+ return "bun";
30
+ if (userAgent.includes("pnpm/"))
31
+ return "pnpm";
32
+ if (userAgent.includes("yarn/"))
33
+ return "yarn";
34
+ if (userAgent.includes("npm/"))
35
+ return "npm";
36
+ return null;
37
+ }
38
+ function detectIsGlobalFromEnv() {
39
+ if (process.env.npm_config_global === "true")
40
+ return true;
41
+ if (process.env.PNPM_HOME && process.env.npm_config_global !== "false")
42
+ return true;
43
+ const npmConfigPrefix = process.env.npm_config_prefix || "";
44
+ if (npmConfigPrefix.includes("/usr/local") || npmConfigPrefix.includes("/.bun/") || npmConfigPrefix.includes("/.nvm/") || npmConfigPrefix.includes("/pnpm/global")) {
45
+ return true;
46
+ }
47
+ return false;
48
+ }
49
+ export {
50
+ saveInstallInfo,
51
+ loadInstallInfo,
52
+ detectPackageManagerFromEnv,
53
+ detectIsGlobalFromEnv
54
+ };
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/install-info.ts
4
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
5
+ import { homedir } from "node:os";
6
+ import { join } from "node:path";
7
+ var CONFIG_DIR = join(homedir(), ".config", "issy");
8
+ var INFO_FILE = join(CONFIG_DIR, "install-info.json");
9
+ function saveInstallInfo(info) {
10
+ try {
11
+ if (!existsSync(CONFIG_DIR)) {
12
+ mkdirSync(CONFIG_DIR, { recursive: true });
13
+ }
14
+ writeFileSync(INFO_FILE, JSON.stringify(info, null, 2));
15
+ } catch {}
16
+ }
17
+ function loadInstallInfo() {
18
+ try {
19
+ if (!existsSync(INFO_FILE)) {
20
+ return null;
21
+ }
22
+ const data = readFileSync(INFO_FILE, "utf-8");
23
+ return JSON.parse(data);
24
+ } catch {
25
+ return null;
26
+ }
27
+ }
28
+ function detectPackageManagerFromEnv() {
29
+ const userAgent = process.env.npm_config_user_agent || "";
30
+ if (userAgent.includes("bun/"))
31
+ return "bun";
32
+ if (userAgent.includes("pnpm/"))
33
+ return "pnpm";
34
+ if (userAgent.includes("yarn/"))
35
+ return "yarn";
36
+ if (userAgent.includes("npm/"))
37
+ return "npm";
38
+ return null;
39
+ }
40
+ function detectIsGlobalFromEnv() {
41
+ if (process.env.npm_config_global === "true")
42
+ return true;
43
+ if (process.env.PNPM_HOME && process.env.npm_config_global !== "false")
44
+ return true;
45
+ const npmConfigPrefix = process.env.npm_config_prefix || "";
46
+ if (npmConfigPrefix.includes("/usr/local") || npmConfigPrefix.includes("/.bun/") || npmConfigPrefix.includes("/.nvm/") || npmConfigPrefix.includes("/pnpm/global")) {
47
+ return true;
48
+ }
49
+ return false;
50
+ }
51
+
52
+ // src/postinstall.ts
53
+ var pm = detectPackageManagerFromEnv();
54
+ var isGlobal = detectIsGlobalFromEnv();
55
+ if (pm) {
56
+ saveInstallInfo({
57
+ packageManager: pm,
58
+ isGlobal,
59
+ installedAt: new Date().toISOString()
60
+ });
61
+ }
@@ -0,0 +1,99 @@
1
+ // src/update-checker.ts
2
+ import { existsSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ function detectPackageManagerFromPath(scriptPath) {
5
+ if (scriptPath.includes("/.bun/"))
6
+ return "bun";
7
+ if (scriptPath.includes("/pnpm/") || scriptPath.includes("/.pnpm"))
8
+ return "pnpm";
9
+ if (scriptPath.includes("/.yarn/") || scriptPath.includes("/yarn/global"))
10
+ return "yarn";
11
+ if (scriptPath.includes("/.nvm/"))
12
+ return "npm";
13
+ if (scriptPath.includes("/usr/local/lib/node_modules") || scriptPath.includes("/usr/lib/node_modules") || scriptPath.includes("/.npm-global/")) {
14
+ return "npm";
15
+ }
16
+ return null;
17
+ }
18
+ function detectPackageManager(scriptPath, env = process.env, cwd = process.cwd()) {
19
+ if (scriptPath) {
20
+ const fromPath = detectPackageManagerFromPath(scriptPath);
21
+ if (fromPath)
22
+ return fromPath;
23
+ }
24
+ const userAgent = env.npm_config_user_agent || "";
25
+ if (userAgent.includes("bun/"))
26
+ return "bun";
27
+ if (userAgent.includes("pnpm/"))
28
+ return "pnpm";
29
+ if (userAgent.includes("yarn/"))
30
+ return "yarn";
31
+ if (userAgent.includes("npm/"))
32
+ return "npm";
33
+ if (existsSync(join(cwd, "bun.lockb")) || existsSync(join(cwd, "bun.lock")))
34
+ return "bun";
35
+ if (existsSync(join(cwd, "pnpm-lock.yaml")))
36
+ return "pnpm";
37
+ if (existsSync(join(cwd, "yarn.lock")))
38
+ return "yarn";
39
+ if (existsSync(join(cwd, "package-lock.json")))
40
+ return "npm";
41
+ return "npm";
42
+ }
43
+ function isGlobalInstall(scriptPath, env = process.env) {
44
+ if (env.npm_config_global === "true")
45
+ return true;
46
+ const globalPaths = [
47
+ "/usr/local/lib/node_modules",
48
+ "/usr/lib/node_modules",
49
+ join(env.HOME || "", ".npm-global"),
50
+ join(env.HOME || "", ".nvm"),
51
+ join(env.HOME || "", ".bun/install/global"),
52
+ join(env.APPDATA || "", "npm"),
53
+ join(env.LOCALAPPDATA || "", "pnpm/global")
54
+ ].filter(Boolean);
55
+ for (const globalPath of globalPaths) {
56
+ if (scriptPath.startsWith(globalPath))
57
+ return true;
58
+ }
59
+ const parts = scriptPath.split("/node_modules/");
60
+ if (parts.length > 1) {
61
+ const projectRoot = parts[0];
62
+ if (existsSync(join(projectRoot, "package.json"))) {
63
+ return false;
64
+ }
65
+ }
66
+ if (env.npm_execpath?.includes("npx") || env._?.includes("npx") || env._?.includes("bunx") || env._?.includes("pnpm dlx")) {
67
+ return true;
68
+ }
69
+ return true;
70
+ }
71
+ function getUpdateCommand(packageManager, isGlobal) {
72
+ const pkgName = "issy";
73
+ const commands = {
74
+ npm: {
75
+ global: `npm install -g ${pkgName}`,
76
+ local: `npm update ${pkgName}`
77
+ },
78
+ yarn: {
79
+ global: `yarn global add ${pkgName}`,
80
+ local: `yarn upgrade ${pkgName}`
81
+ },
82
+ pnpm: {
83
+ global: `pnpm add -g ${pkgName}`,
84
+ local: `pnpm update ${pkgName}`
85
+ },
86
+ bun: {
87
+ global: `bun add -g ${pkgName}`,
88
+ local: `bun update ${pkgName}`
89
+ }
90
+ };
91
+ const pm = commands[packageManager] || commands.npm;
92
+ return isGlobal ? pm.global : pm.local;
93
+ }
94
+ export {
95
+ isGlobalInstall,
96
+ getUpdateCommand,
97
+ detectPackageManagerFromPath,
98
+ detectPackageManager
99
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "issy",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "AI-native issue tracking. Markdown files in .issues/, managed by your coding assistant.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -28,14 +28,15 @@
28
28
  "node": ">=18.0.0"
29
29
  },
30
30
  "scripts": {
31
- "build": "bun build src/cli.ts --outdir dist --target node --format esm --external @miketromba/issy-app",
31
+ "build": "bun build src/cli.ts src/update-checker.ts --outdir dist --target node --format esm --external @miketromba/issy-app",
32
32
  "prepublishOnly": "bun run build",
33
33
  "cli": "bun src/cli.ts",
34
+ "test": "bun test",
34
35
  "lint": "biome check src bin"
35
36
  },
36
37
  "dependencies": {
37
- "@miketromba/issy-app": "^0.3.0",
38
- "@miketromba/issy-core": "^0.3.0",
38
+ "@miketromba/issy-app": "^0.4.0",
39
+ "@miketromba/issy-core": "^0.4.0",
39
40
  "update-notifier": "^7.3.1"
40
41
  }
41
42
  }