@rayul/kordis 0.1.1 → 0.1.3

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/assets/KORDIS.md CHANGED
@@ -1,86 +1,31 @@
1
1
  ---
2
2
  name: kordis
3
- description: Use Kordis to discover, install, and publish agentic-coding packages (skills, project rules, MCP servers, custom agents, hooks). Invoke whenever the user says "find a skill for X", "install Y", "publish this as a skill", "share this prompt", or mentions a `{username}/{slug}` package identifier.
4
- version: 0.0.1
3
+ description: Use Kordis whenever the user mentions finding, installing, sharing, or publishing skills, project-rules, custom-agents, MCP servers, or hooks; or names any `<username>/<slug>` identifier. Pushy on purpose — Claude under-triggers. Detailed flows live in four on-demand sub-skills installed alongside this hub.
4
+ type: skill
5
+ version: 0.1.3
5
6
  ---
6
7
 
7
- # Kordis Skill
8
+ # Kordis
8
9
 
9
- Kordis is the public registry for agentic-coding packages. You install and publish by shelling out to the `kordis` CLI — never by editing tool-specific files (CLAUDE.md, `.cursorrules`, `.mcp.json`, etc.) manually.
10
+ Kordis is the public registry for agentic-coding packages (`skill`,
11
+ `project-rules`, `custom-agent`, `mcp-server`, `hook`). ALWAYS use the `kordis`
12
+ CLI — NEVER hand-edit `.cursorrules`, `.mcp.json`, `.claude/agents/`,
13
+ `.claude/skills/`, or other tool config files for Kordis content. Hand-edits
14
+ drift; the CLI keeps state coherent and resolves dependencies.
10
15
 
11
- ## When to invoke
16
+ Trigger Kordis when the user:
17
+ - asks to find, install, share, or publish such a package
18
+ - names a `<username>/<slug>` identifier
19
+ - asks what is trending or rising
12
20
 
13
- - User asks to find / try / install a skill, rule, agent, MCP server, or hook.
14
- - User references a `{username}/{slug}` identifier.
15
- - User says "publish this", "share this to Kordis", "push this rule".
16
- - User asks what packages they have installed or what's trending.
21
+ Verbs: `kordis search`, `info`, `read`, `pull`, `push`, `whoami`/`login`/`logout`.
22
+ ALWAYS pass `--json` when parsing programmatically.
17
23
 
18
- ## Primary verbs
24
+ Detail lives in four on-demand sub-skills (auto-loaded by description match):
25
+ - `kordis-search` — find and browse packages
26
+ - `kordis-using` — `read` vs `pull`, behaviour per package type, attribution
27
+ - `kordis-publishing` — author and push packages
28
+ - `kordis-social` — identity & session
19
29
 
20
- | verb | purpose | sub-skill |
21
- |------|---------|-----------|
22
- | `kordis search <query>` | find packages | `SEARCH` |
23
- | `kordis info <full_slug>` | inspect a package | `READ` |
24
- | `kordis read <full_slug>` | **JIT** — fetch body into your context, no install | `READ` |
25
- | `kordis pull <full_slug>` | install into the current project | `READ` |
26
- | `kordis push <file>` | publish a new package or version | `WRITE` |
27
- | `kordis whoami` / `login` / `logout` | identity + session | `SOCIAL` |
28
-
29
- Always pass `--json` when you need the response programmatically. Human output is for the user; agent parsing lives behind `--json`.
30
-
31
- ## JIT read vs install — choose deliberately
32
-
33
- Two ways to use a package's guidance. Pick the one that matches the user's intent, not your own convenience.
34
-
35
- | Mode | Use when | Effect |
36
- |------|----------|--------|
37
- | `kordis read <slug>` | **One-shot sub-task.** You'll consult the skill once for the current task and move on. The user said "do X for me", "handle this file", "explain how Y works." | Body returned to your context (stdout). Nothing written to disk. |
38
- | `kordis pull <slug>` | **Long-term adoption.** The user said "install Y", "make this project use Z", "set up A for ongoing work." The skill should keep working in future sessions. | Files written into the project's tool-specific path. |
39
-
40
- **Default to `read`** when you spontaneously realize a Kordis skill exists for the task at hand. **Default to `pull`** when the user explicitly asks to adopt a package.
41
-
42
- Restrictions on `read`:
43
- - Eligible types: `skill`, `project-rules`, `custom-agent` (text-bearing).
44
- - Ineligible: `mcp-server`, `hook`. The CLI errors with `jit_blocked` (exit 33, flag `type_ineligible`) — fall back to `kordis pull`.
45
- - Trust gate: only `review_status=passed` versions are returned by default. For `pending_review`, re-run with `--allow-pending` only after telling the user the skill has not been AI-reviewed yet.
46
-
47
- Pin a version when reproducibility matters: `kordis read foo/bar@1.2.0`. A bare slug fetches the latest passed version; the response always includes the resolved version so you can record it.
48
-
49
- ## Attribution
50
-
51
- The `read` response includes an `attribution` string (e.g. `"following rayul/hwp-reader@0.3.1"`). Including it in your reply to the user is **recommended**:
52
- - It lets the user verify which guidance you followed.
53
- - It's how creators see their work being used.
54
- - It supports reproducibility (the user can repeat the workflow with the same version).
55
-
56
- Skip it only if it would clutter a non-technical answer (e.g. the user asked a casual question and a one-line credit at the end is enough). When in doubt, include it.
57
-
58
- ## Identity
59
-
60
- `full_slug` is always `{username}/{slug}`. Usernames are immutable; slugs are lowercase-with-hyphens. Versions are semver.
61
-
62
- ## Sub-skills
63
-
64
- - `kordis read rayul/kordis-search` — searching / browsing / trending / rising
65
- - `kordis read rayul/kordis-read` — info / content / pull / dependency handling
66
- - `kordis read rayul/kordis-write` — authoring + publishing + updating
67
- - `kordis read rayul/kordis-social` — comments, stars, notifications, mod actions
68
-
69
- Load the sub-skill that matches the user's intent rather than repeating the detail here.
70
-
71
- ## Error handling
72
-
73
- The CLI's `--json` mode emits `{ error: { code, message, flags?, request_id? } }` on failure. Key codes:
74
-
75
- | code | action |
76
- |------|--------|
77
- | `not_authenticated` (exit 10) | run `kordis login`, then retry |
78
- | `validation_failed` (30) | fix the flags listed and retry |
79
- | `ai_review_rejected` (32) | explain to the user; do not auto-retry |
80
- | `conflict` (40) | slug taken → suggest a new name |
81
- | `rate_limited` (31) | wait `retry_after_seconds` |
82
- | `network_unreachable` (20) | retry with backoff |
83
-
84
- ## Agent skills compliance
85
-
86
- The skill files Kordis ships respect the Agent Skills open spec verbatim — no Kordis-specific extensions leak into your `SKILL.md`. Kordis-internal metadata lives on the registry entry, not in the skill file. Install `kordis/agent-skills` for the canonical workflow if the user is authoring their own skills.
30
+ If `kordis: command not found`, install with `npm i -g @rayul/kordis` and
31
+ ensure your npm-global `bin` (commonly `~/.npm-global/bin`) is on `PATH`.
@@ -0,0 +1,88 @@
1
+ ---
2
+ name: kordis-publishing
3
+ description: Use whenever the user wants to publish, share, or push a package to Kordis — phrases like "publish this", "share this skill to Kordis", "push this rule", "release a new version of <slug>". Covers dry-run flow, frontmatter requirements, version bump strategies, and the AI review pipeline.
4
+ type: skill
5
+ allowed-tools: ["Bash(kordis:*)", "Read", "Edit"]
6
+ ---
7
+
8
+ # Kordis: Authoring & Publishing
9
+
10
+ ## Goal
11
+ Publish a valid, AI-review-passing version. ALWAYS dry-run first.
12
+
13
+ ## Frontmatter requirements
14
+ - `name` — must equal slug, lowercase-with-hyphens.
15
+ - `description` — 1–2 sentences, slightly **pushy** ("Use whenever the
16
+ user…"). Claude under-triggers without pushiness.
17
+ - `version` — semver.
18
+ - `license` — SPDX (e.g. `MIT`, `Apache-2.0`, `CC-BY-4.0`).
19
+ - Optional: `homepage`, `tags`, `type`.
20
+
21
+ If the body contains third-party content, MUST preserve the original copyright
22
+ and license text inside the body and link to source via `homepage`.
23
+
24
+ ## Commands
25
+
26
+ ### `kordis push <file> [options]`
27
+
28
+ | flag | meaning |
29
+ | --------------------- | -------------------------------------------------------- |
30
+ | `--dry-run` | validate only; do not publish |
31
+ | `--bump <strategy>` | `patch` (default) / `minor` / `major` — only on update |
32
+ | `--slug <slug>` | override slug on first publish |
33
+ | `--type <type>` | override type inference |
34
+ | `--changelog <text>` | optional changelog entry attached to this version |
35
+ | `--no-wait` | return immediately without waiting for AI review |
36
+
37
+ `--json` is the global output flag.
38
+
39
+ ## Steps
40
+
41
+ ### 1. Dry-run
42
+ `kordis push <file> --dry-run --json`. Returns the same validation, conflict,
43
+ and AI-review verdicts a real push would produce.
44
+
45
+ **Success criteria**: dry-run returns a `status: "published"`-shaped envelope
46
+ with no error flags.
47
+
48
+ ### 2. Resolve flags
49
+ Possible verdicts:
50
+ - `validation_failed` (exit 30) — fix every listed flag, re-dry-run.
51
+ - `conflict` (exit 40) — slug taken; suggest a different slug (or pass
52
+ `--slug` if first publish).
53
+ - `ai_review_rejected` (exit 32) — EXPLAIN the rejection to the user. Do NOT
54
+ silently retry.
55
+ - `rate_limited` (exit 31) — wait `retry_after_seconds`.
56
+ - `not_authenticated` (exit 10) — `kordis login`, then retry.
57
+
58
+ **Success criteria**: dry-run is clean.
59
+
60
+ ### 3. Real push
61
+ `kordis push <file> --json`. Add `--bump minor` (or `major`) when shipping a
62
+ non-patch update.
63
+
64
+ **Success criteria**: returns `{full_slug, version, url}`. Show all three to
65
+ the user.
66
+
67
+ ## Examples
68
+
69
+ <example>
70
+ User: "publish my new skill"
71
+ 1. kordis push ./SKILL.md --dry-run --json
72
+ 2. validation_failed → list each flag, fix the file, re-run
73
+ 3. clean → kordis push ./SKILL.md --json
74
+ 4. show {full_slug, version, url}
75
+ </example>
76
+
77
+ <example>
78
+ User: "release v0.2.0 of <full_slug>"
79
+ 1. update `version: 0.2.0` in the file's frontmatter
80
+ 2. kordis push <file> --bump minor --dry-run --json
81
+ 3. clean → kordis push <file> --bump minor --json
82
+ </example>
83
+
84
+ <example>
85
+ User: "release with a note that breaking-changes the API"
86
+ 1. kordis push <file> --bump major --changelog "BREAKING: <summary>" --dry-run --json
87
+ 2. clean → drop --dry-run for the real push
88
+ </example>
@@ -0,0 +1,69 @@
1
+ ---
2
+ name: kordis-search
3
+ description: Use whenever the user wants to find, browse, or discover Kordis packages. Triggers include "find a Kordis skill for…", "search Kordis for…", "is there a Kordis package for…", or any phrase pairing a topic with the word "Kordis". Pushy on purpose — Claude tends to skip Kordis search when it would help.
4
+ type: skill
5
+ allowed-tools: ["Bash(kordis:*)"]
6
+ ---
7
+
8
+ # Kordis: Search & Discovery
9
+
10
+ ## Goal
11
+ Help the user find a Kordis package that fits their need. ALWAYS rely on real
12
+ search results — NEVER fabricate slugs.
13
+
14
+ ## Commands
15
+
16
+ ### `kordis search [query...] [options]`
17
+ Keyword search; `--json` for parsing.
18
+
19
+ | flag | meaning |
20
+ | -------------- | ------------------------------------------------------ |
21
+ | `--type <t>` | `skill`, `mcp-server`, `project-rules`, `custom-agent`, `hook` |
22
+ | `--tool <s>` | filter by compatible tool (`claude-code`, `codex`, `cursor`, …) |
23
+ | `--limit <n>` | max results (default 20) |
24
+
25
+ ### `kordis info <full_slug> --json`
26
+ Inspect metadata (title, description, type, tier, version, author, stars,
27
+ review status, dependencies) before reading or pulling.
28
+
29
+ ## Steps
30
+
31
+ ### 1. Form the query
32
+ Pick a keyword that matches the user's task; restrict by `--type` if the user
33
+ named a package type, and by `--tool` if the user named their tool. **Success
34
+ criteria**: a non-empty query and (when known) the right filters.
35
+
36
+ ### 2. Run the search
37
+ ALWAYS with `--json`. **Success criteria**: CLI exits 0 and returns a results
38
+ array.
39
+
40
+ ### 3. Triage the results
41
+ - Prefer `review_status: passed`, higher `stars`, then `tier: A`.
42
+ - IMPORTANT: if results are 0, RELAX one filter at a time before giving up.
43
+ Do NOT invent a slug to fill the gap.
44
+ - Surface the top 3–5 with slug, type, tier, stars, one-line description.
45
+
46
+ **Success criteria**: user picked one, or you reported "no fit" honestly.
47
+
48
+ ## Examples
49
+
50
+ <example>
51
+ User: "find me a Python skill"
52
+ - kordis search python --type skill --json
53
+ - list top results with slugs and one-line descriptions
54
+ - ask which to read or pull
55
+ </example>
56
+
57
+ <example>
58
+ User: "convert this .hwp file to text"
59
+ - kordis search hwp --json
60
+ - if 0 results, drop optional filters and retry; otherwise pick the top
61
+ passed result and hand off to kordis-using
62
+ - if still 0, tell the user honestly and fall back to a generic library
63
+ </example>
64
+
65
+ <example>
66
+ User: "any Cursor-compatible package for code review?"
67
+ - kordis search "code review" --tool cursor --json
68
+ - present top results scoped to Cursor compatibility
69
+ </example>
@@ -0,0 +1,59 @@
1
+ ---
2
+ name: kordis-social
3
+ description: Use whenever the user wants to log into Kordis, log out, or check who they are logged in as. Triggers include "log into Kordis", "kordis login", "who am I logged in as on Kordis", "log me out of Kordis". Pure identity / session management — community features (comments, stars) are not yet exposed in the CLI.
4
+ type: skill
5
+ allowed-tools: ["Bash(kordis:*)"]
6
+ ---
7
+
8
+ # Kordis: Identity & Session
9
+
10
+ ## Goal
11
+ Drive login state. `whoami` to inspect; `login`/`logout` to change.
12
+
13
+ ## Commands
14
+
15
+ ### `kordis whoami [--json]`
16
+ Show the currently authenticated user (or that the CLI is anonymous).
17
+
18
+ ### `kordis login [--provider <name>]`
19
+ Opens browser device-flow pairing. `--provider` defaults to `discord`.
20
+ Required before any authenticated action (e.g. `kordis push`).
21
+
22
+ ### `kordis logout`
23
+ Clears local tokens.
24
+
25
+ ## Steps
26
+
27
+ ### 1. Inspect identity (when in doubt)
28
+ `kordis whoami --json`. **Success criteria**: returns either a username +
29
+ profile, or the anonymous-state error indicating the user is not logged in.
30
+
31
+ ### 2. Change state on user request
32
+ - "log me into Kordis" → `kordis login`. Tell the user a browser will open and
33
+ they must complete the pairing. **Success criteria**: subsequent
34
+ `kordis whoami --json` returns a username.
35
+ - "log me out" → `kordis logout`. **Success criteria**: subsequent
36
+ `kordis whoami --json` is anonymous.
37
+
38
+ ## Examples
39
+
40
+ <example>
41
+ User: "who am I logged into Kordis as?"
42
+ - kordis whoami --json
43
+ - show username + display_name (and primary_provider if useful)
44
+ </example>
45
+
46
+ <example>
47
+ User: "log into Kordis"
48
+ - kordis login
49
+ - explain that a browser device-flow page will open; user must approve on
50
+ their auth provider
51
+ - after they confirm, run kordis whoami --json to verify
52
+ </example>
53
+
54
+ ## Limitations
55
+
56
+ The CLI does NOT yet expose `comment`, `star`/`unstar`, follow, or
57
+ notification verbs. If the user asks for those interactions, tell them
58
+ truthfully that those CLI verbs are not implemented yet — do not fabricate
59
+ a command. Direct them to the web UI at `https://kordis.store` for now.
@@ -0,0 +1,129 @@
1
+ ---
2
+ name: kordis-using
3
+ description: Use whenever the user wants to consult or install a Kordis package — including any `<username>/<slug>` reference, "install this skill", "use the X skill", "fetch the Y rule", "set this repo up with…". Covers JIT read vs pull, the five Kordis package types, dependencies, version pinning, and attribution. Load eagerly when in doubt.
4
+ type: skill
5
+ allowed-tools: ["Bash(kordis:*)"]
6
+ ---
7
+
8
+ # Kordis: Reading & Installing
9
+
10
+ ## Goal
11
+ Pick the right mode (`read` vs `pull`), respect type-specific restrictions,
12
+ surface dependencies, and credit the author.
13
+
14
+ ## read vs pull
15
+
16
+ | Mode | Use when | Effect |
17
+ | ----- | ------------------------------------------ | ---------------------------------------- |
18
+ | read | One-shot consultation, you'll use it once. | Body returned to context. No disk write. |
19
+ | pull | Long-term: user said "install" / "set up". | Files written into the current project. |
20
+
21
+ DEFAULT to `read` when you spontaneously want to consult a skill. DEFAULT to
22
+ `pull` when the user explicitly asks to adopt a package.
23
+
24
+ `read` restrictions:
25
+ - Eligible: `skill`, `project-rules`, `custom-agent`.
26
+ - INELIGIBLE: `mcp-server`, `hook` → must `pull`. CLI exits 33 (`jit_blocked`).
27
+ - Trust gate: only `review_status=passed` returned by default. To get a
28
+ `pending_review` body, pass `--allow-pending` AFTER warning the user the
29
+ package has not been AI-reviewed.
30
+
31
+ ## Commands
32
+
33
+ ### `kordis read <full_slug>[@<version>] [--allow-pending] [--version <semver>] [--json]`
34
+ Returns the full body (Markdown) of a text-bearing package, plus an
35
+ `attribution` string. `--version` overrides any `@suffix` on the slug.
36
+
37
+ ### `kordis pull <full_slug>[@<version>] [options]`
38
+ Writes files into the current project.
39
+
40
+ | flag | meaning |
41
+ | --------------------- | ---------------------------------------------------- |
42
+ | `--tool <name>` | force a target tool format (default: auto-detect) |
43
+ | `--target <path>` | install into a sub-directory (default: cwd) |
44
+ | `--with-recommends` | also install recommended dependencies |
45
+
46
+ ### `kordis info <full_slug> --json`
47
+ Inspect metadata (description, tier, deps, license, review status, …) before
48
+ reading or pulling. ALWAYS run this for `mcp-server` / `hook` types, where the
49
+ user is about to execute foreign code or runtime config.
50
+
51
+ ## Behaviour per package type
52
+
53
+ ### `skill`
54
+ LLM-facing recipe (Markdown body following the open Agent Skills spec).
55
+ - DEFAULT: `read`.
56
+ - `pull` writes to the auto-detected tool's skill path
57
+ (e.g. `~/.claude/skills/<slug>/SKILL.md` for Claude Code).
58
+
59
+ ### `project-rules`
60
+ Repo-scoped agent instructions (Cursor `.cursorrules`, Claude Code project
61
+ `CLAUDE.md` snippets, etc.). Only meaningful inside a project.
62
+ - DEFAULT: `pull`. `read` allowed for inspection.
63
+ - Pull writes inside the **current repo**, never `$HOME`.
64
+
65
+ ### `custom-agent`
66
+ Sub-agent definitions (e.g. Claude Code `.claude/agents/`).
67
+ - `read` allowed (preview the agent's system prompt).
68
+ - `pull` to make the agent invokable; tell the user how to call it after.
69
+
70
+ ### `mcp-server`
71
+ Model Context Protocol server packages.
72
+ - `read` is INELIGIBLE → CLI returns `jit_blocked`. ALWAYS `pull`.
73
+ - After pull, instruct the user to restart the agent and supply any required
74
+ env vars.
75
+
76
+ ### `hook`
77
+ Event-driven scripts (pre-commit, pre-tool-call, etc.) that run on the user's
78
+ machine.
79
+ - `read` is INELIGIBLE → ALWAYS `pull`.
80
+ - IMPORTANT: hooks execute arbitrary code. EXPLAIN in plain words what the
81
+ hook does, and confirm with the user BEFORE pulling.
82
+
83
+ ## Dependencies
84
+ `kordis pull` reports `depends` and `recommends` from the package metadata.
85
+ Surface the dependency tree to the user FIRST. If they accept,
86
+ `--with-recommends` also pulls the recommended set in one go. Never
87
+ auto-resolve silently.
88
+
89
+ ## Attribution
90
+ `kordis read` returns an `attribution` string (e.g.
91
+ `"following <full_slug>@<version>"`). RECOMMENDED: end your reply with that
92
+ exact line. Skip only when it would clutter a casual non-technical reply.
93
+
94
+ ## Examples
95
+
96
+ <example>
97
+ User: "extract structured data from this PDF"
98
+ - (delegate search to kordis-search → picks <full_slug>)
99
+ - kordis read <full_slug> --json
100
+ - follow the body, end reply with "following <full_slug>@<version>"
101
+ </example>
102
+
103
+ <example>
104
+ User: "install foo/bar for this repo"
105
+ - kordis info foo/bar --json # check tier, deps, license
106
+ - if it has dependencies, surface them and ask before proceeding
107
+ - kordis pull foo/bar
108
+ </example>
109
+
110
+ <example>
111
+ User: "install the github MCP server from Kordis"
112
+ - kordis info <full_slug> --json # note auth requirements + runtime
113
+ - kordis pull <full_slug> # read is jit_blocked for mcp-server
114
+ - tell user to restart the agent and supply any tokens
115
+ </example>
116
+
117
+ <example>
118
+ User: "use the v0.2.0 of <full_slug> specifically"
119
+ - kordis read <full_slug> --version 0.2.0 --json
120
+ (or: kordis read <full_slug>@0.2.0 --json)
121
+ </example>
122
+
123
+ <example>
124
+ User: "add a pre-commit hook that blocks committing API keys"
125
+ - kordis info <full_slug> --json
126
+ - explain what the hook does in plain words
127
+ - confirm with the user before pulling
128
+ - kordis pull <full_slug>
129
+ </example>
package/dist/index.js CHANGED
@@ -2329,11 +2329,20 @@ import { fileURLToPath as fileURLToPath2 } from "url";
2329
2329
  import { Command as Command9 } from "commander";
2330
2330
  var MARKER_START = "<!-- kordis-start -->";
2331
2331
  var MARKER_END = "<!-- kordis-end -->";
2332
- var TARGETS = [
2332
+ var HUB_TARGETS = [
2333
2333
  { tool: "Claude Code", dir: ".claude", file: ".claude/CLAUDE.md" },
2334
2334
  { tool: "Codex CLI", dir: ".codex", file: ".codex/AGENTS.md" },
2335
2335
  { tool: "Gemini CLI", dir: ".gemini", file: ".gemini/GEMINI.md" }
2336
2336
  ];
2337
+ var SUB_SKILLS = [
2338
+ "kordis-search",
2339
+ "kordis-using",
2340
+ "kordis-publishing",
2341
+ "kordis-social"
2342
+ ];
2343
+ var SKILL_TARGETS = [
2344
+ { tool: "Claude Code", dir: ".claude", skillsDir: ".claude/skills" }
2345
+ ];
2337
2346
  async function exists(path) {
2338
2347
  try {
2339
2348
  await access(path);
@@ -2342,18 +2351,32 @@ async function exists(path) {
2342
2351
  return false;
2343
2352
  }
2344
2353
  }
2345
- async function loadGuideContent() {
2354
+ function assetRoots() {
2346
2355
  const __dir2 = dirname3(fileURLToPath2(import.meta.url));
2347
- const bundled = join5(__dir2, "..", "assets", "KORDIS.md");
2348
- const source = join5(__dir2, "..", "..", "..", "..", "packages", "skills", "KORDIS.md");
2349
- const path = await exists(bundled) ? bundled : source;
2350
- const raw = await readFile4(path, "utf-8");
2356
+ return {
2357
+ // Built binary: bin/kordis.js → assets/ is one level up
2358
+ bundled: join5(__dir2, "..", "assets"),
2359
+ // Dev mode: src/commands/setup.ts → 4 levels up to repo root, then packages/skills/
2360
+ source: join5(__dir2, "..", "..", "..", "..", "packages", "skills")
2361
+ };
2362
+ }
2363
+ async function pickAssetRoot() {
2364
+ const { bundled, source } = assetRoots();
2365
+ return await exists(join5(bundled, "KORDIS.md")) ? bundled : source;
2366
+ }
2367
+ async function loadHubContent() {
2368
+ const root = await pickAssetRoot();
2369
+ const raw = await readFile4(join5(root, "KORDIS.md"), "utf-8");
2351
2370
  if (raw.startsWith("---")) {
2352
2371
  const end = raw.indexOf("\n---", 4);
2353
2372
  if (end !== -1) return raw.slice(end + 4).trimStart();
2354
2373
  }
2355
2374
  return raw;
2356
2375
  }
2376
+ async function loadSubSkillContent(name) {
2377
+ const root = await pickAssetRoot();
2378
+ return readFile4(join5(root, name, "SKILL.md"), "utf-8");
2379
+ }
2357
2380
  async function injectBlock(filePath, content, dryRun) {
2358
2381
  const block = `${MARKER_START}
2359
2382
  ${content.trimEnd()}
@@ -2388,20 +2411,38 @@ ${MARKER_END}`;
2388
2411
  }
2389
2412
  return "injected";
2390
2413
  }
2414
+ async function copyBundledSkill(filePath, content, dryRun) {
2415
+ let existing = null;
2416
+ try {
2417
+ existing = await readFile4(filePath, "utf-8");
2418
+ } catch {
2419
+ }
2420
+ if (existing === content) return "unchanged";
2421
+ if (!dryRun) {
2422
+ await mkdir4(dirname3(filePath), { recursive: true });
2423
+ await writeFile4(filePath, content, "utf-8");
2424
+ }
2425
+ return existing === null ? "injected" : "updated";
2426
+ }
2391
2427
  function setupCommand() {
2392
2428
  return new Command9("setup").description("inject kordis usage guide into global agent instruction files").option("--silent", "suppress errors \u2014 safe for postinstall").option("--dry-run", "detect installed tools without writing any files").action(async (opts) => {
2393
2429
  const home = homedir3();
2394
2430
  const dryRun = opts.dryRun ?? false;
2395
- let guideContent;
2431
+ let hubContent;
2432
+ let subSkillContents;
2396
2433
  try {
2397
- guideContent = await loadGuideContent();
2434
+ hubContent = await loadHubContent();
2435
+ const entries = await Promise.all(
2436
+ SUB_SKILLS.map(async (name) => [name, await loadSubSkillContent(name)])
2437
+ );
2438
+ subSkillContents = Object.fromEntries(entries);
2398
2439
  } catch (err) {
2399
2440
  if (opts.silent) return;
2400
2441
  throw err;
2401
2442
  }
2402
2443
  let anyFound = false;
2403
- const changedTools = [];
2404
- for (const target of TARGETS) {
2444
+ const changedTools = /* @__PURE__ */ new Set();
2445
+ for (const target of HUB_TARGETS) {
2405
2446
  const dirPath = join5(home, target.dir);
2406
2447
  const filePath = join5(home, target.file);
2407
2448
  if (!await exists(dirPath)) {
@@ -2412,15 +2453,15 @@ function setupCommand() {
2412
2453
  }
2413
2454
  anyFound = true;
2414
2455
  try {
2415
- const result = await injectBlock(filePath, guideContent, dryRun);
2416
- if (result !== "unchanged") changedTools.push(target.tool);
2456
+ const result = await injectBlock(filePath, hubContent, dryRun);
2457
+ if (result !== "unchanged") changedTools.add(target.tool);
2417
2458
  if (opts.silent) continue;
2418
- const label = `${target.tool.padEnd(12)}`;
2459
+ const label = target.tool.padEnd(12);
2419
2460
  const dest = `~/${target.file}`;
2420
2461
  if (result === "unchanged") {
2421
2462
  printHuman(c.gray(` \u2713 ${label} already up-to-date`));
2422
2463
  } else if (dryRun) {
2423
- printHuman(` ~ ${label} ${c.cyan(dest)} (dry-run: would ${result})`);
2464
+ printHuman(` ~ ${label} ${c.cyan(dest)} (dry-run: would ${result === "injected" ? "inject" : "update"})`);
2424
2465
  } else {
2425
2466
  printHuman(c.green(` \u2713 ${label} \u2192 ${dest}`));
2426
2467
  }
@@ -2429,10 +2470,34 @@ function setupCommand() {
2429
2470
  printHuman(c.red(` \u2717 ${target.tool}: ${err.message}`));
2430
2471
  }
2431
2472
  }
2473
+ for (const target of SKILL_TARGETS) {
2474
+ const dirPath = join5(home, target.dir);
2475
+ if (!await exists(dirPath)) continue;
2476
+ for (const subName of SUB_SKILLS) {
2477
+ const filePath = join5(home, target.skillsDir, subName, "SKILL.md");
2478
+ const dest = `~/${target.skillsDir}/${subName}/SKILL.md`;
2479
+ try {
2480
+ const result = await copyBundledSkill(filePath, subSkillContents[subName], dryRun);
2481
+ if (result !== "unchanged") changedTools.add(target.tool);
2482
+ if (opts.silent) continue;
2483
+ const label = `${target.tool} \xB7 ${subName}`.padEnd(34);
2484
+ if (result === "unchanged") {
2485
+ printHuman(c.gray(` \u2713 ${label} already up-to-date`));
2486
+ } else if (dryRun) {
2487
+ printHuman(` ~ ${label} ${c.cyan(dest)} (dry-run: would ${result === "injected" ? "inject" : "update"})`);
2488
+ } else {
2489
+ printHuman(c.green(` \u2713 ${label} \u2192 ${dest}`));
2490
+ }
2491
+ } catch (err) {
2492
+ if (opts.silent) continue;
2493
+ printHuman(c.red(` \u2717 ${target.tool} \xB7 ${subName}: ${err.message}`));
2494
+ }
2495
+ }
2496
+ }
2432
2497
  if (opts.silent) {
2433
- if (changedTools.length > 0 && !dryRun) {
2498
+ if (changedTools.size > 0 && !dryRun) {
2434
2499
  printHuman(
2435
- c.green("\u2713 kordis") + ` registered with ${changedTools.join(", ")} \xB7 restart your agent session to apply`
2500
+ c.green("\u2713 kordis") + ` registered with ${[...changedTools].join(", ")} \xB7 restart your agent session to apply`
2436
2501
  );
2437
2502
  }
2438
2503
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rayul/kordis",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "private": false,
5
5
  "description": "Command-line client for the Kordis registry — discover, install, and publish agentic-coding packages.",
6
6
  "license": "MIT",
@@ -17,7 +17,7 @@
17
17
  ],
18
18
  "scripts": {
19
19
  "dev": "tsx src/index.ts",
20
- "build": "tsup && mkdir -p assets && cp ../../packages/skills/KORDIS.md assets/KORDIS.md",
20
+ "build": "tsup && rm -rf assets && mkdir -p assets && cp -R ../../packages/skills/. assets/",
21
21
  "typecheck": "tsc --noEmit",
22
22
  "test": "vitest run",
23
23
  "postinstall": "node ./bin/kordis.js setup --silent"