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 +20 -1
- package/dist/install-info.js +54 -0
- package/dist/postinstall.js +61 -0
- package/dist/update-checker.js +99 -0
- package/package.json +5 -4
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
|
-
|
|
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
|
+
"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.
|
|
38
|
-
"@miketromba/issy-core": "^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
|
}
|