@agentplugins/adapter-kimi 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 +47 -0
- package/dist/index.d.mts +172 -0
- package/dist/index.d.ts +172 -0
- package/dist/index.js +439 -0
- package/dist/index.mjs +412 -0
- package/package.json +50 -0
package/README.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# @agentplugins/adapter-kimi
|
|
2
|
+
|
|
3
|
+
> AgentPlugins platform adapter for [Kimi (Moonshot AI)](https://www.moonshot.cn/).
|
|
4
|
+
|
|
5
|
+
Compiles a universal `PluginManifest` into Kimi's plugin layout: `kimi.plugin.json` manifest, hook wiring, and MCP server config. Inline handlers are wrapped as command scripts.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @agentplugins/adapter-kimi
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Typically installed transitively via [`@agentplugins/cli`](https://www.npmjs.com/package/@agentplugins/cli).
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { createKimiAdapter } from '@agentplugins/adapter-kimi';
|
|
19
|
+
|
|
20
|
+
const adapter = createKimiAdapter();
|
|
21
|
+
const output = await adapter.compile(manifest);
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Or via the CLI:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npx agentplugins build --target kimi
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Output shape
|
|
31
|
+
|
|
32
|
+
A successful build writes to `dist/kimi/`:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
dist/kimi/
|
|
36
|
+
├── kimi.plugin.json
|
|
37
|
+
├── hooks/
|
|
38
|
+
│ └── hooks.json
|
|
39
|
+
├── skills/
|
|
40
|
+
└── .mcp.json
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Install with: `cp -r dist/kimi ~/.kimi/plugins/`
|
|
44
|
+
|
|
45
|
+
## License
|
|
46
|
+
|
|
47
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { PlatformAdapter, TargetPlatform, UniversalHookName, HandlerType, PluginManifest, ValidationIssue, AdapterOutput } from '@agentplugins/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @agentplugins/adapter-kimi
|
|
5
|
+
*
|
|
6
|
+
* Platform adapter for Kimi (Moonshot AI) — AgentPlugins plugin system.
|
|
7
|
+
*
|
|
8
|
+
* Kimi plugins are installed via the `/plugins install <github-url|local-path>`
|
|
9
|
+
* command in the Kimi chat interface. They are **user-level only**; there is no
|
|
10
|
+
* project-level scope. Any change to a plugin requires a `/new` session restart
|
|
11
|
+
* to take effect.
|
|
12
|
+
*
|
|
13
|
+
* Architecture overview
|
|
14
|
+
* ---------------------
|
|
15
|
+
* - Manifest : `kimi.plugin.json` (JSON)
|
|
16
|
+
* - Hooks : Command-based (JSON over stdin / stdout), FAIL-OPEN
|
|
17
|
+
* - Skills : Markdown files with YAML frontmatter (`SKILL.md`)
|
|
18
|
+
* - Registration : `/plugins install <url>`
|
|
19
|
+
* - Security model : Conservative loading + trust badges; hooks are *not* a
|
|
20
|
+
* sole security barrier (they fail open)
|
|
21
|
+
*
|
|
22
|
+
* Supported universal hooks
|
|
23
|
+
* -------------------------
|
|
24
|
+
* | Universal hook | Kimi event |
|
|
25
|
+
* |----------------------|-------------------|
|
|
26
|
+
* | preToolUse | PreToolUse |
|
|
27
|
+
* | userPromptSubmit | UserPromptSubmit |
|
|
28
|
+
* | sessionStart | SessionStart |
|
|
29
|
+
* | notification | Notification |
|
|
30
|
+
* | permissionRequest | PermissionRequest |
|
|
31
|
+
*
|
|
32
|
+
* Unsupported hooks (explicitly rejected during validation):
|
|
33
|
+
* sessionEnd, userPromptExpansion, postToolUse, postToolUseFailure,
|
|
34
|
+
* permissionDenied, subagentStart, subagentStop, preCompact, postCompact,
|
|
35
|
+
* stop, stopFailure, fileChanged, cwdChanged, setup
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Shape of the `kimi.plugin.json` manifest.
|
|
40
|
+
*
|
|
41
|
+
* @see https://platform.moonshot.cn/docs/plugins/kimi-plugin-json
|
|
42
|
+
*/
|
|
43
|
+
interface KimiPluginJson {
|
|
44
|
+
/** Plugin machine-name (kebab-case, unique within user scope). */
|
|
45
|
+
name: string;
|
|
46
|
+
/** Relative path to the skills directory containing `SKILL.md` files. */
|
|
47
|
+
skills: string;
|
|
48
|
+
/**
|
|
49
|
+
* Relative path to the session-start hook entry point.
|
|
50
|
+
* Executed once when a new chat session begins.
|
|
51
|
+
*/
|
|
52
|
+
sessionStart?: string;
|
|
53
|
+
/**
|
|
54
|
+
* Relative path to an MCP (Model Context Protocol) server config JSON.
|
|
55
|
+
* Optional — only needed if the plugin exposes MCP-based tools.
|
|
56
|
+
*/
|
|
57
|
+
mcpServers?: string;
|
|
58
|
+
/** Display metadata shown in the Kimi plugin store / `/plugins list`. */
|
|
59
|
+
interface: {
|
|
60
|
+
/** Human-readable plugin name. */
|
|
61
|
+
displayName: string;
|
|
62
|
+
/** Short description (≤ 120 chars recommended). */
|
|
63
|
+
description: string;
|
|
64
|
+
/** Optional icon URL or emoji. */
|
|
65
|
+
icon?: string;
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Shape of the `kimi-hooks.json` configuration file.
|
|
70
|
+
*
|
|
71
|
+
* Each top-level key is a Kimi event name. Under each event lives a list of
|
|
72
|
+
* matcher rules; every matching rule runs its `hooks` array in order.
|
|
73
|
+
*
|
|
74
|
+
* Kimi hooks are **fail-open**: if a hook process exits non-zero or times out,
|
|
75
|
+
* Kimi logs the failure but continues execution. This is by design — hooks are
|
|
76
|
+
* *not* a security barrier on their own.
|
|
77
|
+
*/
|
|
78
|
+
interface KimiHooksJson {
|
|
79
|
+
hooks: Record<string, Array<{
|
|
80
|
+
/** Glob-style matcher (`*`, `read_file`, etc.). */
|
|
81
|
+
matcher: string;
|
|
82
|
+
/** Ordered list of hook invocations for this matcher. */
|
|
83
|
+
hooks: Array<{
|
|
84
|
+
type: "command";
|
|
85
|
+
command: string;
|
|
86
|
+
/** Message shown in the Kimi UI while the hook runs. */
|
|
87
|
+
statusMessage?: string;
|
|
88
|
+
}>;
|
|
89
|
+
}>>;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Shape of a single `SKILL.md` file’s YAML frontmatter.
|
|
93
|
+
*/
|
|
94
|
+
interface SkillFrontmatter {
|
|
95
|
+
name: string;
|
|
96
|
+
description: string;
|
|
97
|
+
version?: string;
|
|
98
|
+
author?: string;
|
|
99
|
+
tags?: string[];
|
|
100
|
+
tools?: string[];
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Kimi (Moonshot AI) platform adapter for AgentPlugins.
|
|
104
|
+
*
|
|
105
|
+
* Transforms universal AgentPlugins plugins into the Kimi-native format:
|
|
106
|
+
* - `kimi.plugin.json` — main manifest
|
|
107
|
+
* - `kimi-hooks.json` — command-based hook configuration
|
|
108
|
+
* - `skills/SKILL__*.md` — Markdown skill files with YAML frontmatter
|
|
109
|
+
*
|
|
110
|
+
* ### Hook model
|
|
111
|
+
* Kimi uses a **fail-open** command-based hook system. Hooks communicate with
|
|
112
|
+
* Kimi via JSON messages over stdin/stdout. If a hook fails (non-zero exit,
|
|
113
|
+
* timeout), Kimi logs the error but continues — hooks are not a security
|
|
114
|
+
* barrier on their own.
|
|
115
|
+
*
|
|
116
|
+
* ### Installation flow
|
|
117
|
+
* 1. User runs `/plugins install <url>` in Kimi chat
|
|
118
|
+
* 2. Kimi downloads and validates `kimi.plugin.json`
|
|
119
|
+
* 3. On session start (`/new`), `sessionStart` hook runs
|
|
120
|
+
* 4. Registered event hooks fire as their mapped events occur
|
|
121
|
+
*
|
|
122
|
+
* ### Session lifecycle
|
|
123
|
+
* - Plugins are **user-scoped** (no project-level isolation)
|
|
124
|
+
* - Any plugin change requires `/new` session restart
|
|
125
|
+
* - Hooks are re-evaluated on every session start
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* ```typescript
|
|
129
|
+
* import { KimiAdapter } from "@agentplugins/adapter-kimi";
|
|
130
|
+
*
|
|
131
|
+
* const adapter = new KimiAdapter();
|
|
132
|
+
* const issues = adapter.validate(manifest);
|
|
133
|
+
* if (issues.every((i) => i.severity !== "error")) {
|
|
134
|
+
* const output = adapter.compile(manifest);
|
|
135
|
+
* // write output.files to disk...
|
|
136
|
+
* }
|
|
137
|
+
* ```
|
|
138
|
+
*/
|
|
139
|
+
declare class KimiAdapter implements PlatformAdapter {
|
|
140
|
+
/** @inheritDoc */
|
|
141
|
+
readonly name: TargetPlatform;
|
|
142
|
+
/** @inheritDoc */
|
|
143
|
+
readonly displayName: string;
|
|
144
|
+
/** @inheritDoc */
|
|
145
|
+
readonly supportedHooks: readonly UniversalHookName[];
|
|
146
|
+
/** @inheritDoc */
|
|
147
|
+
readonly supportedHandlers: readonly HandlerType[];
|
|
148
|
+
/** @inheritDoc */
|
|
149
|
+
readonly manifestPath: string;
|
|
150
|
+
/** @inheritDoc */
|
|
151
|
+
readonly manifestFormat: "json";
|
|
152
|
+
/**
|
|
153
|
+
* Validates a universal plugin manifest for Kimi compatibility.
|
|
154
|
+
*
|
|
155
|
+
* @param manifest — The plugin manifest to validate.
|
|
156
|
+
* @returns Array of validation issues (empty if fully valid).
|
|
157
|
+
*/
|
|
158
|
+
validate(manifest: PluginManifest): ValidationIssue[];
|
|
159
|
+
/**
|
|
160
|
+
* Compiles a universal plugin manifest into Kimi-native artifacts.
|
|
161
|
+
*
|
|
162
|
+
* @param manifest — The plugin manifest to compile.
|
|
163
|
+
* @returns AdapterOutput containing all files to write.
|
|
164
|
+
*/
|
|
165
|
+
compile(manifest: PluginManifest): AdapterOutput;
|
|
166
|
+
}
|
|
167
|
+
/** Default adapter instance for convenience imports. */
|
|
168
|
+
declare const kimiAdapter: KimiAdapter;
|
|
169
|
+
/** Factory function for creating a new Kimi adapter instance. */
|
|
170
|
+
declare function createKimiAdapter(): PlatformAdapter;
|
|
171
|
+
|
|
172
|
+
export { KimiAdapter, type KimiHooksJson, type KimiPluginJson, type SkillFrontmatter, createKimiAdapter, KimiAdapter as default, kimiAdapter };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { PlatformAdapter, TargetPlatform, UniversalHookName, HandlerType, PluginManifest, ValidationIssue, AdapterOutput } from '@agentplugins/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @agentplugins/adapter-kimi
|
|
5
|
+
*
|
|
6
|
+
* Platform adapter for Kimi (Moonshot AI) — AgentPlugins plugin system.
|
|
7
|
+
*
|
|
8
|
+
* Kimi plugins are installed via the `/plugins install <github-url|local-path>`
|
|
9
|
+
* command in the Kimi chat interface. They are **user-level only**; there is no
|
|
10
|
+
* project-level scope. Any change to a plugin requires a `/new` session restart
|
|
11
|
+
* to take effect.
|
|
12
|
+
*
|
|
13
|
+
* Architecture overview
|
|
14
|
+
* ---------------------
|
|
15
|
+
* - Manifest : `kimi.plugin.json` (JSON)
|
|
16
|
+
* - Hooks : Command-based (JSON over stdin / stdout), FAIL-OPEN
|
|
17
|
+
* - Skills : Markdown files with YAML frontmatter (`SKILL.md`)
|
|
18
|
+
* - Registration : `/plugins install <url>`
|
|
19
|
+
* - Security model : Conservative loading + trust badges; hooks are *not* a
|
|
20
|
+
* sole security barrier (they fail open)
|
|
21
|
+
*
|
|
22
|
+
* Supported universal hooks
|
|
23
|
+
* -------------------------
|
|
24
|
+
* | Universal hook | Kimi event |
|
|
25
|
+
* |----------------------|-------------------|
|
|
26
|
+
* | preToolUse | PreToolUse |
|
|
27
|
+
* | userPromptSubmit | UserPromptSubmit |
|
|
28
|
+
* | sessionStart | SessionStart |
|
|
29
|
+
* | notification | Notification |
|
|
30
|
+
* | permissionRequest | PermissionRequest |
|
|
31
|
+
*
|
|
32
|
+
* Unsupported hooks (explicitly rejected during validation):
|
|
33
|
+
* sessionEnd, userPromptExpansion, postToolUse, postToolUseFailure,
|
|
34
|
+
* permissionDenied, subagentStart, subagentStop, preCompact, postCompact,
|
|
35
|
+
* stop, stopFailure, fileChanged, cwdChanged, setup
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Shape of the `kimi.plugin.json` manifest.
|
|
40
|
+
*
|
|
41
|
+
* @see https://platform.moonshot.cn/docs/plugins/kimi-plugin-json
|
|
42
|
+
*/
|
|
43
|
+
interface KimiPluginJson {
|
|
44
|
+
/** Plugin machine-name (kebab-case, unique within user scope). */
|
|
45
|
+
name: string;
|
|
46
|
+
/** Relative path to the skills directory containing `SKILL.md` files. */
|
|
47
|
+
skills: string;
|
|
48
|
+
/**
|
|
49
|
+
* Relative path to the session-start hook entry point.
|
|
50
|
+
* Executed once when a new chat session begins.
|
|
51
|
+
*/
|
|
52
|
+
sessionStart?: string;
|
|
53
|
+
/**
|
|
54
|
+
* Relative path to an MCP (Model Context Protocol) server config JSON.
|
|
55
|
+
* Optional — only needed if the plugin exposes MCP-based tools.
|
|
56
|
+
*/
|
|
57
|
+
mcpServers?: string;
|
|
58
|
+
/** Display metadata shown in the Kimi plugin store / `/plugins list`. */
|
|
59
|
+
interface: {
|
|
60
|
+
/** Human-readable plugin name. */
|
|
61
|
+
displayName: string;
|
|
62
|
+
/** Short description (≤ 120 chars recommended). */
|
|
63
|
+
description: string;
|
|
64
|
+
/** Optional icon URL or emoji. */
|
|
65
|
+
icon?: string;
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Shape of the `kimi-hooks.json` configuration file.
|
|
70
|
+
*
|
|
71
|
+
* Each top-level key is a Kimi event name. Under each event lives a list of
|
|
72
|
+
* matcher rules; every matching rule runs its `hooks` array in order.
|
|
73
|
+
*
|
|
74
|
+
* Kimi hooks are **fail-open**: if a hook process exits non-zero or times out,
|
|
75
|
+
* Kimi logs the failure but continues execution. This is by design — hooks are
|
|
76
|
+
* *not* a security barrier on their own.
|
|
77
|
+
*/
|
|
78
|
+
interface KimiHooksJson {
|
|
79
|
+
hooks: Record<string, Array<{
|
|
80
|
+
/** Glob-style matcher (`*`, `read_file`, etc.). */
|
|
81
|
+
matcher: string;
|
|
82
|
+
/** Ordered list of hook invocations for this matcher. */
|
|
83
|
+
hooks: Array<{
|
|
84
|
+
type: "command";
|
|
85
|
+
command: string;
|
|
86
|
+
/** Message shown in the Kimi UI while the hook runs. */
|
|
87
|
+
statusMessage?: string;
|
|
88
|
+
}>;
|
|
89
|
+
}>>;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Shape of a single `SKILL.md` file’s YAML frontmatter.
|
|
93
|
+
*/
|
|
94
|
+
interface SkillFrontmatter {
|
|
95
|
+
name: string;
|
|
96
|
+
description: string;
|
|
97
|
+
version?: string;
|
|
98
|
+
author?: string;
|
|
99
|
+
tags?: string[];
|
|
100
|
+
tools?: string[];
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Kimi (Moonshot AI) platform adapter for AgentPlugins.
|
|
104
|
+
*
|
|
105
|
+
* Transforms universal AgentPlugins plugins into the Kimi-native format:
|
|
106
|
+
* - `kimi.plugin.json` — main manifest
|
|
107
|
+
* - `kimi-hooks.json` — command-based hook configuration
|
|
108
|
+
* - `skills/SKILL__*.md` — Markdown skill files with YAML frontmatter
|
|
109
|
+
*
|
|
110
|
+
* ### Hook model
|
|
111
|
+
* Kimi uses a **fail-open** command-based hook system. Hooks communicate with
|
|
112
|
+
* Kimi via JSON messages over stdin/stdout. If a hook fails (non-zero exit,
|
|
113
|
+
* timeout), Kimi logs the error but continues — hooks are not a security
|
|
114
|
+
* barrier on their own.
|
|
115
|
+
*
|
|
116
|
+
* ### Installation flow
|
|
117
|
+
* 1. User runs `/plugins install <url>` in Kimi chat
|
|
118
|
+
* 2. Kimi downloads and validates `kimi.plugin.json`
|
|
119
|
+
* 3. On session start (`/new`), `sessionStart` hook runs
|
|
120
|
+
* 4. Registered event hooks fire as their mapped events occur
|
|
121
|
+
*
|
|
122
|
+
* ### Session lifecycle
|
|
123
|
+
* - Plugins are **user-scoped** (no project-level isolation)
|
|
124
|
+
* - Any plugin change requires `/new` session restart
|
|
125
|
+
* - Hooks are re-evaluated on every session start
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* ```typescript
|
|
129
|
+
* import { KimiAdapter } from "@agentplugins/adapter-kimi";
|
|
130
|
+
*
|
|
131
|
+
* const adapter = new KimiAdapter();
|
|
132
|
+
* const issues = adapter.validate(manifest);
|
|
133
|
+
* if (issues.every((i) => i.severity !== "error")) {
|
|
134
|
+
* const output = adapter.compile(manifest);
|
|
135
|
+
* // write output.files to disk...
|
|
136
|
+
* }
|
|
137
|
+
* ```
|
|
138
|
+
*/
|
|
139
|
+
declare class KimiAdapter implements PlatformAdapter {
|
|
140
|
+
/** @inheritDoc */
|
|
141
|
+
readonly name: TargetPlatform;
|
|
142
|
+
/** @inheritDoc */
|
|
143
|
+
readonly displayName: string;
|
|
144
|
+
/** @inheritDoc */
|
|
145
|
+
readonly supportedHooks: readonly UniversalHookName[];
|
|
146
|
+
/** @inheritDoc */
|
|
147
|
+
readonly supportedHandlers: readonly HandlerType[];
|
|
148
|
+
/** @inheritDoc */
|
|
149
|
+
readonly manifestPath: string;
|
|
150
|
+
/** @inheritDoc */
|
|
151
|
+
readonly manifestFormat: "json";
|
|
152
|
+
/**
|
|
153
|
+
* Validates a universal plugin manifest for Kimi compatibility.
|
|
154
|
+
*
|
|
155
|
+
* @param manifest — The plugin manifest to validate.
|
|
156
|
+
* @returns Array of validation issues (empty if fully valid).
|
|
157
|
+
*/
|
|
158
|
+
validate(manifest: PluginManifest): ValidationIssue[];
|
|
159
|
+
/**
|
|
160
|
+
* Compiles a universal plugin manifest into Kimi-native artifacts.
|
|
161
|
+
*
|
|
162
|
+
* @param manifest — The plugin manifest to compile.
|
|
163
|
+
* @returns AdapterOutput containing all files to write.
|
|
164
|
+
*/
|
|
165
|
+
compile(manifest: PluginManifest): AdapterOutput;
|
|
166
|
+
}
|
|
167
|
+
/** Default adapter instance for convenience imports. */
|
|
168
|
+
declare const kimiAdapter: KimiAdapter;
|
|
169
|
+
/** Factory function for creating a new Kimi adapter instance. */
|
|
170
|
+
declare function createKimiAdapter(): PlatformAdapter;
|
|
171
|
+
|
|
172
|
+
export { KimiAdapter, type KimiHooksJson, type KimiPluginJson, type SkillFrontmatter, createKimiAdapter, KimiAdapter as default, kimiAdapter };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
KimiAdapter: () => KimiAdapter,
|
|
24
|
+
createKimiAdapter: () => createKimiAdapter,
|
|
25
|
+
default: () => index_default,
|
|
26
|
+
kimiAdapter: () => kimiAdapter
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(index_exports);
|
|
29
|
+
var import_core = require("@agentplugins/core");
|
|
30
|
+
var PLATFORM_NAME = "kimi";
|
|
31
|
+
var PLATFORM_DISPLAY_NAME = "Kimi (Moonshot AI)";
|
|
32
|
+
var MANIFEST_FILENAME = "kimi.plugin.json";
|
|
33
|
+
var MANIFEST_FORMAT = "json";
|
|
34
|
+
var SUPPORTED_HOOKS = [
|
|
35
|
+
"preToolUse",
|
|
36
|
+
"userPromptSubmit",
|
|
37
|
+
"sessionStart",
|
|
38
|
+
"notification",
|
|
39
|
+
"permissionRequest"
|
|
40
|
+
];
|
|
41
|
+
var SUPPORTED_HANDLERS = ["command"];
|
|
42
|
+
var HOOK_NAME_MAP = {
|
|
43
|
+
preToolUse: "PreToolUse",
|
|
44
|
+
userPromptSubmit: "UserPromptSubmit",
|
|
45
|
+
sessionStart: "SessionStart",
|
|
46
|
+
notification: "Notification",
|
|
47
|
+
permissionRequest: "PermissionRequest",
|
|
48
|
+
// — unsupported mappings (not present in SUPPORTED_HOOKS) —
|
|
49
|
+
sessionEnd: "SessionEnd",
|
|
50
|
+
userPromptExpansion: "UserPromptExpansion",
|
|
51
|
+
postToolUse: "PostToolUse",
|
|
52
|
+
postToolUseFailure: "PostToolUseFailure",
|
|
53
|
+
permissionDenied: "PermissionDenied",
|
|
54
|
+
subagentStart: "SubagentStart",
|
|
55
|
+
subagentStop: "SubagentStop",
|
|
56
|
+
preCompact: "PreCompact",
|
|
57
|
+
postCompact: "PostCompact",
|
|
58
|
+
stop: "Stop",
|
|
59
|
+
stopFailure: "StopFailure",
|
|
60
|
+
fileChanged: "FileChanged",
|
|
61
|
+
cwdChanged: "CwdChanged",
|
|
62
|
+
setup: "Setup"
|
|
63
|
+
};
|
|
64
|
+
var UNSUPPORTED_HOOKS = [
|
|
65
|
+
"sessionEnd",
|
|
66
|
+
"userPromptExpansion",
|
|
67
|
+
"postToolUse",
|
|
68
|
+
"postToolUseFailure",
|
|
69
|
+
"permissionDenied",
|
|
70
|
+
"subagentStart",
|
|
71
|
+
"subagentStop",
|
|
72
|
+
"preCompact",
|
|
73
|
+
"postCompact",
|
|
74
|
+
"stop",
|
|
75
|
+
"stopFailure",
|
|
76
|
+
"fileChanged",
|
|
77
|
+
"cwdChanged",
|
|
78
|
+
"setup"
|
|
79
|
+
];
|
|
80
|
+
function validateForKimi(manifest) {
|
|
81
|
+
const issues = [];
|
|
82
|
+
if (!manifest.name || manifest.name.trim().length === 0) {
|
|
83
|
+
issues.push({
|
|
84
|
+
severity: import_core.Severity.ERROR,
|
|
85
|
+
field: "name",
|
|
86
|
+
message: "Plugin 'name' is required for kimi.plugin.json."
|
|
87
|
+
});
|
|
88
|
+
} else if (!/^[a-z0-9-]+$/.test(manifest.name)) {
|
|
89
|
+
issues.push({
|
|
90
|
+
severity: import_core.Severity.WARNING,
|
|
91
|
+
field: "name",
|
|
92
|
+
message: "Plugin name should be kebab-case (lowercase letters, numbers, hyphens) for best compatibility with Kimi."
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
if (!manifest.version || manifest.version.trim().length === 0) {
|
|
96
|
+
issues.push({
|
|
97
|
+
severity: import_core.Severity.ERROR,
|
|
98
|
+
field: "version",
|
|
99
|
+
message: "Plugin 'version' is required."
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
if (!manifest.description || manifest.description.trim().length === 0) {
|
|
103
|
+
issues.push({
|
|
104
|
+
severity: import_core.Severity.WARNING,
|
|
105
|
+
field: "description",
|
|
106
|
+
message: "Plugin 'description' is recommended for Kimi interface metadata."
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
if (manifest.hooks) {
|
|
110
|
+
for (const [hookName, hook] of Object.entries(manifest.hooks)) {
|
|
111
|
+
if (!hook) continue;
|
|
112
|
+
if (UNSUPPORTED_HOOKS.includes(hookName)) {
|
|
113
|
+
issues.push({
|
|
114
|
+
severity: import_core.Severity.ERROR,
|
|
115
|
+
field: `hooks.${hookName}`,
|
|
116
|
+
message: `Hook "${hookName}" is not supported by Kimi. Supported hooks: ${SUPPORTED_HOOKS.join(", ")}. Consider refactoring to use a supported hook or removing it.`
|
|
117
|
+
});
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if (!SUPPORTED_HOOKS.includes(hookName)) {
|
|
121
|
+
issues.push({
|
|
122
|
+
severity: import_core.Severity.ERROR,
|
|
123
|
+
field: `hooks.${hookName}`,
|
|
124
|
+
message: `Unknown hook "${hookName}". Kimi supports: ${SUPPORTED_HOOKS.join(", ")}.`
|
|
125
|
+
});
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (hook.handler) {
|
|
129
|
+
if (hook.handler.type === "inline") {
|
|
130
|
+
issues.push({
|
|
131
|
+
severity: import_core.Severity.ERROR,
|
|
132
|
+
field: `hooks.${hookName}.handler`,
|
|
133
|
+
message: `Kimi does not support inline/function handlers for "${hookName}". Wrap the logic in a CLI command and set handler.type to "command" (e.g., "node ./hooks/${hookName}.js").`
|
|
134
|
+
});
|
|
135
|
+
} else if (hook.handler.type === "command") {
|
|
136
|
+
if (!hook.handler.command || hook.handler.command.trim().length === 0) {
|
|
137
|
+
issues.push({
|
|
138
|
+
severity: import_core.Severity.ERROR,
|
|
139
|
+
field: `hooks.${hookName}.handler.command`,
|
|
140
|
+
message: `Command handler for "${hookName}" has an empty command string.`
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
const securityKeywords = ["security", "block", "deny", "prevent", "guard"];
|
|
144
|
+
const lowerHookName = hookName.toLowerCase();
|
|
145
|
+
if (securityKeywords.some((k) => lowerHookName.includes(k))) {
|
|
146
|
+
issues.push({
|
|
147
|
+
severity: import_core.Severity.WARNING,
|
|
148
|
+
field: `hooks.${hookName}`,
|
|
149
|
+
message: `Hook "${hookName}" appears security-oriented. Kimi hooks are FAIL-OPEN (not a sole security barrier). Do not rely on this hook for access control.`
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
} else if (hook.handler.type !== "http") {
|
|
153
|
+
issues.push({
|
|
154
|
+
severity: import_core.Severity.ERROR,
|
|
155
|
+
field: `hooks.${hookName}.handler.type`,
|
|
156
|
+
message: `Kimi does not support handler type "${hook.handler.type}". Use "command" instead.`
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (hook.blocking && hookName !== "preToolUse") {
|
|
161
|
+
issues.push({
|
|
162
|
+
severity: import_core.Severity.WARNING,
|
|
163
|
+
field: `hooks.${hookName}.blocking`,
|
|
164
|
+
message: `Only "preToolUse" supports blocking semantics in Kimi. Hook "${hookName}" marked blocking=true but will run non-blocking.`
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (manifest.skills && manifest.skills.length > 0) {
|
|
170
|
+
for (let i = 0; i < manifest.skills.length; i++) {
|
|
171
|
+
const skill = manifest.skills[i];
|
|
172
|
+
if (!skill.name || skill.name.trim().length === 0) {
|
|
173
|
+
issues.push({
|
|
174
|
+
severity: import_core.Severity.ERROR,
|
|
175
|
+
field: `skills[${i}].name`,
|
|
176
|
+
message: `Skill at index ${i} is missing a name.`
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
if (!skill.description || skill.description.trim().length === 0) {
|
|
180
|
+
issues.push({
|
|
181
|
+
severity: import_core.Severity.WARNING,
|
|
182
|
+
field: `skills[${i}].description`,
|
|
183
|
+
message: `Skill "${skill.name || `#${i}`}" is missing a description.`
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return issues;
|
|
189
|
+
}
|
|
190
|
+
function compileForKimi(manifest) {
|
|
191
|
+
const files = [];
|
|
192
|
+
const pluginJson = {
|
|
193
|
+
name: manifest.name,
|
|
194
|
+
skills: "./skills",
|
|
195
|
+
interface: {
|
|
196
|
+
displayName: manifest.displayName || manifest.name,
|
|
197
|
+
description: manifest.description || ""
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
const sessionStartHook = manifest.hooks?.sessionStart;
|
|
201
|
+
if (sessionStartHook?.handler?.type === "command") {
|
|
202
|
+
pluginJson.sessionStart = sessionStartHook.handler.command;
|
|
203
|
+
}
|
|
204
|
+
if (manifest.mcpServers && Object.keys(manifest.mcpServers).length > 0) {
|
|
205
|
+
pluginJson.mcpServers = "./mcp.json";
|
|
206
|
+
}
|
|
207
|
+
files.push({
|
|
208
|
+
path: MANIFEST_FILENAME,
|
|
209
|
+
content: JSON.stringify(pluginJson, null, 2)
|
|
210
|
+
});
|
|
211
|
+
const hooksJson = buildHooksJson(manifest.hooks || {});
|
|
212
|
+
if (Object.keys(hooksJson.hooks).length > 0) {
|
|
213
|
+
files.push({
|
|
214
|
+
path: "kimi-hooks.json",
|
|
215
|
+
content: JSON.stringify(hooksJson, null, 2)
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
if (manifest.skills && manifest.skills.length > 0) {
|
|
219
|
+
for (const skill of manifest.skills) {
|
|
220
|
+
const skillFile = compileSkillToMarkdown(skill);
|
|
221
|
+
files.push(skillFile);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
files.push({
|
|
225
|
+
path: "KIMI_INSTALL.md",
|
|
226
|
+
content: generateInstallInstructions(manifest)
|
|
227
|
+
});
|
|
228
|
+
return {
|
|
229
|
+
files,
|
|
230
|
+
manifest: {},
|
|
231
|
+
warnings: [],
|
|
232
|
+
issues: []
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
function buildHooksJson(hooks) {
|
|
236
|
+
const hooksConfig = { hooks: {} };
|
|
237
|
+
for (const [hookName, hook] of Object.entries(hooks)) {
|
|
238
|
+
if (!hook) continue;
|
|
239
|
+
const universalName = hookName;
|
|
240
|
+
if (!SUPPORTED_HOOKS.includes(universalName)) {
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
if (universalName === "sessionStart") {
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
const kimiEventName = HOOK_NAME_MAP[universalName];
|
|
247
|
+
if (!kimiEventName) {
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
const command = extractCommand(hook, universalName);
|
|
251
|
+
if (!command) {
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
const statusMessage = buildStatusMessage(hook, universalName);
|
|
255
|
+
if (!hooksConfig.hooks[kimiEventName]) {
|
|
256
|
+
hooksConfig.hooks[kimiEventName] = [];
|
|
257
|
+
}
|
|
258
|
+
hooksConfig.hooks[kimiEventName].push({
|
|
259
|
+
matcher: hook.matcher || "*",
|
|
260
|
+
hooks: [
|
|
261
|
+
{
|
|
262
|
+
type: "command",
|
|
263
|
+
command,
|
|
264
|
+
statusMessage
|
|
265
|
+
}
|
|
266
|
+
]
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
return hooksConfig;
|
|
270
|
+
}
|
|
271
|
+
function extractCommand(hook, hookName) {
|
|
272
|
+
if (!hook.handler) {
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
if (hook.handler.type === "command") {
|
|
276
|
+
return hook.handler.command || null;
|
|
277
|
+
}
|
|
278
|
+
if (hook.handler.type === "inline") {
|
|
279
|
+
return `node ./hooks/${hookName}.js`;
|
|
280
|
+
}
|
|
281
|
+
return null;
|
|
282
|
+
}
|
|
283
|
+
function buildStatusMessage(hook, universalName) {
|
|
284
|
+
if (hook.handler.type === "command" && hook.handler.statusMessage) {
|
|
285
|
+
return hook.handler.statusMessage;
|
|
286
|
+
}
|
|
287
|
+
const defaultMessages = {
|
|
288
|
+
preToolUse: "Running pre-tool check",
|
|
289
|
+
userPromptSubmit: "Processing user prompt",
|
|
290
|
+
sessionStart: "Initializing plugin session",
|
|
291
|
+
notification: "Handling notification",
|
|
292
|
+
permissionRequest: "Requesting permission",
|
|
293
|
+
sessionEnd: "Cleaning up session",
|
|
294
|
+
userPromptExpansion: "Expanding prompt context",
|
|
295
|
+
postToolUse: "Processing tool result",
|
|
296
|
+
postToolUseFailure: "Handling tool failure",
|
|
297
|
+
permissionDenied: "Processing permission denial",
|
|
298
|
+
subagentStart: "Subagent starting",
|
|
299
|
+
subagentStop: "Subagent stopping",
|
|
300
|
+
preCompact: "Pre-compacting session",
|
|
301
|
+
postCompact: "Post-compacting session",
|
|
302
|
+
stop: "Stopping",
|
|
303
|
+
stopFailure: "Handling stop failure",
|
|
304
|
+
fileChanged: "Processing file change",
|
|
305
|
+
cwdChanged: "Processing directory change",
|
|
306
|
+
setup: "Running setup"
|
|
307
|
+
};
|
|
308
|
+
return defaultMessages[universalName];
|
|
309
|
+
}
|
|
310
|
+
function compileSkillToMarkdown(skill) {
|
|
311
|
+
const frontmatter = {
|
|
312
|
+
name: skill.name,
|
|
313
|
+
description: skill.description
|
|
314
|
+
};
|
|
315
|
+
const frontmatterYaml = Object.entries(frontmatter).map(([key, value]) => {
|
|
316
|
+
if (Array.isArray(value)) {
|
|
317
|
+
return `${key}:
|
|
318
|
+
${value.map((v) => ` - ${v}`).join("\n")}`;
|
|
319
|
+
}
|
|
320
|
+
return `${key}: ${value}`;
|
|
321
|
+
}).join("\n");
|
|
322
|
+
const body = skill.content || skill.description || "";
|
|
323
|
+
const content = `---
|
|
324
|
+
${frontmatterYaml}
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
${body}
|
|
328
|
+
`;
|
|
329
|
+
const safeName = skill.name.toLowerCase().replace(/[^a-z0-9]+/g, "-");
|
|
330
|
+
return {
|
|
331
|
+
path: `skills/SKILL__${safeName}.md`,
|
|
332
|
+
content
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
function generateInstallInstructions(manifest) {
|
|
336
|
+
const displayName = manifest.displayName || manifest.name;
|
|
337
|
+
return `# Installing ${displayName} for Kimi
|
|
338
|
+
|
|
339
|
+
> Generated by AgentPlugins \u2014 Kimi Platform Adapter
|
|
340
|
+
|
|
341
|
+
## Quick Install
|
|
342
|
+
|
|
343
|
+
In any Kimi chat, run:
|
|
344
|
+
|
|
345
|
+
\`\`\`
|
|
346
|
+
/plugins install <github-url|local-path>
|
|
347
|
+
\`\`\`
|
|
348
|
+
|
|
349
|
+
Replace \`<github-url|local-path>\` with the repository URL or local directory
|
|
350
|
+
path containing this plugin.
|
|
351
|
+
|
|
352
|
+
## Post-Install
|
|
353
|
+
|
|
354
|
+
Kimi plugins are **user-level** \u2014 they apply to all your sessions, not just the
|
|
355
|
+
current project.
|
|
356
|
+
|
|
357
|
+
After installing (or updating) this plugin, start a **new session** with:
|
|
358
|
+
|
|
359
|
+
\`\`\`
|
|
360
|
+
/new
|
|
361
|
+
\`\`\`
|
|
362
|
+
|
|
363
|
+
Plugin changes do **not** take effect in existing sessions.
|
|
364
|
+
|
|
365
|
+
## Important Notes
|
|
366
|
+
|
|
367
|
+
- **Fail-open hooks**: Kimi hooks are designed to fail open. If a hook process
|
|
368
|
+
crashes or times out, Kimi continues execution. Do not rely on hooks as a
|
|
369
|
+
sole security mechanism.
|
|
370
|
+
|
|
371
|
+
- **Trust badges**: Kimi shows trust indicators for plugins installed from
|
|
372
|
+
GitHub. Install only plugins from sources you trust.
|
|
373
|
+
|
|
374
|
+
- **No project scope**: Kimi does not support project-level plugin scoping.
|
|
375
|
+
All plugins are installed at the user level.
|
|
376
|
+
|
|
377
|
+
## Supported Features
|
|
378
|
+
|
|
379
|
+
This plugin uses the following Kimi features:
|
|
380
|
+
|
|
381
|
+
${manifest.hooks && Object.keys(manifest.hooks).length > 0 ? `**Hooks**: ${Object.entries(manifest.hooks).filter(([name]) => SUPPORTED_HOOKS.includes(name)).map(([name]) => HOOK_NAME_MAP[name] || name).join(", ")}` : "**Hooks**: (none)"}
|
|
382
|
+
|
|
383
|
+
${manifest.skills && manifest.skills.length > 0 ? `**Skills**: ${manifest.skills.map((s) => s.name).join(", ")}` : "**Skills**: (none)"}
|
|
384
|
+
|
|
385
|
+
## Troubleshooting
|
|
386
|
+
|
|
387
|
+
| Problem | Solution |
|
|
388
|
+
|---------|----------|
|
|
389
|
+
| Plugin not active | Run \`/new\` to start a fresh session |
|
|
390
|
+
| Hook not firing | Check that the command in \`kimi-hooks.json\` is executable |
|
|
391
|
+
| Skill not recognized | Verify \`kimi.plugin.json\` has correct \`skills\` path |
|
|
392
|
+
| Changes not applied | Remember: **every change requires \`/new\`** |
|
|
393
|
+
`;
|
|
394
|
+
}
|
|
395
|
+
var KimiAdapter = class {
|
|
396
|
+
constructor() {
|
|
397
|
+
/** @inheritDoc */
|
|
398
|
+
this.name = PLATFORM_NAME;
|
|
399
|
+
/** @inheritDoc */
|
|
400
|
+
this.displayName = PLATFORM_DISPLAY_NAME;
|
|
401
|
+
/** @inheritDoc */
|
|
402
|
+
this.supportedHooks = SUPPORTED_HOOKS;
|
|
403
|
+
/** @inheritDoc */
|
|
404
|
+
this.supportedHandlers = SUPPORTED_HANDLERS;
|
|
405
|
+
/** @inheritDoc */
|
|
406
|
+
this.manifestPath = MANIFEST_FILENAME;
|
|
407
|
+
/** @inheritDoc */
|
|
408
|
+
this.manifestFormat = MANIFEST_FORMAT;
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Validates a universal plugin manifest for Kimi compatibility.
|
|
412
|
+
*
|
|
413
|
+
* @param manifest — The plugin manifest to validate.
|
|
414
|
+
* @returns Array of validation issues (empty if fully valid).
|
|
415
|
+
*/
|
|
416
|
+
validate(manifest) {
|
|
417
|
+
return validateForKimi(manifest);
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Compiles a universal plugin manifest into Kimi-native artifacts.
|
|
421
|
+
*
|
|
422
|
+
* @param manifest — The plugin manifest to compile.
|
|
423
|
+
* @returns AdapterOutput containing all files to write.
|
|
424
|
+
*/
|
|
425
|
+
compile(manifest) {
|
|
426
|
+
return compileForKimi(manifest);
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
var kimiAdapter = new KimiAdapter();
|
|
430
|
+
function createKimiAdapter() {
|
|
431
|
+
return new KimiAdapter();
|
|
432
|
+
}
|
|
433
|
+
var index_default = KimiAdapter;
|
|
434
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
435
|
+
0 && (module.exports = {
|
|
436
|
+
KimiAdapter,
|
|
437
|
+
createKimiAdapter,
|
|
438
|
+
kimiAdapter
|
|
439
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { Severity } from "@agentplugins/core";
|
|
3
|
+
var PLATFORM_NAME = "kimi";
|
|
4
|
+
var PLATFORM_DISPLAY_NAME = "Kimi (Moonshot AI)";
|
|
5
|
+
var MANIFEST_FILENAME = "kimi.plugin.json";
|
|
6
|
+
var MANIFEST_FORMAT = "json";
|
|
7
|
+
var SUPPORTED_HOOKS = [
|
|
8
|
+
"preToolUse",
|
|
9
|
+
"userPromptSubmit",
|
|
10
|
+
"sessionStart",
|
|
11
|
+
"notification",
|
|
12
|
+
"permissionRequest"
|
|
13
|
+
];
|
|
14
|
+
var SUPPORTED_HANDLERS = ["command"];
|
|
15
|
+
var HOOK_NAME_MAP = {
|
|
16
|
+
preToolUse: "PreToolUse",
|
|
17
|
+
userPromptSubmit: "UserPromptSubmit",
|
|
18
|
+
sessionStart: "SessionStart",
|
|
19
|
+
notification: "Notification",
|
|
20
|
+
permissionRequest: "PermissionRequest",
|
|
21
|
+
// — unsupported mappings (not present in SUPPORTED_HOOKS) —
|
|
22
|
+
sessionEnd: "SessionEnd",
|
|
23
|
+
userPromptExpansion: "UserPromptExpansion",
|
|
24
|
+
postToolUse: "PostToolUse",
|
|
25
|
+
postToolUseFailure: "PostToolUseFailure",
|
|
26
|
+
permissionDenied: "PermissionDenied",
|
|
27
|
+
subagentStart: "SubagentStart",
|
|
28
|
+
subagentStop: "SubagentStop",
|
|
29
|
+
preCompact: "PreCompact",
|
|
30
|
+
postCompact: "PostCompact",
|
|
31
|
+
stop: "Stop",
|
|
32
|
+
stopFailure: "StopFailure",
|
|
33
|
+
fileChanged: "FileChanged",
|
|
34
|
+
cwdChanged: "CwdChanged",
|
|
35
|
+
setup: "Setup"
|
|
36
|
+
};
|
|
37
|
+
var UNSUPPORTED_HOOKS = [
|
|
38
|
+
"sessionEnd",
|
|
39
|
+
"userPromptExpansion",
|
|
40
|
+
"postToolUse",
|
|
41
|
+
"postToolUseFailure",
|
|
42
|
+
"permissionDenied",
|
|
43
|
+
"subagentStart",
|
|
44
|
+
"subagentStop",
|
|
45
|
+
"preCompact",
|
|
46
|
+
"postCompact",
|
|
47
|
+
"stop",
|
|
48
|
+
"stopFailure",
|
|
49
|
+
"fileChanged",
|
|
50
|
+
"cwdChanged",
|
|
51
|
+
"setup"
|
|
52
|
+
];
|
|
53
|
+
function validateForKimi(manifest) {
|
|
54
|
+
const issues = [];
|
|
55
|
+
if (!manifest.name || manifest.name.trim().length === 0) {
|
|
56
|
+
issues.push({
|
|
57
|
+
severity: Severity.ERROR,
|
|
58
|
+
field: "name",
|
|
59
|
+
message: "Plugin 'name' is required for kimi.plugin.json."
|
|
60
|
+
});
|
|
61
|
+
} else if (!/^[a-z0-9-]+$/.test(manifest.name)) {
|
|
62
|
+
issues.push({
|
|
63
|
+
severity: Severity.WARNING,
|
|
64
|
+
field: "name",
|
|
65
|
+
message: "Plugin name should be kebab-case (lowercase letters, numbers, hyphens) for best compatibility with Kimi."
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
if (!manifest.version || manifest.version.trim().length === 0) {
|
|
69
|
+
issues.push({
|
|
70
|
+
severity: Severity.ERROR,
|
|
71
|
+
field: "version",
|
|
72
|
+
message: "Plugin 'version' is required."
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
if (!manifest.description || manifest.description.trim().length === 0) {
|
|
76
|
+
issues.push({
|
|
77
|
+
severity: Severity.WARNING,
|
|
78
|
+
field: "description",
|
|
79
|
+
message: "Plugin 'description' is recommended for Kimi interface metadata."
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
if (manifest.hooks) {
|
|
83
|
+
for (const [hookName, hook] of Object.entries(manifest.hooks)) {
|
|
84
|
+
if (!hook) continue;
|
|
85
|
+
if (UNSUPPORTED_HOOKS.includes(hookName)) {
|
|
86
|
+
issues.push({
|
|
87
|
+
severity: Severity.ERROR,
|
|
88
|
+
field: `hooks.${hookName}`,
|
|
89
|
+
message: `Hook "${hookName}" is not supported by Kimi. Supported hooks: ${SUPPORTED_HOOKS.join(", ")}. Consider refactoring to use a supported hook or removing it.`
|
|
90
|
+
});
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (!SUPPORTED_HOOKS.includes(hookName)) {
|
|
94
|
+
issues.push({
|
|
95
|
+
severity: Severity.ERROR,
|
|
96
|
+
field: `hooks.${hookName}`,
|
|
97
|
+
message: `Unknown hook "${hookName}". Kimi supports: ${SUPPORTED_HOOKS.join(", ")}.`
|
|
98
|
+
});
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
if (hook.handler) {
|
|
102
|
+
if (hook.handler.type === "inline") {
|
|
103
|
+
issues.push({
|
|
104
|
+
severity: Severity.ERROR,
|
|
105
|
+
field: `hooks.${hookName}.handler`,
|
|
106
|
+
message: `Kimi does not support inline/function handlers for "${hookName}". Wrap the logic in a CLI command and set handler.type to "command" (e.g., "node ./hooks/${hookName}.js").`
|
|
107
|
+
});
|
|
108
|
+
} else if (hook.handler.type === "command") {
|
|
109
|
+
if (!hook.handler.command || hook.handler.command.trim().length === 0) {
|
|
110
|
+
issues.push({
|
|
111
|
+
severity: Severity.ERROR,
|
|
112
|
+
field: `hooks.${hookName}.handler.command`,
|
|
113
|
+
message: `Command handler for "${hookName}" has an empty command string.`
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
const securityKeywords = ["security", "block", "deny", "prevent", "guard"];
|
|
117
|
+
const lowerHookName = hookName.toLowerCase();
|
|
118
|
+
if (securityKeywords.some((k) => lowerHookName.includes(k))) {
|
|
119
|
+
issues.push({
|
|
120
|
+
severity: Severity.WARNING,
|
|
121
|
+
field: `hooks.${hookName}`,
|
|
122
|
+
message: `Hook "${hookName}" appears security-oriented. Kimi hooks are FAIL-OPEN (not a sole security barrier). Do not rely on this hook for access control.`
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
} else if (hook.handler.type !== "http") {
|
|
126
|
+
issues.push({
|
|
127
|
+
severity: Severity.ERROR,
|
|
128
|
+
field: `hooks.${hookName}.handler.type`,
|
|
129
|
+
message: `Kimi does not support handler type "${hook.handler.type}". Use "command" instead.`
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (hook.blocking && hookName !== "preToolUse") {
|
|
134
|
+
issues.push({
|
|
135
|
+
severity: Severity.WARNING,
|
|
136
|
+
field: `hooks.${hookName}.blocking`,
|
|
137
|
+
message: `Only "preToolUse" supports blocking semantics in Kimi. Hook "${hookName}" marked blocking=true but will run non-blocking.`
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (manifest.skills && manifest.skills.length > 0) {
|
|
143
|
+
for (let i = 0; i < manifest.skills.length; i++) {
|
|
144
|
+
const skill = manifest.skills[i];
|
|
145
|
+
if (!skill.name || skill.name.trim().length === 0) {
|
|
146
|
+
issues.push({
|
|
147
|
+
severity: Severity.ERROR,
|
|
148
|
+
field: `skills[${i}].name`,
|
|
149
|
+
message: `Skill at index ${i} is missing a name.`
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
if (!skill.description || skill.description.trim().length === 0) {
|
|
153
|
+
issues.push({
|
|
154
|
+
severity: Severity.WARNING,
|
|
155
|
+
field: `skills[${i}].description`,
|
|
156
|
+
message: `Skill "${skill.name || `#${i}`}" is missing a description.`
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return issues;
|
|
162
|
+
}
|
|
163
|
+
function compileForKimi(manifest) {
|
|
164
|
+
const files = [];
|
|
165
|
+
const pluginJson = {
|
|
166
|
+
name: manifest.name,
|
|
167
|
+
skills: "./skills",
|
|
168
|
+
interface: {
|
|
169
|
+
displayName: manifest.displayName || manifest.name,
|
|
170
|
+
description: manifest.description || ""
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
const sessionStartHook = manifest.hooks?.sessionStart;
|
|
174
|
+
if (sessionStartHook?.handler?.type === "command") {
|
|
175
|
+
pluginJson.sessionStart = sessionStartHook.handler.command;
|
|
176
|
+
}
|
|
177
|
+
if (manifest.mcpServers && Object.keys(manifest.mcpServers).length > 0) {
|
|
178
|
+
pluginJson.mcpServers = "./mcp.json";
|
|
179
|
+
}
|
|
180
|
+
files.push({
|
|
181
|
+
path: MANIFEST_FILENAME,
|
|
182
|
+
content: JSON.stringify(pluginJson, null, 2)
|
|
183
|
+
});
|
|
184
|
+
const hooksJson = buildHooksJson(manifest.hooks || {});
|
|
185
|
+
if (Object.keys(hooksJson.hooks).length > 0) {
|
|
186
|
+
files.push({
|
|
187
|
+
path: "kimi-hooks.json",
|
|
188
|
+
content: JSON.stringify(hooksJson, null, 2)
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
if (manifest.skills && manifest.skills.length > 0) {
|
|
192
|
+
for (const skill of manifest.skills) {
|
|
193
|
+
const skillFile = compileSkillToMarkdown(skill);
|
|
194
|
+
files.push(skillFile);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
files.push({
|
|
198
|
+
path: "KIMI_INSTALL.md",
|
|
199
|
+
content: generateInstallInstructions(manifest)
|
|
200
|
+
});
|
|
201
|
+
return {
|
|
202
|
+
files,
|
|
203
|
+
manifest: {},
|
|
204
|
+
warnings: [],
|
|
205
|
+
issues: []
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
function buildHooksJson(hooks) {
|
|
209
|
+
const hooksConfig = { hooks: {} };
|
|
210
|
+
for (const [hookName, hook] of Object.entries(hooks)) {
|
|
211
|
+
if (!hook) continue;
|
|
212
|
+
const universalName = hookName;
|
|
213
|
+
if (!SUPPORTED_HOOKS.includes(universalName)) {
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
if (universalName === "sessionStart") {
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
const kimiEventName = HOOK_NAME_MAP[universalName];
|
|
220
|
+
if (!kimiEventName) {
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
const command = extractCommand(hook, universalName);
|
|
224
|
+
if (!command) {
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
const statusMessage = buildStatusMessage(hook, universalName);
|
|
228
|
+
if (!hooksConfig.hooks[kimiEventName]) {
|
|
229
|
+
hooksConfig.hooks[kimiEventName] = [];
|
|
230
|
+
}
|
|
231
|
+
hooksConfig.hooks[kimiEventName].push({
|
|
232
|
+
matcher: hook.matcher || "*",
|
|
233
|
+
hooks: [
|
|
234
|
+
{
|
|
235
|
+
type: "command",
|
|
236
|
+
command,
|
|
237
|
+
statusMessage
|
|
238
|
+
}
|
|
239
|
+
]
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
return hooksConfig;
|
|
243
|
+
}
|
|
244
|
+
function extractCommand(hook, hookName) {
|
|
245
|
+
if (!hook.handler) {
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
if (hook.handler.type === "command") {
|
|
249
|
+
return hook.handler.command || null;
|
|
250
|
+
}
|
|
251
|
+
if (hook.handler.type === "inline") {
|
|
252
|
+
return `node ./hooks/${hookName}.js`;
|
|
253
|
+
}
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
function buildStatusMessage(hook, universalName) {
|
|
257
|
+
if (hook.handler.type === "command" && hook.handler.statusMessage) {
|
|
258
|
+
return hook.handler.statusMessage;
|
|
259
|
+
}
|
|
260
|
+
const defaultMessages = {
|
|
261
|
+
preToolUse: "Running pre-tool check",
|
|
262
|
+
userPromptSubmit: "Processing user prompt",
|
|
263
|
+
sessionStart: "Initializing plugin session",
|
|
264
|
+
notification: "Handling notification",
|
|
265
|
+
permissionRequest: "Requesting permission",
|
|
266
|
+
sessionEnd: "Cleaning up session",
|
|
267
|
+
userPromptExpansion: "Expanding prompt context",
|
|
268
|
+
postToolUse: "Processing tool result",
|
|
269
|
+
postToolUseFailure: "Handling tool failure",
|
|
270
|
+
permissionDenied: "Processing permission denial",
|
|
271
|
+
subagentStart: "Subagent starting",
|
|
272
|
+
subagentStop: "Subagent stopping",
|
|
273
|
+
preCompact: "Pre-compacting session",
|
|
274
|
+
postCompact: "Post-compacting session",
|
|
275
|
+
stop: "Stopping",
|
|
276
|
+
stopFailure: "Handling stop failure",
|
|
277
|
+
fileChanged: "Processing file change",
|
|
278
|
+
cwdChanged: "Processing directory change",
|
|
279
|
+
setup: "Running setup"
|
|
280
|
+
};
|
|
281
|
+
return defaultMessages[universalName];
|
|
282
|
+
}
|
|
283
|
+
function compileSkillToMarkdown(skill) {
|
|
284
|
+
const frontmatter = {
|
|
285
|
+
name: skill.name,
|
|
286
|
+
description: skill.description
|
|
287
|
+
};
|
|
288
|
+
const frontmatterYaml = Object.entries(frontmatter).map(([key, value]) => {
|
|
289
|
+
if (Array.isArray(value)) {
|
|
290
|
+
return `${key}:
|
|
291
|
+
${value.map((v) => ` - ${v}`).join("\n")}`;
|
|
292
|
+
}
|
|
293
|
+
return `${key}: ${value}`;
|
|
294
|
+
}).join("\n");
|
|
295
|
+
const body = skill.content || skill.description || "";
|
|
296
|
+
const content = `---
|
|
297
|
+
${frontmatterYaml}
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
${body}
|
|
301
|
+
`;
|
|
302
|
+
const safeName = skill.name.toLowerCase().replace(/[^a-z0-9]+/g, "-");
|
|
303
|
+
return {
|
|
304
|
+
path: `skills/SKILL__${safeName}.md`,
|
|
305
|
+
content
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
function generateInstallInstructions(manifest) {
|
|
309
|
+
const displayName = manifest.displayName || manifest.name;
|
|
310
|
+
return `# Installing ${displayName} for Kimi
|
|
311
|
+
|
|
312
|
+
> Generated by AgentPlugins \u2014 Kimi Platform Adapter
|
|
313
|
+
|
|
314
|
+
## Quick Install
|
|
315
|
+
|
|
316
|
+
In any Kimi chat, run:
|
|
317
|
+
|
|
318
|
+
\`\`\`
|
|
319
|
+
/plugins install <github-url|local-path>
|
|
320
|
+
\`\`\`
|
|
321
|
+
|
|
322
|
+
Replace \`<github-url|local-path>\` with the repository URL or local directory
|
|
323
|
+
path containing this plugin.
|
|
324
|
+
|
|
325
|
+
## Post-Install
|
|
326
|
+
|
|
327
|
+
Kimi plugins are **user-level** \u2014 they apply to all your sessions, not just the
|
|
328
|
+
current project.
|
|
329
|
+
|
|
330
|
+
After installing (or updating) this plugin, start a **new session** with:
|
|
331
|
+
|
|
332
|
+
\`\`\`
|
|
333
|
+
/new
|
|
334
|
+
\`\`\`
|
|
335
|
+
|
|
336
|
+
Plugin changes do **not** take effect in existing sessions.
|
|
337
|
+
|
|
338
|
+
## Important Notes
|
|
339
|
+
|
|
340
|
+
- **Fail-open hooks**: Kimi hooks are designed to fail open. If a hook process
|
|
341
|
+
crashes or times out, Kimi continues execution. Do not rely on hooks as a
|
|
342
|
+
sole security mechanism.
|
|
343
|
+
|
|
344
|
+
- **Trust badges**: Kimi shows trust indicators for plugins installed from
|
|
345
|
+
GitHub. Install only plugins from sources you trust.
|
|
346
|
+
|
|
347
|
+
- **No project scope**: Kimi does not support project-level plugin scoping.
|
|
348
|
+
All plugins are installed at the user level.
|
|
349
|
+
|
|
350
|
+
## Supported Features
|
|
351
|
+
|
|
352
|
+
This plugin uses the following Kimi features:
|
|
353
|
+
|
|
354
|
+
${manifest.hooks && Object.keys(manifest.hooks).length > 0 ? `**Hooks**: ${Object.entries(manifest.hooks).filter(([name]) => SUPPORTED_HOOKS.includes(name)).map(([name]) => HOOK_NAME_MAP[name] || name).join(", ")}` : "**Hooks**: (none)"}
|
|
355
|
+
|
|
356
|
+
${manifest.skills && manifest.skills.length > 0 ? `**Skills**: ${manifest.skills.map((s) => s.name).join(", ")}` : "**Skills**: (none)"}
|
|
357
|
+
|
|
358
|
+
## Troubleshooting
|
|
359
|
+
|
|
360
|
+
| Problem | Solution |
|
|
361
|
+
|---------|----------|
|
|
362
|
+
| Plugin not active | Run \`/new\` to start a fresh session |
|
|
363
|
+
| Hook not firing | Check that the command in \`kimi-hooks.json\` is executable |
|
|
364
|
+
| Skill not recognized | Verify \`kimi.plugin.json\` has correct \`skills\` path |
|
|
365
|
+
| Changes not applied | Remember: **every change requires \`/new\`** |
|
|
366
|
+
`;
|
|
367
|
+
}
|
|
368
|
+
var KimiAdapter = class {
|
|
369
|
+
constructor() {
|
|
370
|
+
/** @inheritDoc */
|
|
371
|
+
this.name = PLATFORM_NAME;
|
|
372
|
+
/** @inheritDoc */
|
|
373
|
+
this.displayName = PLATFORM_DISPLAY_NAME;
|
|
374
|
+
/** @inheritDoc */
|
|
375
|
+
this.supportedHooks = SUPPORTED_HOOKS;
|
|
376
|
+
/** @inheritDoc */
|
|
377
|
+
this.supportedHandlers = SUPPORTED_HANDLERS;
|
|
378
|
+
/** @inheritDoc */
|
|
379
|
+
this.manifestPath = MANIFEST_FILENAME;
|
|
380
|
+
/** @inheritDoc */
|
|
381
|
+
this.manifestFormat = MANIFEST_FORMAT;
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Validates a universal plugin manifest for Kimi compatibility.
|
|
385
|
+
*
|
|
386
|
+
* @param manifest — The plugin manifest to validate.
|
|
387
|
+
* @returns Array of validation issues (empty if fully valid).
|
|
388
|
+
*/
|
|
389
|
+
validate(manifest) {
|
|
390
|
+
return validateForKimi(manifest);
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Compiles a universal plugin manifest into Kimi-native artifacts.
|
|
394
|
+
*
|
|
395
|
+
* @param manifest — The plugin manifest to compile.
|
|
396
|
+
* @returns AdapterOutput containing all files to write.
|
|
397
|
+
*/
|
|
398
|
+
compile(manifest) {
|
|
399
|
+
return compileForKimi(manifest);
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
var kimiAdapter = new KimiAdapter();
|
|
403
|
+
function createKimiAdapter() {
|
|
404
|
+
return new KimiAdapter();
|
|
405
|
+
}
|
|
406
|
+
var index_default = KimiAdapter;
|
|
407
|
+
export {
|
|
408
|
+
KimiAdapter,
|
|
409
|
+
createKimiAdapter,
|
|
410
|
+
index_default as default,
|
|
411
|
+
kimiAdapter
|
|
412
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@agentplugins/adapter-kimi",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "AgentPlugins platform adapter for Kimi (Moonshot AI)",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./dist/index.mjs",
|
|
10
|
+
"require": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"README.md"
|
|
17
|
+
],
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|
|
23
|
+
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
24
|
+
"lint": "eslint src/**/*.ts",
|
|
25
|
+
"typecheck": "tsc --noEmit"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@agentplugins/core": "workspace:*"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"tsup": "^8.0.0",
|
|
32
|
+
"typescript": "^5.3.0"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"@agentplugins/core": "^0.1.0"
|
|
36
|
+
},
|
|
37
|
+
"keywords": [
|
|
38
|
+
"agentplugins",
|
|
39
|
+
"kimi",
|
|
40
|
+
"moonshot",
|
|
41
|
+
"plugin",
|
|
42
|
+
"adapter"
|
|
43
|
+
],
|
|
44
|
+
"license": "MIT",
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "https://github.com/espetro/agentplugins.git",
|
|
48
|
+
"directory": "packages/adapter-kimi"
|
|
49
|
+
}
|
|
50
|
+
}
|