@ai-sdk/harness-cline 1.0.19 → 1.0.21

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/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # @ai-sdk/harness-cline
2
2
 
3
+ ## 1.0.21
4
+
5
+ ### Patch Changes
6
+
7
+ - 8961fde: feat(harness): allow changing `model` between turns via call options
8
+ - Updated dependencies [8961fde]
9
+ - Updated dependencies [eb59f2a]
10
+ - Updated dependencies [aa45741]
11
+ - @ai-sdk/harness@1.0.94
12
+ - @ai-sdk/provider@4.0.9
13
+ - @ai-sdk/provider-utils@5.0.34
14
+
15
+ ## 1.0.20
16
+
17
+ ### Patch Changes
18
+
19
+ - 7608210: feat(harness): add `model` parameter to `HarnessAgent` instead of having each harness adapter support it on their own constructor functions
20
+ - f7bd978: chore(harness): update underlying adapter SDKs to their latest versions
21
+ - 14d4fc0: feat(harness): allow changing harness settings between turns via `prepareCall()` support on `HarnessAgent`
22
+ - 5021768: fix(harness-cline): use native `skills` tool instead of manual file-based workaround
23
+ - Updated dependencies [cc9f6ce]
24
+ - Updated dependencies [7608210]
25
+ - Updated dependencies [6f8a2d7]
26
+ - Updated dependencies [14d4fc0]
27
+ - Updated dependencies [90192f1]
28
+ - @ai-sdk/harness@1.0.93
29
+ - @ai-sdk/provider-utils@5.0.33
30
+
3
31
  ## 1.0.19
4
32
 
5
33
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -26,6 +26,8 @@ type ClineHarnessSettings = {
26
26
  /**
27
27
  * Model id for the configured provider. When omitted, Cline selects the
28
28
  * provider's default model.
29
+ *
30
+ * @deprecated Use `model` on `HarnessAgent` instead.
29
31
  */
30
32
  readonly modelId?: string;
31
33
  /**
@@ -80,6 +82,7 @@ declare const CLINE_BUILTIN_TOOLS: {
80
82
  limit?: number | undefined;
81
83
  }, unknown>;
82
84
  readonly ls: HarnessV1BuiltinTool;
85
+ readonly skills: HarnessV1BuiltinTool;
83
86
  };
84
87
  declare function createCline(settings?: ClineHarnessSettings): HarnessV1<typeof CLINE_BUILTIN_TOOLS>;
85
88
 
@@ -122,6 +125,7 @@ declare const cline: _ai_sdk_harness.HarnessV1<{
122
125
  limit?: number | undefined;
123
126
  }, unknown>;
124
127
  readonly ls: _ai_sdk_harness.HarnessV1BuiltinTool;
128
+ readonly skills: _ai_sdk_harness.HarnessV1BuiltinTool;
125
129
  }>;
126
130
 
127
131
  export { type ClineAuthenticationMode, type ClineHarnessSettings, VERSION, cline, createCline };
package/dist/index.js CHANGED
@@ -241,7 +241,6 @@ import { shellQuote } from "@ai-sdk/harness/utils";
241
241
  function createClineRemoteOps(options) {
242
242
  const { sandbox, workDir } = options;
243
243
  const normalizedWorkDir = path2.posix.normalize(workDir);
244
- const readableRoots = options.readableRoots?.map((root) => path2.posix.normalize(root.sandboxDir)) ?? [];
245
244
  const isInsidePath = ({
246
245
  parent,
247
246
  candidate
@@ -256,25 +255,10 @@ function createClineRemoteOps(options) {
256
255
  }
257
256
  return normalized;
258
257
  };
259
- const assertReadablePath = (inputPath) => {
260
- const normalized = path2.posix.normalize(inputPath);
261
- if (!isInsidePath({ parent: normalizedWorkDir, candidate: normalized }) && !readableRoots.some(
262
- (parent) => isInsidePath({ parent, candidate: normalized })
263
- )) {
264
- throw new Error(`Cline path escapes the readable roots: ${inputPath}`);
265
- }
266
- return normalized;
267
- };
268
258
  const resolvePath = (inputPath) => {
269
259
  const resolved = path2.posix.isAbsolute(inputPath) ? path2.posix.normalize(inputPath) : path2.posix.normalize(path2.posix.join(normalizedWorkDir, inputPath));
270
260
  return assertWorkspacePath(resolved);
271
261
  };
272
- const resolveReadablePath = (inputPath) => {
273
- if (!path2.posix.isAbsolute(inputPath)) {
274
- return resolvePath(inputPath);
275
- }
276
- return assertReadablePath(inputPath);
277
- };
278
262
  const runShell = async ({
279
263
  command,
280
264
  signal
@@ -319,7 +303,7 @@ function createClineRemoteOps(options) {
319
303
  remotePath,
320
304
  inputPath,
321
305
  missingMessage
322
- }) => assertReadablePath(
306
+ }) => assertWorkspacePath(
323
307
  await resolveExistingSandboxPath({
324
308
  remotePath,
325
309
  inputPath,
@@ -352,7 +336,7 @@ function createClineRemoteOps(options) {
352
336
  return assertWorkspacePath(resolvedPath);
353
337
  };
354
338
  const readFile = async (inputPath) => {
355
- const remotePath = resolveReadablePath(inputPath);
339
+ const remotePath = resolvePath(inputPath);
356
340
  const resolved = await resolveReadableSandboxPath({
357
341
  remotePath,
358
342
  inputPath,
@@ -420,7 +404,7 @@ function createClineRemoteOps(options) {
420
404
  },
421
405
  async grep(pattern, input = {}) {
422
406
  const inputPath = input.path ?? ".";
423
- const remotePath = resolveReadablePath(inputPath);
407
+ const remotePath = resolvePath(inputPath);
424
408
  const target = await resolveReadableSandboxPath({
425
409
  remotePath,
426
410
  inputPath
@@ -452,7 +436,7 @@ function createClineRemoteOps(options) {
452
436
  return output || "No matches found";
453
437
  },
454
438
  async glob(pattern, inputPath = ".", limit = 1e3) {
455
- const remotePath = resolveReadablePath(inputPath);
439
+ const remotePath = resolvePath(inputPath);
456
440
  const target = await resolveReadableSandboxPath({
457
441
  remotePath,
458
442
  inputPath
@@ -483,7 +467,7 @@ function createClineRemoteOps(options) {
483
467
  ).slice(0, limit);
484
468
  },
485
469
  async ls(inputPath = ".", limit = 500) {
486
- const remotePath = resolveReadablePath(inputPath);
470
+ const remotePath = resolvePath(inputPath);
487
471
  const target = await resolveReadableSandboxPath({
488
472
  remotePath,
489
473
  inputPath
@@ -516,33 +500,123 @@ function createClineRemoteOps(options) {
516
500
 
517
501
  // src/cline-skills.ts
518
502
  import path3 from "path";
519
- import { writeSkills } from "@ai-sdk/harness/utils";
520
- async function writeClineSkills({
521
- sandbox,
522
- sandboxHomeDir,
523
- skills,
524
- abortSignal
503
+ import { createDefaultTools } from "@cline/core";
504
+ var CLINE_SKILL_NAME_PATTERN = /^[A-Za-z0-9._-]+$/;
505
+ function createClineSkillsRuntime({
506
+ skills
525
507
  }) {
526
- if (skills.length === 0) return void 0;
527
- const skillRootDir = path3.posix.join(sandboxHomeDir, ".agents", "skills");
528
- await writeSkills({
529
- sandbox,
530
- rootDir: skillRootDir,
531
- skills,
532
- ...abortSignal ? { abortSignal } : {}
533
- });
534
- return skillRootDir;
508
+ const projectedSkills = projectClineSkills({ skills });
509
+ const signature = JSON.stringify(projectedSkills);
510
+ if (projectedSkills.length === 0) {
511
+ return { signature };
512
+ }
513
+ const executor = async (...executorArguments) => {
514
+ const [requestedSkill, args] = executorArguments;
515
+ const skill = projectedSkills.find(
516
+ (candidate) => candidate.id === normalizeSkillToken(requestedSkill)
517
+ );
518
+ if (skill == null) {
519
+ const availableSkills = projectedSkills.map((skill2) => skill2.name);
520
+ return availableSkills.length > 0 ? `Skill "${requestedSkill}" not found. Available skills: ${availableSkills.join(", ")}` : "No skills are currently available.";
521
+ }
522
+ return renderSkillInstructions({ skill, args });
523
+ };
524
+ executor.configuredSkills = projectedSkills.map((skill) => ({
525
+ id: skill.id,
526
+ name: skill.name,
527
+ description: skill.description,
528
+ disabled: false
529
+ }));
530
+ const tool2 = createDefaultTools({
531
+ executors: { skills: executor },
532
+ enableReadFiles: false,
533
+ enableSearch: false,
534
+ enableBash: false,
535
+ enableWebFetch: false,
536
+ enableApplyPatch: false,
537
+ enableEditor: false,
538
+ enableSkills: true,
539
+ enableAskQuestion: false,
540
+ enableSubmitAndExit: false
541
+ }).find((tool3) => tool3.name === "skills");
542
+ if (tool2 == null) {
543
+ throw new Error("Cline did not create its native skills tool.");
544
+ }
545
+ return { signature, tool: tool2 };
546
+ }
547
+ function projectClineSkills({
548
+ skills
549
+ }) {
550
+ const names = /* @__PURE__ */ new Set();
551
+ const ids = /* @__PURE__ */ new Set();
552
+ return skills.map((skill) => {
553
+ if (!CLINE_SKILL_NAME_PATTERN.test(skill.name) || skill.name === "." || skill.name === "..") {
554
+ throw new Error(`Invalid Cline skill name: ${skill.name}`);
555
+ }
556
+ if (names.has(skill.name)) {
557
+ throw new Error(`Duplicate Cline skill name: ${skill.name}`);
558
+ }
559
+ names.add(skill.name);
560
+ const id = normalizeSkillToken(skill.name);
561
+ if (ids.has(id)) {
562
+ throw new Error(`Duplicate Cline skill identifier: ${id}`);
563
+ }
564
+ ids.add(id);
565
+ return {
566
+ id,
567
+ name: skill.name,
568
+ description: skill.description,
569
+ content: skill.content,
570
+ files: (skill.files ?? []).map((file) => ({
571
+ path: normalizeSkillFilePath({
572
+ skillName: skill.name,
573
+ filePath: file.path
574
+ }),
575
+ content: file.content
576
+ })).sort(
577
+ (left, right) => left.path.localeCompare(right.path) || left.content.localeCompare(right.content)
578
+ )
579
+ };
580
+ }).sort((left, right) => left.name.localeCompare(right.name));
581
+ }
582
+ function normalizeSkillToken(skill) {
583
+ return skill.trim().replace(/^\/+/, "").toLowerCase();
584
+ }
585
+ function normalizeSkillFilePath({
586
+ skillName,
587
+ filePath
588
+ }) {
589
+ const normalized = path3.posix.normalize(filePath);
590
+ if (normalized === "" || normalized === "." || normalized.startsWith("../") || normalized.includes("/../") || normalized.endsWith("/..") || path3.posix.isAbsolute(normalized)) {
591
+ throw new Error(
592
+ `Invalid Cline skill file path for ${skillName}: ${filePath}`
593
+ );
594
+ }
595
+ return normalized;
535
596
  }
536
- function renderSkillsPromptSection(skills, skillRootDir) {
537
- const entries = skills.map(
538
- (skill) => `- ${skill.name}: ${skill.description}
539
- SKILL.md: ${path3.posix.join(
540
- skillRootDir,
541
- skill.name,
542
- "SKILL.md"
543
- )}`
544
- ).join("\n");
545
- return "## Skills\n\nThe following skills are available. When a task matches a skill, read its SKILL.md with the `read` tool and follow its instructions.\n\n" + entries;
597
+ function renderSkillInstructions({
598
+ skill,
599
+ args
600
+ }) {
601
+ const trimmedArgs = args?.trim();
602
+ const argsTag = trimmedArgs ? `
603
+ <command-args>${trimmedArgs}</command-args>` : "";
604
+ const description = skill.description.trim() ? `Description: ${skill.description.trim()}
605
+
606
+ ` : "";
607
+ const files = skill.files.length === 0 ? "" : `
608
+
609
+ <skill-files>
610
+ ${skill.files.map(
611
+ (file) => `<skill-file path=${JSON.stringify(file.path)}>
612
+ ${file.content}
613
+ </skill-file>`
614
+ ).join("\n")}
615
+ </skill-files>`;
616
+ return `<command-name>${skill.name}</command-name>${argsTag}
617
+ <command-instructions>
618
+ ${description}${skill.content}${files}
619
+ </command-instructions>`;
546
620
  }
547
621
 
548
622
  // src/cline-tools.ts
@@ -657,7 +731,8 @@ var CLINE_NATIVE_BUILTIN_NAMES = [
657
731
  "bash",
658
732
  "grep",
659
733
  "glob",
660
- "ls"
734
+ "ls",
735
+ "skills"
661
736
  ];
662
737
  var CLINE_NATIVE_TOOL_KINDS = {
663
738
  read: "readonly",
@@ -666,7 +741,8 @@ var CLINE_NATIVE_TOOL_KINDS = {
666
741
  bash: "bash",
667
742
  grep: "readonly",
668
743
  glob: "readonly",
669
- ls: "readonly"
744
+ ls: "readonly",
745
+ skills: "readonly"
670
746
  };
671
747
  function isClineBuiltinToolName(name) {
672
748
  return CLINE_NATIVE_BUILTIN_NAMES.includes(name);
@@ -1110,7 +1186,7 @@ function clineBuiltinToolRequiresApproval(input) {
1110
1186
  return input.kind === "edit" || input.kind === "bash";
1111
1187
  }
1112
1188
  function buildSystemPrompt(input) {
1113
- const sections = [
1189
+ return [
1114
1190
  "You are Cline, an autonomous coding agent operating inside a sandboxed workspace.",
1115
1191
  `## Workspace
1116
1192
 
@@ -1118,11 +1194,7 @@ The workspace root is \`${input.sessionWorkDir}\`. All relative file paths and s
1118
1194
  `## Sandbox
1119
1195
 
1120
1196
  ${input.sandboxDescription}`
1121
- ];
1122
- if (input.skillsSection) {
1123
- sections.push(input.skillsSection);
1124
- }
1125
- return sections.join("\n\n");
1197
+ ].join("\n\n");
1126
1198
  }
1127
1199
  function toClineBackendProviderBaseUrl(baseUrl) {
1128
1200
  return `${baseUrl.replace(/\/+$/, "")}/api/v1`;
@@ -1133,7 +1205,8 @@ function toAiGatewayProviderBaseUrl(baseUrl) {
1133
1205
  }
1134
1206
  function createClineAgentModel({
1135
1207
  settings,
1136
- clientApp
1208
+ clientApp,
1209
+ modelId
1137
1210
  }) {
1138
1211
  const gatewayBaseUrl = settings.authEnv.AI_GATEWAY_BASE_URL;
1139
1212
  const isAiGateway = gatewayBaseUrl != null;
@@ -1158,7 +1231,7 @@ function createClineAgentModel({
1158
1231
  });
1159
1232
  const modelSelection = {
1160
1233
  providerId,
1161
- ...settings.modelId ? { modelId: settings.modelId } : {}
1234
+ ...modelId ? { modelId } : {}
1162
1235
  };
1163
1236
  const modelOptions = settings.reasoningEffort === "none" ? { reasoning: { enabled: false } } : {
1164
1237
  reasoning: {
@@ -1199,28 +1272,9 @@ async function createClineSession(input) {
1199
1272
  const activeBuiltinNames = resolveActiveClineBuiltinNames(
1200
1273
  input.builtinToolFiltering
1201
1274
  );
1202
- let skillsSection;
1203
- let skillRootDir;
1204
- if (input.skills.length > 0) {
1205
- skillRootDir = await writeClineSkills({
1206
- sandbox: toolSafeSandboxSession,
1207
- sandboxHomeDir,
1208
- skills: input.skills,
1209
- ...input.abortSignal ? { abortSignal: input.abortSignal } : {}
1210
- });
1211
- if (skillRootDir) {
1212
- skillsSection = renderSkillsPromptSection(input.skills, skillRootDir);
1213
- }
1214
- }
1215
1275
  const ops = createClineRemoteOps({
1216
1276
  sandbox: toolSafeSandboxSession,
1217
- workDir: input.sessionWorkDir,
1218
- ...skillRootDir ? { readableRoots: [{ sandboxDir: skillRootDir }] } : {}
1219
- });
1220
- const baseSystemPrompt = buildSystemPrompt({
1221
- sessionWorkDir: input.sessionWorkDir,
1222
- sandboxDescription: toolSafeSandboxSession.description,
1223
- ...skillsSection ? { skillsSection } : {}
1277
+ workDir: input.sessionWorkDir
1224
1278
  });
1225
1279
  let currentMessages = [];
1226
1280
  if (input.isResume && input.resumeHistoryFileName) {
@@ -1237,13 +1291,15 @@ async function createClineSession(input) {
1237
1291
  const mcpRuntime = await createClineMcpRuntime({
1238
1292
  mcpServers: input.settings.mcpServers
1239
1293
  });
1240
- const { model, providerId } = createClineAgentModel({
1294
+ let activeModelId = input.settings.modelId;
1295
+ let agentModel = createClineAgentModel({
1241
1296
  settings: input.settings,
1242
- clientApp: input.clientApp
1297
+ clientApp: input.clientApp,
1298
+ modelId: activeModelId
1243
1299
  });
1244
1300
  let agent;
1245
1301
  let unsubscribe;
1246
- let lastToolsSignature;
1302
+ let lastAgentConfigurationSignature;
1247
1303
  let agentHasRun = false;
1248
1304
  let stopped = false;
1249
1305
  let suspending = false;
@@ -1306,22 +1362,29 @@ async function createClineSession(input) {
1306
1362
  unsubscribe?.();
1307
1363
  unsubscribe = void 0;
1308
1364
  agent = new Agent({
1309
- model,
1310
- ...input.settings.modelId ? {
1365
+ model: agentModel.model,
1366
+ ...activeModelId ? {
1311
1367
  messageModelInfo: {
1312
- id: input.settings.modelId,
1313
- provider: providerId
1368
+ id: activeModelId,
1369
+ provider: agentModel.providerId
1314
1370
  }
1315
1371
  } : {},
1316
1372
  sessionId: input.sessionId,
1317
- systemPrompt: rebuildInput.instructions ? `${baseSystemPrompt}
1373
+ systemPrompt: (() => {
1374
+ const baseSystemPrompt = buildSystemPrompt({
1375
+ sessionWorkDir: input.sessionWorkDir,
1376
+ sandboxDescription: toolSafeSandboxSession.description
1377
+ });
1378
+ return rebuildInput.instructions ? `${baseSystemPrompt}
1318
1379
 
1319
- ${rebuildInput.instructions}` : baseSystemPrompt,
1380
+ ${rebuildInput.instructions}` : baseSystemPrompt;
1381
+ })(),
1320
1382
  initialMessages: currentMessages,
1321
1383
  toolExecution: "parallel",
1322
1384
  ...input.settings.maxIterations !== void 0 ? { maxIterations: input.settings.maxIterations } : {},
1323
1385
  tools: [
1324
1386
  ...buildBuiltinAgentTools({ ops, activeNames: activeBuiltinNames }),
1387
+ ...rebuildInput.skillsRuntime.tool && activeBuiltinNames.includes("skills") ? [rebuildInput.skillsRuntime.tool] : [],
1325
1388
  ...mcpRuntime.tools,
1326
1389
  ...buildUserAgentTools({
1327
1390
  specs: rebuildInput.userTools,
@@ -1438,46 +1501,64 @@ ${rebuildInput.instructions}` : baseSystemPrompt,
1438
1501
  done: controlInput.done
1439
1502
  };
1440
1503
  }
1441
- function runTurn(turnOpts) {
1504
+ async function runTurn(turnOpts) {
1442
1505
  if (stopped) {
1443
1506
  throw new Error("Cline session has been stopped.");
1444
1507
  }
1445
1508
  const userTools = turnOpts.tools;
1509
+ const skillsRuntime = createClineSkillsRuntime({
1510
+ skills: turnOpts.skills
1511
+ });
1446
1512
  if (turnOpts.responseFormat?.type === "json" && turnOpts.responseFormat.schema == null) {
1447
1513
  throw new HarnessCapabilityUnsupportedError2({
1448
1514
  message: "Harness 'cline' requires a JSON schema for structured output.",
1449
1515
  harnessId: HARNESS_ID2
1450
1516
  });
1451
1517
  }
1452
- if (turnOpts.responseFormat?.type === "json" && providerId === "openai-codex-cli") {
1518
+ if (turnOpts.responseFormat?.type === "json" && agentModel.providerId === "openai-codex-cli") {
1453
1519
  throw new HarnessCapabilityUnsupportedError2({
1454
1520
  message: "Harness 'cline' cannot require structured output with the openai-codex-cli provider because that provider does not expose external tools.",
1455
1521
  harnessId: HARNESS_ID2
1456
1522
  });
1457
1523
  }
1524
+ if (turnOpts.model != null && turnOpts.model !== activeModelId) {
1525
+ activeModelId = turnOpts.model;
1526
+ agentModel = createClineAgentModel({
1527
+ settings: input.settings,
1528
+ clientApp: input.clientApp,
1529
+ modelId: activeModelId
1530
+ });
1531
+ }
1458
1532
  const signature = JSON.stringify({
1459
- tools: userTools.map((t) => t.name).sort(),
1460
- responseFormat: turnOpts.responseFormat
1533
+ modelId: activeModelId,
1534
+ tools: userTools,
1535
+ responseFormat: turnOpts.responseFormat,
1536
+ instructions: turnOpts.instructions,
1537
+ skills: skillsRuntime.signature
1461
1538
  });
1462
- if (agent == null || signature !== lastToolsSignature) {
1539
+ if (agent == null || signature !== lastAgentConfigurationSignature) {
1463
1540
  currentMessages = agent?.snapshot().messages ?? currentMessages;
1464
1541
  rebuildAgent({
1465
1542
  userTools,
1543
+ skillsRuntime,
1466
1544
  responseFormat: turnOpts.responseFormat,
1467
1545
  ...turnOpts.instructions ? { instructions: turnOpts.instructions } : {}
1468
1546
  });
1469
- lastToolsSignature = signature;
1547
+ lastAgentConfigurationSignature = signature;
1470
1548
  }
1471
1549
  currentEmit = turnOpts.emit;
1550
+ const userToolNames = new Set(userTools.map((tool2) => tool2.name));
1472
1551
  translatorState = createClineTranslatorState({
1473
- builtinToolNames: activeBuiltinNames,
1474
- hostToolNames: userTools.map((tool2) => tool2.name),
1552
+ builtinToolNames: activeBuiltinNames.filter(
1553
+ (name) => !userToolNames.has(name)
1554
+ ),
1555
+ hostToolNames: userToolNames,
1475
1556
  ignoredToolNames: turnOpts.responseFormat?.type === "json" ? ["structured_output"] : [],
1476
1557
  mcpToolNames: mcpRuntime.toolNames
1477
1558
  });
1478
1559
  turnOpts.emit({
1479
1560
  type: "stream-start",
1480
- ...input.settings.modelId ? { modelId: input.settings.modelId } : {}
1561
+ ...activeModelId ? { modelId: activeModelId } : {}
1481
1562
  });
1482
1563
  acceptingUserMessages = true;
1483
1564
  const turnPromise = (async () => {
@@ -1603,13 +1684,14 @@ ${rebuildInput.instructions}` : baseSystemPrompt,
1603
1684
  const sessionImpl = {
1604
1685
  sessionId: input.sessionId,
1605
1686
  isResume: input.isResume,
1606
- ...input.settings.modelId ? { modelId: input.settings.modelId } : {},
1607
1687
  // The Cline runtime has no bridge to attach to and no in-sandbox event
1608
1688
  // log to replay; its only cross-process resume path is restoring the
1609
1689
  // persisted history on a fresh runtime, i.e. `rerun`.
1610
1690
  doPromptTurn: async (promptOpts) => {
1611
1691
  return runTurn({
1612
1692
  text: extractUserText(promptOpts.prompt),
1693
+ ...promptOpts.model ? { model: promptOpts.model } : {},
1694
+ skills: promptOpts.skills,
1613
1695
  tools: promptOpts.tools ?? [],
1614
1696
  ...promptOpts.instructions ? { instructions: promptOpts.instructions } : {},
1615
1697
  emit: promptOpts.emit,
@@ -1627,6 +1709,8 @@ ${rebuildInput.instructions}` : baseSystemPrompt,
1627
1709
  }
1628
1710
  return runTurn({
1629
1711
  text: void 0,
1712
+ ...continueOpts.model ? { model: continueOpts.model } : {},
1713
+ skills: continueOpts.skills,
1630
1714
  tools: continueOpts.tools ?? [],
1631
1715
  ...continueOpts.instructions ? { instructions: continueOpts.instructions } : {},
1632
1716
  emit: continueOpts.emit,
@@ -1710,7 +1794,7 @@ ${rebuildInput.instructions}` : baseSystemPrompt,
1710
1794
  }
1711
1795
 
1712
1796
  // src/version.ts
1713
- var VERSION = true ? "1.0.19" : "0.0.0-test";
1797
+ var VERSION = true ? "1.0.21" : "0.0.0-test";
1714
1798
 
1715
1799
  // src/cline-harness.ts
1716
1800
  var CLINE_CLIENT_APP = `ai-sdk/harness-cline/${VERSION}`;
@@ -1786,6 +1870,18 @@ var CLINE_BUILTIN_TOOLS = {
1786
1870
  }),
1787
1871
  nativeName: "ls",
1788
1872
  toolUseKind: "readonly"
1873
+ },
1874
+ skills: {
1875
+ ...tool({
1876
+ description: "Execute a configured skill by name.",
1877
+ inputSchema: z2.object({
1878
+ skill: z2.string(),
1879
+ args: z2.string().optional()
1880
+ }),
1881
+ outputSchema: z2.string()
1882
+ }),
1883
+ nativeName: "skills",
1884
+ toolUseKind: "readonly"
1789
1885
  }
1790
1886
  };
1791
1887
  function createCline(settings = {}) {
@@ -1804,13 +1900,12 @@ function createCline(settings = {}) {
1804
1900
  sessionId: startOpts.sessionId,
1805
1901
  sandboxSession: startOpts.sandboxSession,
1806
1902
  sessionWorkDir: startOpts.sessionWorkDir,
1807
- skills: startOpts.skills ?? [],
1808
1903
  settings: {
1809
1904
  authEnv,
1810
1905
  isAuthenticationEnvironmentOverride: isHarnessAuthenticationEnvironment2(settings.auth),
1811
1906
  ...settings.mcpServers ? { mcpServers: settings.mcpServers } : {},
1812
1907
  ...settings.providerId ? { providerId: settings.providerId } : {},
1813
- ...settings.modelId ? { modelId: settings.modelId } : {},
1908
+ ...settings.modelId == null ? {} : { modelId: settings.modelId },
1814
1909
  ...settings.apiKey ? { apiKey: settings.apiKey } : {},
1815
1910
  ...settings.baseUrl ? { baseUrl: settings.baseUrl } : {},
1816
1911
  ...settings.headers ? { headers: settings.headers } : {},