@mhd1999/morkit-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 +85 -0
- package/dist/commands/commands.js +66 -0
- package/dist/commands/commands.js.map +1 -0
- package/dist/commands/install.js +107 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/index.js +56 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/api.js +93 -0
- package/dist/lib/api.js.map +1 -0
- package/dist/lib/config.js +57 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/install.js +79 -0
- package/dist/lib/install.js.map +1 -0
- package/dist/lib/lock.js +55 -0
- package/dist/lib/lock.js.map +1 -0
- package/dist/lib/paths.js +46 -0
- package/dist/lib/paths.js.map +1 -0
- package/dist/lib/tar.js +113 -0
- package/dist/lib/tar.js.map +1 -0
- package/package.json +39 -0
package/README.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# @mhd1999/morkit-cli
|
|
2
|
+
|
|
3
|
+
Install the **morkit** kit — skills, commands and agents for Claude Code — into a
|
|
4
|
+
project's `.claude/` directory.
|
|
5
|
+
|
|
6
|
+
This package ships **no kit content**. It is a downloader: the kit lives in a private
|
|
7
|
+
SkillHub instance and is fetched with your personal access token. That is what replaces
|
|
8
|
+
the old `/plugin marketplace add mor-duongmh/morkit-plugin` flow, which required the
|
|
9
|
+
plugin repository to be public.
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm i -g @mhd1999/morkit-cli
|
|
13
|
+
|
|
14
|
+
morkit login --pat skh_… # once per machine
|
|
15
|
+
morkit init --dir my-project # once per project
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Migrating from `/plugin marketplace add`
|
|
19
|
+
|
|
20
|
+
| Before | Now |
|
|
21
|
+
| ----------------------------------------------------- | -------------------------------- |
|
|
22
|
+
| `/plugin marketplace add mor-duongmh/morkit-plugin` | `morkit login` + `morkit init` |
|
|
23
|
+
| `/plugin install morkit` | `morkit init --dir <project>` |
|
|
24
|
+
| new release lands automatically for everyone | `morkit update` per project |
|
|
25
|
+
| content installed under `~/.claude/` | content installed under `<project>/.claude/` |
|
|
26
|
+
|
|
27
|
+
The kit is now **per project**, not per machine. Commit `.claude/` and
|
|
28
|
+
`.morkit-lock.json` with the project so everyone on the team gets the same version, and
|
|
29
|
+
so a version bump shows up in a pull-request diff instead of changing silently.
|
|
30
|
+
|
|
31
|
+
Ask the SkillHub owner for a personal access token with the `skills:read` scope.
|
|
32
|
+
|
|
33
|
+
## Commands
|
|
34
|
+
|
|
35
|
+
| Command | What it does |
|
|
36
|
+
| --- | --- |
|
|
37
|
+
| `morkit login --pat skh_…` | Save credentials to `~/.morkit/config.json` (mode `0600`). |
|
|
38
|
+
| `morkit list` | List the kits your token can read. |
|
|
39
|
+
| `morkit init [--dir .] [--kit morkit]` | Install a kit into `<dir>/.claude/` and write `<dir>/.morkit-lock.json`. |
|
|
40
|
+
| `morkit new --dir <d>` | Create an empty directory and install into it. Refuses a non-empty one. |
|
|
41
|
+
| `morkit update [--force]` | Re-install the kit recorded in the lockfile, keeping files you edited. |
|
|
42
|
+
|
|
43
|
+
Common flags: `--api-url <url>` and `--pat <token>` work on every command that talks to
|
|
44
|
+
the server and override the saved configuration. The default server is
|
|
45
|
+
`https://skillhub.rainbow-banfish.ts.net`.
|
|
46
|
+
|
|
47
|
+
## Where files land
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
my-project/
|
|
51
|
+
.claude/
|
|
52
|
+
skills/… ← kit skills/, loaded by Claude Code
|
|
53
|
+
commands/… ← kit commands/
|
|
54
|
+
agents/… ← kit agents/
|
|
55
|
+
plugins/morkit/… ← everything else (manifest, hooks, lib, scripts)
|
|
56
|
+
.morkit-lock.json ← what was installed — commit this
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`.morkit-lock.json` records the API URL, kit slug, version, install timestamp, a
|
|
60
|
+
`sha256` per artifact and one combined hash for the rest. **It never contains a token.**
|
|
61
|
+
|
|
62
|
+
## Updating, and what happens to your edits
|
|
63
|
+
|
|
64
|
+
`morkit update` compares each file on disk against the lockfile:
|
|
65
|
+
|
|
66
|
+
- untouched since install → replaced with the new version;
|
|
67
|
+
- **edited locally → left alone**, and the CLI prints the file name;
|
|
68
|
+
- `--force` → overwritten with the server's copy.
|
|
69
|
+
|
|
70
|
+
Files under `.claude/plugins/<kit>/` are the kit's machinery (manifest, hooks, helper
|
|
71
|
+
scripts). They are replaced as a unit whenever the server's shell hash changes, so treat
|
|
72
|
+
that directory as managed and keep your own changes in `.claude/skills|commands|agents`.
|
|
73
|
+
|
|
74
|
+
## Credentials
|
|
75
|
+
|
|
76
|
+
The token is stored **only** in `~/.morkit/config.json` with mode `0600` — never inside
|
|
77
|
+
a project directory, never in the lockfile. Delete that file to log out.
|
|
78
|
+
|
|
79
|
+
## Requirements
|
|
80
|
+
|
|
81
|
+
Node.js ≥ 18.
|
|
82
|
+
|
|
83
|
+
## Licence
|
|
84
|
+
|
|
85
|
+
Apache-2.0
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { listKits } from "../lib/api.js";
|
|
4
|
+
import { DEFAULT_API_URL, normaliseApiUrl, readConfig, resolveAuth, writeConfig, } from "../lib/config.js";
|
|
5
|
+
import { readLock } from "../lib/lock.js";
|
|
6
|
+
import { DEFAULT_KIT, installKit, reportInstall, } from "./install.js";
|
|
7
|
+
/** Store the PAT in ~/.morkit/config.json (0600). Never inside a project. */
|
|
8
|
+
export async function login(opts) {
|
|
9
|
+
if (!opts.pat) {
|
|
10
|
+
throw new Error("Missing --pat. Create a token with the 'skills:read' scope in SkillHub.");
|
|
11
|
+
}
|
|
12
|
+
const apiUrl = normaliseApiUrl(opts.apiUrl ?? readConfig()?.apiUrl ?? DEFAULT_API_URL);
|
|
13
|
+
// Fail fast on a bad token. A server without the listing endpoint answers 404,
|
|
14
|
+
// which says nothing about the token — accept it and let `init` be the check.
|
|
15
|
+
try {
|
|
16
|
+
await listKits(apiUrl, opts.pat);
|
|
17
|
+
process.stdout.write(`✓ Token accepted by ${apiUrl}\n`);
|
|
18
|
+
}
|
|
19
|
+
catch (err) {
|
|
20
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
21
|
+
if (/\(401\)|\(403\)/.test(msg))
|
|
22
|
+
throw err;
|
|
23
|
+
process.stderr.write(`! Could not verify the token (${msg})\n`);
|
|
24
|
+
}
|
|
25
|
+
const file = writeConfig({ apiUrl, pat: opts.pat });
|
|
26
|
+
process.stdout.write(`✓ Saved credentials to ${file} (mode 0600)\n`);
|
|
27
|
+
}
|
|
28
|
+
export async function list(opts) {
|
|
29
|
+
const auth = resolveAuth({ apiUrl: opts.apiUrl, pat: opts.pat });
|
|
30
|
+
const kits = await listKits(auth.apiUrl, auth.pat);
|
|
31
|
+
if (kits.length === 0) {
|
|
32
|
+
process.stdout.write(`No kits available on ${auth.apiUrl}.\n`);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
for (const kit of kits) {
|
|
36
|
+
const version = kit.version ? ` ${kit.version}` : "";
|
|
37
|
+
const desc = kit.description ? ` — ${kit.description}` : "";
|
|
38
|
+
process.stdout.write(` ${kit.slug}${version}${desc}\n`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export async function init(opts) {
|
|
42
|
+
const dir = path.resolve(opts.dir);
|
|
43
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
44
|
+
const res = await installKit({ ...opts, dir });
|
|
45
|
+
reportInstall(res, dir, opts.force ?? false);
|
|
46
|
+
}
|
|
47
|
+
/** Like `init`, but refuses to install into a directory that already has content. */
|
|
48
|
+
export async function newProject(opts) {
|
|
49
|
+
const dir = path.resolve(opts.dir);
|
|
50
|
+
if (fs.existsSync(dir) && fs.readdirSync(dir).length > 0) {
|
|
51
|
+
throw new Error(`${dir} is not empty — use 'morkit init --dir ${opts.dir}' instead.`);
|
|
52
|
+
}
|
|
53
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
54
|
+
const res = await installKit({ ...opts, dir });
|
|
55
|
+
reportInstall(res, dir, opts.force ?? false);
|
|
56
|
+
}
|
|
57
|
+
export async function update(opts) {
|
|
58
|
+
const dir = path.resolve(opts.dir);
|
|
59
|
+
const lock = readLock(dir);
|
|
60
|
+
if (!lock) {
|
|
61
|
+
throw new Error(`No .morkit-lock.json in ${dir} — run 'morkit init --dir ${opts.dir}' first.`);
|
|
62
|
+
}
|
|
63
|
+
const res = await installKit({ ...opts, dir, kit: opts.kit ?? lock.kit ?? DEFAULT_KIT });
|
|
64
|
+
reportInstall(res, dir, opts.force ?? false);
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=commands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/commands/commands.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { fetchKitArchive, fetchKitMeta } from "../lib/api.js";
|
|
3
|
+
import { resolveAuth } from "../lib/config.js";
|
|
4
|
+
import { applyPlan, computePlan, ensureClaudeLayout } from "../lib/install.js";
|
|
5
|
+
import { isoUtc, lockMatches, readLock, sha256, writeLock } from "../lib/lock.js";
|
|
6
|
+
import { isArtifactPath } from "../lib/paths.js";
|
|
7
|
+
import { extractTarGz } from "../lib/tar.js";
|
|
8
|
+
export const DEFAULT_KIT = "morkit";
|
|
9
|
+
/**
|
|
10
|
+
* Shared body of `init` and `update`.
|
|
11
|
+
*
|
|
12
|
+
* Always fetches metadata + archive and diffs against disk rather than
|
|
13
|
+
* short-circuiting on the version number, so a locally modified file is
|
|
14
|
+
* reported even when the server version has not moved.
|
|
15
|
+
*/
|
|
16
|
+
export async function installKit(opts) {
|
|
17
|
+
const dir = path.resolve(opts.dir);
|
|
18
|
+
const existing = readLock(dir);
|
|
19
|
+
const kit = opts.kit ?? existing?.kit ?? DEFAULT_KIT;
|
|
20
|
+
const auth = resolveAuth({
|
|
21
|
+
apiUrl: opts.apiUrl,
|
|
22
|
+
pat: opts.pat,
|
|
23
|
+
lockApiUrl: existing?.apiUrl || undefined,
|
|
24
|
+
});
|
|
25
|
+
const meta = await fetchKitMeta(auth.apiUrl, auth.pat, kit);
|
|
26
|
+
const archive = await fetchKitArchive(auth.apiUrl, auth.pat, kit);
|
|
27
|
+
const { files, executable } = extractTarGz(archive);
|
|
28
|
+
if (files.size === 0) {
|
|
29
|
+
throw new Error(`Kit '${kit}' archive from ${auth.apiUrl} is empty.`);
|
|
30
|
+
}
|
|
31
|
+
// The lock records what is actually on disk, so hashes come from the bytes we
|
|
32
|
+
// write. Cross-check them against the server's metadata as an integrity
|
|
33
|
+
// signal, but do not fail the install on a hashing-convention mismatch.
|
|
34
|
+
const mismatched = Object.entries(meta.artifacts).filter(([rel, hash]) => {
|
|
35
|
+
const content = files.get(rel);
|
|
36
|
+
return content !== undefined && sha256(content) !== hash;
|
|
37
|
+
});
|
|
38
|
+
// Lock is authoritative for the previous install only when it describes this kit.
|
|
39
|
+
const lock = existing && existing.kit === kit ? existing : null;
|
|
40
|
+
const plan = computePlan({
|
|
41
|
+
files,
|
|
42
|
+
kit,
|
|
43
|
+
dir,
|
|
44
|
+
lock,
|
|
45
|
+
remoteShell: meta.shell,
|
|
46
|
+
force: opts.force ?? false,
|
|
47
|
+
});
|
|
48
|
+
// `artifacts` holds skills/ commands/ agents/ only — the rest of the kit is
|
|
49
|
+
// covered by the single `shell` hash (design.md, "Định dạng .morkit-lock.json").
|
|
50
|
+
const artifacts = {};
|
|
51
|
+
for (const entry of [...plan.write, ...plan.unchanged]) {
|
|
52
|
+
if (isArtifactPath(entry.rel))
|
|
53
|
+
artifacts[entry.rel] = entry.remoteHash;
|
|
54
|
+
}
|
|
55
|
+
for (const entry of plan.keep) {
|
|
56
|
+
if (!isArtifactPath(entry.rel))
|
|
57
|
+
continue;
|
|
58
|
+
// Kept a local edit: remember the hash we last installed so the file stays
|
|
59
|
+
// recognisable as modified next time. Fall back to the remote hash when
|
|
60
|
+
// there is no prior record, so every artifact has an entry.
|
|
61
|
+
artifacts[entry.rel] = lock?.artifacts[entry.rel] ?? entry.remoteHash;
|
|
62
|
+
}
|
|
63
|
+
const next = {
|
|
64
|
+
apiUrl: auth.apiUrl,
|
|
65
|
+
kit,
|
|
66
|
+
version: meta.version,
|
|
67
|
+
artifacts,
|
|
68
|
+
shell: meta.shell,
|
|
69
|
+
};
|
|
70
|
+
const upToDate = plan.write.length === 0 && lockMatches(existing, next);
|
|
71
|
+
if (!upToDate) {
|
|
72
|
+
ensureClaudeLayout(dir);
|
|
73
|
+
applyPlan(plan, files, executable);
|
|
74
|
+
writeLock(dir, { ...next, installedAt: isoUtc() });
|
|
75
|
+
}
|
|
76
|
+
if (mismatched.length > 0) {
|
|
77
|
+
process.stderr.write(`! ${mismatched.length} file(s) hash differently than ${auth.apiUrl} reported; ` +
|
|
78
|
+
"installed the archive contents as received.\n");
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
kit,
|
|
82
|
+
version: meta.version,
|
|
83
|
+
written: plan.write.map((e) => e.rel),
|
|
84
|
+
kept: plan.keep.map((e) => e.rel),
|
|
85
|
+
unchanged: plan.unchanged.length,
|
|
86
|
+
upToDate,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/** Human-readable summary shared by `init`, `new` and `update`. */
|
|
90
|
+
export function reportInstall(res, dir, force) {
|
|
91
|
+
if (res.upToDate) {
|
|
92
|
+
process.stdout.write(`✓ ${dir} is already up to date (${res.kit} ${res.version}).\n`);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
for (const rel of res.written)
|
|
96
|
+
process.stdout.write(` + ${rel}\n`);
|
|
97
|
+
if (res.kept.length > 0) {
|
|
98
|
+
process.stderr.write(`\n! Kept ${res.kept.length} locally modified file(s) — not overwritten:\n`);
|
|
99
|
+
for (const rel of res.kept)
|
|
100
|
+
process.stderr.write(` ~ ${rel}\n`);
|
|
101
|
+
if (!force)
|
|
102
|
+
process.stderr.write(" Re-run with --force to overwrite them.\n");
|
|
103
|
+
}
|
|
104
|
+
process.stdout.write(`\n✓ ${res.kit} ${res.version} → ${dir}` +
|
|
105
|
+
` (${res.written.length} written, ${res.kept.length} kept, ${res.unchanged} unchanged)\n`);
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { DEFAULT_API_URL } from "./lib/config.js";
|
|
4
|
+
import { init, list, login, newProject, update } from "./commands/commands.js";
|
|
5
|
+
import { DEFAULT_KIT } from "./commands/install.js";
|
|
6
|
+
const program = new Command();
|
|
7
|
+
program
|
|
8
|
+
.name("morkit")
|
|
9
|
+
.description("Install the morkit kit into a project's .claude/ directory from SkillHub. " +
|
|
10
|
+
"Ships no kit content — everything is downloaded with your personal access token.")
|
|
11
|
+
.version("0.1.0");
|
|
12
|
+
program
|
|
13
|
+
.command("login")
|
|
14
|
+
.description("Save a personal access token to ~/.morkit/config.json (mode 0600)")
|
|
15
|
+
.requiredOption("--pat <token>", "personal access token skh_… with the skills:read scope")
|
|
16
|
+
.option("--api-url <url>", `SkillHub API base URL (default ${DEFAULT_API_URL})`)
|
|
17
|
+
.action((opts) => login(opts));
|
|
18
|
+
program
|
|
19
|
+
.command("list")
|
|
20
|
+
.description("List the kits your token can read")
|
|
21
|
+
.option("--api-url <url>", "SkillHub API base URL")
|
|
22
|
+
.option("--pat <token>", "personal access token (overrides ~/.morkit/config.json)")
|
|
23
|
+
.action((opts) => list(opts));
|
|
24
|
+
program
|
|
25
|
+
.command("init")
|
|
26
|
+
.description("Install a kit into <dir>/.claude/ and write <dir>/.morkit-lock.json")
|
|
27
|
+
.option("--dir <path>", "project directory", ".")
|
|
28
|
+
.option("--kit <slug>", "kit slug", DEFAULT_KIT)
|
|
29
|
+
.option("--api-url <url>", "SkillHub API base URL")
|
|
30
|
+
.option("--pat <token>", "personal access token (overrides ~/.morkit/config.json)")
|
|
31
|
+
.action((opts) => init(opts));
|
|
32
|
+
program
|
|
33
|
+
.command("new")
|
|
34
|
+
.description("Create an empty project directory and install a kit into it")
|
|
35
|
+
.requiredOption("--dir <path>", "directory to create")
|
|
36
|
+
.option("--kit <slug>", "kit slug", DEFAULT_KIT)
|
|
37
|
+
.option("--api-url <url>", "SkillHub API base URL")
|
|
38
|
+
.option("--pat <token>", "personal access token (overrides ~/.morkit/config.json)")
|
|
39
|
+
.action((opts) => newProject(opts));
|
|
40
|
+
program
|
|
41
|
+
.command("update")
|
|
42
|
+
.description("Update an installed kit, keeping files you edited locally")
|
|
43
|
+
.option("--dir <path>", "project directory", ".")
|
|
44
|
+
.option("--kit <slug>", "kit slug (default: the one in .morkit-lock.json)")
|
|
45
|
+
.option("--api-url <url>", "SkillHub API base URL")
|
|
46
|
+
.option("--pat <token>", "personal access token (overrides ~/.morkit/config.json)")
|
|
47
|
+
.option("--force", "overwrite locally modified files", false)
|
|
48
|
+
.action((opts) => update(opts));
|
|
49
|
+
try {
|
|
50
|
+
await program.parseAsync(process.argv);
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
process.stderr.write(`\n✗ ${err instanceof Error ? err.message : String(err)}\n`);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/api.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SkillHub kit endpoints. Contract (agreed with the API side):
|
|
3
|
+
*
|
|
4
|
+
* GET /api/kits/{slug} X-PAT → { version, artifacts: {path: "sha256:…"}, shell: "sha256:…" }
|
|
5
|
+
* GET /api/kits/{slug}/archive X-PAT → tar.gz with the whole kit at the archive root
|
|
6
|
+
* GET /api/kits X-PAT → kit listing (see listKits — best effort)
|
|
7
|
+
*
|
|
8
|
+
* 401 invalid/revoked PAT · 403 missing `skills:read` scope · 404 unknown slug
|
|
9
|
+
*/
|
|
10
|
+
/** Auth header for every kit call. PAT-only: kit endpoints do not take MCP tokens. */
|
|
11
|
+
export function patHeaders(pat) {
|
|
12
|
+
if (!pat)
|
|
13
|
+
throw new Error("No PAT — run 'morkit login' or pass --pat skh_…");
|
|
14
|
+
return { "X-PAT": pat };
|
|
15
|
+
}
|
|
16
|
+
function httpError(status, base, slug, body) {
|
|
17
|
+
if (status === 401) {
|
|
18
|
+
return new Error(`${base} rejected the token (401) — it is invalid or has been revoked. Run 'morkit login' again.`);
|
|
19
|
+
}
|
|
20
|
+
if (status === 403) {
|
|
21
|
+
return new Error(`${base} refused the request (403) — the token is missing the 'skills:read' scope.`);
|
|
22
|
+
}
|
|
23
|
+
if (status === 404) {
|
|
24
|
+
return new Error(`Kit '${slug}' does not exist on ${base} (404).`);
|
|
25
|
+
}
|
|
26
|
+
return new Error(`${base} returned ${status}: ${body.slice(0, 400)}`);
|
|
27
|
+
}
|
|
28
|
+
async function request(url, pat, base, slug) {
|
|
29
|
+
let res;
|
|
30
|
+
try {
|
|
31
|
+
res = await fetch(url, { headers: patHeaders(pat) });
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
throw new Error(`Cannot reach SkillHub at ${base} — check the URL or your network (${String(err)})`);
|
|
35
|
+
}
|
|
36
|
+
if (!res.ok)
|
|
37
|
+
throw httpError(res.status, base, slug, await res.text().catch(() => ""));
|
|
38
|
+
return res;
|
|
39
|
+
}
|
|
40
|
+
export async function fetchKitMeta(base, pat, slug) {
|
|
41
|
+
const res = await request(`${base}/api/kits/${encodeURIComponent(slug)}`, pat, base, slug);
|
|
42
|
+
const data = (await res.json());
|
|
43
|
+
if (typeof data.version !== "string" || typeof data.artifacts !== "object" || !data.artifacts) {
|
|
44
|
+
throw new Error(`Malformed response from ${base}/api/kits/${slug}: missing version/artifacts`);
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
version: data.version,
|
|
48
|
+
artifacts: data.artifacts,
|
|
49
|
+
shell: typeof data.shell === "string" ? data.shell : "",
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export async function fetchKitArchive(base, pat, slug) {
|
|
53
|
+
const res = await request(`${base}/api/kits/${encodeURIComponent(slug)}/archive`, pat, base, slug);
|
|
54
|
+
return Buffer.from(await res.arrayBuffer());
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* List kits visible to the token.
|
|
58
|
+
*
|
|
59
|
+
* ASSUMPTION: `GET /api/kits` returns either a bare array or `{ kits: [...] }`.
|
|
60
|
+
* The frozen contract only covers `/api/kits/{slug}`; both shapes are accepted
|
|
61
|
+
* and a 404 is reported as "listing not available" rather than crashing.
|
|
62
|
+
*/
|
|
63
|
+
export async function listKits(base, pat) {
|
|
64
|
+
let res;
|
|
65
|
+
try {
|
|
66
|
+
res = await fetch(`${base}/api/kits`, { headers: patHeaders(pat) });
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
throw new Error(`Cannot reach SkillHub at ${base} (${String(err)})`);
|
|
70
|
+
}
|
|
71
|
+
if (res.status === 404) {
|
|
72
|
+
throw new Error(`${base} has no kit listing endpoint (404). Use 'morkit init --kit <slug>' with a known slug.`);
|
|
73
|
+
}
|
|
74
|
+
if (!res.ok)
|
|
75
|
+
throw httpError(res.status, base, "", await res.text().catch(() => ""));
|
|
76
|
+
const data = (await res.json());
|
|
77
|
+
const rows = Array.isArray(data)
|
|
78
|
+
? data
|
|
79
|
+
: Array.isArray(data.kits)
|
|
80
|
+
? data.kits
|
|
81
|
+
: null;
|
|
82
|
+
if (!rows)
|
|
83
|
+
throw new Error(`Malformed response from ${base}/api/kits: expected an array`);
|
|
84
|
+
return rows.map((r) => {
|
|
85
|
+
const o = r;
|
|
86
|
+
return {
|
|
87
|
+
slug: String(o.slug ?? o.name ?? ""),
|
|
88
|
+
version: typeof o.version === "string" ? o.version : undefined,
|
|
89
|
+
description: typeof o.description === "string" ? o.description : undefined,
|
|
90
|
+
};
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/lib/api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { configDir, configFile } from "./paths.js";
|
|
4
|
+
/** SkillHub instance the CLI talks to when nothing overrides it. */
|
|
5
|
+
export const DEFAULT_API_URL = "https://skillhub.rainbow-banfish.ts.net";
|
|
6
|
+
export class NotLoggedInError extends Error {
|
|
7
|
+
constructor() {
|
|
8
|
+
super(`Not logged in — ${configFile()} is missing.\n` +
|
|
9
|
+
"Run: morkit login --pat skh_…\n" +
|
|
10
|
+
"or pass the token once with: --pat skh_…");
|
|
11
|
+
this.name = "NotLoggedInError";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function normaliseApiUrl(url) {
|
|
15
|
+
return url.replace(/\/+$/, "");
|
|
16
|
+
}
|
|
17
|
+
export function readConfig() {
|
|
18
|
+
const file = configFile();
|
|
19
|
+
if (!fs.existsSync(file))
|
|
20
|
+
return null;
|
|
21
|
+
const raw = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
22
|
+
if (!raw.pat)
|
|
23
|
+
return null;
|
|
24
|
+
return {
|
|
25
|
+
apiUrl: normaliseApiUrl(raw.apiUrl ?? DEFAULT_API_URL),
|
|
26
|
+
pat: raw.pat,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Write credentials with mode 0600. `mkdirSync` cannot tighten an existing
|
|
31
|
+
* directory and `writeFileSync`'s mode is masked by umask on create and ignored
|
|
32
|
+
* on overwrite, so chmod explicitly afterwards.
|
|
33
|
+
*/
|
|
34
|
+
export function writeConfig(cfg) {
|
|
35
|
+
const dir = configDir();
|
|
36
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
37
|
+
const file = path.join(dir, "config.json");
|
|
38
|
+
fs.writeFileSync(file, `${JSON.stringify(cfg, null, 2)}\n`, { mode: 0o600 });
|
|
39
|
+
fs.chmodSync(file, 0o600);
|
|
40
|
+
return file;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Resolve credentials. Precedence, most explicit first:
|
|
44
|
+
* --pat / --api-url > ~/.morkit/config.json > project lockfile > DEFAULT_API_URL
|
|
45
|
+
*
|
|
46
|
+
* Throws NotLoggedInError when no PAT can be found, so callers exit non-zero
|
|
47
|
+
* with the `morkit login` hint.
|
|
48
|
+
*/
|
|
49
|
+
export function resolveAuth(o = {}) {
|
|
50
|
+
const cfg = readConfig();
|
|
51
|
+
const pat = o.pat ?? cfg?.pat;
|
|
52
|
+
if (!pat)
|
|
53
|
+
throw new NotLoggedInError();
|
|
54
|
+
const apiUrl = o.apiUrl ?? cfg?.apiUrl ?? o.lockApiUrl ?? DEFAULT_API_URL;
|
|
55
|
+
return { apiUrl: normaliseApiUrl(apiUrl), pat };
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEnD,oEAAoE;AACpE,MAAM,CAAC,MAAM,eAAe,GAAG,yCAAyC,CAAC;AASzE,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IACzC;QACE,KAAK,CACH,mBAAmB,UAAU,EAAE,gBAAgB;YAC7C,kCAAkC;YAClC,2CAA2C,CAC9C,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACjC,CAAC;CACF;AAED,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,OAAO,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;IAC1B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACtC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAoB,CAAC;IACzE,IAAI,CAAC,GAAG,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IAC1B,OAAO;QACL,MAAM,EAAE,eAAe,CAAC,GAAG,CAAC,MAAM,IAAI,eAAe,CAAC;QACtD,GAAG,EAAE,GAAG,CAAC,GAAG;KACb,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,MAAM,GAAG,GAAG,SAAS,EAAE,CAAC;IACxB,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACpD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;IAC3C,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC7E,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC1B,OAAO,IAAI,CAAC;AACd,CAAC;AASD;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,IAAmB,EAAE;IAC/C,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;IACzB,MAAM,GAAG,GAAG,CAAC,CAAC,GAAG,IAAI,GAAG,EAAE,GAAG,CAAC;IAC9B,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,gBAAgB,EAAE,CAAC;IACvC,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,IAAI,GAAG,EAAE,MAAM,IAAI,CAAC,CAAC,UAAU,IAAI,eAAe,CAAC;IAC1E,OAAO,EAAE,MAAM,EAAE,eAAe,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC;AAClD,CAAC"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { sha256 } from "./lock.js";
|
|
4
|
+
import { CLAUDE_SUBDIRS, destPathFor, isArtifactPath } from "./paths.js";
|
|
5
|
+
const defaultRead = (p) => (fs.existsSync(p) ? fs.readFileSync(p) : null);
|
|
6
|
+
/**
|
|
7
|
+
* Decide, per file, whether to write / keep / skip. Pure: all disk access goes
|
|
8
|
+
* through `readFile`, so the whole policy is unit-testable without a project.
|
|
9
|
+
*
|
|
10
|
+
* Artifacts (skills/ commands/ agents/) carry a per-file hash in the lockfile,
|
|
11
|
+
* so "the user edited it" is detectable exactly: disk ≠ lock ⇒ keep + warn.
|
|
12
|
+
*
|
|
13
|
+
* Shell files only have one combined hash in the lockfile (design.md), so the
|
|
14
|
+
* rule is coarser: while the server's shell hash is unchanged, any difference on
|
|
15
|
+
* disk must be a local edit ⇒ keep + warn. Once the server's shell hash moves,
|
|
16
|
+
* the shell is replaced as a unit — it is machinery (hooks, lib, scripts,
|
|
17
|
+
* manifest) that morkit owns, not user-editable content.
|
|
18
|
+
*/
|
|
19
|
+
export function computePlan(input) {
|
|
20
|
+
const readFile = input.readFile ?? defaultRead;
|
|
21
|
+
const plan = { write: [], keep: [], unchanged: [] };
|
|
22
|
+
const shellUnchanged = input.lock !== null && input.lock.shell === input.remoteShell;
|
|
23
|
+
for (const rel of [...input.files.keys()].sort()) {
|
|
24
|
+
const content = input.files.get(rel);
|
|
25
|
+
const dest = destPathFor(input.dir, input.kit, rel);
|
|
26
|
+
const remoteHash = sha256(content);
|
|
27
|
+
const existing = readFile(dest);
|
|
28
|
+
const entry = { rel, dest, remoteHash };
|
|
29
|
+
if (existing === null) {
|
|
30
|
+
plan.write.push({ ...entry, decision: "write" });
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
const diskHash = sha256(existing);
|
|
34
|
+
if (diskHash === remoteHash) {
|
|
35
|
+
plan.unchanged.push({ ...entry, decision: "unchanged" });
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (input.force) {
|
|
39
|
+
plan.write.push({ ...entry, decision: "write" });
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (isArtifactPath(rel)) {
|
|
43
|
+
const lockHash = input.lock?.artifacts[rel];
|
|
44
|
+
// Matches what we installed ⇒ untouched locally ⇒ safe to update.
|
|
45
|
+
if (lockHash && diskHash === lockHash)
|
|
46
|
+
plan.write.push({ ...entry, decision: "write" });
|
|
47
|
+
else
|
|
48
|
+
plan.keep.push({ ...entry, decision: "keep" });
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
if (shellUnchanged)
|
|
52
|
+
plan.keep.push({ ...entry, decision: "keep" });
|
|
53
|
+
else
|
|
54
|
+
plan.write.push({ ...entry, decision: "write" });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return plan;
|
|
58
|
+
}
|
|
59
|
+
/** Write via temp-then-rename so a crash never leaves a half-written file. */
|
|
60
|
+
export function applyPlan(plan, files, executable = new Set()) {
|
|
61
|
+
for (const entry of plan.write) {
|
|
62
|
+
const content = files.get(entry.rel);
|
|
63
|
+
if (!content)
|
|
64
|
+
continue;
|
|
65
|
+
fs.mkdirSync(path.dirname(entry.dest), { recursive: true });
|
|
66
|
+
const tmp = `${entry.dest}.${process.pid}.tmp`;
|
|
67
|
+
fs.writeFileSync(tmp, content);
|
|
68
|
+
fs.renameSync(tmp, entry.dest);
|
|
69
|
+
if (executable.has(entry.rel))
|
|
70
|
+
fs.chmodSync(entry.dest, 0o755);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/** Create `.claude/{skills,commands,agents,plugins}` even when the kit has none. */
|
|
74
|
+
export function ensureClaudeLayout(dir) {
|
|
75
|
+
for (const sub of CLAUDE_SUBDIRS) {
|
|
76
|
+
fs.mkdirSync(path.join(dir, ".claude", sub), { recursive: true });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/lib/install.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/lock.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import { lockPath } from "./paths.js";
|
|
4
|
+
export function sha256(data) {
|
|
5
|
+
return `sha256:${crypto.createHash("sha256").update(data).digest("hex")}`;
|
|
6
|
+
}
|
|
7
|
+
/** ISO 8601 UTC without milliseconds, e.g. 2026-08-11T09:00:00Z. */
|
|
8
|
+
export function isoUtc(d = new Date()) {
|
|
9
|
+
return d.toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
10
|
+
}
|
|
11
|
+
export function readLock(dir) {
|
|
12
|
+
const file = lockPath(dir);
|
|
13
|
+
if (!fs.existsSync(file))
|
|
14
|
+
return null;
|
|
15
|
+
try {
|
|
16
|
+
const raw = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
17
|
+
if (!raw.kit || !raw.version)
|
|
18
|
+
return null;
|
|
19
|
+
return {
|
|
20
|
+
apiUrl: raw.apiUrl ?? "",
|
|
21
|
+
kit: raw.kit,
|
|
22
|
+
version: raw.version,
|
|
23
|
+
installedAt: raw.installedAt ?? "",
|
|
24
|
+
artifacts: raw.artifacts ?? {},
|
|
25
|
+
shell: raw.shell ?? "",
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export function writeLock(dir, lock) {
|
|
33
|
+
const ordered = {
|
|
34
|
+
apiUrl: lock.apiUrl,
|
|
35
|
+
kit: lock.kit,
|
|
36
|
+
version: lock.version,
|
|
37
|
+
installedAt: lock.installedAt,
|
|
38
|
+
// Sorted so a re-install produces a byte-identical file and PR diffs stay readable.
|
|
39
|
+
artifacts: Object.fromEntries(Object.entries(lock.artifacts).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))),
|
|
40
|
+
shell: lock.shell,
|
|
41
|
+
};
|
|
42
|
+
fs.writeFileSync(lockPath(dir), `${JSON.stringify(ordered, null, 2)}\n`);
|
|
43
|
+
}
|
|
44
|
+
/** True when the lock already describes exactly this install. */
|
|
45
|
+
export function lockMatches(lock, next) {
|
|
46
|
+
if (!lock)
|
|
47
|
+
return false;
|
|
48
|
+
return (lock.apiUrl === next.apiUrl &&
|
|
49
|
+
lock.kit === next.kit &&
|
|
50
|
+
lock.version === next.version &&
|
|
51
|
+
lock.shell === next.shell &&
|
|
52
|
+
JSON.stringify(Object.entries(lock.artifacts).sort()) ===
|
|
53
|
+
JSON.stringify(Object.entries(next.artifacts).sort()));
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock.js","sourceRoot":"","sources":["../../src/lib/lock.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Two separate homes, on purpose:
|
|
5
|
+
*
|
|
6
|
+
* ~/.morkit/config.json credentials (PAT). NEVER inside a project.
|
|
7
|
+
* <project>/.claude/ kit content, committed with the project.
|
|
8
|
+
* <project>/.morkit-lock.json what was installed, committed with the project.
|
|
9
|
+
*
|
|
10
|
+
* Resolved lazily (not module-level constants) so tests can point HOME at a
|
|
11
|
+
* temp directory without re-importing the module.
|
|
12
|
+
*/
|
|
13
|
+
export const LOCK_NAME = ".morkit-lock.json";
|
|
14
|
+
/** Kit sub-trees that map straight into `.claude/` (Claude Code reads them there). */
|
|
15
|
+
export const ARTIFACT_ROOTS = ["skills", "commands", "agents"];
|
|
16
|
+
/** Directories `morkit init` always creates, even when the kit has none of them. */
|
|
17
|
+
export const CLAUDE_SUBDIRS = ["skills", "commands", "agents", "plugins"];
|
|
18
|
+
export function configDir() {
|
|
19
|
+
return path.join(os.homedir(), ".morkit");
|
|
20
|
+
}
|
|
21
|
+
export function configFile() {
|
|
22
|
+
return path.join(configDir(), "config.json");
|
|
23
|
+
}
|
|
24
|
+
export function lockPath(dir) {
|
|
25
|
+
return path.join(dir, LOCK_NAME);
|
|
26
|
+
}
|
|
27
|
+
/** True for kit paths that Claude Code loads directly (skills/, commands/, agents/). */
|
|
28
|
+
export function isArtifactPath(rel) {
|
|
29
|
+
const top = rel.split("/")[0];
|
|
30
|
+
return ARTIFACT_ROOTS.includes(top);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Where a kit-relative path lands inside the project.
|
|
34
|
+
*
|
|
35
|
+
* skills/x/SKILL.md → <dir>/.claude/skills/x/SKILL.md
|
|
36
|
+
* .claude-plugin/plugin.json → <dir>/.claude/plugins/<kit>/.claude-plugin/plugin.json
|
|
37
|
+
*
|
|
38
|
+
* Everything that is not an artifact is "shell" (hooks, lib, scripts, manifest)
|
|
39
|
+
* and is kept together under one plugin directory so it stays removable.
|
|
40
|
+
*/
|
|
41
|
+
export function destPathFor(dir, kit, rel) {
|
|
42
|
+
return isArtifactPath(rel)
|
|
43
|
+
? path.join(dir, ".claude", rel)
|
|
44
|
+
: path.join(dir, ".claude", "plugins", kit, rel);
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/lib/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B;;;;;;;;;GASG;AAEH,MAAM,CAAC,MAAM,SAAS,GAAG,mBAAmB,CAAC;AAE7C,sFAAsF;AACtF,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,QAAQ,EAAE,UAAU,EAAE,QAAQ,CAAU,CAAC;AAExE,oFAAoF;AACpF,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,CAAU,CAAC;AAEnF,MAAM,UAAU,SAAS;IACvB,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,aAAa,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,GAAW;IAClC,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;AACnC,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9B,OAAQ,cAAoC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;AAC7D,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,WAAW,CAAC,GAAW,EAAE,GAAW,EAAE,GAAW;IAC/D,OAAO,cAAc,CAAC,GAAG,CAAC;QACxB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,GAAG,CAAC;QAChC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AACrD,CAAC"}
|
package/dist/lib/tar.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import zlib from "node:zlib";
|
|
2
|
+
/**
|
|
3
|
+
* Minimal read-only tar.gz extractor.
|
|
4
|
+
*
|
|
5
|
+
* Deliberately dependency-free: this package is published as an empty
|
|
6
|
+
* downloader, so every runtime dependency is one more thing a user installs
|
|
7
|
+
* globally just to fetch a kit. Node ships gzip; tar's header format is 512-byte
|
|
8
|
+
* blocks and needs ~100 lines to read.
|
|
9
|
+
*
|
|
10
|
+
* Supports: regular files, directories, GNU long names ('L'), pax extended
|
|
11
|
+
* headers ('x'). Anything else (symlinks, devices) is skipped — a kit is plain
|
|
12
|
+
* files, and silently ignoring links is safer than following them.
|
|
13
|
+
*/
|
|
14
|
+
const BLOCK = 512;
|
|
15
|
+
function readString(block, offset, length) {
|
|
16
|
+
const end = block.indexOf(0, offset);
|
|
17
|
+
const stop = end === -1 || end > offset + length ? offset + length : end;
|
|
18
|
+
return block.toString("utf8", offset, stop);
|
|
19
|
+
}
|
|
20
|
+
function readOctal(block, offset, length) {
|
|
21
|
+
const raw = block.toString("ascii", offset, offset + length).replace(/[\0 ]+$/, "").trim();
|
|
22
|
+
if (raw === "")
|
|
23
|
+
return 0;
|
|
24
|
+
const n = parseInt(raw, 8);
|
|
25
|
+
return Number.isNaN(n) ? 0 : n;
|
|
26
|
+
}
|
|
27
|
+
function isZeroBlock(block) {
|
|
28
|
+
for (const byte of block)
|
|
29
|
+
if (byte !== 0)
|
|
30
|
+
return false;
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
/** Reject absolute paths and any `..` segment before a single byte is written. */
|
|
34
|
+
export function assertSafePath(name) {
|
|
35
|
+
if (name.startsWith("/") || /^[A-Za-z]:[\\/]/.test(name)) {
|
|
36
|
+
throw new Error(`Archive contains an absolute path: ${name}`);
|
|
37
|
+
}
|
|
38
|
+
if (name.split(/[/\\]/).includes("..")) {
|
|
39
|
+
throw new Error(`Archive contains a path traversal entry: ${name}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function normalise(name) {
|
|
43
|
+
return name.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/+$/, "");
|
|
44
|
+
}
|
|
45
|
+
/** Pull `path=` out of a pax extended header record block. */
|
|
46
|
+
function paxPath(data) {
|
|
47
|
+
const text = data.toString("utf8");
|
|
48
|
+
const m = /(?:^|\n)\d+ path=([^\n]*)\n/.exec(text);
|
|
49
|
+
return m ? m[1] : null;
|
|
50
|
+
}
|
|
51
|
+
export function extractTarGz(gz) {
|
|
52
|
+
return extractTar(zlib.gunzipSync(gz));
|
|
53
|
+
}
|
|
54
|
+
export function extractTar(tar) {
|
|
55
|
+
const files = new Map();
|
|
56
|
+
const executable = new Set();
|
|
57
|
+
let offset = 0;
|
|
58
|
+
let pendingName = null;
|
|
59
|
+
while (offset + BLOCK <= tar.length) {
|
|
60
|
+
const header = tar.subarray(offset, offset + BLOCK);
|
|
61
|
+
offset += BLOCK;
|
|
62
|
+
if (isZeroBlock(header))
|
|
63
|
+
break;
|
|
64
|
+
const size = readOctal(header, 124, 12);
|
|
65
|
+
const dataBlocks = Math.ceil(size / BLOCK) * BLOCK;
|
|
66
|
+
const data = tar.subarray(offset, offset + size);
|
|
67
|
+
offset += dataBlocks;
|
|
68
|
+
const type = String.fromCharCode(header[156]) || "0";
|
|
69
|
+
if (type === "L") {
|
|
70
|
+
pendingName = normalise(data.toString("utf8").replace(/\0+$/, ""));
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (type === "x" || type === "X") {
|
|
74
|
+
const p = paxPath(data);
|
|
75
|
+
if (p)
|
|
76
|
+
pendingName = normalise(p);
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (type === "g")
|
|
80
|
+
continue; // global pax header — nothing we need
|
|
81
|
+
const prefix = readString(header, 345, 155);
|
|
82
|
+
const rawName = readString(header, 0, 100);
|
|
83
|
+
const name = pendingName ?? normalise(prefix ? `${prefix}/${rawName}` : rawName);
|
|
84
|
+
pendingName = null;
|
|
85
|
+
if (name === "")
|
|
86
|
+
continue;
|
|
87
|
+
assertSafePath(name);
|
|
88
|
+
if (type === "5")
|
|
89
|
+
continue; // directory — created implicitly when writing files
|
|
90
|
+
if (type !== "0" && type !== "\0" && type !== "7")
|
|
91
|
+
continue;
|
|
92
|
+
files.set(name, Buffer.from(data));
|
|
93
|
+
if (readOctal(header, 100, 8) & 0o100)
|
|
94
|
+
executable.add(name);
|
|
95
|
+
}
|
|
96
|
+
return { files, executable };
|
|
97
|
+
}
|
|
98
|
+
/** Drop a single shared top-level directory (npm tarballs use `package/`). */
|
|
99
|
+
export function stripRoot(entries, root) {
|
|
100
|
+
const prefix = `${root}/`;
|
|
101
|
+
const files = new Map();
|
|
102
|
+
const executable = new Set();
|
|
103
|
+
for (const [name, data] of entries.files) {
|
|
104
|
+
if (!name.startsWith(prefix))
|
|
105
|
+
continue;
|
|
106
|
+
const rel = name.slice(prefix.length);
|
|
107
|
+
files.set(rel, data);
|
|
108
|
+
if (entries.executable.has(name))
|
|
109
|
+
executable.add(rel);
|
|
110
|
+
}
|
|
111
|
+
return { files, executable };
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=tar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tar.js","sourceRoot":"","sources":["../../src/lib/tar.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mhd1999/morkit-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Install the private morkit kit (skills, commands, agents) into a project's .claude/ directory from SkillHub. Ships no kit content — downloads it with your personal access token.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/mor-duongmh/skill-hub.git",
|
|
10
|
+
"directory": "packages/morkit-cli"
|
|
11
|
+
},
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
15
|
+
"bin": {
|
|
16
|
+
"morkit": "./dist/index.js"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"README.md"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsc",
|
|
24
|
+
"dev": "tsx src/index.ts",
|
|
25
|
+
"clean": "rm -rf dist",
|
|
26
|
+
"test": "node --import tsx --test tests/*.test.ts"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"commander": "^13.0.0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^22.0.0",
|
|
33
|
+
"tsx": "^4.19.0",
|
|
34
|
+
"typescript": "^5.6.0"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=18"
|
|
38
|
+
}
|
|
39
|
+
}
|