@kodycodes/cli 0.0.0 → 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kent C. Dodds
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,64 @@
1
1
  # @kodycodes/cli
2
2
 
3
- Under construction
3
+ Turn one-off agent work into something you can rerun: a local MCP client for
4
+ [Kody](https://kody.codes) with login, OS keychain token storage, `search`, and
5
+ `execute`.
6
+
7
+ ```bash
8
+ npx @kodycodes/cli login
9
+ npx @kodycodes/cli search "what can you do"
10
+ npx @kodycodes/cli skill install
11
+ ```
12
+
13
+ Copyright © 2026 [Kent C. Dodds](https://kentcdodds.com). MIT licensed.
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ npm install -g @kodycodes/cli
19
+ kody login
20
+ ```
21
+
22
+ Or run via `npx @kodycodes/cli` without a global install.
23
+
24
+ ## Commands
25
+
26
+ | Command | Purpose |
27
+ | --- | --- |
28
+ | `kody login` | Browser OAuth (DCR + PKCE). Stores access and refresh tokens. |
29
+ | `kody logout` | Deletes stored credentials. |
30
+ | `kody status` | Shows login state without printing secrets. |
31
+ | `kody whoami` | Confirms the MCP connection and lists tools. |
32
+ | `kody search [query]` | Calls Kody `search`. |
33
+ | `kody execute` | Calls Kody `execute` (`--code`, `--file`, or stdin via `--file -`). |
34
+ | `kody skill install` | Copies the getting-started skill into Claude Code / Cursor / Agents. |
35
+
36
+ `--mcp-url` or `KODY_MCP_URL` overrides the default `https://kody.codes/mcp`.
37
+ `--json` prints structured MCP results.
38
+
39
+ ## Token storage
40
+
41
+ Credentials are stored in the OS secret store:
42
+
43
+ - macOS: Keychain
44
+ - Windows: Credential Manager
45
+ - Linux: Secret Service / libsecret
46
+
47
+ If the keychain is unavailable (common on headless Linux), the CLI writes a
48
+ `0600` file under `$XDG_CONFIG_HOME/kody` (or `%APPDATA%\\kody` on Windows,
49
+ `~/Library/Application Support/kody` on macOS). Tokens are never printed.
50
+
51
+ Access tokens refresh automatically on expiry or HTTP 401.
52
+
53
+ ## Releases
54
+
55
+ This repo uses [semantic-release](https://github.com/semantic-release/semantic-release)
56
+ the same way [match-sorter](https://github.com/kentcdodds/match-sorter) does:
57
+ conventional commits on `main` publish `@kodycodes/cli` with npm provenance
58
+ (`id-token: write`). Version in `package.json` stays `0.0.0-semantically-released`.
59
+
60
+ Trusted publishing for `@kodycodes/cli` is attached to this GitHub repository.
61
+
62
+ ## License
63
+
64
+ MIT © Kent C. Dodds
package/dist/auth.d.ts ADDED
@@ -0,0 +1,39 @@
1
+ import type { OAuthClientInformationMixed, OAuthTokens } from '@modelcontextprotocol/sdk/shared/auth.js';
2
+ import { type SecretBackend, type StoredCredentials } from './store.js';
3
+ export type LoginOptions = {
4
+ mcpUrl?: string;
5
+ openBrowser?: boolean;
6
+ backend?: SecretBackend;
7
+ timeoutMs?: number;
8
+ now?: () => number;
9
+ fetchFn?: typeof fetch;
10
+ onAuthorizationUrl?: (url: URL) => void;
11
+ };
12
+ export declare function isAccessTokenExpired(credentials: StoredCredentials, now?: number): boolean;
13
+ export declare function credentialsFromTokens(input: {
14
+ mcpUrl: string;
15
+ resource: string;
16
+ authorizationServerUrl: string;
17
+ client: OAuthClientInformationMixed;
18
+ tokens: OAuthTokens;
19
+ previous?: StoredCredentials;
20
+ now?: number;
21
+ }): StoredCredentials;
22
+ export declare function refreshStoredCredentials(input: {
23
+ credentials: StoredCredentials;
24
+ backend?: SecretBackend;
25
+ now?: number;
26
+ fetchFn?: typeof fetch;
27
+ }): Promise<StoredCredentials>;
28
+ export declare function ensureFreshCredentials(input: {
29
+ mcpUrl?: string;
30
+ backend?: SecretBackend;
31
+ now?: number;
32
+ fetchFn?: typeof fetch;
33
+ }): Promise<StoredCredentials>;
34
+ export declare function login(options?: LoginOptions): Promise<{
35
+ credentials: StoredCredentials;
36
+ authorizationUrl: URL;
37
+ backendKind: 'keyring' | 'file';
38
+ backendPath?: string;
39
+ }>;
package/dist/auth.js ADDED
@@ -0,0 +1,223 @@
1
+ import { createServer, } from 'node:http';
2
+ import { discoverOAuthServerInfo, exchangeAuthorization, refreshAuthorization, registerClient, startAuthorization, } from '@modelcontextprotocol/sdk/client/auth.js';
3
+ import { accessTokenSkewMs, defaultMcpUrl, defaultScopes, loginTimeoutMs, } from './defaults.js';
4
+ import { openUrl } from './open-url.js';
5
+ import { redactError } from './redact.js';
6
+ import { loadCredentials, saveCredentials, } from './store.js';
7
+ function clientInformationFrom(credentials) {
8
+ return {
9
+ client_id: credentials.clientId,
10
+ ...(credentials.clientSecret
11
+ ? { client_secret: credentials.clientSecret }
12
+ : {}),
13
+ };
14
+ }
15
+ export function isAccessTokenExpired(credentials, now = Date.now()) {
16
+ if (!credentials.expiresAt)
17
+ return false;
18
+ return now + accessTokenSkewMs >= credentials.expiresAt;
19
+ }
20
+ export function credentialsFromTokens(input) {
21
+ const now = input.now ?? Date.now();
22
+ const expiresAt = typeof input.tokens.expires_in === 'number'
23
+ ? now + input.tokens.expires_in * 1000
24
+ : input.previous?.expiresAt;
25
+ return {
26
+ version: 1,
27
+ mcpUrl: input.mcpUrl,
28
+ resource: input.resource,
29
+ authorizationServerUrl: input.authorizationServerUrl,
30
+ clientId: input.client.client_id,
31
+ clientSecret: 'client_secret' in input.client
32
+ ? input.client.client_secret
33
+ : input.previous?.clientSecret,
34
+ accessToken: input.tokens.access_token,
35
+ refreshToken: input.tokens.refresh_token ?? input.previous?.refreshToken,
36
+ tokenType: input.tokens.token_type ?? 'bearer',
37
+ expiresAt,
38
+ scope: input.tokens.scope ?? input.previous?.scope,
39
+ };
40
+ }
41
+ export async function refreshStoredCredentials(input) {
42
+ const { credentials } = input;
43
+ if (!credentials.refreshToken) {
44
+ throw new Error('No refresh token is stored. Run `kody login`.');
45
+ }
46
+ const info = await discoverOAuthServerInfo(credentials.mcpUrl, {
47
+ fetchFn: input.fetchFn,
48
+ });
49
+ const tokens = await refreshAuthorization(info.authorizationServerUrl, {
50
+ metadata: info.authorizationServerMetadata,
51
+ clientInformation: clientInformationFrom(credentials),
52
+ refreshToken: credentials.refreshToken,
53
+ resource: new URL(credentials.resource),
54
+ fetchFn: input.fetchFn,
55
+ });
56
+ const next = credentialsFromTokens({
57
+ mcpUrl: credentials.mcpUrl,
58
+ resource: credentials.resource,
59
+ authorizationServerUrl: credentials.authorizationServerUrl,
60
+ client: clientInformationFrom(credentials),
61
+ tokens,
62
+ previous: credentials,
63
+ now: input.now,
64
+ });
65
+ saveCredentials(next, input.backend);
66
+ return next;
67
+ }
68
+ export async function ensureFreshCredentials(input) {
69
+ const mcpUrl = input.mcpUrl ?? defaultMcpUrl;
70
+ const credentials = loadCredentials(mcpUrl, input.backend);
71
+ if (!credentials) {
72
+ throw new Error('Not logged in. Run `kody login`.');
73
+ }
74
+ if (!isAccessTokenExpired(credentials, input.now)) {
75
+ return credentials;
76
+ }
77
+ return await refreshStoredCredentials({
78
+ credentials,
79
+ backend: input.backend,
80
+ now: input.now,
81
+ fetchFn: input.fetchFn,
82
+ });
83
+ }
84
+ function startCallbackServer() {
85
+ return new Promise((resolve, reject) => {
86
+ const server = createServer();
87
+ server.listen(0, '127.0.0.1', () => {
88
+ const address = server.address();
89
+ if (!address || typeof address === 'string') {
90
+ server.close();
91
+ reject(new Error('Could not bind a localhost callback port.'));
92
+ return;
93
+ }
94
+ resolve({
95
+ server,
96
+ redirectUri: new URL(`http://127.0.0.1:${address.port}/callback`),
97
+ });
98
+ });
99
+ server.on('error', reject);
100
+ });
101
+ }
102
+ function waitForCallback(input) {
103
+ return new Promise((resolve, reject) => {
104
+ const finish = (error, code) => {
105
+ clearTimeout(timer);
106
+ input.server.close();
107
+ if (error)
108
+ reject(error);
109
+ else
110
+ resolve(code ?? '');
111
+ };
112
+ const timer = setTimeout(() => {
113
+ finish(new Error('Timed out waiting for the browser login.'));
114
+ }, input.timeoutMs);
115
+ input.server.on('request', (request, response) => {
116
+ try {
117
+ const url = new URL(request.url ?? '/', input.redirectUri);
118
+ if (url.pathname !== input.redirectUri.pathname) {
119
+ response.writeHead(404).end('Not found');
120
+ return;
121
+ }
122
+ const error = url.searchParams.get('error');
123
+ if (error) {
124
+ const description = url.searchParams.get('error_description') ?? '';
125
+ response
126
+ .writeHead(400, { 'Content-Type': 'text/html' })
127
+ .end('<p>Authorization failed. You can close this window.</p>');
128
+ finish(new Error(description || error));
129
+ return;
130
+ }
131
+ const state = url.searchParams.get('state');
132
+ const code = url.searchParams.get('code');
133
+ if (state !== input.expectedState || !code) {
134
+ response
135
+ .writeHead(400, { 'Content-Type': 'text/html' })
136
+ .end('<p>Invalid OAuth callback. You can close this window.</p>');
137
+ finish(new Error('OAuth callback missing code or state.'));
138
+ return;
139
+ }
140
+ response
141
+ .writeHead(200, { 'Content-Type': 'text/html' })
142
+ .end('<p>Kody CLI is signed in. You can close this window.</p>');
143
+ finish(null, code);
144
+ }
145
+ catch (error) {
146
+ finish(redactError(error));
147
+ }
148
+ });
149
+ });
150
+ }
151
+ export async function login(options = {}) {
152
+ const mcpUrl = options.mcpUrl ?? defaultMcpUrl;
153
+ const fetchFn = options.fetchFn;
154
+ const info = await discoverOAuthServerInfo(mcpUrl, { fetchFn });
155
+ const resource = info.resourceMetadata?.resource ??
156
+ `${new URL(mcpUrl).origin}${new URL(mcpUrl).pathname}`;
157
+ const { server, redirectUri } = await startCallbackServer();
158
+ try {
159
+ const metadata = info.authorizationServerMetadata;
160
+ const client = await registerClient(info.authorizationServerUrl, {
161
+ metadata,
162
+ clientMetadata: {
163
+ redirect_uris: [redirectUri.href],
164
+ client_name: 'Kody CLI',
165
+ client_uri: 'https://github.com/kody-bot/cli',
166
+ grant_types: ['authorization_code', 'refresh_token'],
167
+ response_types: ['code'],
168
+ token_endpoint_auth_method: 'none',
169
+ scope: defaultScopes.join(' '),
170
+ },
171
+ scope: defaultScopes.join(' '),
172
+ fetchFn,
173
+ });
174
+ const state = crypto.randomUUID();
175
+ const { authorizationUrl, codeVerifier } = await startAuthorization(info.authorizationServerUrl, {
176
+ metadata,
177
+ clientInformation: client,
178
+ redirectUrl: redirectUri,
179
+ scope: defaultScopes.join(' '),
180
+ state,
181
+ resource: new URL(resource),
182
+ });
183
+ options.onAuthorizationUrl?.(authorizationUrl);
184
+ if (options.openBrowser !== false) {
185
+ await openUrl(authorizationUrl.href);
186
+ }
187
+ const code = await waitForCallback({
188
+ server,
189
+ redirectUri,
190
+ expectedState: state,
191
+ timeoutMs: options.timeoutMs ?? loginTimeoutMs,
192
+ });
193
+ const tokens = await exchangeAuthorization(info.authorizationServerUrl, {
194
+ metadata,
195
+ clientInformation: client,
196
+ authorizationCode: code,
197
+ codeVerifier,
198
+ redirectUri,
199
+ resource: new URL(resource),
200
+ fetchFn,
201
+ });
202
+ const credentials = credentialsFromTokens({
203
+ mcpUrl,
204
+ resource,
205
+ authorizationServerUrl: info.authorizationServerUrl,
206
+ client,
207
+ tokens,
208
+ now: options.now?.(),
209
+ });
210
+ const saved = saveCredentials(credentials, options.backend);
211
+ return {
212
+ credentials,
213
+ authorizationUrl,
214
+ backendKind: saved.backend.kind,
215
+ backendPath: saved.backend.path,
216
+ };
217
+ }
218
+ catch (error) {
219
+ server.close();
220
+ throw redactError(error);
221
+ }
222
+ }
223
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
package/dist/bin.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/bin.js ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import { runCli } from './cli.js';
3
+ const code = await runCli();
4
+ process.exitCode = code;
5
+ //# sourceMappingURL=bin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAEjC,MAAM,IAAI,GAAG,MAAM,MAAM,EAAE,CAAA;AAC3B,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAA"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,31 @@
1
+ export type CommandName = 'login' | 'logout' | 'status' | 'whoami' | 'search' | 'execute' | 'skill' | 'help' | 'version';
2
+ declare function parseKnown(args: Array<string>): {
3
+ values: {
4
+ [longOption: string]: string | boolean | undefined;
5
+ } & {
6
+ help?: string | boolean | undefined;
7
+ version?: string | boolean | undefined;
8
+ json?: string | boolean | undefined;
9
+ 'mcp-url'?: string | boolean | undefined;
10
+ entity?: string | boolean | undefined;
11
+ domain?: string | boolean | undefined;
12
+ limit?: string | boolean | undefined;
13
+ code?: string | boolean | undefined;
14
+ file?: string | boolean | undefined;
15
+ params?: string | boolean | undefined;
16
+ 'conversation-id'?: string | boolean | undefined;
17
+ project?: string | boolean | undefined;
18
+ 'no-browser'?: string | boolean | undefined;
19
+ };
20
+ positionals: string[];
21
+ };
22
+ export declare function resolveCommand(argv: Array<string>): {
23
+ command: CommandName;
24
+ positionals: Array<string>;
25
+ values: ReturnType<typeof parseKnown>['values'];
26
+ };
27
+ export declare function runCli(argv?: Array<string>, io?: {
28
+ stdout?: (text: string) => void;
29
+ stderr?: (text: string) => void;
30
+ }): Promise<number>;
31
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,205 @@
1
+ import { parseArgs } from 'node:util';
2
+ import { readFile } from 'node:fs/promises';
3
+ import { defaultMcpUrl } from './defaults.js';
4
+ import { usage } from './help.js';
5
+ import { ensureFreshCredentials, login } from './auth.js';
6
+ import { deleteCredentials, loadCredentials } from './store.js';
7
+ import { callKodyTool, formatToolResult, listKodyTools } from './mcp.js';
8
+ import { installSkill } from './skill.js';
9
+ import { readPackageVersion } from './package-info.js';
10
+ import { redactError } from './redact.js';
11
+ function mcpUrlFrom(values) {
12
+ return values.mcpUrl || process.env.KODY_MCP_URL || defaultMcpUrl;
13
+ }
14
+ function parseKnown(args) {
15
+ return parseArgs({
16
+ args,
17
+ allowPositionals: true,
18
+ strict: false,
19
+ options: {
20
+ help: { type: 'boolean', short: 'h' },
21
+ version: { type: 'boolean', short: 'v' },
22
+ json: { type: 'boolean' },
23
+ 'mcp-url': { type: 'string' },
24
+ entity: { type: 'string' },
25
+ domain: { type: 'string' },
26
+ limit: { type: 'string' },
27
+ code: { type: 'string' },
28
+ file: { type: 'string' },
29
+ params: { type: 'string' },
30
+ 'conversation-id': { type: 'string' },
31
+ project: { type: 'boolean' },
32
+ 'no-browser': { type: 'boolean' },
33
+ },
34
+ });
35
+ }
36
+ export function resolveCommand(argv) {
37
+ const { positionals, values } = parseKnown(argv);
38
+ if (values.help && positionals.length === 0) {
39
+ return { command: 'help', positionals, values };
40
+ }
41
+ if (values.version && positionals.length === 0) {
42
+ return { command: 'version', positionals, values };
43
+ }
44
+ const raw = positionals[0];
45
+ if (!raw)
46
+ return { command: 'help', positionals, values };
47
+ switch (raw) {
48
+ case 'login':
49
+ case 'logout':
50
+ case 'status':
51
+ case 'whoami':
52
+ case 'search':
53
+ case 'execute':
54
+ case 'skill':
55
+ case 'help':
56
+ case 'version':
57
+ return { command: raw, positionals: positionals.slice(1), values };
58
+ default:
59
+ throw new Error(`Unknown command "${raw}".\n\n${usage}`);
60
+ }
61
+ }
62
+ export async function runCli(argv = process.argv.slice(2), io = {}) {
63
+ const write = io.stdout ?? ((text) => process.stdout.write(text));
64
+ const writeErr = io.stderr ?? ((text) => process.stderr.write(text));
65
+ try {
66
+ const parsed = resolveCommand(argv);
67
+ if (parsed.values.help && parsed.command !== 'help') {
68
+ write(usage);
69
+ return 0;
70
+ }
71
+ return await dispatch(parsed, write);
72
+ }
73
+ catch (error) {
74
+ writeErr(`${redactError(error).message}\n`);
75
+ return 1;
76
+ }
77
+ }
78
+ async function dispatch(parsed, write) {
79
+ const mcpUrl = mcpUrlFrom({
80
+ mcpUrl: typeof parsed.values['mcp-url'] === 'string' ? parsed.values['mcp-url'] : undefined,
81
+ });
82
+ const json = parsed.values.json === true;
83
+ switch (parsed.command) {
84
+ case 'help':
85
+ write(usage);
86
+ return 0;
87
+ case 'version':
88
+ write(`${readPackageVersion()}\n`);
89
+ return 0;
90
+ case 'login': {
91
+ write('Opening the Kody login page in your browser…\n');
92
+ const result = await login({
93
+ mcpUrl,
94
+ openBrowser: parsed.values['no-browser'] !== true,
95
+ onAuthorizationUrl: (url) => {
96
+ write(`If the browser does not open, visit:\n${url.href}\n`);
97
+ },
98
+ });
99
+ write(`Logged in to ${result.credentials.mcpUrl}.\n`);
100
+ if (result.backendKind === 'file' && result.backendPath) {
101
+ write(`OS keychain was unavailable; credentials saved at ${result.backendPath} (mode 0600).\n`);
102
+ }
103
+ else {
104
+ write('Credentials stored in the OS keychain.\n');
105
+ }
106
+ return 0;
107
+ }
108
+ case 'logout': {
109
+ const result = deleteCredentials(mcpUrl);
110
+ write(result.deleted ? 'Logged out.\n' : 'No stored credentials.\n');
111
+ return 0;
112
+ }
113
+ case 'status': {
114
+ const credentials = loadCredentials(mcpUrl);
115
+ if (!credentials) {
116
+ write('Not logged in.\n');
117
+ return 1;
118
+ }
119
+ const expires = credentials.expiresAt
120
+ ? new Date(credentials.expiresAt).toISOString()
121
+ : 'unknown';
122
+ write([
123
+ `mcp: ${credentials.mcpUrl}`,
124
+ `logged in: yes`,
125
+ `access token expires: ${expires}`,
126
+ `refresh token: ${credentials.refreshToken ? 'yes' : 'no'}`,
127
+ `scope: ${credentials.scope ?? 'unknown'}`,
128
+ '',
129
+ ].join('\n'));
130
+ return 0;
131
+ }
132
+ case 'whoami': {
133
+ const credentials = await ensureFreshCredentials({ mcpUrl });
134
+ const tools = await listKodyTools({ mcpUrl });
135
+ if (json) {
136
+ write(`${JSON.stringify({
137
+ mcpUrl: credentials.mcpUrl,
138
+ scope: credentials.scope ?? null,
139
+ tools: tools.map((tool) => tool.name),
140
+ }, null, 2)}\n`);
141
+ return 0;
142
+ }
143
+ write(`Connected to ${credentials.mcpUrl}\nTools: ${tools.map((tool) => tool.name).join(', ') || '(none)'}\n`);
144
+ return 0;
145
+ }
146
+ case 'search': {
147
+ const query = parsed.positionals.join(' ').trim();
148
+ const args = {};
149
+ if (query)
150
+ args.query = query;
151
+ if (typeof parsed.values.entity === 'string')
152
+ args.entity = parsed.values.entity;
153
+ if (typeof parsed.values.domain === 'string')
154
+ args.domain = parsed.values.domain;
155
+ if (typeof parsed.values.limit === 'string')
156
+ args.limit = Number(parsed.values.limit);
157
+ const result = await callKodyTool({ name: 'search', args, mcpUrl });
158
+ write(formatToolResult(result, json));
159
+ return result.isError ? 1 : 0;
160
+ }
161
+ case 'execute': {
162
+ const code = typeof parsed.values.code === 'string'
163
+ ? parsed.values.code
164
+ : typeof parsed.values.file === 'string'
165
+ ? parsed.values.file === '-'
166
+ ? await readStdin()
167
+ : await readFile(parsed.values.file, 'utf8')
168
+ : parsed.positionals.join('\n').trim();
169
+ if (!code) {
170
+ throw new Error('Provide --code, --file, or a module string.');
171
+ }
172
+ const args = { code };
173
+ if (typeof parsed.values.params === 'string') {
174
+ args.params = JSON.parse(parsed.values.params);
175
+ }
176
+ if (typeof parsed.values['conversation-id'] === 'string') {
177
+ args.conversationId = parsed.values['conversation-id'];
178
+ }
179
+ const result = await callKodyTool({ name: 'execute', args, mcpUrl });
180
+ write(formatToolResult(result, json));
181
+ return result.isError ? 1 : 0;
182
+ }
183
+ case 'skill': {
184
+ const action = parsed.positionals[0] ?? 'install';
185
+ if (action !== 'install') {
186
+ throw new Error('Usage: kody skill install [--project]');
187
+ }
188
+ const targets = await installSkill({ project: parsed.values.project === true });
189
+ write(`Installed the Kody skill to:\n${targets.map((target) => `- ${target.host}: ${target.path}`).join('\n')}\n`);
190
+ return 0;
191
+ }
192
+ default: {
193
+ const exhaustive = parsed.command;
194
+ throw new Error(`Unhandled command: ${String(exhaustive)}`);
195
+ }
196
+ }
197
+ }
198
+ async function readStdin() {
199
+ const chunks = [];
200
+ for await (const chunk of process.stdin) {
201
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
202
+ }
203
+ return Buffer.concat(chunks).toString('utf8');
204
+ }
205
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export declare const defaultMcpUrl = "https://kody.codes/mcp";
2
+ export declare const defaultScopes: readonly ["profile", "email"];
3
+ export declare const keyringService = "kody.codes";
4
+ export declare const loginTimeoutMs: number;
5
+ export declare const accessTokenSkewMs = 60000;
6
+ export declare const cliName = "@kodycodes/cli";
@@ -0,0 +1,7 @@
1
+ export const defaultMcpUrl = 'https://kody.codes/mcp';
2
+ export const defaultScopes = ['profile', 'email'];
3
+ export const keyringService = 'kody.codes';
4
+ export const loginTimeoutMs = 5 * 60 * 1000;
5
+ export const accessTokenSkewMs = 60_000;
6
+ export const cliName = '@kodycodes/cli';
7
+ //# sourceMappingURL=defaults.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaults.js","sourceRoot":"","sources":["../src/defaults.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,aAAa,GAAG,wBAAwB,CAAA;AACrD,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,SAAS,EAAE,OAAO,CAAU,CAAA;AAC1D,MAAM,CAAC,MAAM,cAAc,GAAG,YAAY,CAAA;AAC1C,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAA;AAC3C,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAA;AACvC,MAAM,CAAC,MAAM,OAAO,GAAG,gBAAgB,CAAA"}
package/dist/help.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare const usage: string;
package/dist/help.js ADDED
@@ -0,0 +1,19 @@
1
+ import { defaultMcpUrl } from './defaults.js';
2
+ import { readPackageVersion } from './package-info.js';
3
+ export const usage = `Kody CLI ${readPackageVersion()}
4
+
5
+ A local MCP client for https://kody.codes — login once, then search and execute.
6
+
7
+ Usage:
8
+ kody login [--mcp-url <url>] [--no-browser]
9
+ kody logout [--mcp-url <url>]
10
+ kody status [--mcp-url <url>]
11
+ kody whoami [--mcp-url <url>] [--json]
12
+ kody search [query] [--entity <ref>] [--domain <id>] [--limit <n>] [--json]
13
+ kody execute [--code <esm>] [--file <path>] [--params <json>] [--conversation-id <id>] [--json]
14
+ kody skill install [--project]
15
+
16
+ Environment:
17
+ KODY_MCP_URL Override the default MCP URL (${defaultMcpUrl})
18
+ `;
19
+ //# sourceMappingURL=help.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"help.js","sourceRoot":"","sources":["../src/help.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAEtD,MAAM,CAAC,MAAM,KAAK,GAAG,YAAY,kBAAkB,EAAE;;;;;;;;;;;;;;iDAcJ,aAAa;CAC7D,CAAA"}
package/dist/mcp.d.ts ADDED
@@ -0,0 +1,27 @@
1
+ import type { SecretBackend } from './store.js';
2
+ export type CallToolInput = {
3
+ name: 'search' | 'execute';
4
+ args: Record<string, unknown>;
5
+ mcpUrl?: string;
6
+ backend?: SecretBackend;
7
+ fetchFn?: typeof fetch;
8
+ };
9
+ export type ToolCallResult = {
10
+ content: Array<{
11
+ type: string;
12
+ text?: string;
13
+ [key: string]: unknown;
14
+ }>;
15
+ structuredContent?: unknown;
16
+ isError?: boolean;
17
+ };
18
+ export declare function callKodyTool(input: CallToolInput): Promise<ToolCallResult>;
19
+ export declare function listKodyTools(input: {
20
+ mcpUrl?: string;
21
+ backend?: SecretBackend;
22
+ fetchFn?: typeof fetch;
23
+ }): Promise<Array<{
24
+ name: string;
25
+ description?: string;
26
+ }>>;
27
+ export declare function formatToolResult(result: ToolCallResult, json: boolean): string;
package/dist/mcp.js ADDED
@@ -0,0 +1,105 @@
1
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
2
+ import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
3
+ import { UnauthorizedError } from '@modelcontextprotocol/sdk/client/auth.js';
4
+ import { cliName } from './defaults.js';
5
+ import { ensureFreshCredentials, refreshStoredCredentials } from './auth.js';
6
+ import { readPackageVersion } from './package-info.js';
7
+ import { redactError } from './redact.js';
8
+ async function connect(mcpUrl, accessToken, fetchFn) {
9
+ const transport = new StreamableHTTPClientTransport(new URL(mcpUrl), {
10
+ requestInit: {
11
+ headers: {
12
+ Authorization: `Bearer ${accessToken}`,
13
+ },
14
+ },
15
+ fetch: fetchFn,
16
+ });
17
+ const client = new Client({
18
+ name: cliName,
19
+ version: readPackageVersion(),
20
+ });
21
+ await client.connect(transport);
22
+ return { client, transport };
23
+ }
24
+ export async function callKodyTool(input) {
25
+ const credentials = await ensureFreshCredentials({
26
+ mcpUrl: input.mcpUrl,
27
+ backend: input.backend,
28
+ fetchFn: input.fetchFn,
29
+ });
30
+ try {
31
+ return await invoke(credentials.mcpUrl, credentials.accessToken, input);
32
+ }
33
+ catch (error) {
34
+ if (!(error instanceof UnauthorizedError) && !isUnauthorized(error)) {
35
+ throw redactError(error);
36
+ }
37
+ const refreshed = await refreshStoredCredentials({
38
+ credentials,
39
+ backend: input.backend,
40
+ fetchFn: input.fetchFn,
41
+ });
42
+ try {
43
+ return await invoke(refreshed.mcpUrl, refreshed.accessToken, input);
44
+ }
45
+ catch (retryError) {
46
+ throw redactError(retryError);
47
+ }
48
+ }
49
+ }
50
+ async function invoke(mcpUrl, accessToken, input) {
51
+ const { client, transport } = await connect(mcpUrl, accessToken, input.fetchFn);
52
+ try {
53
+ const result = await client.callTool({
54
+ name: input.name,
55
+ arguments: input.args,
56
+ });
57
+ return result;
58
+ }
59
+ finally {
60
+ await transport.close().catch(() => undefined);
61
+ }
62
+ }
63
+ export async function listKodyTools(input) {
64
+ const credentials = await ensureFreshCredentials({
65
+ mcpUrl: input.mcpUrl,
66
+ backend: input.backend,
67
+ fetchFn: input.fetchFn,
68
+ });
69
+ const { client, transport } = await connect(credentials.mcpUrl, credentials.accessToken, input.fetchFn);
70
+ try {
71
+ const listed = await client.listTools();
72
+ return listed.tools.map((tool) => ({
73
+ name: tool.name,
74
+ description: tool.description,
75
+ }));
76
+ }
77
+ finally {
78
+ await transport.close().catch(() => undefined);
79
+ }
80
+ }
81
+ export function formatToolResult(result, json) {
82
+ if (json) {
83
+ return `${JSON.stringify({
84
+ isError: result.isError ?? false,
85
+ structuredContent: result.structuredContent ?? null,
86
+ content: result.content,
87
+ }, null, 2)}\n`;
88
+ }
89
+ const texts = result.content
90
+ .filter((part) => part.type === 'text' && typeof part.text === 'string')
91
+ .map((part) => part.text ?? '');
92
+ if (texts.length > 0)
93
+ return texts.join('\n\n').replace(/\n*$/, '\n');
94
+ if (result.structuredContent != null) {
95
+ return `${JSON.stringify(result.structuredContent, null, 2)}\n`;
96
+ }
97
+ return `${JSON.stringify(result.content, null, 2)}\n`;
98
+ }
99
+ function isUnauthorized(error) {
100
+ if (!error || typeof error !== 'object')
101
+ return false;
102
+ const status = 'code' in error ? error.code : undefined;
103
+ return status === 401 || (error instanceof Error && /401|unauthorized/i.test(error.message));
104
+ }
105
+ //# sourceMappingURL=mcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function openUrl(url: string): Promise<boolean>;
@@ -0,0 +1,21 @@
1
+ import { spawn } from 'node:child_process';
2
+ export async function openUrl(url) {
3
+ const platform = process.platform;
4
+ const command = platform === 'darwin'
5
+ ? 'open'
6
+ : platform === 'win32'
7
+ ? 'cmd'
8
+ : 'xdg-open';
9
+ const args = platform === 'win32' ? ['/c', 'start', '', url] : [url];
10
+ return await new Promise((resolve) => {
11
+ const child = spawn(command, args, {
12
+ detached: true,
13
+ stdio: 'ignore',
14
+ windowsHide: true,
15
+ });
16
+ child.on('error', () => resolve(false));
17
+ child.unref();
18
+ resolve(true);
19
+ });
20
+ }
21
+ //# sourceMappingURL=open-url.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"open-url.js","sourceRoot":"","sources":["../src/open-url.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAE1C,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,GAAW;IACxC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAA;IACjC,MAAM,OAAO,GACZ,QAAQ,KAAK,QAAQ;QACpB,CAAC,CAAC,MAAM;QACR,CAAC,CAAC,QAAQ,KAAK,OAAO;YACrB,CAAC,CAAC,KAAK;YACP,CAAC,CAAC,UAAU,CAAA;IACf,MAAM,IAAI,GAAG,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;IACpE,OAAO,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QACpC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE;YAClC,QAAQ,EAAE,IAAI;YACd,KAAK,EAAE,QAAQ;YACf,WAAW,EAAE,IAAI;SACjB,CAAC,CAAA;QACF,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAA;QACvC,KAAK,CAAC,KAAK,EAAE,CAAA;QACb,OAAO,CAAC,IAAI,CAAC,CAAA;IACd,CAAC,CAAC,CAAA;AACH,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function readPackageVersion(): string;
@@ -0,0 +1,7 @@
1
+ import { createRequire } from 'node:module';
2
+ const require = createRequire(import.meta.url);
3
+ export function readPackageVersion() {
4
+ const pkg = require('../package.json');
5
+ return pkg.version ?? '0.0.0';
6
+ }
7
+ //# sourceMappingURL=package-info.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package-info.js","sourceRoot":"","sources":["../src/package-info.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAE3C,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAE9C,MAAM,UAAU,kBAAkB;IACjC,MAAM,GAAG,GAAG,OAAO,CAAC,iBAAiB,CAAyB,CAAA;IAC9D,OAAO,GAAG,CAAC,OAAO,IAAI,OAAO,CAAA;AAC9B,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function redact(value: string): string;
2
+ export declare function redactError(error: unknown): Error;
package/dist/redact.js ADDED
@@ -0,0 +1,12 @@
1
+ const secretPattern = /(access_token|refresh_token|client_secret|authorization)["']?\s*[:=]\s*["']?[^\s"',}]+/gi;
2
+ export function redact(value) {
3
+ return value.replace(secretPattern, '$1=[redacted]');
4
+ }
5
+ export function redactError(error) {
6
+ if (error instanceof Error) {
7
+ error.message = redact(error.message);
8
+ return error;
9
+ }
10
+ return new Error(redact(String(error)));
11
+ }
12
+ //# sourceMappingURL=redact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redact.js","sourceRoot":"","sources":["../src/redact.ts"],"names":[],"mappings":"AAAA,MAAM,aAAa,GAClB,0FAA0F,CAAA;AAE3F,MAAM,UAAU,MAAM,CAAC,KAAa;IACnC,OAAO,KAAK,CAAC,OAAO,CAAC,aAAa,EAAE,eAAe,CAAC,CAAA;AACrD,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAc;IACzC,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC5B,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;QACrC,OAAO,KAAK,CAAA;IACb,CAAC;IACD,OAAO,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;AACxC,CAAC"}
@@ -0,0 +1,18 @@
1
+ export declare function bundledSkillPath(): string;
2
+ export declare function userSkillTargets(home?: string): Array<{
3
+ host: string;
4
+ path: string;
5
+ }>;
6
+ export declare function projectSkillTargets(cwd?: string): Array<{
7
+ host: string;
8
+ path: string;
9
+ }>;
10
+ export declare function installSkill(input: {
11
+ project?: boolean;
12
+ cwd?: string;
13
+ home?: string;
14
+ sourcePath?: string;
15
+ }): Promise<Array<{
16
+ host: string;
17
+ path: string;
18
+ }>>;
package/dist/skill.js ADDED
@@ -0,0 +1,35 @@
1
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
2
+ import { dirname, join } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { homedir } from 'node:os';
5
+ const skillRelativePath = 'skills/kody/SKILL.md';
6
+ export function bundledSkillPath() {
7
+ return join(fileURLToPath(new URL('../', import.meta.url)), skillRelativePath);
8
+ }
9
+ export function userSkillTargets(home = homedir()) {
10
+ return [
11
+ { host: 'Claude Code', path: join(home, '.claude', 'skills', 'kody', 'SKILL.md') },
12
+ { host: 'Cursor', path: join(home, '.cursor', 'skills', 'kody', 'SKILL.md') },
13
+ { host: 'Agents', path: join(home, '.agents', 'skills', 'kody', 'SKILL.md') },
14
+ ];
15
+ }
16
+ export function projectSkillTargets(cwd = process.cwd()) {
17
+ return [
18
+ { host: 'Agents', path: join(cwd, '.agents', 'skills', 'kody', 'SKILL.md') },
19
+ { host: 'Cursor', path: join(cwd, '.cursor', 'skills', 'kody', 'SKILL.md') },
20
+ { host: 'Claude Code', path: join(cwd, '.claude', 'skills', 'kody', 'SKILL.md') },
21
+ ];
22
+ }
23
+ export async function installSkill(input) {
24
+ const source = input.sourcePath ?? bundledSkillPath();
25
+ const body = await readFile(source, 'utf8');
26
+ const targets = input.project
27
+ ? projectSkillTargets(input.cwd)
28
+ : userSkillTargets(input.home);
29
+ for (const target of targets) {
30
+ await mkdir(dirname(target.path), { recursive: true });
31
+ await writeFile(target.path, body, 'utf8');
32
+ }
33
+ return targets;
34
+ }
35
+ //# sourceMappingURL=skill.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill.js","sourceRoot":"","sources":["../src/skill.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAC7D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAEjC,MAAM,iBAAiB,GAAG,sBAAsB,CAAA;AAEhD,MAAM,UAAU,gBAAgB;IAC/B,OAAO,IAAI,CAAC,aAAa,CAAC,IAAI,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,iBAAiB,CAAC,CAAA;AAC/E,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,OAAe,OAAO,EAAE;IAIxD,OAAO;QACN,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE;QAClF,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE;QAC7E,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE;KAC7E,CAAA;AACF,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,MAAc,OAAO,CAAC,GAAG,EAAE;IAI9D,OAAO;QACN,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE;QAC5E,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE;QAC5E,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE;KACjF,CAAA;AACF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,KAKlC;IACA,MAAM,MAAM,GAAG,KAAK,CAAC,UAAU,IAAI,gBAAgB,EAAE,CAAA;IACrD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC3C,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO;QAC5B,CAAC,CAAC,mBAAmB,CAAC,KAAK,CAAC,GAAG,CAAC;QAChC,CAAC,CAAC,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IAC/B,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC9B,MAAM,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QACtD,MAAM,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAA;IAC3C,CAAC;IACD,OAAO,OAAO,CAAA;AACf,CAAC"}
@@ -0,0 +1,34 @@
1
+ export type StoredCredentials = {
2
+ version: 1;
3
+ mcpUrl: string;
4
+ resource: string;
5
+ authorizationServerUrl: string;
6
+ clientId: string;
7
+ clientSecret?: string;
8
+ accessToken: string;
9
+ refreshToken?: string;
10
+ tokenType: string;
11
+ expiresAt?: number;
12
+ scope?: string;
13
+ };
14
+ export type SecretBackend = {
15
+ kind: 'keyring' | 'file';
16
+ path?: string;
17
+ get(): string | null;
18
+ set(value: string): void;
19
+ delete(): boolean;
20
+ };
21
+ export declare function accountForMcpUrl(mcpUrl: string): string;
22
+ export declare function fileStorePath(mcpUrl: string, home?: string): string;
23
+ export declare function createFileBackend(path: string): SecretBackend;
24
+ export declare function createKeyringBackend(mcpUrl: string): SecretBackend;
25
+ export declare function resolveBackend(mcpUrl: string, preferred?: SecretBackend): SecretBackend;
26
+ export declare function parseCredentials(raw: string): StoredCredentials;
27
+ export declare function loadCredentials(mcpUrl?: string, backend?: SecretBackend): StoredCredentials | null;
28
+ export declare function saveCredentials(credentials: StoredCredentials, backend?: SecretBackend): {
29
+ backend: SecretBackend;
30
+ };
31
+ export declare function deleteCredentials(mcpUrl?: string, backend?: SecretBackend): {
32
+ deleted: boolean;
33
+ backend: SecretBackend;
34
+ };
package/dist/store.js ADDED
@@ -0,0 +1,140 @@
1
+ import { chmodSync, mkdirSync, readFileSync, unlinkSync, writeFileSync, } from 'node:fs';
2
+ import { dirname, join } from 'node:path';
3
+ import { homedir } from 'node:os';
4
+ import { Entry } from '@napi-rs/keyring';
5
+ import { defaultMcpUrl, keyringService } from './defaults.js';
6
+ export function accountForMcpUrl(mcpUrl) {
7
+ return `cli:${new URL(mcpUrl).origin}`;
8
+ }
9
+ export function fileStorePath(mcpUrl, home = homedir()) {
10
+ const origin = new URL(mcpUrl).host.replace(/[^a-zA-Z0-9.-]/g, '_');
11
+ const base = process.platform === 'win32'
12
+ ? join(process.env.APPDATA || join(home, 'AppData', 'Roaming'), 'kody')
13
+ : process.platform === 'darwin'
14
+ ? join(home, 'Library', 'Application Support', 'kody')
15
+ : join(process.env.XDG_CONFIG_HOME || join(home, '.config'), 'kody');
16
+ return join(base, `credentials-${origin}.json`);
17
+ }
18
+ export function createFileBackend(path) {
19
+ return {
20
+ kind: 'file',
21
+ path,
22
+ get() {
23
+ try {
24
+ return readFileSync(path, 'utf8');
25
+ }
26
+ catch {
27
+ return null;
28
+ }
29
+ },
30
+ set(value) {
31
+ mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
32
+ writeFileSync(path, value, { mode: 0o600 });
33
+ try {
34
+ chmodSync(path, 0o600);
35
+ }
36
+ catch {
37
+ // Windows cannot apply POSIX modes.
38
+ }
39
+ },
40
+ delete() {
41
+ try {
42
+ unlinkSync(path);
43
+ return true;
44
+ }
45
+ catch {
46
+ return false;
47
+ }
48
+ },
49
+ };
50
+ }
51
+ export function createKeyringBackend(mcpUrl) {
52
+ const entry = new Entry(keyringService, accountForMcpUrl(mcpUrl));
53
+ return {
54
+ kind: 'keyring',
55
+ get() {
56
+ try {
57
+ return entry.getPassword();
58
+ }
59
+ catch {
60
+ return null;
61
+ }
62
+ },
63
+ set(value) {
64
+ entry.setPassword(value);
65
+ },
66
+ delete() {
67
+ try {
68
+ return entry.deleteCredential();
69
+ }
70
+ catch {
71
+ return false;
72
+ }
73
+ },
74
+ };
75
+ }
76
+ export function resolveBackend(mcpUrl, preferred) {
77
+ if (preferred)
78
+ return preferred;
79
+ try {
80
+ return createKeyringBackend(mcpUrl);
81
+ }
82
+ catch {
83
+ return createFileBackend(fileStorePath(mcpUrl));
84
+ }
85
+ }
86
+ export function parseCredentials(raw) {
87
+ const parsed = JSON.parse(raw);
88
+ if (parsed.version !== 1 || !parsed.accessToken || !parsed.clientId) {
89
+ throw new Error('Stored Kody credentials are invalid. Run `kody login`.');
90
+ }
91
+ return parsed;
92
+ }
93
+ export function loadCredentials(mcpUrl = defaultMcpUrl, backend) {
94
+ const store = resolveBackend(mcpUrl, backend);
95
+ try {
96
+ const raw = store.get();
97
+ if (!raw)
98
+ return null;
99
+ return parseCredentials(raw);
100
+ }
101
+ catch (error) {
102
+ if (store.kind === 'keyring' && !backend) {
103
+ const fallback = createFileBackend(fileStorePath(mcpUrl));
104
+ const raw = fallback.get();
105
+ return raw ? parseCredentials(raw) : null;
106
+ }
107
+ throw error;
108
+ }
109
+ }
110
+ export function saveCredentials(credentials, backend) {
111
+ const store = resolveBackend(credentials.mcpUrl, backend);
112
+ try {
113
+ store.set(JSON.stringify(credentials));
114
+ return { backend: store };
115
+ }
116
+ catch (error) {
117
+ if (store.kind === 'keyring' && !backend) {
118
+ const fallback = createFileBackend(fileStorePath(credentials.mcpUrl));
119
+ fallback.set(JSON.stringify(credentials));
120
+ return { backend: fallback };
121
+ }
122
+ throw error;
123
+ }
124
+ }
125
+ export function deleteCredentials(mcpUrl = defaultMcpUrl, backend) {
126
+ const store = resolveBackend(mcpUrl, backend);
127
+ let deleted = false;
128
+ try {
129
+ deleted = store.delete();
130
+ }
131
+ catch {
132
+ deleted = false;
133
+ }
134
+ if (store.kind === 'keyring' && !backend) {
135
+ const file = createFileBackend(fileStorePath(mcpUrl));
136
+ deleted = file.delete() || deleted;
137
+ }
138
+ return { deleted, backend: store };
139
+ }
140
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,9 +1,58 @@
1
1
  {
2
- "name": "@kodycodes/cli",
3
- "version": "0.0.0",
4
- "description": "Under construction",
5
- "license": "MIT",
6
- "publishConfig": {
7
- "access": "public"
8
- }
2
+ "name": "@kodycodes/cli",
3
+ "version": "1.0.0",
4
+ "description": "Official Kody MCP client CLI: login, secure token storage, search, and execute.",
5
+ "type": "module",
6
+ "bin": {
7
+ "kody": "dist/bin.js"
8
+ },
9
+ "main": "./dist/cli.js",
10
+ "types": "./dist/cli.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/cli.d.ts",
14
+ "default": "./dist/cli.js"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "skills"
20
+ ],
21
+ "scripts": {
22
+ "build": "tsc -p tsconfig.build.json",
23
+ "typecheck": "tsc --noEmit",
24
+ "test": "tsx --test test/*.test.ts",
25
+ "validate": "npm run typecheck && npm run test && npm run build"
26
+ },
27
+ "engines": {
28
+ "node": ">=22"
29
+ },
30
+ "keywords": [
31
+ "kody",
32
+ "mcp",
33
+ "cli",
34
+ "oauth"
35
+ ],
36
+ "author": "Kent C. Dodds <me@kentcdodds.com> (https://kentcdodds.com)",
37
+ "license": "MIT",
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "git+https://github.com/kody-bot/cli.git"
41
+ },
42
+ "bugs": {
43
+ "url": "https://github.com/kody-bot/cli/issues"
44
+ },
45
+ "homepage": "https://kody.codes",
46
+ "publishConfig": {
47
+ "access": "public"
48
+ },
49
+ "dependencies": {
50
+ "@modelcontextprotocol/sdk": "^1.30.0",
51
+ "@napi-rs/keyring": "^1.3.0"
52
+ },
53
+ "devDependencies": {
54
+ "@types/node": "^24.5.2",
55
+ "tsx": "^4.20.5",
56
+ "typescript": "^5.9.2"
57
+ }
9
58
  }
@@ -0,0 +1,59 @@
1
+ ---
2
+ name: kody
3
+ description: >
4
+ Use the Kody CLI to log in, search Kody capabilities, and execute
5
+ sandboxed modules against kody.codes. Use when the user mentions Kody,
6
+ kody.codes, durable agent tasks, or wants to search/execute via `kody`.
7
+ ---
8
+
9
+ # Kody CLI
10
+
11
+ Kody is a remote MCP personal assistant. This skill uses the `@kodycodes/cli`
12
+ package — a local MCP client — instead of configuring an MCP server by hand.
13
+
14
+ ## Install
15
+
16
+ ```bash
17
+ npm install -g @kodycodes/cli
18
+ # or
19
+ npx @kodycodes/cli --help
20
+ ```
21
+
22
+ Then install this skill into the current host if it is not already present:
23
+
24
+ ```bash
25
+ npx @kodycodes/cli skill install
26
+ ```
27
+
28
+ ## Login
29
+
30
+ ```bash
31
+ kody login
32
+ ```
33
+
34
+ The CLI opens a browser for Kody OAuth (PKCE + dynamic client registration).
35
+ If a browser cannot open, it prints the URL. Tokens (access + refresh) are
36
+ stored in the OS keychain on macOS, Windows, and Linux. Linux without Secret
37
+ Service falls back to a `0600` file under `$XDG_CONFIG_HOME/kody`.
38
+
39
+ Never ask the user to paste tokens into chat.
40
+
41
+ ## Use Kody
42
+
43
+ ```bash
44
+ kody search "what can you do"
45
+ kody search --domain email
46
+ kody execute --code "import { kody } from 'kody:runtime'\nexport default async function main() { return await kody.search({ query: 'what can you do' }) }"
47
+ ```
48
+
49
+ `search` and `execute` are Kody's only MCP tools. Prefer `kody search` before
50
+ `kody execute`. Add `--json` when you need structured output.
51
+
52
+ ```bash
53
+ kody status
54
+ kody whoami
55
+ kody logout
56
+ ```
57
+
58
+ Override the MCP URL with `--mcp-url` or `KODY_MCP_URL` for preview or local
59
+ servers. Default: `https://kody.codes/mcp`.