@contentful/experience-design-system-cli 2.15.1-dev-build-074e179.0 → 2.15.1-dev-build-b11e821.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentful/experience-design-system-cli",
3
- "version": "2.15.1-dev-build-074e179.0",
3
+ "version": "2.15.1-dev-build-b11e821.0",
4
4
  "description": "Contentful Experiences design system import CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -306,7 +306,7 @@ export function registerAnalyzeSelectAgentCommand(program) {
306
306
  .option('--session <id>', 'Session ID from analyze extract (defaults to most recent)')
307
307
  .option('--project-root <path>', 'Project root for resolving component source files')
308
308
  .option('--agent <name>', 'Agent to use: claude, codex, opencode, cursor (defaults to value saved by experiences setup)')
309
- .option('--model <name>', 'Model to use (defaults to a small/fast model per agent)')
309
+ .option('--model <name>', 'Model to use (defaults to a cheap per-agent model; override with --model or EDS_AGENT_MODEL_<AGENT>)')
310
310
  .option('--verbose', 'Show full agent output including reasoning text')
311
311
  .option('--dry-run', 'Print the prompt for the first component without invoking the agent')
312
312
  .option('--exclude-invalid', 'Auto-reject components with validation errors instead of failing loud (LLM cannot fix structural issues)')
@@ -89,6 +89,14 @@ export interface ParsedToolCalls {
89
89
  export declare function parseToolCallLines(stdout: string): ParsedToolCalls;
90
90
  export declare function parseTokenToolCallLines(stdout: string): ParsedTokenToolCalls;
91
91
  export declare function resolveBinary(agent: AgentName): string;
92
+ /**
93
+ * Resolve the model for an agent. Explicit flag/creds value wins, then a
94
+ * per-agent `EDS_AGENT_MODEL_<AGENT>` env override (mirrors the
95
+ * `EDS_AGENT_BINARY_<AGENT>` pattern), otherwise the cheap default for that
96
+ * agent.
97
+ */
98
+ export declare function resolveAgentModel(agent: AgentName, explicit?: string): string;
99
+ export declare function buildArgs(agent: AgentName, prompt: string, model?: string, promptViaStdin?: boolean): string[];
92
100
  export declare function runAgent(options: {
93
101
  agent: AgentName;
94
102
  prompt: string;
@@ -105,4 +113,11 @@ export declare function runAgent(options: {
105
113
  }): Promise<AgentRunResult>;
106
114
  export type AgentAuthStatus = 'ok' | 'unauthenticated' | 'not-found';
107
115
  export declare function checkAgentAuth(agent: AgentName): Promise<AgentAuthStatus>;
116
+ /**
117
+ * Build a diagnostic string from a failed agent run, surfacing the agent's own
118
+ * stderr (or stdout, when stderr is empty) so callers never emit a context-free
119
+ * "agent failed" (AIS-392 B1). `exitCode === 0` with no output is treated as the
120
+ * "produced no tool calls" case.
121
+ */
122
+ export declare function describeAgentFailure(result: AgentRunResult, maxDetail?: number): string;
108
123
  export declare function extractSentinelOutput(stdout: string): string | null | 'multiple';
@@ -211,37 +211,55 @@ const AGENT_BINARIES = {
211
211
  opencode: 'opencode',
212
212
  cursor: 'cursor-agent',
213
213
  };
214
- // Default to small/fast models for single-component classification — cheap and accurate enough.
214
+ export function resolveBinary(agent) {
215
+ const envKey = `EDS_AGENT_BINARY_${agent.toUpperCase()}`;
216
+ const override = process.env[envKey];
217
+ if (override && override.trim())
218
+ return override.trim();
219
+ return AGENT_BINARIES[agent];
220
+ }
221
+ /**
222
+ * Default models per agent — cheap/fast picks to control cost when no explicit
223
+ * model is configured. cursor uses `gpt-mini` (verified alias from
224
+ * GetUsableModels; haiku is not available in cursor's model catalog).
225
+ */
215
226
  const DEFAULT_MODELS = {
216
227
  claude: 'haiku',
217
228
  codex: 'gpt-5.4-mini', // requires OPENAI_API_KEY; ChatGPT account users must pass --model
218
229
  opencode: 'claude-haiku-4-5',
219
- cursor: 'claude-3-5-haiku-20241022',
230
+ cursor: 'gpt-mini', // cursor alias for gpt-5.4-mini-medium; haiku not in cursor's catalog
220
231
  };
221
- export function resolveBinary(agent) {
222
- const envKey = `EDS_AGENT_BINARY_${agent.toUpperCase()}`;
223
- const override = process.env[envKey];
232
+ /**
233
+ * Resolve the model for an agent. Explicit flag/creds value wins, then a
234
+ * per-agent `EDS_AGENT_MODEL_<AGENT>` env override (mirrors the
235
+ * `EDS_AGENT_BINARY_<AGENT>` pattern), otherwise the cheap default for that
236
+ * agent.
237
+ */
238
+ export function resolveAgentModel(agent, explicit) {
239
+ if (explicit && explicit.trim())
240
+ return explicit.trim();
241
+ const override = process.env[`EDS_AGENT_MODEL_${agent.toUpperCase()}`];
224
242
  if (override && override.trim())
225
243
  return override.trim();
226
- return AGENT_BINARIES[agent];
244
+ return DEFAULT_MODELS[agent];
227
245
  }
228
- function buildArgs(agent, prompt, model, promptViaStdin = false) {
229
- const m = model ?? DEFAULT_MODELS[agent];
246
+ export function buildArgs(agent, prompt, model, promptViaStdin = false) {
247
+ const modelArg = ['--model', resolveAgentModel(agent, model)];
230
248
  // When the prompt is delivered on stdin, omit it from argv — a large prompt
231
249
  // as a command-line argument overflows ARG_MAX (spawn E2BIG). All four CLIs
232
250
  // read the prompt from stdin when it isn't passed positionally.
233
251
  const promptArg = promptViaStdin ? [] : [prompt];
234
252
  switch (agent) {
235
253
  case 'claude':
236
- return ['--print', '--model', m, ...promptArg];
254
+ return ['--print', ...modelArg, ...promptArg];
237
255
  case 'codex':
238
256
  // --dangerously-bypass-approvals-and-sandbox required for non-interactive use
239
- return ['exec', '--model', m, '--dangerously-bypass-approvals-and-sandbox', ...promptArg];
257
+ return ['exec', ...modelArg, '--dangerously-bypass-approvals-and-sandbox', ...promptArg];
240
258
  case 'opencode':
241
- return ['run', '--model', m, ...promptArg];
259
+ return ['run', ...modelArg, ...promptArg];
242
260
  case 'cursor':
243
261
  // cursor-agent uses --print for non-interactive stdout output
244
- return ['--print', '--model', m, ...promptArg];
262
+ return ['--print', ...modelArg, ...promptArg];
245
263
  }
246
264
  }
247
265
  export async function runAgent(options) {
@@ -319,13 +337,12 @@ export async function runAgent(options) {
319
337
  });
320
338
  }
321
339
  export async function checkAgentAuth(agent) {
322
- if (agent !== 'claude')
323
- return 'ok';
324
340
  const binary = resolveBinary(agent);
325
- // Verify the binary exists first. When `binary` is an absolute path (e.g.
326
- // set via EDS_AGENT_BINARY_CLAUDE=/opt/custom/claude), `which` on some
327
- // shells doesn't resolve it — check the filesystem directly for absolute
328
- // paths, and fall back to `which` for bare names on $PATH.
341
+ // Verify the selected agent's binary exists first for EVERY agent, not
342
+ // just claude. When `binary` is an absolute path (e.g. set via
343
+ // EDS_AGENT_BINARY_<AGENT>=/opt/custom/bin), `which` on some shells doesn't
344
+ // resolve it check the filesystem directly for absolute paths, and fall
345
+ // back to `which` for bare names on $PATH.
329
346
  const binaryExists = await new Promise((resolve) => {
330
347
  if (binary.startsWith('/')) {
331
348
  import('node:fs/promises').then((fs) => fs.access(binary).then(() => resolve(true), () => resolve(false)));
@@ -336,6 +353,11 @@ export async function checkAgentAuth(agent) {
336
353
  });
337
354
  if (!binaryExists)
338
355
  return 'not-found';
356
+ // Only Claude exposes `auth status --json`. Non-Claude agents are considered
357
+ // authenticated once their binary is present — never gate them on claude
358
+ // (AIS-392 B3: Codex-via-Bedrock users were blocked by a claude check).
359
+ if (agent !== 'claude')
360
+ return 'ok';
339
361
  // Use `claude auth status` — fast, no API call, works regardless of which
340
362
  // auth provider (direct, Bedrock, Vertex) or whether AWS_PROFILE is set.
341
363
  return new Promise((resolve) => {
@@ -373,6 +395,17 @@ export async function checkAgentAuth(agent) {
373
395
  });
374
396
  });
375
397
  }
398
+ /**
399
+ * Build a diagnostic string from a failed agent run, surfacing the agent's own
400
+ * stderr (or stdout, when stderr is empty) so callers never emit a context-free
401
+ * "agent failed" (AIS-392 B1). `exitCode === 0` with no output is treated as the
402
+ * "produced no tool calls" case.
403
+ */
404
+ export function describeAgentFailure(result, maxDetail = 800) {
405
+ const base = result.exitCode !== 0 ? `agent exited with code ${result.exitCode}` : 'agent produced no tool calls';
406
+ const detail = (result.stderr.trim() || result.stdout.trim()).slice(-maxDetail).trim();
407
+ return detail ? `${base} — ${detail}` : base;
408
+ }
376
409
  export function extractSentinelOutput(stdout) {
377
410
  const START = '<<<EDS_OUTPUT_START>>>';
378
411
  const END = '<<<EDS_OUTPUT_END>>>';
@@ -4,7 +4,7 @@ import { access, readFile, readdir, stat } from 'node:fs/promises';
4
4
  import { join, resolve } from 'node:path';
5
5
  import { execFile } from 'node:child_process';
6
6
  import { promisify } from 'node:util';
7
- import { parseToolCallLines, parseTokenToolCallLines, resolveBinary, runAgent, } from './agent-runner.js';
7
+ import { describeAgentFailure, parseToolCallLines, parseTokenToolCallLines, resolveBinary, runAgent, } from './agent-runner.js';
8
8
  import { OutputFormatter, c } from '../output/format.js';
9
9
  import { formatGenerateProgressLine } from './progress.js';
10
10
  import { buildPrompt, resolveSkillPath } from './prompt-builder.js';
@@ -217,12 +217,12 @@ async function runOneComponent(agent, model, db, sessionId, component, tokensInl
217
217
  };
218
218
  }
219
219
  if (result.exitCode !== 0) {
220
- lastError = `agent exited with code ${result.exitCode}`;
220
+ lastError = describeAgentFailure(result);
221
221
  continue;
222
222
  }
223
223
  const { calls, warnings } = parseToolCallLines(result.stdout);
224
224
  if (calls.length === 0) {
225
- lastError = 'agent produced no tool calls';
225
+ lastError = describeAgentFailure(result);
226
226
  continue;
227
227
  }
228
228
  const applied = applyToolCalls(db, sessionId, component.component_id, component.name, calls, warnings);
@@ -452,7 +452,7 @@ async function runGenerateSkill(skill, opts, verbose = false) {
452
452
  // Machine-parseable summary on stdout for the wizard / orchestrator.
453
453
  process.stdout.write(`renamed-slots: ${totalRenamedSlots}\n`);
454
454
  if (generated.length === 0 && cachedResults.length === 0) {
455
- die('Error: all components failed to generate — check agent output above');
455
+ die(`Error: all ${componentResults.length} component(s) failed to generate — see the per-component errors above.`);
456
456
  }
457
457
  }
458
458
  else if (skill === 'tokens') {
@@ -570,7 +570,7 @@ async function runGenerateSkill(skill, opts, verbose = false) {
570
570
  function addAgentFlags(cmd) {
571
571
  return cmd
572
572
  .option('--agent <name>', 'Agent to use: claude, codex, opencode, cursor (defaults to value saved by experiences setup)')
573
- .option('--model <name>', 'Model to use (defaults to a small/fast model per agent)')
573
+ .option('--model <name>', 'Model to use (defaults to a cheap per-agent model; override with --model or EDS_AGENT_MODEL_<AGENT>)')
574
574
  .option('--verbose', 'Show full agent output including reasoning text')
575
575
  .option('--dry-run', 'Print the prompt without invoking the agent')
576
576
  .option('--no-cache', 'Bypass ALL fine-grained caches (extract, select, generate) and force AI re-run. ' +
@@ -20,7 +20,7 @@ export function registerImportCommand(program) {
20
20
  .option('--project <path>', 'Path to the project root to analyze', '.')
21
21
  .option('--out <path>', 'Output directory for pipeline artifacts')
22
22
  .option('--agent <name>', 'Agent to use for generate components (overrides credentials.json; falls back to "claude")')
23
- .option('--model <name>', 'Model to use for generate components (defaults to a small/fast model per agent)')
23
+ .option('--model <name>', 'Model to use for generate components (defaults to a cheap per-agent model; override with --model or EDS_AGENT_MODEL_<AGENT>)')
24
24
  .option('--tokens <path>', 'Path to a DTCG tokens.json file to push alongside generated components')
25
25
  .option('--raw-tokens <path>', 'Path to a raw token source file (SCSS, CSS variables, JS/TS, Style Dictionary, etc.) to classify and import alongside components. Bypasses the interactive token prompt.')
26
26
  .option('--select-all', 'Select all extracted components for generation (default)')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentful/experience-design-system-cli",
3
- "version": "2.15.1-dev-build-074e179.0",
3
+ "version": "2.15.1-dev-build-b11e821.0",
4
4
  "description": "Contentful Experiences design system import CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -34,8 +34,8 @@
34
34
  "react": "^18.3.1",
35
35
  "react-devtools-core": "^4.19.1",
36
36
  "react-dom": "^18.3.1",
37
- "@contentful/experience-design-system-extraction": "2.15.1-dev-build-074e179.0",
38
- "@contentful/experience-design-system-types": "2.15.1-dev-build-074e179.0"
37
+ "@contentful/experience-design-system-extraction": "2.15.1-dev-build-b11e821.0",
38
+ "@contentful/experience-design-system-types": "2.15.1-dev-build-b11e821.0"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@tsconfig/node24": "^24.0.3",