@angelmsger/jira-cli 0.1.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/README.md +23 -0
- package/bin/jira-cli.js +30 -0
- package/install.js +151 -0
- package/package.json +44 -0
package/README.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# @angelmsger/jira-cli
|
|
2
|
+
|
|
3
|
+
npm distribution of [`jira-cli`](https://github.com/angelmsger/jira-cli)
|
|
4
|
+
— a command-line tool that lets coding agents use a Jira instance as an
|
|
5
|
+
external knowledge base.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @angelmsger/jira-cli
|
|
9
|
+
jira-cli config init --pretty # interactive TUI: server URL + credentials
|
|
10
|
+
jira-cli skill install # deploy the companion agent Skill
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Installing this package downloads the prebuilt binary for your platform from the
|
|
14
|
+
matching GitHub Release and verifies its SHA-256 checksum. If your npm setup
|
|
15
|
+
disables install scripts, the binary is fetched on first run instead.
|
|
16
|
+
|
|
17
|
+
The companion `jira` Skill for coding agents is embedded in the binary;
|
|
18
|
+
`jira-cli skill install` deploys a copy that always matches the installed
|
|
19
|
+
CLI version.
|
|
20
|
+
|
|
21
|
+
See the [project README](https://github.com/angelmsger/jira-cli) and the
|
|
22
|
+
[installation guide](https://github.com/angelmsger/jira-cli/blob/main/docs/installation.md)
|
|
23
|
+
for full documentation.
|
package/bin/jira-cli.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
// Thin launcher for the jira-cli binary. It execs the platform binary
|
|
4
|
+
// downloaded by install.js, fetching it on demand if it is not present yet
|
|
5
|
+
// (e.g. when the package was installed with --ignore-scripts).
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const { spawnSync } = require('child_process');
|
|
9
|
+
const { binPath, install } = require('../install.js');
|
|
10
|
+
|
|
11
|
+
async function main() {
|
|
12
|
+
const { file } = binPath();
|
|
13
|
+
|
|
14
|
+
if (!fs.existsSync(file)) {
|
|
15
|
+
process.stderr.write('jira-cli: downloading binary...\n');
|
|
16
|
+
await install();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const res = spawnSync(file, process.argv.slice(2), { stdio: 'inherit' });
|
|
20
|
+
if (res.error) {
|
|
21
|
+
process.stderr.write(`jira-cli: ${res.error.message}\n`);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
process.exit(res.status === null ? 1 : res.status);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
main().catch((err) => {
|
|
28
|
+
process.stderr.write(`jira-cli: ${err.message}\n`);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
});
|
package/install.js
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// install.js downloads the prebuilt jira-cli binary that matches the host
|
|
3
|
+
// platform from the matching GitHub Release. It runs as the npm `postinstall`
|
|
4
|
+
// script, and is also called lazily by the bin shim when the binary is missing
|
|
5
|
+
// (so installs with `--ignore-scripts` still work on first run).
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const https = require('https');
|
|
10
|
+
const crypto = require('crypto');
|
|
11
|
+
|
|
12
|
+
const pkg = require('./package.json');
|
|
13
|
+
|
|
14
|
+
const REPO = 'angelmsger/jira-cli';
|
|
15
|
+
|
|
16
|
+
const goosByPlatform = { darwin: 'darwin', linux: 'linux', win32: 'windows' };
|
|
17
|
+
const goarchByArch = { x64: 'amd64', arm64: 'arm64' };
|
|
18
|
+
|
|
19
|
+
// assetName returns the release asset file name for the current platform.
|
|
20
|
+
function assetName(platform = process.platform, arch = process.arch) {
|
|
21
|
+
const goos = goosByPlatform[platform];
|
|
22
|
+
const goarch = goarchByArch[arch];
|
|
23
|
+
if (!goos || !goarch) {
|
|
24
|
+
throw new Error(
|
|
25
|
+
`unsupported platform ${platform}/${arch}; ` +
|
|
26
|
+
`build from source instead (see https://github.com/${REPO})`
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
return `jira-cli-${goos}-${goarch}` + (goos === 'windows' ? '.exe' : '');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// binPath returns the directory and file path for the installed binary.
|
|
33
|
+
function binPath(platform = process.platform) {
|
|
34
|
+
const dir = path.join(__dirname, 'binary');
|
|
35
|
+
const exe = platform === 'win32' ? 'jira-cli.exe' : 'jira-cli';
|
|
36
|
+
return { dir, file: path.join(dir, exe) };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// releaseBaseURL is the GitHub Release download prefix for this package version.
|
|
40
|
+
function releaseBaseURL() {
|
|
41
|
+
return `https://github.com/${REPO}/releases/download/v${pkg.version}`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// httpGet fetches a URL into a Buffer, following redirects.
|
|
45
|
+
function httpGet(url, redirects = 0) {
|
|
46
|
+
return new Promise((resolve, reject) => {
|
|
47
|
+
if (redirects > 8) {
|
|
48
|
+
reject(new Error('too many redirects'));
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
https
|
|
52
|
+
.get(url, { headers: { 'User-Agent': 'jira-cli-npm-installer' } }, (res) => {
|
|
53
|
+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
54
|
+
res.resume();
|
|
55
|
+
resolve(httpGet(res.headers.location, redirects + 1));
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (res.statusCode !== 200) {
|
|
59
|
+
res.resume();
|
|
60
|
+
reject(new Error(`GET ${url} -> HTTP ${res.statusCode}`));
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const chunks = [];
|
|
64
|
+
res.on('data', (c) => chunks.push(c));
|
|
65
|
+
res.on('end', () => resolve(Buffer.concat(chunks)));
|
|
66
|
+
})
|
|
67
|
+
.on('error', reject);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// expectedChecksum fetches checksums.txt and returns the SHA-256 for asset.
|
|
72
|
+
// Returns null when the checksum file is unavailable (verification skipped).
|
|
73
|
+
async function expectedChecksum(asset) {
|
|
74
|
+
try {
|
|
75
|
+
const text = (await httpGet(`${releaseBaseURL()}/checksums.txt`)).toString('utf8');
|
|
76
|
+
for (const line of text.split('\n')) {
|
|
77
|
+
const [hash, name] = line.trim().split(/\s+/);
|
|
78
|
+
if (name === asset && hash) return hash.toLowerCase();
|
|
79
|
+
}
|
|
80
|
+
} catch {
|
|
81
|
+
// No checksums published for this release; skip verification.
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// install downloads, verifies and writes the binary. It is idempotent.
|
|
87
|
+
async function install() {
|
|
88
|
+
if (!pkg.version || pkg.version === '0.0.0') {
|
|
89
|
+
throw new Error('package version is unset; install from a published release');
|
|
90
|
+
}
|
|
91
|
+
const asset = assetName();
|
|
92
|
+
const { dir, file } = binPath();
|
|
93
|
+
|
|
94
|
+
const data = await httpGet(`${releaseBaseURL()}/${asset}`);
|
|
95
|
+
|
|
96
|
+
const want = await expectedChecksum(asset);
|
|
97
|
+
if (want) {
|
|
98
|
+
const got = crypto.createHash('sha256').update(data).digest('hex');
|
|
99
|
+
if (got !== want) {
|
|
100
|
+
throw new Error(`checksum mismatch for ${asset} (want ${want}, got ${got})`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
105
|
+
fs.writeFileSync(file, data, { mode: 0o755 });
|
|
106
|
+
return file;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// welcomeText is the getting-started banner printed once at install time (by the
|
|
110
|
+
// postinstall script below). It is never printed by the CLI itself, so command
|
|
111
|
+
// output — JSON and everything else — is never touched.
|
|
112
|
+
function welcomeText() {
|
|
113
|
+
return [
|
|
114
|
+
'',
|
|
115
|
+
'jira-cli is ready. First-time setup:',
|
|
116
|
+
'',
|
|
117
|
+
' jira-cli config init --pretty configure your server + credentials (interactive)',
|
|
118
|
+
' jira-cli skill install install the coding-agent Skill',
|
|
119
|
+
'',
|
|
120
|
+
'Everyday use:',
|
|
121
|
+
' jira-cli search "<text>"',
|
|
122
|
+
' jira-cli page get <pageId>',
|
|
123
|
+
' jira-cli --help',
|
|
124
|
+
'',
|
|
125
|
+
'Docs: https://angelmsger.github.io/jira-cli/',
|
|
126
|
+
'',
|
|
127
|
+
].join('\n');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
module.exports = { install, binPath, assetName, welcomeText, REPO };
|
|
131
|
+
|
|
132
|
+
// When run directly as the npm postinstall script, download best-effort: a
|
|
133
|
+
// failure here is not fatal because the bin shim retries lazily on first run.
|
|
134
|
+
// The getting-started banner is printed here (install time) and nowhere else.
|
|
135
|
+
// Note: npm v7+ hides postinstall output unless `npm install --foreground-scripts`
|
|
136
|
+
// is used, so this may not be visible on a default install.
|
|
137
|
+
if (require.main === module) {
|
|
138
|
+
install()
|
|
139
|
+
.then((file) => {
|
|
140
|
+
process.stdout.write(`jira-cli: installed ${file}\n`);
|
|
141
|
+
})
|
|
142
|
+
.catch((err) => {
|
|
143
|
+
process.stderr.write(
|
|
144
|
+
`jira-cli: postinstall download skipped (${err.message}); ` +
|
|
145
|
+
'the binary will be fetched on first run.\n'
|
|
146
|
+
);
|
|
147
|
+
})
|
|
148
|
+
.finally(() => {
|
|
149
|
+
if (!process.env.CI) process.stdout.write(welcomeText());
|
|
150
|
+
});
|
|
151
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@angelmsger/jira-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Drive Jira issue-tracking workflows from the command line, built for coding agents: read and search issues via JQL, create/edit/assign/transition them, and manage comments across Cloud and Data Center.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"jira",
|
|
7
|
+
"cli",
|
|
8
|
+
"issues",
|
|
9
|
+
"agent",
|
|
10
|
+
"claude",
|
|
11
|
+
"jql"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://angelmsger.github.io/jira-cli/",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/AngelMsger/jira-cli.git"
|
|
17
|
+
},
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"bin": {
|
|
20
|
+
"jira-cli": "bin/jira-cli.js"
|
|
21
|
+
},
|
|
22
|
+
"main": "install.js",
|
|
23
|
+
"scripts": {
|
|
24
|
+
"postinstall": "node install.js",
|
|
25
|
+
"test": "node --test install.test.js"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"bin/",
|
|
29
|
+
"install.js",
|
|
30
|
+
"README.md"
|
|
31
|
+
],
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=18"
|
|
34
|
+
},
|
|
35
|
+
"os": [
|
|
36
|
+
"darwin",
|
|
37
|
+
"linux",
|
|
38
|
+
"win32"
|
|
39
|
+
],
|
|
40
|
+
"cpu": [
|
|
41
|
+
"x64",
|
|
42
|
+
"arm64"
|
|
43
|
+
]
|
|
44
|
+
}
|