@tiny-fish/cli 0.45.2-next.356 → 0.45.2-next.357
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/dist/commands/config-claude.js +1 -1
- package/dist/commands/connect.js +29 -4
- package/dist/lib/auth.d.ts +4 -0
- package/dist/lib/auth.js +26 -4
- package/dist/lib/config/cline-config.d.ts +4 -0
- package/dist/lib/config/cline-config.js +36 -0
- package/dist/lib/{connect-config-file.d.ts → config/connect-config-file.d.ts} +1 -1
- package/dist/lib/{connect-config-file.js → config/connect-config-file.js} +11 -11
- package/dist/lib/{hermes-config.js → config/hermes-config.js} +2 -2
- package/dist/lib/{mcp-json-config.d.ts → config/mcp-json-config.d.ts} +2 -0
- package/dist/lib/{mcp-json-config.js → config/mcp-json-config.js} +11 -5
- package/dist/lib/{omp-config.js → config/omp-config.js} +2 -2
- package/dist/lib/{pi-config.js → config/pi-config.js} +1 -1
- package/dist/lib/connect-all-uninstall.js +3 -3
- package/dist/lib/connect-clients.d.ts +1 -1
- package/dist/lib/connect-clients.js +8 -1
- package/dist/lib/connect-harness.js +1 -1
- package/dist/lib/doctor-checks.js +1 -1
- package/dist/lib/doctor-report.d.ts +6 -0
- package/dist/lib/harness-detect.js +7 -3
- package/dist/lib/harness-spec.d.ts +23 -2
- package/dist/lib/harness-spec.js +23 -0
- package/dist/lib/harness.js +5 -0
- package/dist/lib/hermes-plugin.js +1 -1
- package/dist/lib/registration-detect.js +22 -5
- package/dist/lib/setup-telemetry.d.ts +10 -0
- package/dist/lib/skill-paths.js +1 -0
- package/package.json +1 -1
- /package/dist/lib/{claude-config.d.ts → config/claude-config.d.ts} +0 -0
- /package/dist/lib/{claude-config.js → config/claude-config.js} +0 -0
- /package/dist/lib/{command-code-config.d.ts → config/command-code-config.d.ts} +0 -0
- /package/dist/lib/{command-code-config.js → config/command-code-config.js} +0 -0
- /package/dist/lib/{cursor-config.d.ts → config/cursor-config.d.ts} +0 -0
- /package/dist/lib/{cursor-config.js → config/cursor-config.js} +0 -0
- /package/dist/lib/{hermes-config.d.ts → config/hermes-config.d.ts} +0 -0
- /package/dist/lib/{omp-config.d.ts → config/omp-config.d.ts} +0 -0
- /package/dist/lib/{pi-config.d.ts → config/pi-config.d.ts} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { err, errLine, out } from '../lib/output.js';
|
|
2
2
|
import { validatedApiKey } from '../lib/auth.js';
|
|
3
|
-
import { readSettingsJson, writeSettingsJson, readClaudeMd, writeClaudeMd, mergeSettings, removeFromSettings, mergeClaudeMd, removeFromClaudeMd, claudeSettingsPath, claudeMdPath, isTinyfishConfiguredInSettings, isTinyfishConfiguredInClaudeMd, } from '../lib/claude-config.js';
|
|
3
|
+
import { readSettingsJson, writeSettingsJson, readClaudeMd, writeClaudeMd, mergeSettings, removeFromSettings, mergeClaudeMd, removeFromClaudeMd, claudeSettingsPath, claudeMdPath, isTinyfishConfiguredInSettings, isTinyfishConfiguredInClaudeMd, } from '../lib/config/claude-config.js';
|
|
4
4
|
function loadExistingConfig() {
|
|
5
5
|
let settings;
|
|
6
6
|
try {
|
package/dist/commands/connect.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { saveConnectConfigDir, validateKeyFormat, validatedApiKey } from '../lib/auth.js';
|
|
2
4
|
import { runConnectAll } from '../lib/connect-all.js';
|
|
3
5
|
import { ALL_HARNESSES } from '../lib/harness-detect.js';
|
|
4
6
|
import { detectHumanInitiated } from '../lib/harness.js';
|
|
@@ -8,6 +10,24 @@ import { z } from 'zod';
|
|
|
8
10
|
import { gateApiKey } from '../lib/connect-preflight.js';
|
|
9
11
|
import { DEFAULT_MCP_URL } from '../lib/mcp-endpoint.js';
|
|
10
12
|
import { connectHarness } from '../lib/connect-harness.js';
|
|
13
|
+
/** Cline reads CLINE_DIR itself, so setting it steers the add, the remove and our own probe. */
|
|
14
|
+
function applyConfigDir(client, configDir) {
|
|
15
|
+
if (client !== 'cline') {
|
|
16
|
+
throw new Error('--config-dir is supported only by `tinyfish connect cline`.');
|
|
17
|
+
}
|
|
18
|
+
const resolved = path.resolve(configDir);
|
|
19
|
+
let isDir = false;
|
|
20
|
+
try {
|
|
21
|
+
isDir = fs.statSync(resolved).isDirectory();
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
// Reported below: a typo must fail here, not seed a store nothing reads.
|
|
25
|
+
}
|
|
26
|
+
if (!isDir)
|
|
27
|
+
throw new Error(`--config-dir is not an existing directory: ${resolved}`);
|
|
28
|
+
process.env['CLINE_DIR'] = resolved;
|
|
29
|
+
return resolved;
|
|
30
|
+
}
|
|
11
31
|
function parseMcpUrlOption(options) {
|
|
12
32
|
let mcpUrl = options.url ?? DEFAULT_MCP_URL;
|
|
13
33
|
const parsedUrl = URL.parse(mcpUrl);
|
|
@@ -51,10 +71,14 @@ async function connectManyPath(options, mcpUrl, attemptId) {
|
|
|
51
71
|
process.exitCode = exitCode;
|
|
52
72
|
}
|
|
53
73
|
async function connectSingleClient(client, options, mcpUrl, attemptId) {
|
|
74
|
+
if (!ALL_HARNESSES.includes(client)) {
|
|
75
|
+
throw new Error('Unsupported client: ' + client + '. Supported clients: ' + ALL_HARNESSES.join(', '));
|
|
76
|
+
}
|
|
54
77
|
const connectOptions = {
|
|
55
78
|
apiKey: options.apiKey,
|
|
56
79
|
mcpUrl,
|
|
57
80
|
launch: options.launch ?? false,
|
|
81
|
+
configDir: options.configDir ? applyConfigDir(client, options.configDir) : undefined,
|
|
58
82
|
attemptId,
|
|
59
83
|
// A key that the install cannot use leaves only a browser hop no agent can finish.
|
|
60
84
|
keyAuthOnly: !detectHumanInitiated() && !!validatedApiKey(options.apiKey),
|
|
@@ -63,9 +87,6 @@ async function connectSingleClient(client, options, mcpUrl, attemptId) {
|
|
|
63
87
|
fallbackWhenMissing: true,
|
|
64
88
|
verbose: options.verbose ?? false,
|
|
65
89
|
};
|
|
66
|
-
if (!ALL_HARNESSES.includes(client)) {
|
|
67
|
-
throw new Error('Unsupported client: ' + client + '. Supported clients: ' + ALL_HARNESSES.join(', '));
|
|
68
|
-
}
|
|
69
90
|
const gate = await gateApiKey({
|
|
70
91
|
apiKey: options.apiKey,
|
|
71
92
|
mcpUrl,
|
|
@@ -78,6 +99,9 @@ async function connectSingleClient(client, options, mcpUrl, attemptId) {
|
|
|
78
99
|
return;
|
|
79
100
|
}
|
|
80
101
|
await connectHarness(client, connectOptions);
|
|
102
|
+
// After the connect: a run that wrote no entry must not record a store it never used.
|
|
103
|
+
if (connectOptions.configDir)
|
|
104
|
+
saveConnectConfigDir(client, connectOptions.configDir);
|
|
81
105
|
}
|
|
82
106
|
export function registerConnect(program) {
|
|
83
107
|
program
|
|
@@ -92,6 +116,7 @@ export function registerConnect(program) {
|
|
|
92
116
|
.option('--launch', 'Launch the agent and start the TinyFish walkthrough')
|
|
93
117
|
.option('--url <mcpUrl>', 'MCP endpoint override')
|
|
94
118
|
.option('--verbose', 'Print the full npm and skills output instead of only on failure')
|
|
119
|
+
.option('--config-dir <path>', 'Cline only: config dir of a relocated install (~/.cline)')
|
|
95
120
|
.action(async (client, options) => {
|
|
96
121
|
if (options.apiKey && !validateKeyFormat(options.apiKey)) {
|
|
97
122
|
throw new Error('Invalid --api-key value');
|
package/dist/lib/auth.d.ts
CHANGED
|
@@ -10,6 +10,8 @@ export type RecordedAuthMode = 'api-key' | 'keyless' | 'oauth' | 'deferred';
|
|
|
10
10
|
interface ConnectEntry {
|
|
11
11
|
attempt_id: string;
|
|
12
12
|
auth_mode?: RecordedAuthMode;
|
|
13
|
+
/** `connect cline --config-dir`: uninstall and doctor must find the same store. */
|
|
14
|
+
config_dir?: string;
|
|
13
15
|
}
|
|
14
16
|
interface PendingConnectAttempt {
|
|
15
17
|
id: string;
|
|
@@ -34,6 +36,8 @@ export declare function writeConfig(apiKey: string): void;
|
|
|
34
36
|
export declare function saveConfig(apiKey: string): void;
|
|
35
37
|
/** `authMode` omitted rewrites the entry without one: a reconnect that degraded must not keep a stale key claim. */
|
|
36
38
|
export declare function saveConnectContext(client: string, attemptId: string, authMode?: RecordedAuthMode): void;
|
|
39
|
+
/** Merged after connect wrote the entry; a missing entry means the install never landed. */
|
|
40
|
+
export declare function saveConnectConfigDir(client: string, configDir: string): void;
|
|
37
41
|
/** Distinguishes a missing entry from an unreadable config file. */
|
|
38
42
|
export declare function connectContextState(client: string): 'present' | 'absent' | 'unreadable';
|
|
39
43
|
/** Kept marker after uninstall makes doctor offer to reinstall the harness. */
|
package/dist/lib/auth.js
CHANGED
|
@@ -53,10 +53,14 @@ function parseConnectMap(value) {
|
|
|
53
53
|
if (typeof attemptId !== 'string')
|
|
54
54
|
continue;
|
|
55
55
|
const authMode = record?.auth_mode;
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
const configDir = record?.config_dir;
|
|
57
|
+
out[client] = {
|
|
58
|
+
attempt_id: attemptId,
|
|
59
|
+
...(typeof authMode === 'string' && RECORDED_AUTH_MODES.includes(authMode)
|
|
60
|
+
? { auth_mode: authMode }
|
|
61
|
+
: {}),
|
|
62
|
+
...(typeof configDir === 'string' ? { config_dir: configDir } : {}),
|
|
63
|
+
};
|
|
60
64
|
}
|
|
61
65
|
return Object.keys(out).length > 0 ? out : undefined;
|
|
62
66
|
}
|
|
@@ -144,6 +148,24 @@ export function saveConnectContext(client, attemptId, authMode) {
|
|
|
144
148
|
errLine(`Warning: could not persist connect context: ${e instanceof Error ? e.message : String(e)}`);
|
|
145
149
|
}
|
|
146
150
|
}
|
|
151
|
+
/** Merged after connect wrote the entry; a missing entry means the install never landed. */
|
|
152
|
+
export function saveConnectConfigDir(client, configDir) {
|
|
153
|
+
try {
|
|
154
|
+
updateConfig((config) => {
|
|
155
|
+
const entry = config.connect?.[client];
|
|
156
|
+
if (!entry)
|
|
157
|
+
return config;
|
|
158
|
+
return {
|
|
159
|
+
...config,
|
|
160
|
+
connect: { ...config.connect, [client]: { ...entry, config_dir: configDir } },
|
|
161
|
+
};
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
catch (e) {
|
|
165
|
+
// Best-effort, like saveConnectContext: a working install must not fail on this.
|
|
166
|
+
errLine(`Warning: could not persist the config dir: ${e instanceof Error ? e.message : String(e)}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
147
169
|
/** Distinguishes a missing entry from an unreadable config file. */
|
|
148
170
|
export function connectContextState(client) {
|
|
149
171
|
let raw;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as os from 'os';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { loadConfig } from '../auth.js';
|
|
4
|
+
import { readTinyfishEntry, } from './mcp-json-config.js';
|
|
5
|
+
/** A `--config-dir` install outlives the flag, so later runs read it back. */
|
|
6
|
+
function recordedConfigDir() {
|
|
7
|
+
try {
|
|
8
|
+
return loadConfig().connect?.['cline']?.config_dir;
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
// An unreadable config must degrade to the default, never throw out of a probe.
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
// Cline reads both raw, so neither is tilde-expanded here either.
|
|
16
|
+
export function clineConfigDir() {
|
|
17
|
+
const override = process.env['CLINE_DIR']?.trim() || recordedConfigDir();
|
|
18
|
+
return override ? path.resolve(override) : path.join(os.homedir(), '.cline'); // nosemgrep: path-join-resolve-traversal -- fixed dir names under os.homedir()
|
|
19
|
+
}
|
|
20
|
+
/** CLINE_DIR moves the whole tree; CLINE_DATA_DIR moves only the data subtree. */
|
|
21
|
+
function clineDataDir() {
|
|
22
|
+
const override = process.env['CLINE_DATA_DIR']?.trim();
|
|
23
|
+
return override ? path.resolve(override) : path.join(clineConfigDir(), 'data');
|
|
24
|
+
}
|
|
25
|
+
// Read-only: Cline writes this file itself, through `cline mcp add --yes`.
|
|
26
|
+
const CLINE_TARGET = {
|
|
27
|
+
serverKey: 'tinyfish',
|
|
28
|
+
dir: clineConfigDir,
|
|
29
|
+
file: () => path.join(clineDataDir(), 'settings', 'cline_mcp_settings.json'),
|
|
30
|
+
keyHeader: { name: 'X-API-Key' },
|
|
31
|
+
transportKey: 'transport',
|
|
32
|
+
};
|
|
33
|
+
/** Reports the header's shape, never its value. */
|
|
34
|
+
export function readClineTinyfishEntry() {
|
|
35
|
+
return readTinyfishEntry(CLINE_TARGET);
|
|
36
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ConnectAuthMode } from '
|
|
1
|
+
import { type ConnectAuthMode } from '../connect-runtime.js';
|
|
2
2
|
import { cursorMcpPath, writeCursorMcpConfig } from './cursor-config.js';
|
|
3
3
|
import { ompMcpPath, writeOmpMcpConfig } from './omp-config.js';
|
|
4
4
|
import { piMcpPath, writePiMcpConfig } from './pi-config.js';
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { CONNECT_SOURCE, saveConnectContext, validatedApiKey } from '
|
|
3
|
-
import { CURSOR_SKILL_TARGET, openExternalUrl } from '
|
|
4
|
-
import { ensureCliAuthenticated } from '
|
|
5
|
-
import { installWebSkill } from '
|
|
6
|
-
import { ConnectStepError, createConnectTelemetry, runGuarded, settle, } from '
|
|
2
|
+
import { CONNECT_SOURCE, saveConnectContext, validatedApiKey } from '../auth.js';
|
|
3
|
+
import { CURSOR_SKILL_TARGET, openExternalUrl } from '../connect-clients.js';
|
|
4
|
+
import { ensureCliAuthenticated } from '../connect-auth.js';
|
|
5
|
+
import { installWebSkill } from '../skill-install.js';
|
|
6
|
+
import { ConnectStepError, createConnectTelemetry, runGuarded, settle, } from '../connect-runtime.js';
|
|
7
7
|
import { cursorInstallDeeplink, cursorMcpPath, readCursorTinyfishEntry, writeCursorMcpConfig, } from './cursor-config.js';
|
|
8
8
|
import { ompMcpPath, readOmpTinyfishEntry, writeOmpMcpConfig } from './omp-config.js';
|
|
9
9
|
import { PI_ADAPTER_INSTALL_COMMAND, piMcpAdapterState, piSkillDirMismatch, piMcpPath, readPiTinyfishEntry, writePiMcpConfig, } from './pi-config.js';
|
|
10
|
-
import { AuthMode, HARNESS_DISPLAY_NAMES } from '
|
|
11
|
-
import { detectHumanInitiated } from '
|
|
12
|
-
import { errLine } from '
|
|
13
|
-
import { verifyMcpAuth } from '
|
|
14
|
-
import { apiBaseFromMcpUrl } from '
|
|
15
|
-
import { finishSetupHint, requireKeylessMcp, trackPostInstallFailure } from '
|
|
10
|
+
import { AuthMode, HARNESS_DISPLAY_NAMES } from '../harness-detect.js';
|
|
11
|
+
import { detectHumanInitiated } from '../harness.js';
|
|
12
|
+
import { errLine } from '../output.js';
|
|
13
|
+
import { verifyMcpAuth } from '../verify.js';
|
|
14
|
+
import { apiBaseFromMcpUrl } from '../mcp-endpoint.js';
|
|
15
|
+
import { finishSetupHint, requireKeylessMcp, trackPostInstallFailure } from '../connect-steps.js';
|
|
16
16
|
const PI_RELOAD_COPY = 'TinyFish is configured in pi. Restart pi, then run `/mcp-auth tinyfish` there if it asks you ' +
|
|
17
17
|
'to sign in.';
|
|
18
18
|
// The skill is the working path either way, so the note must not read as a failure.
|
|
@@ -2,8 +2,8 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { parse } from 'yaml';
|
|
4
4
|
import { z } from 'zod';
|
|
5
|
-
import { TINYFISH_ACCESS_MODE_HEADER, TINYFISH_KEYLESS_ACCESS_MODE } from '
|
|
6
|
-
import { HERMES_KEY_VAR } from '
|
|
5
|
+
import { TINYFISH_ACCESS_MODE_HEADER, TINYFISH_KEYLESS_ACCESS_MODE } from '../constants.js';
|
|
6
|
+
import { HERMES_KEY_VAR } from '../hermes-env.js';
|
|
7
7
|
/** Every message about the entry names this path. */
|
|
8
8
|
export function hermesConfigPath(home) {
|
|
9
9
|
return path.join(home, 'config.yaml');
|
|
@@ -8,6 +8,8 @@ export interface McpJsonTarget {
|
|
|
8
8
|
name: string;
|
|
9
9
|
valuePrefix?: string;
|
|
10
10
|
};
|
|
11
|
+
/** Cline nests url and headers under this key; every other harness is flat. */
|
|
12
|
+
transportKey?: string;
|
|
11
13
|
}
|
|
12
14
|
export interface McpJsonWriteResult {
|
|
13
15
|
status: 'written' | 'unchanged' | 'corrupt_skip';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
|
-
import { matchesCliKey } from '
|
|
3
|
-
import { TINYFISH_ACCESS_MODE_HEADER, TINYFISH_KEYLESS_ACCESS_MODE } from '
|
|
2
|
+
import { matchesCliKey } from '../auth.js';
|
|
3
|
+
import { TINYFISH_ACCESS_MODE_HEADER, TINYFISH_KEYLESS_ACCESS_MODE } from '../constants.js';
|
|
4
4
|
function isPlainRecord(value) {
|
|
5
5
|
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
6
6
|
}
|
|
@@ -65,6 +65,11 @@ export function planWrite(target, mcpUrl, apiKey, serverEntry) {
|
|
|
65
65
|
}
|
|
66
66
|
// omp expands `${VAR}` / `${VAR:-default}` header values at load.
|
|
67
67
|
const ENV_TEMPLATE_VALUE = /^\$\{([A-Za-z_][A-Za-z0-9_]*)(?::-[^}]*)?\}$/;
|
|
68
|
+
/** Cline nests url and headers under `transport`; `enabled` stays on the entry itself. */
|
|
69
|
+
function entryFields(entry, transportKey) {
|
|
70
|
+
const nested = transportKey ? entry[transportKey] : undefined;
|
|
71
|
+
return isPlainRecord(nested) ? nested : entry;
|
|
72
|
+
}
|
|
68
73
|
/** Reports the header's shape, never its value. */
|
|
69
74
|
export function readTinyfishEntry(target) {
|
|
70
75
|
const existing = readExisting(target);
|
|
@@ -75,9 +80,10 @@ export function readTinyfishEntry(target) {
|
|
|
75
80
|
const entry = isPlainRecord(servers) ? servers[target.serverKey] : undefined;
|
|
76
81
|
if (!isPlainRecord(entry))
|
|
77
82
|
return { present: false, hasApiKeyHeader: false };
|
|
78
|
-
const
|
|
83
|
+
const fields = entryFields(entry, target.transportKey);
|
|
84
|
+
const key = readKeyHeader(fields.headers, target.keyHeader);
|
|
79
85
|
const templateVar = key ? ENV_TEMPLATE_VALUE.exec(key.value)?.[1] : undefined;
|
|
80
|
-
const accessMode = readKeyHeader(
|
|
86
|
+
const accessMode = readKeyHeader(fields.headers, { name: TINYFISH_ACCESS_MODE_HEADER })?.value;
|
|
81
87
|
const keyless = accessMode?.trim().toLowerCase() === TINYFISH_KEYLESS_ACCESS_MODE;
|
|
82
88
|
return {
|
|
83
89
|
present: true,
|
|
@@ -86,7 +92,7 @@ export function readTinyfishEntry(target) {
|
|
|
86
92
|
...(entry.enabled === false ? { enabled: false } : {}),
|
|
87
93
|
...(matchesCliKey(key?.value) ? { keyMatchesCliKey: true } : {}),
|
|
88
94
|
...(templateVar ? { keyTemplateVar: templateVar } : {}),
|
|
89
|
-
...(typeof
|
|
95
|
+
...(typeof fields.url === 'string' ? { url: fields.url } : {}),
|
|
90
96
|
};
|
|
91
97
|
}
|
|
92
98
|
/** Reports the key header's shape, never its value. */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as path from 'path';
|
|
2
2
|
import spawn from 'cross-spawn';
|
|
3
|
-
import { ConnectStepError } from '
|
|
4
|
-
import { HARNESS_PROBE_TIMEOUT_MS } from '
|
|
3
|
+
import { ConnectStepError } from '../connect-runtime.js';
|
|
4
|
+
import { HARNESS_PROBE_TIMEOUT_MS } from '../constants.js';
|
|
5
5
|
import { buildTinyfishKeylessServerEntry, planWrite, readTinyfishEntry, removeServer, writeMcpConfig, } from './mcp-json-config.js';
|
|
6
6
|
const UNRESOLVED_REASON = '`omp config path` did not report a config directory';
|
|
7
7
|
// Cached: every later read must agree with the write.
|
|
@@ -3,7 +3,7 @@ import * as os from 'os';
|
|
|
3
3
|
import * as path from 'path';
|
|
4
4
|
import spawn from 'cross-spawn';
|
|
5
5
|
import { z } from 'zod';
|
|
6
|
-
import { HARNESS_PROBE_TIMEOUT_MS } from '
|
|
6
|
+
import { HARNESS_PROBE_TIMEOUT_MS } from '../constants.js';
|
|
7
7
|
import { planWrite, readTinyfishEntry, removeServer, writeMcpConfig, } from './mcp-json-config.js';
|
|
8
8
|
// pi expands `~` and `~/`; writing one literally would mkdir '~'.
|
|
9
9
|
function expandTilde(dir) {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { clearConnectContext, connectContextState } from './auth.js';
|
|
2
2
|
import { NATIVE_BY_HARNESS, openclawSkillUninstall } from './connect-clients.js';
|
|
3
3
|
import { ConnectInterruptedError, spawnRemoval } from './connect-runtime.js';
|
|
4
|
-
import { removeCursorMcpServer, planCursorWrite } from './cursor-config.js';
|
|
5
|
-
import { planOmpWrite, removeOmpMcpServer } from './omp-config.js';
|
|
6
|
-
import { piMcpPath, planPiWrite, removePiMcpServer } from './pi-config.js';
|
|
4
|
+
import { removeCursorMcpServer, planCursorWrite } from './config/cursor-config.js';
|
|
5
|
+
import { planOmpWrite, removeOmpMcpServer } from './config/omp-config.js';
|
|
6
|
+
import { piMcpPath, planPiWrite, removePiMcpServer } from './config/pi-config.js';
|
|
7
7
|
import { HERMES_KEY_VAR, hermesEnvPath, removeHermesKey, resolveHermesHome } from './hermes-env.js';
|
|
8
8
|
import { clearHermesWebBackends } from './hermes-plugin.js';
|
|
9
9
|
import { HARNESS_DISPLAY_NAMES as DISPLAY_NAMES } from './harness-detect.js';
|
|
@@ -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" | "claude-code", NativeMcpClient>;
|
|
107
|
+
export declare const NATIVE_BY_HARNESS: Map<"openclaw" | "omp" | "grok" | "cursor" | "codex" | "hermes" | "command-code" | "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';
|
|
@@ -7,7 +7,7 @@ import { ConnectInterruptedError, ConnectStepError, spawnStepError, } from './co
|
|
|
7
7
|
import { TINYFISH_ACCESS_MODE_HEADER, TINYFISH_API_KEY_VAR, TINYFISH_KEYLESS_ACCESS_MODE, } from './constants.js';
|
|
8
8
|
import { HARNESS_SPECS, NATIVE_HARNESSES, harnessSpec, } from './harness-spec.js';
|
|
9
9
|
import { HERMES_KEY_VAR, captureHermesKeyRestore, hermesEnvPath, resolveHermesHome, writeHermesKey, } from './hermes-env.js';
|
|
10
|
-
import { hermesConfigPath, readHermesEntry } from './hermes-config.js';
|
|
10
|
+
import { hermesConfigPath, readHermesEntry } from './config/hermes-config.js';
|
|
11
11
|
import { installHermesPlugin, removeHermesMcpEntry, setHermesWebBackends, writeHermesMcpEntry, } from './hermes-plugin.js';
|
|
12
12
|
const HERMES_SEED_TIMEOUT_MS = 120_000;
|
|
13
13
|
const OPENCLAW_SKILL = '@tinyfish/tinyfish';
|
|
@@ -215,6 +215,12 @@ function installHermesWebPlugin({ apiKey, verbose, seededHome, }) {
|
|
|
215
215
|
function launchOpencode(prompt) {
|
|
216
216
|
handOverTerminal('opencode', ['--prompt', prompt], 'OpenCode');
|
|
217
217
|
}
|
|
218
|
+
// A bare `cline "<prompt>"` is a one-shot act run that auto-approves every tool, so it would
|
|
219
|
+
// bill TinyFish calls unattended and cannot answer the walkthrough's questions. `-i` is the
|
|
220
|
+
// only interactive mode.
|
|
221
|
+
function launchCline(prompt) {
|
|
222
|
+
handOverTerminal('cline', ['-i', '--auto-approve', 'false', prompt], 'Cline');
|
|
223
|
+
}
|
|
218
224
|
/** "positional": flags before `tinyfish <url>`. "flag": flags after `--url`. */
|
|
219
225
|
function specAddArgs(spec, mcpUrl) {
|
|
220
226
|
const flags = spec.addFlags ?? [];
|
|
@@ -272,6 +278,7 @@ function specKeylessAddArgs(spec) {
|
|
|
272
278
|
}
|
|
273
279
|
/** Descriptor behaviour the spec cannot hold; every other field is spec data. */
|
|
274
280
|
const HARNESS_OVERRIDES = {
|
|
281
|
+
cline: { launchWalkthrough: launchCline },
|
|
275
282
|
codex: { detachedLaunch: launchCodexWalkthrough },
|
|
276
283
|
hermes: {
|
|
277
284
|
launchWalkthrough: launchHermesWalkthrough,
|
|
@@ -3,7 +3,7 @@ import { AuthMode } from './harness-detect.js';
|
|
|
3
3
|
import { errLine } from './output.js';
|
|
4
4
|
import { connectNativeMcpClient } from './connect-native.js';
|
|
5
5
|
import { connectOpenClaw } from './connect-openclaw.js';
|
|
6
|
-
import { connectConfigFileHarness, connectCursor } from './connect-config-file.js';
|
|
6
|
+
import { connectConfigFileHarness, connectCursor } from './config/connect-config-file.js';
|
|
7
7
|
import { finishSetupHint } from './connect-steps.js';
|
|
8
8
|
/** Total over the non-native harnesses: a new one fails to compile until wired here. */
|
|
9
9
|
const LAUNCH_OVERRIDES = {
|
|
@@ -4,7 +4,7 @@ import { AuthMode, Registered } from './harness-detect.js';
|
|
|
4
4
|
import { resolveHermesHome } from './hermes-env.js';
|
|
5
5
|
import { hermesWebBackendsOurs, readHermesPluginStatus, } from './hermes-plugin.js';
|
|
6
6
|
import { endpointOf, isDefaultEndpoint } from './mcp-endpoint.js';
|
|
7
|
-
import { PI_ADAPTER_INSTALL_COMMAND, piMcpAdapterState } from './pi-config.js';
|
|
7
|
+
import { PI_ADAPTER_INSTALL_COMMAND, piMcpAdapterState } from './config/pi-config.js';
|
|
8
8
|
import { boundedVersion } from './output.js';
|
|
9
9
|
import { verifyMcpHealth } from './verify.js';
|
|
10
10
|
// A green auth mode with a red auth call proves nothing, so the call's verdict gates the claim.
|
|
@@ -43,6 +43,7 @@ declare const doctorCheckSchema: z.ZodObject<{
|
|
|
43
43
|
"command-code": "command-code";
|
|
44
44
|
opencode: "opencode";
|
|
45
45
|
pi: "pi";
|
|
46
|
+
cline: "cline";
|
|
46
47
|
"claude-code": "claude-code";
|
|
47
48
|
}>>;
|
|
48
49
|
scope: z.ZodEnum<{
|
|
@@ -62,6 +63,7 @@ declare const doctorHarnessSchema: z.ZodObject<{
|
|
|
62
63
|
"command-code": "command-code";
|
|
63
64
|
opencode: "opencode";
|
|
64
65
|
pi: "pi";
|
|
66
|
+
cline: "cline";
|
|
65
67
|
"claude-code": "claude-code";
|
|
66
68
|
}>;
|
|
67
69
|
detected: z.ZodBoolean;
|
|
@@ -91,6 +93,7 @@ declare const doctorRepairSchema: z.ZodObject<{
|
|
|
91
93
|
"command-code": "command-code";
|
|
92
94
|
opencode: "opencode";
|
|
93
95
|
pi: "pi";
|
|
96
|
+
cline: "cline";
|
|
94
97
|
"claude-code": "claude-code";
|
|
95
98
|
}>>;
|
|
96
99
|
command: z.ZodString;
|
|
@@ -129,6 +132,7 @@ export declare const doctorReportSchema: z.ZodObject<{
|
|
|
129
132
|
"command-code": "command-code";
|
|
130
133
|
opencode: "opencode";
|
|
131
134
|
pi: "pi";
|
|
135
|
+
cline: "cline";
|
|
132
136
|
"claude-code": "claude-code";
|
|
133
137
|
}>>;
|
|
134
138
|
scope: z.ZodEnum<{
|
|
@@ -148,6 +152,7 @@ export declare const doctorReportSchema: z.ZodObject<{
|
|
|
148
152
|
"command-code": "command-code";
|
|
149
153
|
opencode: "opencode";
|
|
150
154
|
pi: "pi";
|
|
155
|
+
cline: "cline";
|
|
151
156
|
"claude-code": "claude-code";
|
|
152
157
|
}>;
|
|
153
158
|
detected: z.ZodBoolean;
|
|
@@ -177,6 +182,7 @@ export declare const doctorReportSchema: z.ZodObject<{
|
|
|
177
182
|
"command-code": "command-code";
|
|
178
183
|
opencode: "opencode";
|
|
179
184
|
pi: "pi";
|
|
185
|
+
cline: "cline";
|
|
180
186
|
"claude-code": "claude-code";
|
|
181
187
|
}>>;
|
|
182
188
|
command: z.ZodString;
|
|
@@ -3,7 +3,8 @@ import * as os from 'os';
|
|
|
3
3
|
import * as path from 'path';
|
|
4
4
|
import which from 'which';
|
|
5
5
|
import { ALL_HARNESSES, harnessSpec } from './harness-spec.js';
|
|
6
|
-
import {
|
|
6
|
+
import { clineConfigDir } from './config/cline-config.js';
|
|
7
|
+
import { piAgentDir, piBinaryIsPi } from './config/pi-config.js';
|
|
7
8
|
export { ALL_HARNESSES };
|
|
8
9
|
export const HARNESS_DISPLAY_NAMES = Object.fromEntries(ALL_HARNESSES.map((harness) => [harness, harnessSpec(harness).displayName]));
|
|
9
10
|
export const RELOAD_ACTION = Object.fromEntries(ALL_HARNESSES.map((harness) => [harness, harnessSpec(harness).reloadAction]));
|
|
@@ -33,8 +34,11 @@ export function commandOnPath(command) {
|
|
|
33
34
|
return false;
|
|
34
35
|
}
|
|
35
36
|
}
|
|
36
|
-
// PI_CODING_AGENT_DIR
|
|
37
|
-
const CONFIG_PATH_RESOLVERS = {
|
|
37
|
+
// PI_CODING_AGENT_DIR and CLINE_DIR move these dirs; a guessed path would be written unread.
|
|
38
|
+
const CONFIG_PATH_RESOLVERS = {
|
|
39
|
+
cline: clineConfigDir,
|
|
40
|
+
pi: piAgentDir,
|
|
41
|
+
};
|
|
38
42
|
export function harnessConfigPath(harness) {
|
|
39
43
|
const resolved = CONFIG_PATH_RESOLVERS[harness]?.();
|
|
40
44
|
return resolved ?? path.join(os.homedir(), harnessSpec(harness).configDir); // nosemgrep: path-join-resolve-traversal -- fixed dir names under os.homedir()
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Our skill-path slugs, inherited from the `skills` CLI era. */
|
|
2
|
-
export type SkillAgent = 'claude-code' | 'codex' | 'command-code' | 'cursor' | 'hermes-agent' | 'opencode' | 'pi';
|
|
2
|
+
export type SkillAgent = 'claude-code' | 'cline' | 'codex' | 'command-code' | 'cursor' | 'hermes-agent' | 'opencode' | 'pi';
|
|
3
3
|
export interface HarnessSupportCheck {
|
|
4
4
|
args: string[];
|
|
5
5
|
/** Missing → nothing can work; setup fails. */
|
|
@@ -114,6 +114,27 @@ export declare const HARNESS_SPECS: {
|
|
|
114
114
|
label: string;
|
|
115
115
|
}[];
|
|
116
116
|
};
|
|
117
|
+
cline: {
|
|
118
|
+
command: string;
|
|
119
|
+
displayName: string;
|
|
120
|
+
configDir: string;
|
|
121
|
+
reloadAction: string;
|
|
122
|
+
skillAgent: "cline";
|
|
123
|
+
nonInteractiveAdd: true;
|
|
124
|
+
keyRequired: true;
|
|
125
|
+
supportCheck: {
|
|
126
|
+
args: string[];
|
|
127
|
+
patterns: RegExp[];
|
|
128
|
+
keyAuthPattern: RegExp;
|
|
129
|
+
unavailableMessage: string;
|
|
130
|
+
};
|
|
131
|
+
urlStyle: "positional";
|
|
132
|
+
addFlags: string[];
|
|
133
|
+
header: {
|
|
134
|
+
name: string;
|
|
135
|
+
sep: ": ";
|
|
136
|
+
};
|
|
137
|
+
};
|
|
117
138
|
codex: {
|
|
118
139
|
command: string;
|
|
119
140
|
displayName: string;
|
|
@@ -286,4 +307,4 @@ export type NonNativeHarness = {
|
|
|
286
307
|
[K in Harness]: 'urlStyle' extends keyof (typeof HARNESS_SPECS)[K] ? never : 'keyRequired' extends keyof (typeof HARNESS_SPECS)[K] ? never : K;
|
|
287
308
|
}[Harness];
|
|
288
309
|
/** Native MCP harnesses generate an add; key-only ones generate only the keyed form. */
|
|
289
|
-
export declare const NATIVE_HARNESSES: ("openclaw" | "omp" | "grok" | "cursor" | "codex" | "hermes" | "command-code" | "opencode" | "pi" | "claude-code")[];
|
|
310
|
+
export declare const NATIVE_HARNESSES: ("openclaw" | "omp" | "grok" | "cursor" | "codex" | "hermes" | "command-code" | "opencode" | "pi" | "cline" | "claude-code")[];
|
package/dist/lib/harness-spec.js
CHANGED
|
@@ -46,6 +46,9 @@ const OPENCODE_MODEL_NOTE = 'Note: TinyFish runs on tool calls, so OpenCode need
|
|
|
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
48
|
const HERMES_RESTART_NOTE = 'Restart your Hermes session to pick up TinyFish — Hermes discovers MCP servers at startup.';
|
|
49
|
+
const CLINE_MCP_ADD_UNAVAILABLE_MESSAGE = 'Could not confirm this Cline installation supports non-interactive MCP setup: `cline mcp add ' +
|
|
50
|
+
'--help` did not list `--transport` and `--yes`. Update Cline with `cline --update` and retry.' +
|
|
51
|
+
SUPPORT_CHECK_DEBUG_HINT;
|
|
49
52
|
const COMMAND_CODE_ADD_JSON_UNAVAILABLE_MESSAGE = 'Could not confirm this Command Code installation supports keyed MCP setup: `commandcode mcp ' +
|
|
50
53
|
'--help` did not list `add-json`. Run `commandcode update` and retry.' +
|
|
51
54
|
SUPPORT_CHECK_DEBUG_HINT;
|
|
@@ -87,6 +90,26 @@ export const HARNESS_SPECS = {
|
|
|
87
90
|
},
|
|
88
91
|
],
|
|
89
92
|
},
|
|
93
|
+
cline: {
|
|
94
|
+
command: 'cline',
|
|
95
|
+
displayName: 'Cline',
|
|
96
|
+
configDir: '.cline',
|
|
97
|
+
reloadAction: 'restart it',
|
|
98
|
+
skillAgent: 'cline',
|
|
99
|
+
nonInteractiveAdd: true,
|
|
100
|
+
// Key-only by decision: TinyFish never sends CLI-harness users through OAuth.
|
|
101
|
+
keyRequired: true,
|
|
102
|
+
supportCheck: {
|
|
103
|
+
args: ['mcp', 'add', '--help'],
|
|
104
|
+
// Without `--yes` the add opens a wizard and exits non-zero off a TTY.
|
|
105
|
+
patterns: [/--transport(?:[\s<=]|$)/m, /--yes(?:[\s<=]|$)/m],
|
|
106
|
+
keyAuthPattern: HEADER_FLAG,
|
|
107
|
+
unavailableMessage: CLINE_MCP_ADD_UNAVAILABLE_MESSAGE,
|
|
108
|
+
},
|
|
109
|
+
urlStyle: 'positional',
|
|
110
|
+
addFlags: ['--transport', 'http', '--yes'],
|
|
111
|
+
header: { name: 'X-API-Key', sep: ': ' },
|
|
112
|
+
},
|
|
90
113
|
codex: {
|
|
91
114
|
command: 'codex',
|
|
92
115
|
displayName: 'Codex',
|
package/dist/lib/harness.js
CHANGED
|
@@ -32,6 +32,11 @@ const HARNESS_FINGERPRINTS = [
|
|
|
32
32
|
{ name: 'opencode', matches: (env) => Boolean(env['OPENCODE']) },
|
|
33
33
|
// Observed: pi launched from Claude Code inherits CLAUDECODE=1.
|
|
34
34
|
{ name: 'pi', matches: (env) => env['PI_CODING_AGENT'] === 'true' },
|
|
35
|
+
// Runtime-only markers; users export CLINE_API_KEY, so no prefix match.
|
|
36
|
+
{
|
|
37
|
+
name: 'cline',
|
|
38
|
+
matches: (env) => Boolean(env['CLINE_CONNECTOR_CLI_LAUNCH'] || env['CLINE_WRAPPER_PATH']),
|
|
39
|
+
},
|
|
35
40
|
{
|
|
36
41
|
name: 'claude-code',
|
|
37
42
|
matches: (env) => env['CLAUDECODE'] === '1' || Boolean(env['CLAUDE_CODE_ENTRYPOINT']),
|
|
@@ -7,7 +7,7 @@ import { z } from 'zod';
|
|
|
7
7
|
import { capturedOutput, replay, SKILL_INSTALL_TIMEOUT_MS, STEP_MAX_BUFFER, } from './cli-install.js';
|
|
8
8
|
import { commandNotFound, ConnectStepError, spawnStepError, throwIfInterrupted, } from './connect-runtime.js';
|
|
9
9
|
import { HARNESS_PROBE_TIMEOUT_MS, TINYFISH_ACCESS_MODE_HEADER, TINYFISH_KEYLESS_ACCESS_MODE, } from './constants.js';
|
|
10
|
-
import { HERMES_HEADER_TEMPLATE, HERMES_MCP_SERVER_KEY } from './hermes-config.js';
|
|
10
|
+
import { HERMES_HEADER_TEMPLATE, HERMES_MCP_SERVER_KEY } from './config/hermes-config.js';
|
|
11
11
|
import { errLine, parseJson } from './output.js';
|
|
12
12
|
// Resolved at connect time, so plugin releases need no CLI release.
|
|
13
13
|
export const HERMES_PLUGIN_PACKAGE = '@tiny-fish/hermes';
|
|
@@ -6,12 +6,13 @@ import { loadConfig, matchesCliKey } from './auth.js';
|
|
|
6
6
|
import { NATIVE_BY_HARNESS } from './connect-clients.js';
|
|
7
7
|
import { HARNESS_PROBE_TIMEOUT_MS } from './constants.js';
|
|
8
8
|
import { errLine } from './output.js';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
9
|
+
import { readClineTinyfishEntry } from './config/cline-config.js';
|
|
10
|
+
import { readCommandCodeTinyfishEntry } from './config/command-code-config.js';
|
|
11
|
+
import { readCursorTinyfishEntry } from './config/cursor-config.js';
|
|
12
|
+
import { readOmpTinyfishEntry } from './config/omp-config.js';
|
|
13
|
+
import { readPiTinyfishEntry } from './config/pi-config.js';
|
|
13
14
|
import { readHermesKey, resolveHermesHome } from './hermes-env.js';
|
|
14
|
-
import { readHermesEntry } from './hermes-config.js';
|
|
15
|
+
import { readHermesEntry } from './config/hermes-config.js';
|
|
15
16
|
import { detectInstalledHarnesses, harnessConfigPath, harnessDisplayPath, AuthMode, Registered, } from './harness-detect.js';
|
|
16
17
|
const NOT_REGISTERED = Object.freeze({
|
|
17
18
|
registered: Registered.No,
|
|
@@ -279,6 +280,21 @@ function probeCodex() {
|
|
|
279
280
|
...(envVar ? envKeyVerdict(envVar) : {}),
|
|
280
281
|
};
|
|
281
282
|
}
|
|
283
|
+
// No `cline mcp list`, so the settings file is the only registration evidence.
|
|
284
|
+
function probeCline() {
|
|
285
|
+
const entry = readClineTinyfishEntry();
|
|
286
|
+
if (entry.error) {
|
|
287
|
+
return unverified('cline_mcp_settings.json exists but could not be read or parsed');
|
|
288
|
+
}
|
|
289
|
+
if (!entry.present)
|
|
290
|
+
return NOT_REGISTERED;
|
|
291
|
+
return {
|
|
292
|
+
registered: Registered.Yes,
|
|
293
|
+
authMode: entry.hasApiKeyHeader ? AuthMode.ApiKey : AuthMode.Unknown,
|
|
294
|
+
...(entry.url ? { registeredUrl: entry.url } : {}),
|
|
295
|
+
...(entry.keyMatchesCliKey ? { keyMatchesCliKey: true } : {}),
|
|
296
|
+
};
|
|
297
|
+
}
|
|
282
298
|
function probeCommandCode() {
|
|
283
299
|
const entry = readCommandCodeTinyfishEntry();
|
|
284
300
|
if (entry.error)
|
|
@@ -523,6 +539,7 @@ function probeGrok() {
|
|
|
523
539
|
}
|
|
524
540
|
const PROBES = {
|
|
525
541
|
'claude-code': () => fromMcpGet('claude'),
|
|
542
|
+
cline: probeCline,
|
|
526
543
|
codex: probeCodex,
|
|
527
544
|
'command-code': probeCommandCode,
|
|
528
545
|
cursor: probeCursor,
|
|
@@ -31,6 +31,7 @@ declare const harnessResultSchema: z.ZodObject<{
|
|
|
31
31
|
"command-code": "command-code";
|
|
32
32
|
opencode: "opencode";
|
|
33
33
|
pi: "pi";
|
|
34
|
+
cline: "cline";
|
|
34
35
|
"claude-code": "claude-code";
|
|
35
36
|
}>;
|
|
36
37
|
detected: z.ZodBoolean;
|
|
@@ -67,6 +68,7 @@ export declare const setupCompletedPayloadSchema: z.ZodObject<{
|
|
|
67
68
|
"command-code": "command-code";
|
|
68
69
|
opencode: "opencode";
|
|
69
70
|
pi: "pi";
|
|
71
|
+
cline: "cline";
|
|
70
72
|
"claude-code": "claude-code";
|
|
71
73
|
}>;
|
|
72
74
|
detected: z.ZodBoolean;
|
|
@@ -130,6 +132,7 @@ declare const doctorCompletedPayloadSchema: z.ZodObject<{
|
|
|
130
132
|
"command-code": "command-code";
|
|
131
133
|
opencode: "opencode";
|
|
132
134
|
pi: "pi";
|
|
135
|
+
cline: "cline";
|
|
133
136
|
"claude-code": "claude-code";
|
|
134
137
|
all: "all";
|
|
135
138
|
}>;
|
|
@@ -145,6 +148,7 @@ declare const doctorCompletedPayloadSchema: z.ZodObject<{
|
|
|
145
148
|
"command-code": "command-code";
|
|
146
149
|
opencode: "opencode";
|
|
147
150
|
pi: "pi";
|
|
151
|
+
cline: "cline";
|
|
148
152
|
"claude-code": "claude-code";
|
|
149
153
|
}>>;
|
|
150
154
|
harnesses: z.ZodArray<z.ZodObject<{
|
|
@@ -158,6 +162,7 @@ declare const doctorCompletedPayloadSchema: z.ZodObject<{
|
|
|
158
162
|
"command-code": "command-code";
|
|
159
163
|
opencode: "opencode";
|
|
160
164
|
pi: "pi";
|
|
165
|
+
cline: "cline";
|
|
161
166
|
"claude-code": "claude-code";
|
|
162
167
|
}>;
|
|
163
168
|
detected: z.ZodBoolean;
|
|
@@ -200,6 +205,7 @@ declare const doctorCouldNotRunPayloadSchema: z.ZodObject<{
|
|
|
200
205
|
"command-code": "command-code";
|
|
201
206
|
opencode: "opencode";
|
|
202
207
|
pi: "pi";
|
|
208
|
+
cline: "cline";
|
|
203
209
|
"claude-code": "claude-code";
|
|
204
210
|
all: "all";
|
|
205
211
|
}>;
|
|
@@ -238,6 +244,7 @@ declare const doctorPayloadSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
238
244
|
"command-code": "command-code";
|
|
239
245
|
opencode: "opencode";
|
|
240
246
|
pi: "pi";
|
|
247
|
+
cline: "cline";
|
|
241
248
|
"claude-code": "claude-code";
|
|
242
249
|
all: "all";
|
|
243
250
|
}>;
|
|
@@ -253,6 +260,7 @@ declare const doctorPayloadSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
253
260
|
"command-code": "command-code";
|
|
254
261
|
opencode: "opencode";
|
|
255
262
|
pi: "pi";
|
|
263
|
+
cline: "cline";
|
|
256
264
|
"claude-code": "claude-code";
|
|
257
265
|
}>>;
|
|
258
266
|
harnesses: z.ZodArray<z.ZodObject<{
|
|
@@ -266,6 +274,7 @@ declare const doctorPayloadSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
266
274
|
"command-code": "command-code";
|
|
267
275
|
opencode: "opencode";
|
|
268
276
|
pi: "pi";
|
|
277
|
+
cline: "cline";
|
|
269
278
|
"claude-code": "claude-code";
|
|
270
279
|
}>;
|
|
271
280
|
detected: z.ZodBoolean;
|
|
@@ -307,6 +316,7 @@ declare const doctorPayloadSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
307
316
|
"command-code": "command-code";
|
|
308
317
|
opencode: "opencode";
|
|
309
318
|
pi: "pi";
|
|
319
|
+
cline: "cline";
|
|
310
320
|
"claude-code": "claude-code";
|
|
311
321
|
all: "all";
|
|
312
322
|
}>;
|
package/dist/lib/skill-paths.js
CHANGED
|
@@ -14,6 +14,7 @@ const SKILL_DIR_BY_AGENT = {
|
|
|
14
14
|
'claude-code': () => path.join(agentHome('CLAUDE_CONFIG_DIR', '.claude'), 'skills'),
|
|
15
15
|
// The env value, not resolveHermesHome(): skills@1.5.15 reads $HERMES_HOME directly.
|
|
16
16
|
'hermes-agent': () => path.join(agentHome('HERMES_HOME', '.hermes'), 'skills'),
|
|
17
|
+
cline: canonicalSkillsDir,
|
|
17
18
|
codex: canonicalSkillsDir,
|
|
18
19
|
// Not the canonical dir: `skills` writes this one under the harness's own config dir.
|
|
19
20
|
'command-code': () => path.join(os.homedir(), '.commandcode', 'skills'), // nosemgrep: path-join-resolve-traversal -- fixed dir names under os.homedir()
|
package/package.json
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|