create-agentmark 0.10.0 → 0.10.2

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/index.js CHANGED
@@ -34,7 +34,7 @@ var createAdapterConfig = (provider) => {
34
34
  }
35
35
  },
36
36
  "claude-agent-sdk": {
37
- package: "@agentmark-ai/claude-agent-sdk-adapter",
37
+ package: "@agentmark-ai/claude-agent-sdk-v0-adapter",
38
38
  dependencies: ["@anthropic-ai/claude-agent-sdk@^0.1.0"],
39
39
  classes: {
40
40
  modelRegistry: "ClaudeAgentModelRegistry",
@@ -84,7 +84,7 @@ sdk.initTracing({ disableBatch: true });
84
84
  if (adapter === "claude-agent-sdk") {
85
85
  return `import "dotenv/config";
86
86
  import { query } from "@anthropic-ai/claude-agent-sdk";
87
- import { withTracing } from "@agentmark-ai/claude-agent-sdk-adapter";
87
+ import { withTracing } from "@agentmark-ai/claude-agent-sdk-v0-adapter";
88
88
  ${tracingImport}import { client } from "./agentmark.client";
89
89
  ${tracingInit}
90
90
  const telemetry = {
@@ -598,15 +598,7 @@ text_config:
598
598
  model_name: ${model}
599
599
  max_calls: 2
600
600
  tools:
601
- search_knowledgebase:
602
- description: Search the company knowledgebase for information about shipping, warranty, and returns policies.
603
- parameters:
604
- type: object
605
- properties:
606
- query:
607
- type: string
608
- description: The search query to find relevant information
609
- required: [query]
601
+ - search_knowledgebase
610
602
  test_settings:
611
603
  dataset: customer-query.jsonl
612
604
  props:
@@ -649,7 +641,7 @@ object_config:
649
641
  - names
650
642
  test_settings:
651
643
  dataset: party.jsonl
652
- evals:
644
+ scores:
653
645
  - exact_match_json
654
646
  props:
655
647
  party_text: "We're having a party with Alice, Bob, and Carol."
@@ -823,7 +815,7 @@ import type { ToolsInput } from '@mastra/core/agent';
823
815
  import { z } from 'zod';` : `import { tool } from 'ai';
824
816
  import type { Tool } from 'ai';
825
817
  import { z } from 'zod';`;
826
- const createClientCall = isClaudeAgentSdk ? `return createAgentMarkClient<AgentMarkTypes>({ loader, modelRegistry, evalRegistry, adapterOptions, mcpServers: { 'customer-support': customerSupportTools } });` : `return createAgentMarkClient<AgentMarkTypes>({ loader, modelRegistry, tools, evalRegistry });`;
818
+ const createClientCall = isClaudeAgentSdk ? `return createAgentMarkClient<AgentMarkTypes>({ loader, modelRegistry, scores, adapterOptions, mcpServers: { 'customer-support': customerSupportTools } });` : `return createAgentMarkClient<AgentMarkTypes>({ loader, modelRegistry, tools, scores });`;
827
819
  const toolSchemaField = isMastra ? `parameters: z.object({ query: z.string().describe('The search query') })` : `inputSchema: z.object({ query: z.string().describe('The search query') })`;
828
820
  const toolsReturnType = isMastra ? "ToolsInput" : "Record<string, Tool>";
829
821
  const toolsSetup = isClaudeAgentSdk ? `
@@ -876,7 +868,7 @@ import path from 'node:path';
876
868
  import dotenv from 'dotenv';
877
869
  dotenv.config({ path: path.resolve(__dirname, '.env') });
878
870
  import { createAgentMarkClient, ${modelRegistry} } from "${adapterConfig.package}";
879
- import type { EvalRegistry } from "${adapterConfig.package}";
871
+ import type { ScoreRegistry } from "@agentmark-ai/prompt-core";
880
872
  ${loaderImport}
881
873
  import AgentMarkTypes from './agentmark.types';
882
874
  ${providerImport}
@@ -886,22 +878,26 @@ ${adapterOptionsImport}
886
878
  ${modelRegistrySetup}
887
879
  ${toolsSetup}
888
880
 
889
- const evalRegistry: EvalRegistry = {
890
- exact_match_json: ({ output, expectedOutput }) => {
891
- if (!expectedOutput) {
892
- return { score: 0, label: 'error', reason: 'No expected output provided', passed: false };
893
- }
894
- try {
895
- const ok = JSON.stringify(output) === JSON.stringify(JSON.parse(expectedOutput));
896
- return {
897
- score: ok ? 1 : 0,
898
- label: ok ? 'correct' : 'incorrect',
899
- reason: ok ? 'Exact match' : 'Mismatch',
900
- passed: ok
901
- };
902
- } catch (e) {
903
- return { score: 0, label: 'error', reason: 'Failed to parse expected output as JSON', passed: false };
904
- }
881
+ const scores: ScoreRegistry = {
882
+ exact_match_json: {
883
+ schema: { type: 'boolean' },
884
+ description: 'Whether output matches expected JSON exactly',
885
+ eval: ({ output, expectedOutput }) => {
886
+ if (!expectedOutput) {
887
+ return { score: 0, label: 'error', reason: 'No expected output provided', passed: false };
888
+ }
889
+ try {
890
+ const ok = JSON.stringify(output) === JSON.stringify(JSON.parse(expectedOutput));
891
+ return {
892
+ score: ok ? 1 : 0,
893
+ label: ok ? 'correct' : 'incorrect',
894
+ reason: ok ? 'Exact match' : 'Mismatch',
895
+ passed: ok
896
+ };
897
+ } catch (e) {
898
+ return { score: 0, label: 'error', reason: 'Failed to parse expected output as JSON', passed: false };
899
+ }
900
+ },
905
901
  },
906
902
  };
907
903
 
@@ -1229,7 +1225,7 @@ const adapter = new ${handlerClass}(client as any);
1229
1225
  export default async function handler(request: {
1230
1226
  type: 'prompt-run' | 'dataset-run';
1231
1227
  data: {
1232
- ast: unknown;
1228
+ ast: any;
1233
1229
  customProps?: Record<string, unknown>;
1234
1230
  options?: { shouldStream?: boolean };
1235
1231
  experimentId?: string;
@@ -1484,7 +1480,7 @@ description = "An AgentMark application using Claude Agent SDK"
1484
1480
  requires-python = ">=3.12"
1485
1481
  dependencies = [
1486
1482
  "agentmark-sdk>=0.1.0",
1487
- "agentmark-claude-agent-sdk>=0.1.0",
1483
+ "agentmark-claude-agent-sdk-v0>=0.1.0",
1488
1484
  "agentmark-prompt-core>=0.1.0",
1489
1485
  "python-dotenv>=1.0.0",
1490
1486
  "claude-agent-sdk>=0.1.0",
@@ -1515,7 +1511,7 @@ description = "An AgentMark application using Pydantic AI"
1515
1511
  requires-python = ">=3.12"
1516
1512
  dependencies = [
1517
1513
  "agentmark-sdk>=0.1.0",
1518
- "agentmark-pydantic-ai>=0.1.0",
1514
+ "agentmark-pydantic-ai-v0>=0.1.0",
1519
1515
  "agentmark-prompt-core>=0.1.0",
1520
1516
  "python-dotenv>=1.0.0",
1521
1517
  "pydantic-ai[openai]>=0.1.0",
@@ -1541,40 +1537,122 @@ asyncio_mode = "auto"
1541
1537
  strict = true
1542
1538
  `;
1543
1539
  };
1544
- var getAgentmarkClientContent = (_deploymentMode, adapter) => {
1540
+ var getHandlerPyContent = (adapter) => {
1541
+ const webhookClass = adapter === "claude-agent-sdk" ? "ClaudeAgentSDKWebhookHandler" : "PydanticAIWebhookHandler";
1542
+ const webhookImport = adapter === "claude-agent-sdk" ? "from agentmark_claude_agent_sdk import ClaudeAgentSDKWebhookHandler" : "from agentmark_pydantic_ai_v0 import PydanticAIWebhookHandler";
1543
+ return `"""AgentMark handler for managed cloud deployments.
1544
+
1545
+ This file is used by the AgentMark platform to execute prompts and experiments
1546
+ on deployed infrastructure. It mirrors the TypeScript handler.ts pattern.
1547
+ """
1548
+
1549
+ import os
1550
+
1551
+ from agentmark_sdk import AgentMarkSDK
1552
+ ${webhookImport}
1553
+ from agentmark_client import client
1554
+
1555
+ # Initialize tracing
1556
+ sdk = AgentMarkSDK(
1557
+ api_key=os.environ.get("AGENTMARK_API_KEY", ""),
1558
+ app_id=os.environ.get("AGENTMARK_APP_ID", ""),
1559
+ base_url=os.environ.get("AGENTMARK_BASE_URL"),
1560
+ )
1561
+ sdk.init_tracing(disable_batch=True)
1562
+
1563
+ adapter = ${webhookClass}(client)
1564
+
1565
+
1566
+ async def handler(request: dict):
1567
+ """Handle prompt-run and dataset-run requests from the platform."""
1568
+ req_type = request.get("type")
1569
+ data = request.get("data", {})
1570
+
1571
+ if req_type == "prompt-run":
1572
+ return await adapter.run_prompt(data["ast"], {
1573
+ "shouldStream": data.get("options", {}).get("shouldStream", True),
1574
+ "customProps": data.get("customProps"),
1575
+ })
1576
+
1577
+ if req_type == "dataset-run":
1578
+ return await adapter.run_experiment(
1579
+ data["ast"],
1580
+ data.get("experimentId", ""),
1581
+ data.get("datasetPath"),
1582
+ )
1583
+
1584
+ raise ValueError(f"Unknown request type: {req_type}")
1585
+ `;
1586
+ };
1587
+ var getAgentmarkClientContent = (deploymentMode, adapter) => {
1588
+ const isCloud = deploymentMode === "cloud";
1589
+ const loaderImport = isCloud ? `from agentmark.prompt_core import ApiLoader` : `from agentmark.prompt_core import FileLoader`;
1590
+ const loaderSetup = isCloud ? `# API loader for cloud deployment \u2014 fetches datasets from the AgentMark gateway
1591
+ loader = ApiLoader.cloud()` : `# File loader for local development
1592
+ project_root = Path(__file__).parent.resolve()
1593
+ loader = FileLoader(base_dir=str(project_root))`;
1545
1594
  if (adapter === "claude-agent-sdk") {
1546
1595
  return `"""AgentMark client configuration.
1547
1596
 
1548
1597
  This file configures the AgentMark client with Claude Agent SDK adapter.
1549
- Customize the model registry as needed.
1598
+ Customize the model registry and eval registry as needed.
1550
1599
  """
1551
1600
 
1601
+ import json
1552
1602
  import os
1553
1603
  from pathlib import Path
1554
1604
  from dotenv import load_dotenv
1555
1605
 
1556
- from agentmark.prompt_core import FileLoader
1606
+ ${loaderImport}
1557
1607
  from agentmark_claude_agent_sdk import (
1558
1608
  create_claude_agent_client,
1559
- create_default_model_registry,
1609
+ ClaudeAgentModelRegistry,
1560
1610
  )
1561
1611
 
1562
1612
  # Load environment variables
1563
1613
  load_dotenv()
1564
1614
 
1565
- # Configure model registry with default mappings
1566
- # Supports: claude-* models
1567
- model_registry = create_default_model_registry()
1615
+ # Register the model providers your prompts use.
1616
+ # This maps "anthropic/claude-sonnet-4-20250514" in prompt files to the Claude Agent SDK.
1617
+ model_registry = ClaudeAgentModelRegistry()
1618
+ model_registry.register_providers({
1619
+ "anthropic": "anthropic",
1620
+ })
1621
+
1622
+
1623
+ # Eval registry \u2014 define evaluation functions for experiments
1624
+ # TODO: Update to use scores (ScoreRegistry) once the Python SDK supports it.
1625
+ # The TypeScript SDK already uses scores with schema definitions.
1626
+ def exact_match_json(params):
1627
+ """Check if output matches expected output exactly."""
1628
+ output = params.get("output")
1629
+ expected_output = params.get("expectedOutput")
1630
+ if not expected_output:
1631
+ return {"score": 0, "label": "error", "reason": "No expected output provided", "passed": False}
1632
+ try:
1633
+ actual = json.loads(output) if isinstance(output, str) else output
1634
+ expected = json.loads(expected_output) if isinstance(expected_output, str) else expected_output
1635
+ ok = actual == expected
1636
+ return {
1637
+ "score": 1 if ok else 0,
1638
+ "label": "correct" if ok else "incorrect",
1639
+ "reason": "Exact match" if ok else "Mismatch",
1640
+ "passed": ok,
1641
+ }
1642
+ except (json.JSONDecodeError, TypeError):
1643
+ return {"score": 0, "label": "error", "reason": "Failed to parse JSON", "passed": False}
1568
1644
 
1569
- # Create file loader for local development
1570
- # Uses the project root as base directory for resolving relative paths
1571
- project_root = Path(__file__).parent.resolve()
1572
- loader = FileLoader(base_dir=str(project_root))
1645
+ eval_registry = {
1646
+ "exact_match_json": exact_match_json,
1647
+ }
1648
+
1649
+ ${loaderSetup}
1573
1650
 
1574
1651
  # Create the client
1575
1652
  # Claude Agent SDK handles tools natively through the SDK
1576
1653
  client = create_claude_agent_client(
1577
1654
  model_registry=model_registry,
1655
+ eval_registry=eval_registry,
1578
1656
  loader=loader,
1579
1657
  )
1580
1658
 
@@ -1584,25 +1662,30 @@ __all__ = ["client"]
1584
1662
  return `"""AgentMark client configuration.
1585
1663
 
1586
1664
  This file configures the AgentMark client with Pydantic AI adapter.
1587
- Customize the model registry and tools as needed.
1665
+ Customize the model registry, tools, and eval registry as needed.
1588
1666
  """
1589
1667
 
1668
+ import json
1590
1669
  import os
1591
1670
  from pathlib import Path
1592
1671
  from dotenv import load_dotenv
1593
1672
 
1594
- from agentmark.prompt_core import FileLoader
1673
+ ${loaderImport}
1595
1674
  from agentmark_pydantic_ai_v0 import (
1596
1675
  create_pydantic_ai_client,
1597
- create_default_model_registry,
1676
+ PydanticAIModelRegistry,
1598
1677
  )
1599
1678
 
1600
1679
  # Load environment variables
1601
1680
  load_dotenv()
1602
1681
 
1603
- # Configure model registry with default mappings
1604
- # Supports: gpt-*, claude-*, gemini-*, etc.
1605
- model_registry = create_default_model_registry()
1682
+ # Register the model providers your prompts use.
1683
+ # This maps "openai/gpt-4o" in prompt files to "openai:gpt-4o" for Pydantic AI.
1684
+ model_registry = PydanticAIModelRegistry()
1685
+ model_registry.register_providers({
1686
+ "openai": "openai",
1687
+ "anthropic": "anthropic",
1688
+ })
1606
1689
 
1607
1690
  # Define tools as native pydantic-ai Tool objects or callables
1608
1691
  # Example:
@@ -1611,15 +1694,40 @@ model_registry = create_default_model_registry()
1611
1694
  # tools = [search]
1612
1695
  tools = []
1613
1696
 
1614
- # Create file loader for local development
1615
- # Uses the project root as base directory for resolving relative paths
1616
- project_root = Path(__file__).parent.resolve()
1617
- loader = FileLoader(base_dir=str(project_root))
1697
+
1698
+ # Eval registry \u2014 define evaluation functions for experiments
1699
+ # TODO: Update to use scores (ScoreRegistry) once the Python SDK supports it.
1700
+ # The TypeScript SDK already uses scores with schema definitions.
1701
+ def exact_match_json(params):
1702
+ """Check if output matches expected output exactly."""
1703
+ output = params.get("output")
1704
+ expected_output = params.get("expectedOutput")
1705
+ if not expected_output:
1706
+ return {"score": 0, "label": "error", "reason": "No expected output provided", "passed": False}
1707
+ try:
1708
+ actual = json.loads(output) if isinstance(output, str) else output
1709
+ expected = json.loads(expected_output) if isinstance(expected_output, str) else expected_output
1710
+ ok = actual == expected
1711
+ return {
1712
+ "score": 1 if ok else 0,
1713
+ "label": "correct" if ok else "incorrect",
1714
+ "reason": "Exact match" if ok else "Mismatch",
1715
+ "passed": ok,
1716
+ }
1717
+ except (json.JSONDecodeError, TypeError):
1718
+ return {"score": 0, "label": "error", "reason": "Failed to parse JSON", "passed": False}
1719
+
1720
+ eval_registry = {
1721
+ "exact_match_json": exact_match_json,
1722
+ }
1723
+
1724
+ ${loaderSetup}
1618
1725
 
1619
1726
  # Create the client
1620
1727
  client = create_pydantic_ai_client(
1621
1728
  model_registry=model_registry,
1622
1729
  tools=tools,
1730
+ eval_registry=eval_registry,
1623
1731
  loader=loader,
1624
1732
  )
1625
1733
 
@@ -1847,6 +1955,15 @@ var createPythonApp = async (client, targetPath = ".", apiKey = "", deploymentMo
1847
1955
  console.log("\u23ED\uFE0F Skipped pyproject.toml (existing project)");
1848
1956
  }
1849
1957
  fs5.writeFileSync(`${targetPath}/agentmark_client.py`, getAgentmarkClientContent(deploymentMode, adapter));
1958
+ if (deploymentMode === "cloud") {
1959
+ const handlerPath = path3.join(targetPath, "handler.py");
1960
+ if (fs5.existsSync(handlerPath)) {
1961
+ console.log("\u23ED\uFE0F Skipped handler.py (already exists - preserving customizations)");
1962
+ } else {
1963
+ fs5.writeFileSync(handlerPath, getHandlerPyContent(adapter));
1964
+ console.log(`\u2705 Created handler.py for cloud deployment`);
1965
+ }
1966
+ }
1850
1967
  if (!isExistingProject) {
1851
1968
  fs5.writeFileSync(`${targetPath}/main.py`, getMainPyContent(adapter, deploymentMode));
1852
1969
  } else {
@@ -1935,7 +2052,7 @@ var createPythonApp = async (client, targetPath = ".", apiKey = "", deploymentMo
1935
2052
  if (pythonVenv) {
1936
2053
  const activateCmd = process.platform === "win32" ? `${pythonVenv.name}\\Scripts\\activate` : `source ${pythonVenv.name}/bin/activate`;
1937
2054
  console.log(` $ ${activateCmd}`);
1938
- console.log(' $ pip install agentmark-pydantic-ai agentmark-prompt-core python-dotenv "pydantic-ai[openai]"');
2055
+ console.log(' $ pip install agentmark-pydantic-ai-v0 agentmark-prompt-core python-dotenv "pydantic-ai[openai]"');
1939
2056
  } else {
1940
2057
  console.log(" $ python -m venv .venv");
1941
2058
  console.log(" $ source .venv/bin/activate # On Windows: .venv\\Scripts\\activate");
@@ -2097,22 +2214,45 @@ function initGitRepo(targetPath) {
2097
2214
  }
2098
2215
 
2099
2216
  // src/index.ts
2217
+ var VALID_ADAPTERS_TS = ["ai-sdk", "claude-agent-sdk", "mastra"];
2218
+ var VALID_ADAPTERS_PY = ["pydantic-ai", "claude-agent-sdk"];
2219
+ var VALID_CLIENTS = ["claude-code", "cursor", "vscode", "zed", "skip"];
2100
2220
  var parseArgs = () => {
2101
2221
  const args = process.argv.slice(2);
2102
- let deploymentMode;
2103
- let language;
2104
- for (const arg of args) {
2105
- if (arg === "--cloud") {
2106
- deploymentMode = "cloud";
2107
- } else if (arg === "--self-host") {
2108
- deploymentMode = "static";
2109
- } else if (arg === "--python") {
2110
- language = "python";
2111
- } else if (arg === "--typescript") {
2112
- language = "typescript";
2222
+ const result = {};
2223
+ for (let i = 0; i < args.length; i++) {
2224
+ const arg = args[i];
2225
+ switch (arg) {
2226
+ case "--cloud":
2227
+ result.deploymentMode = "cloud";
2228
+ break;
2229
+ case "--self-host":
2230
+ result.deploymentMode = "static";
2231
+ break;
2232
+ case "--python":
2233
+ result.language = "python";
2234
+ break;
2235
+ case "--typescript":
2236
+ result.language = "typescript";
2237
+ break;
2238
+ case "--overwrite":
2239
+ result.overwrite = true;
2240
+ break;
2241
+ case "--path":
2242
+ result.path = args[++i];
2243
+ break;
2244
+ case "--adapter":
2245
+ result.adapter = args[++i];
2246
+ break;
2247
+ case "--api-key":
2248
+ result.apiKey = args[++i];
2249
+ break;
2250
+ case "--client":
2251
+ result.client = args[++i];
2252
+ break;
2113
2253
  }
2114
2254
  }
2115
- return { deploymentMode, language };
2255
+ return result;
2116
2256
  };
2117
2257
  var main = async () => {
2118
2258
  const cliArgs = parseArgs();
@@ -2123,12 +2263,16 @@ var main = async () => {
2123
2263
  agentmarkPath: "."
2124
2264
  };
2125
2265
  console.log("Initializing project.");
2126
- const { folderName } = await prompts2({
2127
- name: "folderName",
2128
- type: "text",
2129
- message: "Where would you like to create your AgentMark app?",
2130
- initial: "my-agentmark-app"
2131
- });
2266
+ let folderName = cliArgs.path;
2267
+ if (!folderName) {
2268
+ const response = await prompts2({
2269
+ name: "folderName",
2270
+ type: "text",
2271
+ message: "Where would you like to create your AgentMark app?",
2272
+ initial: "my-agentmark-app"
2273
+ });
2274
+ folderName = response.folderName;
2275
+ }
2132
2276
  const isCurrentDir = isCurrentDirectory(folderName);
2133
2277
  const targetPath = isCurrentDir ? process.cwd() : path6.resolve(folderName);
2134
2278
  if (!isCurrentDir) {
@@ -2138,7 +2282,15 @@ var main = async () => {
2138
2282
  if (projectInfo.isExistingProject) {
2139
2283
  displayProjectDetectionSummary(projectInfo);
2140
2284
  }
2141
- const resolutions = await promptForResolutions(projectInfo.conflictingFiles);
2285
+ let resolutions;
2286
+ if (cliArgs.overwrite) {
2287
+ resolutions = projectInfo.conflictingFiles.map((f) => ({
2288
+ path: f.path,
2289
+ action: "overwrite"
2290
+ }));
2291
+ } else {
2292
+ resolutions = await promptForResolutions(projectInfo.conflictingFiles);
2293
+ }
2142
2294
  let language = cliArgs.language;
2143
2295
  if (!language) {
2144
2296
  const response = await prompts2({
@@ -2152,40 +2304,49 @@ var main = async () => {
2152
2304
  });
2153
2305
  language = response.language;
2154
2306
  }
2155
- let adapter;
2156
- if (language === "python") {
2157
- const response = await prompts2({
2158
- name: "adapter",
2159
- type: "select",
2160
- message: "Which adapter would you like to use?",
2161
- choices: [
2162
- { title: "Pydantic AI", value: "pydantic-ai" },
2163
- { title: "Claude Agent SDK", value: "claude-agent-sdk" }
2164
- ]
2165
- });
2166
- adapter = response.adapter;
2167
- } else {
2168
- const response = await prompts2({
2169
- name: "adapter",
2170
- type: "select",
2171
- message: "Which adapter would you like to use?",
2172
- choices: [
2173
- { title: "AI SDK (Vercel)", value: "ai-sdk" },
2174
- { title: "Claude Agent SDK", value: "claude-agent-sdk" },
2175
- { title: "Mastra", value: "mastra" }
2176
- ]
2307
+ let adapter = cliArgs.adapter;
2308
+ if (!adapter) {
2309
+ if (language === "python") {
2310
+ const response = await prompts2({
2311
+ name: "adapter",
2312
+ type: "select",
2313
+ message: "Which adapter would you like to use?",
2314
+ choices: [
2315
+ { title: "Pydantic AI", value: "pydantic-ai" },
2316
+ { title: "Claude Agent SDK", value: "claude-agent-sdk" }
2317
+ ]
2318
+ });
2319
+ adapter = response.adapter;
2320
+ } else {
2321
+ const response = await prompts2({
2322
+ name: "adapter",
2323
+ type: "select",
2324
+ message: "Which adapter would you like to use?",
2325
+ choices: [
2326
+ { title: "AI SDK (Vercel)", value: "ai-sdk" },
2327
+ { title: "Claude Agent SDK", value: "claude-agent-sdk" },
2328
+ { title: "Mastra", value: "mastra" }
2329
+ ]
2330
+ });
2331
+ adapter = response.adapter;
2332
+ }
2333
+ }
2334
+ const validAdapters = language === "python" ? VALID_ADAPTERS_PY : VALID_ADAPTERS_TS;
2335
+ if (!validAdapters.includes(adapter)) {
2336
+ console.error(`Invalid adapter "${adapter}" for ${language}. Valid: ${validAdapters.join(", ")}`);
2337
+ process.exit(1);
2338
+ }
2339
+ let apiKey = cliArgs.apiKey ?? "";
2340
+ if (!cliArgs.apiKey && cliArgs.apiKey !== "") {
2341
+ const apiKeyName = adapter === "claude-agent-sdk" ? "Anthropic" : "OpenAI";
2342
+ const { providedApiKey } = await prompts2({
2343
+ name: "providedApiKey",
2344
+ type: "password",
2345
+ message: `Enter your ${apiKeyName} API key (or press Enter to skip):`,
2346
+ initial: ""
2177
2347
  });
2178
- adapter = response.adapter;
2179
- }
2180
- const apiKeyName = adapter === "claude-agent-sdk" ? "Anthropic" : "OpenAI";
2181
- let apiKey = "";
2182
- const { providedApiKey } = await prompts2({
2183
- name: "providedApiKey",
2184
- type: "password",
2185
- message: `Enter your ${apiKeyName} API key (or press Enter to skip):`,
2186
- initial: ""
2187
- });
2188
- apiKey = providedApiKey || "";
2348
+ apiKey = providedApiKey || "";
2349
+ }
2189
2350
  let deploymentMode = cliArgs.deploymentMode;
2190
2351
  if (!deploymentMode) {
2191
2352
  const response = await prompts2({
@@ -2207,18 +2368,26 @@ var main = async () => {
2207
2368
  });
2208
2369
  deploymentMode = response.deploymentMode;
2209
2370
  }
2210
- const { client } = await prompts2({
2211
- name: "client",
2212
- type: "select",
2213
- message: "Make your IDE an AgentMark expert",
2214
- choices: [
2215
- { title: "Claude Code", value: "claude-code" },
2216
- { title: "Cursor", value: "cursor" },
2217
- { title: "VS Code", value: "vscode" },
2218
- { title: "Zed", value: "zed" },
2219
- { title: "Skip", value: "skip" }
2220
- ]
2221
- });
2371
+ let client = cliArgs.client;
2372
+ if (!client) {
2373
+ const response = await prompts2({
2374
+ name: "client",
2375
+ type: "select",
2376
+ message: "Make your IDE an AgentMark expert",
2377
+ choices: [
2378
+ { title: "Claude Code", value: "claude-code" },
2379
+ { title: "Cursor", value: "cursor" },
2380
+ { title: "VS Code", value: "vscode" },
2381
+ { title: "Zed", value: "zed" },
2382
+ { title: "Skip", value: "skip" }
2383
+ ]
2384
+ });
2385
+ client = response.client;
2386
+ }
2387
+ if (!VALID_CLIENTS.includes(client)) {
2388
+ console.error(`Invalid client "${client}". Valid: ${VALID_CLIENTS.join(", ")}`);
2389
+ process.exit(1);
2390
+ }
2222
2391
  let usedModels;
2223
2392
  if (language === "python") {
2224
2393
  usedModels = await createPythonApp(client, targetPath, apiKey, deploymentMode, adapter, projectInfo, resolutions);
@@ -2227,7 +2396,7 @@ var main = async () => {
2227
2396
  }
2228
2397
  config.builtInModels = usedModels;
2229
2398
  if (deploymentMode === "cloud") {
2230
- config.handler = "handler.ts";
2399
+ config.handler = language === "python" ? "handler.py" : "handler.ts";
2231
2400
  }
2232
2401
  const agentmarkJsonPath = path6.join(targetPath, "agentmark.json");
2233
2402
  const agentmarkJsonResolution = resolutions.find((r) => r.path === "agentmark.json");