@tiny-fish/cli 0.45.2-next.358 → 0.46.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 CHANGED
@@ -19,7 +19,7 @@ npx -y @tiny-fish/cli@latest connect
19
19
  ```
20
20
 
21
21
  It detects the agent configs on your machine (Claude Code, Codex, Cursor, Grok, Hermes,
22
- omp, OpenClaw, OpenCode) and opens a checklist with everything pre-selected. Press enter to connect
22
+ Kilo Code, omp, OpenClaw, OpenCode) and opens a checklist with everything pre-selected. Press enter to connect
23
23
  them all, or use the arrow keys and space to narrow the set first; `q` cancels without touching
24
24
  anything. Terminals without raw-key support get a numbered prompt instead.
25
25
 
@@ -15,7 +15,7 @@ function applyConfigDir(client, configDir) {
15
15
  if (client !== 'cline') {
16
16
  throw new Error('--config-dir is supported only by `tinyfish connect cline`.');
17
17
  }
18
- const resolved = path.resolve(configDir);
18
+ const resolved = path.resolve(configDir); // nosemgrep: path-join-resolve-traversal -- the operator's own --config-dir, statted below
19
19
  let isDir = false;
20
20
  try {
21
21
  isDir = fs.statSync(resolved).isDirectory();
@@ -10,6 +10,8 @@ export interface McpJsonTarget {
10
10
  };
11
11
  /** Cline nests url and headers under this key; every other harness is flat. */
12
12
  transportKey?: string;
13
+ /** Read-side only; OpenCode and its Kilo fork nest servers under `mcp`. */
14
+ serversKey?: string;
13
15
  }
14
16
  export interface McpJsonWriteResult {
15
17
  status: 'written' | 'unchanged' | 'corrupt_skip';
@@ -76,7 +76,7 @@ export function readTinyfishEntry(target) {
76
76
  if ('error' in existing) {
77
77
  return { present: false, hasApiKeyHeader: false, error: existing.error };
78
78
  }
79
- const servers = existing.parsed.mcpServers;
79
+ const servers = existing.parsed[target.serversKey ?? 'mcpServers'];
80
80
  const entry = isPlainRecord(servers) ? servers[target.serverKey] : undefined;
81
81
  if (!isPlainRecord(entry))
82
82
  return { present: false, hasApiKeyHeader: false };
@@ -0,0 +1,9 @@
1
+ import { type Harness } from '../harness-detect.js';
2
+ import { type McpJsonServerEntry } from './mcp-json-config.js';
3
+ export type OpencodeStyleHarness = 'kilo' | 'opencode';
4
+ /** Narrows before any path is built, so a future removal-less harness cannot borrow these. */
5
+ export declare function isOpencodeStyle(harness: Harness): harness is OpencodeStyleHarness;
6
+ /** The file on disk, else the `.jsonc` default so messages still name something. */
7
+ export declare function opencodeStyleConfigPath(harness: OpencodeStyleHarness): string;
8
+ /** Reports the header's shape, never its value. JSONC comments read as unparseable. */
9
+ export declare function readOpencodeStyleEntry(harness: OpencodeStyleHarness): McpJsonServerEntry;
@@ -0,0 +1,34 @@
1
+ // OpenCode and its Kilo fork share a config file the CLI reads but never writes.
2
+ import * as fs from 'fs';
3
+ import * as path from 'path';
4
+ import { harnessConfigPath } from '../harness-detect.js';
5
+ import { harnessSpec } from '../harness-spec.js';
6
+ import { readTinyfishEntry, } from './mcp-json-config.js';
7
+ /** Narrows before any path is built, so a future removal-less harness cannot borrow these. */
8
+ export function isOpencodeStyle(harness) {
9
+ return harness === 'kilo' || harness === 'opencode';
10
+ }
11
+ /** Both extensions are valid; `mcp add` keeps whichever already exists. */
12
+ const EXTENSIONS = ['.jsonc', '.json'];
13
+ function candidatePaths(harness) {
14
+ const dir = harnessConfigPath(harness);
15
+ const stem = harnessSpec(harness).command;
16
+ return EXTENSIONS.map((ext) => path.join(dir, `${stem}${ext}`)); // nosemgrep: path-join-resolve-traversal -- fixed names from the harness spec
17
+ }
18
+ /** The file on disk, else the `.jsonc` default so messages still name something. */
19
+ export function opencodeStyleConfigPath(harness) {
20
+ const candidates = candidatePaths(harness);
21
+ return candidates.find((file) => fs.existsSync(file)) ?? candidates[0];
22
+ }
23
+ function target(harness) {
24
+ return {
25
+ serverKey: 'tinyfish',
26
+ serversKey: 'mcp',
27
+ dir: () => harnessConfigPath(harness),
28
+ file: () => opencodeStyleConfigPath(harness),
29
+ };
30
+ }
31
+ /** Reports the header's shape, never its value. JSONC comments read as unparseable. */
32
+ export function readOpencodeStyleEntry(harness) {
33
+ return readTinyfishEntry(target(harness));
34
+ }
@@ -1,3 +1,4 @@
1
+ import * as path from 'path';
1
2
  import { clearConnectContext, connectContextState } from './auth.js';
2
3
  import { NATIVE_BY_HARNESS, openclawSkillUninstall } from './connect-clients.js';
3
4
  import { ConnectInterruptedError, spawnRemoval } from './connect-runtime.js';
@@ -6,10 +7,9 @@ import { planOmpWrite, removeOmpMcpServer } from './config/omp-config.js';
6
7
  import { piMcpPath, planPiWrite, removePiMcpServer } from './config/pi-config.js';
7
8
  import { HERMES_KEY_VAR, hermesEnvPath, removeHermesKey, resolveHermesHome } from './hermes-env.js';
8
9
  import { clearHermesWebBackends } from './hermes-plugin.js';
9
- import { HARNESS_DISPLAY_NAMES as DISPLAY_NAMES } from './harness-detect.js';
10
+ import { HARNESS_DISPLAY_NAMES as DISPLAY_NAMES, harnessDisplayPath, } from './harness-detect.js';
11
+ import { isOpencodeStyle, opencodeStyleConfigPath } from './config/opencode-config.js';
10
12
  import { errLine } from './output.js';
11
- // OpenCode ships no `mcp remove`, so the only removal is editing the config it wrote.
12
- const OPENCODE_UNINSTALL_POINTER = 'remove the tinyfish entry from ~/.config/opencode/opencode.json';
13
13
  export function planText(harness, mcpUrl, apiKey) {
14
14
  if (harness === 'cursor')
15
15
  return planCursorWrite(mcpUrl, apiKey);
@@ -61,8 +61,14 @@ function uninstallRuns(harness) {
61
61
  /** Derived from the same args the spawn runs, so the advice cannot drift. */
62
62
  function uninstallPointer(harness) {
63
63
  const removal = uninstallRuns(harness);
64
- if (!removal)
65
- return OPENCODE_UNINSTALL_POINTER;
64
+ // No `mcp remove` ships, so the only removal is editing the config.
65
+ if (!removal) {
66
+ const file = isOpencodeStyle(harness)
67
+ ? `/${path.basename(opencodeStyleConfigPath(harness))}`
68
+ : "'s MCP config";
69
+ const where = `${harnessDisplayPath(harness)}${file}`;
70
+ return `remove the tinyfish entry from ${where}`;
71
+ }
66
72
  // `;` runs every removal but breaks cmd.exe; `&&` is the reverse — see
67
73
  // internal-docs/install-command-shell-quirks.md (PS 5.1 stays a known gap).
68
74
  const joiner = process.platform === 'win32' ? ' && ' : ' ; ';
@@ -104,7 +104,7 @@ export declare function hermesRegistrationEnabled(home: string, mode?: 'api-key'
104
104
  };
105
105
  export declare const NATIVE_MCP_CLIENTS: readonly NativeMcpClient[];
106
106
  /** Native descriptor by harness id; Cursor and OpenClaw have none. */
107
- export declare const NATIVE_BY_HARNESS: Map<"openclaw" | "omp" | "grok" | "cursor" | "codex" | "hermes" | "command-code" | "opencode" | "pi" | "cline" | "claude-code", NativeMcpClient>;
107
+ export declare const NATIVE_BY_HARNESS: Map<"openclaw" | "omp" | "grok" | "cursor" | "codex" | "hermes" | "command-code" | "kilo" | "opencode" | "pi" | "cline" | "claude-code", NativeMcpClient>;
108
108
  export declare const OPENCLAW: SupportedCommand;
109
109
  /** "printed" = handed to the user to paste; the walkthrough was never started for them. */
110
110
  export type WalkthroughOutcome = 'launched' | 'printed';
@@ -209,12 +209,12 @@ function installHermesWebPlugin({ apiKey, verbose, seededHome, }) {
209
209
  setHermesWebBackends(home);
210
210
  return pluginVersion;
211
211
  }
212
- // OpenCode's TUI takes a positional as a project directory, so a bare `opencode "<prompt>"`
213
- // would be read as a folder. The `--prompt` flag seeds the interactive TUI with a first message
212
+ // These TUIs read a positional as a project directory, so a bare `<cmd> "<prompt>"` would be
213
+ // read as a folder. `--prompt` seeds the interactive TUI with a first message
214
214
  // (opencode.ai/docs/cli), so the onboarding guide fires just like the other agents.
215
- function launchOpencode(prompt) {
216
- handOverTerminal('opencode', ['--prompt', prompt], 'OpenCode');
217
- }
215
+ const launchWithPromptFlag = (command, displayName) => (prompt) => {
216
+ handOverTerminal(command, ['--prompt', prompt], displayName);
217
+ };
218
218
  // A bare `cline "<prompt>"` is a one-shot act run that auto-approves every tool, so it would
219
219
  // bill TinyFish calls unattended and cannot answer the walkthrough's questions. `-i` is the
220
220
  // only interactive mode.
@@ -286,7 +286,8 @@ const HARNESS_OVERRIDES = {
286
286
  seedKey: seedHermesKey,
287
287
  prepareKeylessInstall: prepareHermesKeylessInstall,
288
288
  },
289
- opencode: { launchWalkthrough: launchOpencode },
289
+ kilo: { launchWalkthrough: launchWithPromptFlag('kilo', 'Kilo Code') },
290
+ opencode: { launchWalkthrough: launchWithPromptFlag('opencode', 'OpenCode') },
290
291
  };
291
292
  function toNativeClient(harness) {
292
293
  const spec = harnessSpec(harness);
@@ -41,6 +41,7 @@ declare const doctorCheckSchema: z.ZodObject<{
41
41
  codex: "codex";
42
42
  hermes: "hermes";
43
43
  "command-code": "command-code";
44
+ kilo: "kilo";
44
45
  opencode: "opencode";
45
46
  pi: "pi";
46
47
  cline: "cline";
@@ -61,6 +62,7 @@ declare const doctorHarnessSchema: z.ZodObject<{
61
62
  codex: "codex";
62
63
  hermes: "hermes";
63
64
  "command-code": "command-code";
65
+ kilo: "kilo";
64
66
  opencode: "opencode";
65
67
  pi: "pi";
66
68
  cline: "cline";
@@ -91,6 +93,7 @@ declare const doctorRepairSchema: z.ZodObject<{
91
93
  codex: "codex";
92
94
  hermes: "hermes";
93
95
  "command-code": "command-code";
96
+ kilo: "kilo";
94
97
  opencode: "opencode";
95
98
  pi: "pi";
96
99
  cline: "cline";
@@ -130,6 +133,7 @@ export declare const doctorReportSchema: z.ZodObject<{
130
133
  codex: "codex";
131
134
  hermes: "hermes";
132
135
  "command-code": "command-code";
136
+ kilo: "kilo";
133
137
  opencode: "opencode";
134
138
  pi: "pi";
135
139
  cline: "cline";
@@ -150,6 +154,7 @@ export declare const doctorReportSchema: z.ZodObject<{
150
154
  codex: "codex";
151
155
  hermes: "hermes";
152
156
  "command-code": "command-code";
157
+ kilo: "kilo";
153
158
  opencode: "opencode";
154
159
  pi: "pi";
155
160
  cline: "cline";
@@ -180,6 +185,7 @@ export declare const doctorReportSchema: z.ZodObject<{
180
185
  codex: "codex";
181
186
  hermes: "hermes";
182
187
  "command-code": "command-code";
188
+ kilo: "kilo";
183
189
  opencode: "opencode";
184
190
  pi: "pi";
185
191
  cline: "cline";
@@ -1,5 +1,5 @@
1
1
  /** Our skill-path slugs, inherited from the `skills` CLI era. */
2
- export type SkillAgent = 'claude-code' | 'cline' | 'codex' | 'command-code' | 'cursor' | 'hermes-agent' | 'opencode' | 'pi';
2
+ export type SkillAgent = 'claude-code' | 'cline' | 'codex' | 'command-code' | 'cursor' | 'hermes-agent' | 'kilo' | 'opencode' | 'pi';
3
3
  export interface HarnessSupportCheck {
4
4
  args: string[];
5
5
  /** Missing → nothing can work; setup fails. */
@@ -237,6 +237,27 @@ export declare const HARNESS_SPECS: {
237
237
  postConnectNote: string;
238
238
  keyHeldByCli: true;
239
239
  };
240
+ kilo: {
241
+ command: string;
242
+ displayName: string;
243
+ configDir: string;
244
+ reloadAction: string;
245
+ skillAgent: "kilo";
246
+ nonInteractiveAdd: true;
247
+ supportCheck: {
248
+ args: string[];
249
+ patterns: RegExp[];
250
+ keyAuthPattern: RegExp;
251
+ unavailableMessage: string;
252
+ };
253
+ urlStyle: "flag";
254
+ keyRequired: true;
255
+ header: {
256
+ name: string;
257
+ sep: "=";
258
+ };
259
+ removals: never[];
260
+ };
240
261
  omp: {
241
262
  command: string;
242
263
  displayName: string;
@@ -307,4 +328,4 @@ export type NonNativeHarness = {
307
328
  [K in Harness]: 'urlStyle' extends keyof (typeof HARNESS_SPECS)[K] ? never : 'keyRequired' extends keyof (typeof HARNESS_SPECS)[K] ? never : K;
308
329
  }[Harness];
309
330
  /** Native MCP harnesses generate an add; key-only ones generate only the keyed form. */
310
- export declare const NATIVE_HARNESSES: ("openclaw" | "omp" | "grok" | "cursor" | "codex" | "hermes" | "command-code" | "opencode" | "pi" | "cline" | "claude-code")[];
331
+ export declare const NATIVE_HARNESSES: ("openclaw" | "omp" | "grok" | "cursor" | "codex" | "hermes" | "command-code" | "kilo" | "opencode" | "pi" | "cline" | "claude-code")[];
@@ -45,6 +45,10 @@ const OPENCODE_MCP_ADD_UNAVAILABLE_MESSAGE = 'Could not confirm this OpenCode in
45
45
  const OPENCODE_MODEL_NOTE = 'Note: TinyFish runs on tool calls, so OpenCode needs a model that supports tool use. Image-only ' +
46
46
  'models (e.g. Nano Banana Pro) will show "No endpoints found that support tool use" — switch ' +
47
47
  "OpenCode's model if the walkthrough can't start.";
48
+ const KILO_MCP_ADD_UNAVAILABLE_MESSAGE = 'Could not confirm this Kilo Code installation supports remote MCP servers: `kilo mcp add ' +
49
+ '--help` did not list `--url`. Update Kilo Code with `kilo upgrade` and retry, or add ' +
50
+ 'TinyFish manually with `kilo mcp add`.' +
51
+ SUPPORT_CHECK_DEBUG_HINT;
48
52
  const HERMES_RESTART_NOTE = 'Restart your Hermes session to pick up TinyFish — Hermes discovers MCP servers at startup.';
49
53
  const CLINE_MCP_ADD_UNAVAILABLE_MESSAGE = 'Could not confirm this Cline installation supports non-interactive MCP setup: `cline mcp add ' +
50
54
  '--help` did not list `--transport` and `--yes`. Update Cline with `cline --update` and retry.' +
@@ -218,6 +222,28 @@ export const HARNESS_SPECS = {
218
222
  // No canVerifyAuth: keyed already covers auth; true breaks keyless installs.
219
223
  keyHeldByCli: true,
220
224
  },
225
+ // Kilo Code forks OpenCode, so the add flags, config shape and list renderer match it.
226
+ kilo: {
227
+ command: 'kilo',
228
+ displayName: 'Kilo Code',
229
+ configDir: '.config/kilo',
230
+ reloadAction: 'restart it',
231
+ skillAgent: 'kilo',
232
+ nonInteractiveAdd: true,
233
+ supportCheck: {
234
+ args: ['mcp', 'add', '--help'],
235
+ patterns: [/--url(?:\s|$)/m],
236
+ keyAuthPattern: HEADER_FLAG,
237
+ unavailableMessage: KILO_MCP_ADD_UNAVAILABLE_MESSAGE,
238
+ },
239
+ urlStyle: 'flag',
240
+ // `kilo mcp auth` exists, but a stored key skips the browser.
241
+ keyRequired: true,
242
+ // Kilo takes `--header NAME=VALUE`, not Claude Code's `NAME: VALUE`.
243
+ header: { name: 'X-API-Key', sep: '=' },
244
+ // `kilo mcp add` upserts by name and ships no `mcp remove`.
245
+ removals: [],
246
+ },
221
247
  omp: {
222
248
  command: 'omp',
223
249
  displayName: 'omp',
@@ -10,7 +10,9 @@ import { readClineTinyfishEntry } from './config/cline-config.js';
10
10
  import { readCommandCodeTinyfishEntry } from './config/command-code-config.js';
11
11
  import { readCursorTinyfishEntry } from './config/cursor-config.js';
12
12
  import { readOmpTinyfishEntry } from './config/omp-config.js';
13
+ import { readOpencodeStyleEntry } from './config/opencode-config.js';
13
14
  import { readPiTinyfishEntry } from './config/pi-config.js';
15
+ import { harnessSpec } from './harness-spec.js';
14
16
  import { readHermesKey, resolveHermesHome } from './hermes-env.js';
15
17
  import { readHermesEntry } from './config/hermes-config.js';
16
18
  import { detectInstalledHarnesses, harnessConfigPath, harnessDisplayPath, AuthMode, Registered, } from './harness-detect.js';
@@ -382,8 +384,8 @@ function probePi() {
382
384
  // Exit 0 with output we cannot recognise is not evidence of absence: reporting `no` there would
383
385
  // earn a connect repair off a parse failure, which is the bug class this command exists to kill.
384
386
  const MCP_LIST_HEADER = /mcp servers/i;
385
- // OpenCode prints the endpoint on the line under the server row; Hermes truncates its column.
386
- const OPENCODE_ROW_URL = /^[^\w]*tinyfish(?![\w-]).*\n[^\w]*(https?:\S+)/m;
387
+ // OpenCode and its Kilo fork print the endpoint under the row; Hermes truncates its column.
388
+ const MCP_LIST_ROW_URL = /^[^\w]*tinyfish(?![\w-]).*\n[^\w]*(https?:\S+)/m;
387
389
  function fromMcpList(command, output, registeredMode, urlPattern) {
388
390
  if (LISTED_AS_TINYFISH.test(output)) {
389
391
  const url = urlPattern?.exec(output)?.[1];
@@ -444,15 +446,30 @@ function probeOpenClaw() {
444
446
  ? { registered: Registered.Yes, authMode: AuthMode.ApiKey, usesCliKey: true }
445
447
  : NOT_REGISTERED;
446
448
  }
447
- // `opencode mcp list` prints no headers, so a key-authed registration is indistinguishable.
448
- function probeOpencode() {
449
+ // `mcp list` prints no headers, so the config file is the only place the key's shape shows.
450
+ function configFileAuth(harness) {
451
+ const entry = readOpencodeStyleEntry(harness);
452
+ if (entry.error || !entry.present || !entry.hasApiKeyHeader)
453
+ return {};
454
+ return {
455
+ authMode: AuthMode.ApiKey,
456
+ ...(entry.keyMatchesCliKey ? { keyMatchesCliKey: true } : {}),
457
+ };
458
+ }
459
+ // `mcp list` resolves project config too, so it stays the registration source of truth.
460
+ function probeMcpList(harness) {
461
+ const command = harnessSpec(harness).command;
449
462
  const args = ['mcp', 'list'];
450
- const probe = runProbe('opencode', args);
463
+ const probe = runProbe(command, args);
451
464
  if (probe.outcome !== 'ran' || probe.exitCode !== 0) {
452
- const reason = probeReason('opencode', args, probe);
465
+ const reason = probeReason(command, args, probe);
453
466
  return unverified(reason);
454
467
  }
455
- return fromMcpList('opencode', probe.output, AuthMode.Unknown, OPENCODE_ROW_URL);
468
+ const listed = fromMcpList(command, probe.output, AuthMode.Unknown, MCP_LIST_ROW_URL);
469
+ if (listed.registered !== Registered.Yes)
470
+ return listed;
471
+ // A project-scoped entry is listed but absent here, so auth mode stays unknown.
472
+ return { ...listed, ...configFileAuth(harness) };
456
473
  }
457
474
  // Verified against grok 0.0.60: a list. Header values stay `unknown`; only names are read.
458
475
  const grokEntrySchema = z.looseObject({
@@ -545,9 +562,10 @@ const PROBES = {
545
562
  cursor: probeCursor,
546
563
  grok: probeGrok,
547
564
  hermes: probeHermes,
565
+ kilo: () => probeMcpList('kilo'),
548
566
  omp: probeOmp,
549
567
  openclaw: probeOpenClaw,
550
- opencode: probeOpencode,
568
+ opencode: () => probeMcpList('opencode'),
551
569
  pi: probePi,
552
570
  };
553
571
  /** Never throws; a failed probe is `unknown` with a reason, so no caller can read it as healthy. */
@@ -29,6 +29,7 @@ declare const harnessResultSchema: z.ZodObject<{
29
29
  codex: "codex";
30
30
  hermes: "hermes";
31
31
  "command-code": "command-code";
32
+ kilo: "kilo";
32
33
  opencode: "opencode";
33
34
  pi: "pi";
34
35
  cline: "cline";
@@ -66,6 +67,7 @@ export declare const setupCompletedPayloadSchema: z.ZodObject<{
66
67
  codex: "codex";
67
68
  hermes: "hermes";
68
69
  "command-code": "command-code";
70
+ kilo: "kilo";
69
71
  opencode: "opencode";
70
72
  pi: "pi";
71
73
  cline: "cline";
@@ -130,6 +132,7 @@ declare const doctorCompletedPayloadSchema: z.ZodObject<{
130
132
  codex: "codex";
131
133
  hermes: "hermes";
132
134
  "command-code": "command-code";
135
+ kilo: "kilo";
133
136
  opencode: "opencode";
134
137
  pi: "pi";
135
138
  cline: "cline";
@@ -146,6 +149,7 @@ declare const doctorCompletedPayloadSchema: z.ZodObject<{
146
149
  codex: "codex";
147
150
  hermes: "hermes";
148
151
  "command-code": "command-code";
152
+ kilo: "kilo";
149
153
  opencode: "opencode";
150
154
  pi: "pi";
151
155
  cline: "cline";
@@ -160,6 +164,7 @@ declare const doctorCompletedPayloadSchema: z.ZodObject<{
160
164
  codex: "codex";
161
165
  hermes: "hermes";
162
166
  "command-code": "command-code";
167
+ kilo: "kilo";
163
168
  opencode: "opencode";
164
169
  pi: "pi";
165
170
  cline: "cline";
@@ -203,6 +208,7 @@ declare const doctorCouldNotRunPayloadSchema: z.ZodObject<{
203
208
  codex: "codex";
204
209
  hermes: "hermes";
205
210
  "command-code": "command-code";
211
+ kilo: "kilo";
206
212
  opencode: "opencode";
207
213
  pi: "pi";
208
214
  cline: "cline";
@@ -242,6 +248,7 @@ declare const doctorPayloadSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
242
248
  codex: "codex";
243
249
  hermes: "hermes";
244
250
  "command-code": "command-code";
251
+ kilo: "kilo";
245
252
  opencode: "opencode";
246
253
  pi: "pi";
247
254
  cline: "cline";
@@ -258,6 +265,7 @@ declare const doctorPayloadSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
258
265
  codex: "codex";
259
266
  hermes: "hermes";
260
267
  "command-code": "command-code";
268
+ kilo: "kilo";
261
269
  opencode: "opencode";
262
270
  pi: "pi";
263
271
  cline: "cline";
@@ -272,6 +280,7 @@ declare const doctorPayloadSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
272
280
  codex: "codex";
273
281
  hermes: "hermes";
274
282
  "command-code": "command-code";
283
+ kilo: "kilo";
275
284
  opencode: "opencode";
276
285
  pi: "pi";
277
286
  cline: "cline";
@@ -314,6 +323,7 @@ declare const doctorPayloadSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
314
323
  codex: "codex";
315
324
  hermes: "hermes";
316
325
  "command-code": "command-code";
326
+ kilo: "kilo";
317
327
  opencode: "opencode";
318
328
  pi: "pi";
319
329
  cline: "cline";
@@ -19,6 +19,8 @@ const SKILL_DIR_BY_AGENT = {
19
19
  // Not the canonical dir: `skills` writes this one under the harness's own config dir.
20
20
  'command-code': () => path.join(os.homedir(), '.commandcode', 'skills'), // nosemgrep: path-join-resolve-traversal -- fixed dir names under os.homedir()
21
21
  cursor: canonicalSkillsDir,
22
+ // Verified with `kilo debug skill`: Kilo scans the canonical dir too.
23
+ kilo: canonicalSkillsDir,
22
24
  opencode: canonicalSkillsDir,
23
25
  // `skills` writes pi's here whatever PI_CODING_AGENT_DIR says.
24
26
  pi: () => path.join(os.homedir(), '.pi', 'agent', 'skills'), // nosemgrep: path-join-resolve-traversal -- fixed dir names under os.homedir()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiny-fish/cli",
3
- "version": "0.45.2-next.358",
3
+ "version": "0.46.0",
4
4
  "description": "TinyFish CLI — run web automations from your terminal",
5
5
  "type": "module",
6
6
  "bin": {