create-agentmark 0.10.1 → 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 = {
@@ -641,7 +641,7 @@ object_config:
641
641
  - names
642
642
  test_settings:
643
643
  dataset: party.jsonl
644
- evals:
644
+ scores:
645
645
  - exact_match_json
646
646
  props:
647
647
  party_text: "We're having a party with Alice, Bob, and Carol."
@@ -815,7 +815,7 @@ import type { ToolsInput } from '@mastra/core/agent';
815
815
  import { z } from 'zod';` : `import { tool } from 'ai';
816
816
  import type { Tool } from 'ai';
817
817
  import { z } from 'zod';`;
818
- 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 });`;
819
819
  const toolSchemaField = isMastra ? `parameters: z.object({ query: z.string().describe('The search query') })` : `inputSchema: z.object({ query: z.string().describe('The search query') })`;
820
820
  const toolsReturnType = isMastra ? "ToolsInput" : "Record<string, Tool>";
821
821
  const toolsSetup = isClaudeAgentSdk ? `
@@ -868,7 +868,7 @@ import path from 'node:path';
868
868
  import dotenv from 'dotenv';
869
869
  dotenv.config({ path: path.resolve(__dirname, '.env') });
870
870
  import { createAgentMarkClient, ${modelRegistry} } from "${adapterConfig.package}";
871
- import type { EvalRegistry } from "${adapterConfig.package}";
871
+ import type { ScoreRegistry } from "@agentmark-ai/prompt-core";
872
872
  ${loaderImport}
873
873
  import AgentMarkTypes from './agentmark.types';
874
874
  ${providerImport}
@@ -878,22 +878,26 @@ ${adapterOptionsImport}
878
878
  ${modelRegistrySetup}
879
879
  ${toolsSetup}
880
880
 
881
- const evalRegistry: EvalRegistry = {
882
- exact_match_json: ({ output, expectedOutput }) => {
883
- if (!expectedOutput) {
884
- return { score: 0, label: 'error', reason: 'No expected output provided', passed: false };
885
- }
886
- try {
887
- const ok = JSON.stringify(output) === JSON.stringify(JSON.parse(expectedOutput));
888
- return {
889
- score: ok ? 1 : 0,
890
- label: ok ? 'correct' : 'incorrect',
891
- reason: ok ? 'Exact match' : 'Mismatch',
892
- passed: ok
893
- };
894
- } catch (e) {
895
- return { score: 0, label: 'error', reason: 'Failed to parse expected output as JSON', passed: false };
896
- }
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
+ },
897
901
  },
898
902
  };
899
903
 
@@ -1476,7 +1480,7 @@ description = "An AgentMark application using Claude Agent SDK"
1476
1480
  requires-python = ">=3.12"
1477
1481
  dependencies = [
1478
1482
  "agentmark-sdk>=0.1.0",
1479
- "agentmark-claude-agent-sdk>=0.1.0",
1483
+ "agentmark-claude-agent-sdk-v0>=0.1.0",
1480
1484
  "agentmark-prompt-core>=0.1.0",
1481
1485
  "python-dotenv>=1.0.0",
1482
1486
  "claude-agent-sdk>=0.1.0",
@@ -1507,7 +1511,7 @@ description = "An AgentMark application using Pydantic AI"
1507
1511
  requires-python = ">=3.12"
1508
1512
  dependencies = [
1509
1513
  "agentmark-sdk>=0.1.0",
1510
- "agentmark-pydantic-ai>=0.1.0",
1514
+ "agentmark-pydantic-ai-v0>=0.1.0",
1511
1515
  "agentmark-prompt-core>=0.1.0",
1512
1516
  "python-dotenv>=1.0.0",
1513
1517
  "pydantic-ai[openai]>=0.1.0",
@@ -1533,40 +1537,122 @@ asyncio_mode = "auto"
1533
1537
  strict = true
1534
1538
  `;
1535
1539
  };
1536
- 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))`;
1537
1594
  if (adapter === "claude-agent-sdk") {
1538
1595
  return `"""AgentMark client configuration.
1539
1596
 
1540
1597
  This file configures the AgentMark client with Claude Agent SDK adapter.
1541
- Customize the model registry as needed.
1598
+ Customize the model registry and eval registry as needed.
1542
1599
  """
1543
1600
 
1601
+ import json
1544
1602
  import os
1545
1603
  from pathlib import Path
1546
1604
  from dotenv import load_dotenv
1547
1605
 
1548
- from agentmark.prompt_core import FileLoader
1606
+ ${loaderImport}
1549
1607
  from agentmark_claude_agent_sdk import (
1550
1608
  create_claude_agent_client,
1551
- create_default_model_registry,
1609
+ ClaudeAgentModelRegistry,
1552
1610
  )
1553
1611
 
1554
1612
  # Load environment variables
1555
1613
  load_dotenv()
1556
1614
 
1557
- # Configure model registry with default mappings
1558
- # Supports: claude-* models
1559
- 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}
1560
1644
 
1561
- # Create file loader for local development
1562
- # Uses the project root as base directory for resolving relative paths
1563
- project_root = Path(__file__).parent.resolve()
1564
- loader = FileLoader(base_dir=str(project_root))
1645
+ eval_registry = {
1646
+ "exact_match_json": exact_match_json,
1647
+ }
1648
+
1649
+ ${loaderSetup}
1565
1650
 
1566
1651
  # Create the client
1567
1652
  # Claude Agent SDK handles tools natively through the SDK
1568
1653
  client = create_claude_agent_client(
1569
1654
  model_registry=model_registry,
1655
+ eval_registry=eval_registry,
1570
1656
  loader=loader,
1571
1657
  )
1572
1658
 
@@ -1576,25 +1662,30 @@ __all__ = ["client"]
1576
1662
  return `"""AgentMark client configuration.
1577
1663
 
1578
1664
  This file configures the AgentMark client with Pydantic AI adapter.
1579
- Customize the model registry and tools as needed.
1665
+ Customize the model registry, tools, and eval registry as needed.
1580
1666
  """
1581
1667
 
1668
+ import json
1582
1669
  import os
1583
1670
  from pathlib import Path
1584
1671
  from dotenv import load_dotenv
1585
1672
 
1586
- from agentmark.prompt_core import FileLoader
1673
+ ${loaderImport}
1587
1674
  from agentmark_pydantic_ai_v0 import (
1588
1675
  create_pydantic_ai_client,
1589
- create_default_model_registry,
1676
+ PydanticAIModelRegistry,
1590
1677
  )
1591
1678
 
1592
1679
  # Load environment variables
1593
1680
  load_dotenv()
1594
1681
 
1595
- # Configure model registry with default mappings
1596
- # Supports: gpt-*, claude-*, gemini-*, etc.
1597
- 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
+ })
1598
1689
 
1599
1690
  # Define tools as native pydantic-ai Tool objects or callables
1600
1691
  # Example:
@@ -1603,15 +1694,40 @@ model_registry = create_default_model_registry()
1603
1694
  # tools = [search]
1604
1695
  tools = []
1605
1696
 
1606
- # Create file loader for local development
1607
- # Uses the project root as base directory for resolving relative paths
1608
- project_root = Path(__file__).parent.resolve()
1609
- 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}
1610
1725
 
1611
1726
  # Create the client
1612
1727
  client = create_pydantic_ai_client(
1613
1728
  model_registry=model_registry,
1614
1729
  tools=tools,
1730
+ eval_registry=eval_registry,
1615
1731
  loader=loader,
1616
1732
  )
1617
1733
 
@@ -1839,6 +1955,15 @@ var createPythonApp = async (client, targetPath = ".", apiKey = "", deploymentMo
1839
1955
  console.log("\u23ED\uFE0F Skipped pyproject.toml (existing project)");
1840
1956
  }
1841
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
+ }
1842
1967
  if (!isExistingProject) {
1843
1968
  fs5.writeFileSync(`${targetPath}/main.py`, getMainPyContent(adapter, deploymentMode));
1844
1969
  } else {
@@ -1927,7 +2052,7 @@ var createPythonApp = async (client, targetPath = ".", apiKey = "", deploymentMo
1927
2052
  if (pythonVenv) {
1928
2053
  const activateCmd = process.platform === "win32" ? `${pythonVenv.name}\\Scripts\\activate` : `source ${pythonVenv.name}/bin/activate`;
1929
2054
  console.log(` $ ${activateCmd}`);
1930
- 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]"');
1931
2056
  } else {
1932
2057
  console.log(" $ python -m venv .venv");
1933
2058
  console.log(" $ source .venv/bin/activate # On Windows: .venv\\Scripts\\activate");
@@ -2089,22 +2214,45 @@ function initGitRepo(targetPath) {
2089
2214
  }
2090
2215
 
2091
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"];
2092
2220
  var parseArgs = () => {
2093
2221
  const args = process.argv.slice(2);
2094
- let deploymentMode;
2095
- let language;
2096
- for (const arg of args) {
2097
- if (arg === "--cloud") {
2098
- deploymentMode = "cloud";
2099
- } else if (arg === "--self-host") {
2100
- deploymentMode = "static";
2101
- } else if (arg === "--python") {
2102
- language = "python";
2103
- } else if (arg === "--typescript") {
2104
- 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;
2105
2253
  }
2106
2254
  }
2107
- return { deploymentMode, language };
2255
+ return result;
2108
2256
  };
2109
2257
  var main = async () => {
2110
2258
  const cliArgs = parseArgs();
@@ -2115,12 +2263,16 @@ var main = async () => {
2115
2263
  agentmarkPath: "."
2116
2264
  };
2117
2265
  console.log("Initializing project.");
2118
- const { folderName } = await prompts2({
2119
- name: "folderName",
2120
- type: "text",
2121
- message: "Where would you like to create your AgentMark app?",
2122
- initial: "my-agentmark-app"
2123
- });
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
+ }
2124
2276
  const isCurrentDir = isCurrentDirectory(folderName);
2125
2277
  const targetPath = isCurrentDir ? process.cwd() : path6.resolve(folderName);
2126
2278
  if (!isCurrentDir) {
@@ -2130,7 +2282,15 @@ var main = async () => {
2130
2282
  if (projectInfo.isExistingProject) {
2131
2283
  displayProjectDetectionSummary(projectInfo);
2132
2284
  }
2133
- 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
+ }
2134
2294
  let language = cliArgs.language;
2135
2295
  if (!language) {
2136
2296
  const response = await prompts2({
@@ -2144,40 +2304,49 @@ var main = async () => {
2144
2304
  });
2145
2305
  language = response.language;
2146
2306
  }
2147
- let adapter;
2148
- if (language === "python") {
2149
- const response = await prompts2({
2150
- name: "adapter",
2151
- type: "select",
2152
- message: "Which adapter would you like to use?",
2153
- choices: [
2154
- { title: "Pydantic AI", value: "pydantic-ai" },
2155
- { title: "Claude Agent SDK", value: "claude-agent-sdk" }
2156
- ]
2157
- });
2158
- adapter = response.adapter;
2159
- } else {
2160
- const response = await prompts2({
2161
- name: "adapter",
2162
- type: "select",
2163
- message: "Which adapter would you like to use?",
2164
- choices: [
2165
- { title: "AI SDK (Vercel)", value: "ai-sdk" },
2166
- { title: "Claude Agent SDK", value: "claude-agent-sdk" },
2167
- { title: "Mastra", value: "mastra" }
2168
- ]
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: ""
2169
2347
  });
2170
- adapter = response.adapter;
2171
- }
2172
- const apiKeyName = adapter === "claude-agent-sdk" ? "Anthropic" : "OpenAI";
2173
- let apiKey = "";
2174
- const { providedApiKey } = await prompts2({
2175
- name: "providedApiKey",
2176
- type: "password",
2177
- message: `Enter your ${apiKeyName} API key (or press Enter to skip):`,
2178
- initial: ""
2179
- });
2180
- apiKey = providedApiKey || "";
2348
+ apiKey = providedApiKey || "";
2349
+ }
2181
2350
  let deploymentMode = cliArgs.deploymentMode;
2182
2351
  if (!deploymentMode) {
2183
2352
  const response = await prompts2({
@@ -2199,18 +2368,26 @@ var main = async () => {
2199
2368
  });
2200
2369
  deploymentMode = response.deploymentMode;
2201
2370
  }
2202
- const { client } = await prompts2({
2203
- name: "client",
2204
- type: "select",
2205
- message: "Make your IDE an AgentMark expert",
2206
- choices: [
2207
- { title: "Claude Code", value: "claude-code" },
2208
- { title: "Cursor", value: "cursor" },
2209
- { title: "VS Code", value: "vscode" },
2210
- { title: "Zed", value: "zed" },
2211
- { title: "Skip", value: "skip" }
2212
- ]
2213
- });
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
+ }
2214
2391
  let usedModels;
2215
2392
  if (language === "python") {
2216
2393
  usedModels = await createPythonApp(client, targetPath, apiKey, deploymentMode, adapter, projectInfo, resolutions);
@@ -2219,7 +2396,7 @@ var main = async () => {
2219
2396
  }
2220
2397
  config.builtInModels = usedModels;
2221
2398
  if (deploymentMode === "cloud") {
2222
- config.handler = "handler.ts";
2399
+ config.handler = language === "python" ? "handler.py" : "handler.ts";
2223
2400
  }
2224
2401
  const agentmarkJsonPath = path6.join(targetPath, "agentmark.json");
2225
2402
  const agentmarkJsonResolution = resolutions.find((r) => r.path === "agentmark.json");