ukma-cli 0.1.0 → 0.1.1

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.
Files changed (3) hide show
  1. package/README.md +76 -71
  2. package/bin/ukma.js +187 -187
  3. package/package.json +30 -30
package/README.md CHANGED
@@ -1,71 +1,76 @@
1
- # `npx ukma-cli`
2
-
3
- The npm entry point for UKMA. It is a **launcher, not a second implementation**:
4
- `bin/ukma.js` finds the platform binary for this machine (downloading it from the
5
- GitHub release and verifying its published SHA-256 the first time) and then
6
- execs it, forwarding the exit code. All code-graph behaviour lives in the Rust
7
- binary.
8
-
9
- ```shell
10
- npx ukma-cli # download + run `ukma install` (wire your agents)
11
- npx ukma-cli install --dry-run
12
- npx ukma-cli init # inside a project
13
- npx ukma-cli --version
14
- ```
15
-
16
- ## Where it puts the binary
17
-
18
- - `$UKMA_BIN` — use this executable and do not download anything.
19
- - `$UKMA_BIN_DIR` (default `~/.ukma/bin`) — where a downloaded binary is cached.
20
- The cache is per version: a downloaded `ukma` stays until you delete it, so
21
- `npx ukma-cli@<version>` and a locally installed CLI can coexist.
22
-
23
- ## What it does not do
24
-
25
- - It does not implement any CLI command; every argument is forwarded.
26
- - It does not modify your PATH: `npx` resolves it per invocation. For a
27
- permanent install use `scripts/install.ps1` / `scripts/install.sh` or
28
- `cargo install --path crates/ukma-cli`.
29
- - It does not run on Node < 18.
30
-
31
- ## Publishing
32
-
33
- Publishing must happen **inside this directory** (`npm/`): `npm publish` reads
34
- `package.json` from the current directory, and the repository root deliberately
35
- has none. Running it there fails with:
36
-
37
- ```
38
- npm error code ENOENT
39
- npm error path .../package.json
40
- npm error enoent Could not read package.json
41
- ```
42
-
43
- `--prefix npm` does **not** help for `publish`; use the helper, which `cd`s for
44
- you, shows the tarball first, and stays a dry run unless you ask it to upload:
45
-
46
- ```powershell
47
- .\scripts\publish_npm.ps1 # dry run
48
- .\scripts\publish_npm.ps1 -Tag v0.1.0 -Publish # upload
49
- .\scripts\publish_npm.ps1 -Publish -Otp 123456 # account without a bypass-2FA token
50
- ```
51
-
52
- ```shell
53
- sh scripts/publish_npm.sh # dry run
54
- sh scripts/publish_npm.sh --tag v0.1.0 --publish # upload
55
- sh scripts/publish_npm.sh --publish --otp 123456
56
- ```
57
-
58
- ### Common failures
59
-
60
- | Error | Cause | Fix |
61
- | --- | --- | --- |
62
- | `ENOENT … package.json` | ran outside `npm/` | `cd npm` first, or use the helper |
63
- | `E403 … Two-factor authentication or granular access token with bypass 2fa enabled is required` | the credential cannot satisfy npm's 2FA check. **A Granular Access Token needs "Bypass 2FA" even when the account has 2FA disabled** (`npm profile get` → `two-factor auth: disabled`), and then there is no code to type | recreate the token with **Bypass 2FA** checked (Automation tokens always bypass), or `npm logout` + `npm login` and publish with the web-session credential. If the account really has 2FA on, pass `--otp <code>` |
64
- | `E402 Payment Required` | npm treated the package as private | keep `--access public` |
65
- | `E403 Forbidden - PUT … you do not have permission` | the name belongs to someone else, or the scope is not yours | pick another name in `package.json` (e.g. `@<your-scope>/ukma`) |
66
- | `npx ukma-cli` downloads and gets HTTP 404 | the GitHub release (with the bare `ukma-<triple>` assets) does not exist yet | publish the release first: `git tag v<version> && git push origin v<version>` |
67
-
68
- `.github/workflows/release.yml` publishes this package from `npm/` on every `v*`
69
- tag when the repository has an `NPM_TOKEN` secret; without the secret the release
70
- still produces the installers and `ukma upgrade` assets, and the npm step is
71
- skipped with a notice.
1
+ # `npx ukma-cli`
2
+
3
+ The npm entry point for UKMA. It is a **launcher, not a second implementation**:
4
+ `bin/ukma.js` finds the platform binary for this machine (downloading it from the
5
+ GitHub release and verifying its published SHA-256 the first time) and then
6
+ execs it, forwarding the exit code. All code-graph behaviour lives in the Rust
7
+ binary.
8
+
9
+ ```shell
10
+ npx ukma-cli # download + run `ukma install` (wire your agents)
11
+ npx ukma-cli install --dry-run
12
+ npx ukma-cli init # inside a project
13
+ npx ukma-cli --version
14
+ ```
15
+
16
+ ## Where it puts the binary
17
+
18
+ - `$UKMA_BIN` — use this executable and do not download anything.
19
+ - `$UKMA_BIN_DIR` (default `~/.ukma/bin`) — where a downloaded binary is cached.
20
+ The cache is per version: a downloaded `ukma` stays until you delete it, so
21
+ `npx ukma-cli@<version>` and a locally installed CLI can coexist.
22
+
23
+ ## What it does not do
24
+
25
+ - It does not implement any CLI command; every argument is forwarded.
26
+ - It does not modify your PATH: `npx` resolves it per invocation. For a
27
+ permanent install use `scripts/install.ps1` / `scripts/install.sh` or
28
+ `cargo install --path crates/ukma-cli`.
29
+ - It does not run on Node < 18.
30
+
31
+ ## Publishing
32
+
33
+ Publishing must happen **inside this directory** (`npm/`): `npm publish` reads
34
+ `package.json` from the current directory, and the repository root deliberately
35
+ has none. Running it there fails with:
36
+
37
+ ```
38
+ npm error code ENOENT
39
+ npm error path .../package.json
40
+ npm error enoent Could not read package.json
41
+ ```
42
+
43
+ `--prefix npm` does **not** help for `publish`; use the helper, which `cd`s for
44
+ you, shows the tarball first, and stays a dry run unless you ask it to upload:
45
+
46
+ ```powershell
47
+ .\scripts\publish_npm.ps1 # dry run
48
+ .\scripts\publish_npm.ps1 -Tag v0.1.0 -Publish # upload
49
+ .\scripts\publish_npm.ps1 -Publish -Otp 123456 # account without a bypass-2FA token
50
+ ```
51
+
52
+ ```shell
53
+ sh scripts/publish_npm.sh # dry run
54
+ sh scripts/publish_npm.sh --tag v0.1.0 --publish # upload
55
+ sh scripts/publish_npm.sh --publish --otp 123456
56
+ ```
57
+
58
+ ### Common failures
59
+
60
+ | Error | Cause | Fix |
61
+ | --- | --- | --- |
62
+ | `ENOENT … package.json` | ran outside `npm/` | `cd npm` first, or use the helper |
63
+ | `E403 … Two-factor authentication or granular access token with bypass 2fa enabled is required` | the credential cannot satisfy npm's 2FA check. **A Granular Access Token needs "Bypass 2FA" even when the account has 2FA disabled** (`npm profile get` → `two-factor auth: disabled`), and then there is no code to type | recreate the token with **Bypass 2FA** checked (Automation tokens always bypass), or `npm logout` + `npm login` and publish with the web-session credential. If the account really has 2FA on, pass `--otp <code>` |
64
+ | `E402 Payment Required` | npm treated the package as private | keep `--access public` |
65
+ | `E403 Forbidden - PUT … you do not have permission` | the name belongs to someone else, or the scope is not yours | pick another name in `package.json` (e.g. `@<your-scope>/ukma`) |
66
+ | `npx ukma-cli` downloads and gets HTTP 404 | the GitHub release (with the bare `ukma-<triple>` assets) does not exist yet, **or the repository is still private** — a private repo 404s every asset for anonymous users | publish the release first (`git tag v<version> && git push origin v<version>`), and make the repository public |
67
+ | `npm view` still shows `0.0.0-stage` | that is npm's placeholder while the real version propagates; it takes ~2 minutes | wait and re-check instead of re-publishing |
68
+
69
+ `.github/workflows/release.yml` publishes this package from `npm/` on every `v*`
70
+ tag. It prefers **trusted publishing (OIDC)** — no secret, provenance attached —
71
+ and falls back to an `NPM_TOKEN` secret (granular token with **Bypass 2FA**) when
72
+ one is configured. Configure the trusted publisher on the package's npm page:
73
+ owner `jacoblight-chen`, repository `UKMA`, workflow filename `release.yml`.
74
+
75
+ Full runbook, including the setup for trusted publishing and the rollback rules:
76
+ [`docs/publishing.md`](../docs/publishing.md).
package/bin/ukma.js CHANGED
@@ -1,188 +1,188 @@
1
1
  #!/usr/bin/env node
2
- // The npx/npm launcher for UKMA.
3
- //
4
- // This file contains no code-graph logic: it locates the platform binary
5
- // (downloading it from the GitHub release and verifying its SHA-256 on first
6
- // use) and then replaces itself with it, forwarding the exit code. Everything
7
- // that decides what UKMA does lives in the Rust binary.
8
- //
9
- // `npx ukma-cli` with no arguments runs `ukma install`, matching CodeGraph's
10
- // "downloads and runs the installer in one go".
11
-
12
- const { spawnSync } = require('node:child_process');
13
- const { createHash } = require('node:crypto');
14
- const fs = require('node:fs');
15
- const https = require('node:https');
16
- const os = require('node:os');
17
- const path = require('node:path');
18
-
19
- const REPO = 'jacoblight-chen/UKMA';
20
- const TRIPLES = {
21
- 'win32-x64': 'x86_64-pc-windows-msvc',
22
- 'win32-arm64': 'aarch64-pc-windows-msvc',
23
- 'linux-x64': 'x86_64-unknown-linux-gnu',
24
- 'linux-arm64': 'aarch64-unknown-linux-gnu',
25
- 'darwin-x64': 'x86_64-apple-darwin',
26
- 'darwin-arm64': 'aarch64-apple-darwin',
27
- };
28
-
29
- function fail(message) {
30
- process.stderr.write(`ukma: ${message}\n`);
31
- process.exit(1);
32
- }
33
-
34
- function targetTriple() {
35
- const triple = TRIPLES[`${process.platform}-${process.arch}`];
36
- if (!triple) {
37
- fail(
38
- `no UKMA build is published for ${process.platform}/${process.arch}; ` +
39
- 'build it from source (cargo build -p ukma-cli) or set UKMA_BIN',
40
- );
41
- }
42
- return triple;
43
- }
44
-
45
- function packageVersion() {
46
- return require(path.join(__dirname, '..', 'package.json')).version;
47
- }
48
-
49
- function binaryDir() {
50
- return process.env.UKMA_BIN_DIR || path.join(os.homedir(), '.ukma', 'bin');
51
- }
52
-
53
- function binaryPath() {
54
- const name = process.platform === 'win32' ? 'ukma.exe' : 'ukma';
55
- return path.join(binaryDir(), name);
56
- }
57
-
58
- function assetUrl(triple) {
59
- const version = packageVersion();
60
- const name = process.platform === 'win32' ? `ukma-${triple}.exe` : `ukma-${triple}`;
61
- // A dev version is not published, so fall back to the rolling `latest` tag.
62
- const base = /^\d+\.\d+\.\d+$/.test(version)
63
- ? `https://github.com/${REPO}/releases/download/v${version}`
64
- : `https://github.com/${REPO}/releases/latest/download`;
65
- return { url: `${base}/${name}`, name };
66
- }
67
-
68
- function download(url, destination, redirects = 5) {
69
- return new Promise((resolve, reject) => {
70
- const request = https.get(url, { headers: { 'user-agent': 'ukma-cli-npm' } }, (response) => {
71
- const status = response.statusCode || 0;
72
- if (status >= 300 && status < 400 && response.headers.location) {
73
- response.resume();
74
- if (redirects <= 0) {
75
- reject(new Error(`too many redirects for ${url}`));
76
- return;
77
- }
78
- download(new URL(response.headers.location, url).toString(), destination, redirects - 1)
79
- .then(resolve, reject);
80
- return;
81
- }
82
- if (status !== 200) {
83
- response.resume();
84
- reject(new Error(`${url} returned HTTP ${status}`));
85
- return;
86
- }
87
- const file = fs.createWriteStream(destination);
88
- response.pipe(file);
89
- file.on('finish', () => file.close(() => resolve()));
90
- file.on('error', reject);
91
- });
92
- request.on('error', reject);
93
- request.setTimeout(300000, () => request.destroy(new Error(`timeout downloading ${url}`)));
94
- });
95
- }
96
-
97
- async function sha256(file) {
98
- const hash = createHash('sha256');
99
- await new Promise((resolve, reject) => {
100
- const stream = fs.createReadStream(file);
101
- stream.on('data', (chunk) => hash.update(chunk));
102
- stream.on('end', resolve);
103
- stream.on('error', reject);
104
- });
105
- return hash.digest('hex');
106
- }
107
-
108
- async function ensureBinary() {
109
- if (process.env.UKMA_BIN) {
110
- return process.env.UKMA_BIN;
111
- }
112
- const destination = binaryPath();
113
- if (fs.existsSync(destination)) {
114
- return destination;
115
- }
116
- const triple = targetTriple();
117
- const { url, name } = assetUrl(triple);
118
- const directory = path.dirname(destination);
119
- const rootDirectory = path.dirname(directory);
120
- const created = !fs.existsSync(directory);
121
- const createdRoot = !fs.existsSync(rootDirectory);
122
- fs.mkdirSync(directory, { recursive: true });
123
- const temporary = `${destination}.download`;
124
- const cleanup = () => {
125
- // A failed download must not leave a partial binary (or the directories this
126
- // run created) behind for the next attempt to trip over.
127
- for (const leftover of [temporary, `${temporary}.sha256`]) {
128
- fs.rmSync(leftover, { force: true });
129
- }
130
- for (const [ours, target] of [
131
- [created, directory],
132
- [createdRoot, rootDirectory],
133
- ]) {
134
- if (!ours) continue;
135
- try {
136
- if (fs.readdirSync(target).length === 0) {
137
- fs.rmdirSync(target);
138
- }
139
- } catch {
140
- /* the directory is in use; leaving it is harmless */
141
- }
142
- }
143
- };
144
- process.stderr.write(`ukma: downloading ${name} for ${triple}\n`);
145
-
146
- let expected = null;
147
- try {
148
- const digestFile = `${temporary}.sha256`;
149
- await download(`${url}.sha256`, digestFile);
150
- expected = fs.readFileSync(digestFile, 'utf8').trim().split(/\s+/)[0].toLowerCase();
151
- fs.rmSync(digestFile, { force: true });
152
- } catch (error) {
153
- process.stderr.write(`ukma: no published SHA-256 (${error.message}); continuing\n`);
154
- }
155
-
156
- try {
157
- await download(url, temporary);
158
- if (expected) {
159
- const actual = await sha256(temporary);
160
- if (actual !== expected) {
161
- cleanup();
162
- fail(`SHA-256 mismatch for ${name}: expected ${expected}, got ${actual}`);
163
- }
164
- }
165
- if (process.platform !== 'win32') {
166
- fs.chmodSync(temporary, 0o755);
167
- }
168
- fs.renameSync(temporary, destination);
169
- } catch (error) {
170
- cleanup();
171
- throw error;
172
- }
173
- return destination;
174
- }
175
-
176
- async function main() {
177
- const binary = await ensureBinary();
178
- const args = process.argv.slice(2);
179
- // CodeGraph parity: `npx <package>` with no arguments runs the install step.
180
- const forwarded = args.length === 0 ? ['install'] : args;
181
- const result = spawnSync(binary, forwarded, { stdio: 'inherit' });
182
- if (result.error) {
183
- fail(`cannot run ${binary} (${result.error.message})`);
184
- }
185
- process.exit(result.status === null ? 1 : result.status);
186
- }
187
-
188
- main().catch((error) => fail(error.message));
2
+ // The npx/npm launcher for UKMA.
3
+ //
4
+ // This file contains no code-graph logic: it locates the platform binary
5
+ // (downloading it from the GitHub release and verifying its SHA-256 on first
6
+ // use) and then replaces itself with it, forwarding the exit code. Everything
7
+ // that decides what UKMA does lives in the Rust binary.
8
+ //
9
+ // `npx ukma-cli` with no arguments runs `ukma install`, matching CodeGraph's
10
+ // "downloads and runs the installer in one go".
11
+
12
+ const { spawnSync } = require('node:child_process');
13
+ const { createHash } = require('node:crypto');
14
+ const fs = require('node:fs');
15
+ const https = require('node:https');
16
+ const os = require('node:os');
17
+ const path = require('node:path');
18
+
19
+ const REPO = 'jacoblight-chen/UKMA';
20
+ const TRIPLES = {
21
+ 'win32-x64': 'x86_64-pc-windows-msvc',
22
+ 'win32-arm64': 'aarch64-pc-windows-msvc',
23
+ 'linux-x64': 'x86_64-unknown-linux-gnu',
24
+ 'linux-arm64': 'aarch64-unknown-linux-gnu',
25
+ 'darwin-x64': 'x86_64-apple-darwin',
26
+ 'darwin-arm64': 'aarch64-apple-darwin',
27
+ };
28
+
29
+ function fail(message) {
30
+ process.stderr.write(`ukma: ${message}\n`);
31
+ process.exit(1);
32
+ }
33
+
34
+ function targetTriple() {
35
+ const triple = TRIPLES[`${process.platform}-${process.arch}`];
36
+ if (!triple) {
37
+ fail(
38
+ `no UKMA build is published for ${process.platform}/${process.arch}; ` +
39
+ 'build it from source (cargo build -p ukma-cli) or set UKMA_BIN',
40
+ );
41
+ }
42
+ return triple;
43
+ }
44
+
45
+ function packageVersion() {
46
+ return require(path.join(__dirname, '..', 'package.json')).version;
47
+ }
48
+
49
+ function binaryDir() {
50
+ return process.env.UKMA_BIN_DIR || path.join(os.homedir(), '.ukma', 'bin');
51
+ }
52
+
53
+ function binaryPath() {
54
+ const name = process.platform === 'win32' ? 'ukma.exe' : 'ukma';
55
+ return path.join(binaryDir(), name);
56
+ }
57
+
58
+ function assetUrl(triple) {
59
+ const version = packageVersion();
60
+ const name = process.platform === 'win32' ? `ukma-${triple}.exe` : `ukma-${triple}`;
61
+ // A dev version is not published, so fall back to the rolling `latest` tag.
62
+ const base = /^\d+\.\d+\.\d+$/.test(version)
63
+ ? `https://github.com/${REPO}/releases/download/v${version}`
64
+ : `https://github.com/${REPO}/releases/latest/download`;
65
+ return { url: `${base}/${name}`, name };
66
+ }
67
+
68
+ function download(url, destination, redirects = 5) {
69
+ return new Promise((resolve, reject) => {
70
+ const request = https.get(url, { headers: { 'user-agent': 'ukma-cli-npm' } }, (response) => {
71
+ const status = response.statusCode || 0;
72
+ if (status >= 300 && status < 400 && response.headers.location) {
73
+ response.resume();
74
+ if (redirects <= 0) {
75
+ reject(new Error(`too many redirects for ${url}`));
76
+ return;
77
+ }
78
+ download(new URL(response.headers.location, url).toString(), destination, redirects - 1)
79
+ .then(resolve, reject);
80
+ return;
81
+ }
82
+ if (status !== 200) {
83
+ response.resume();
84
+ reject(new Error(`${url} returned HTTP ${status}`));
85
+ return;
86
+ }
87
+ const file = fs.createWriteStream(destination);
88
+ response.pipe(file);
89
+ file.on('finish', () => file.close(() => resolve()));
90
+ file.on('error', reject);
91
+ });
92
+ request.on('error', reject);
93
+ request.setTimeout(300000, () => request.destroy(new Error(`timeout downloading ${url}`)));
94
+ });
95
+ }
96
+
97
+ async function sha256(file) {
98
+ const hash = createHash('sha256');
99
+ await new Promise((resolve, reject) => {
100
+ const stream = fs.createReadStream(file);
101
+ stream.on('data', (chunk) => hash.update(chunk));
102
+ stream.on('end', resolve);
103
+ stream.on('error', reject);
104
+ });
105
+ return hash.digest('hex');
106
+ }
107
+
108
+ async function ensureBinary() {
109
+ if (process.env.UKMA_BIN) {
110
+ return process.env.UKMA_BIN;
111
+ }
112
+ const destination = binaryPath();
113
+ if (fs.existsSync(destination)) {
114
+ return destination;
115
+ }
116
+ const triple = targetTriple();
117
+ const { url, name } = assetUrl(triple);
118
+ const directory = path.dirname(destination);
119
+ const rootDirectory = path.dirname(directory);
120
+ const created = !fs.existsSync(directory);
121
+ const createdRoot = !fs.existsSync(rootDirectory);
122
+ fs.mkdirSync(directory, { recursive: true });
123
+ const temporary = `${destination}.download`;
124
+ const cleanup = () => {
125
+ // A failed download must not leave a partial binary (or the directories this
126
+ // run created) behind for the next attempt to trip over.
127
+ for (const leftover of [temporary, `${temporary}.sha256`]) {
128
+ fs.rmSync(leftover, { force: true });
129
+ }
130
+ for (const [ours, target] of [
131
+ [created, directory],
132
+ [createdRoot, rootDirectory],
133
+ ]) {
134
+ if (!ours) continue;
135
+ try {
136
+ if (fs.readdirSync(target).length === 0) {
137
+ fs.rmdirSync(target);
138
+ }
139
+ } catch {
140
+ /* the directory is in use; leaving it is harmless */
141
+ }
142
+ }
143
+ };
144
+ process.stderr.write(`ukma: downloading ${name} for ${triple}\n`);
145
+
146
+ let expected = null;
147
+ try {
148
+ const digestFile = `${temporary}.sha256`;
149
+ await download(`${url}.sha256`, digestFile);
150
+ expected = fs.readFileSync(digestFile, 'utf8').trim().split(/\s+/)[0].toLowerCase();
151
+ fs.rmSync(digestFile, { force: true });
152
+ } catch (error) {
153
+ process.stderr.write(`ukma: no published SHA-256 (${error.message}); continuing\n`);
154
+ }
155
+
156
+ try {
157
+ await download(url, temporary);
158
+ if (expected) {
159
+ const actual = await sha256(temporary);
160
+ if (actual !== expected) {
161
+ cleanup();
162
+ fail(`SHA-256 mismatch for ${name}: expected ${expected}, got ${actual}`);
163
+ }
164
+ }
165
+ if (process.platform !== 'win32') {
166
+ fs.chmodSync(temporary, 0o755);
167
+ }
168
+ fs.renameSync(temporary, destination);
169
+ } catch (error) {
170
+ cleanup();
171
+ throw error;
172
+ }
173
+ return destination;
174
+ }
175
+
176
+ async function main() {
177
+ const binary = await ensureBinary();
178
+ const args = process.argv.slice(2);
179
+ // CodeGraph parity: `npx <package>` with no arguments runs the install step.
180
+ const forwarded = args.length === 0 ? ['install'] : args;
181
+ const result = spawnSync(binary, forwarded, { stdio: 'inherit' });
182
+ if (result.error) {
183
+ fail(`cannot run ${binary} (${result.error.message})`);
184
+ }
185
+ process.exit(result.status === null ? 1 : result.status);
186
+ }
187
+
188
+ main().catch((error) => fail(error.message));
package/package.json CHANGED
@@ -1,30 +1,30 @@
1
- {
2
- "name": "ukma-cli",
3
- "version": "0.1.0",
4
- "description": "UKMA launcher for npm/npx: downloads the platform binary and runs it (no code-graph logic here)",
5
- "bin": {
6
- "ukma": "bin/ukma.js"
7
- },
8
- "files": [
9
- "bin/ukma.js",
10
- "README.md"
11
- ],
12
- "engines": {
13
- "node": ">=18"
14
- },
15
- "license": "Apache-2.0",
16
- "repository": {
17
- "type": "git",
18
- "url": "git+https://github.com/jacoblight-chen/UKMA.git"
19
- },
20
- "homepage": "https://github.com/jacoblight-chen/UKMA#readme",
21
- "keywords": [
22
- "code-graph",
23
- "mcp",
24
- "codegraph",
25
- "ukma"
26
- ],
27
- "publishConfig": {
28
- "access": "public"
29
- }
30
- }
1
+ {
2
+ "name": "ukma-cli",
3
+ "version": "0.1.1",
4
+ "description": "UKMA launcher for npm/npx: downloads the platform binary and runs it (no code-graph logic here)",
5
+ "bin": {
6
+ "ukma": "bin/ukma.js"
7
+ },
8
+ "files": [
9
+ "bin/ukma.js",
10
+ "README.md"
11
+ ],
12
+ "engines": {
13
+ "node": ">=18"
14
+ },
15
+ "license": "Apache-2.0",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/jacoblight-chen/UKMA.git"
19
+ },
20
+ "homepage": "https://github.com/jacoblight-chen/UKMA#readme",
21
+ "keywords": [
22
+ "code-graph",
23
+ "mcp",
24
+ "codegraph",
25
+ "ukma"
26
+ ],
27
+ "publishConfig": {
28
+ "access": "public"
29
+ }
30
+ }