create-claude-workspace 1.1.79 → 1.1.80

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.
@@ -407,7 +407,8 @@ export function runClaude(opts, log, runOpts = {}) {
407
407
  }
408
408
  // Flush remaining buffer
409
409
  if (buffer.trim()) {
410
- const event = parseStreamEvent(buffer.replace(/\r/g, ''));
410
+ const cleaned = buffer.replace(/\r/g, '');
411
+ const event = parseStreamEvent(cleaned);
411
412
  if (event) {
412
413
  if (event.session_id && !sessionId)
413
414
  sessionId = event.session_id;
@@ -416,6 +417,14 @@ export function runClaude(opts, log, runOpts = {}) {
416
417
  resultReceived = true;
417
418
  }
418
419
  }
420
+ else {
421
+ // Non-JSON buffer (e.g. "You've hit your limit" without trailing newline)
422
+ const lower = cleaned.toLowerCase();
423
+ detectTextSignals(lower);
424
+ if (USAGE_LIMIT_RE.test(lower) || RATE_LIMIT_RE.test(lower) || AUTH_ERROR_RE.test(lower)) {
425
+ stderr += cleaned + '\n';
426
+ }
427
+ }
419
428
  }
420
429
  // Final stderr-based detection
421
430
  detectTextSignals(stderr.toLowerCase());
@@ -431,6 +440,7 @@ export function runClaude(opts, log, runOpts = {}) {
431
440
  timedOut: killReason === 'process_timeout',
432
441
  activityTimedOut: killReason === 'activity_timeout',
433
442
  hasResult: resultReceived,
443
+ durationMs,
434
444
  });
435
445
  if (resolved)
436
446
  return;
@@ -49,7 +49,10 @@ export function classifyError(signals) {
49
49
  return category;
50
50
  }
51
51
  // Exit code 1 with no error indicators → likely max-turns
52
- if (signals.code === 1 && stderr.trim().length < 50)
52
+ // But only if process ran long enough (>10s). Instant exit (e.g. usage limit printed
53
+ // as plain text that wasn't caught) should NOT be classified as max-turns to prevent
54
+ // infinite restart loops with no backoff.
55
+ if (signals.code === 1 && stderr.trim().length < 50 && (signals.durationMs ?? 60_000) > 10_000)
53
56
  return 'max_turns';
54
57
  // Non-zero exit with content
55
58
  if (signals.code !== null && signals.code !== 0)
@@ -56,8 +56,11 @@ describe('classifyError', () => {
56
56
  it('detects context exhaustion from stderr', () => {
57
57
  expect(classifyError({ ...base, code: 1, stderr: 'context_length_exceeded' })).toBe('context_exhausted');
58
58
  });
59
- it('classifies exit code 1 with minimal stderr as max_turns', () => {
60
- expect(classifyError({ ...base, code: 1, stderr: '' })).toBe('max_turns');
59
+ it('classifies exit code 1 with minimal stderr as max_turns when process ran >10s', () => {
60
+ expect(classifyError({ ...base, code: 1, stderr: '', durationMs: 60_000 })).toBe('max_turns');
61
+ });
62
+ it('classifies instant exit (code 1, <10s, no stderr) as cli_crash not max_turns', () => {
63
+ expect(classifyError({ ...base, code: 1, stderr: '', durationMs: 1_000 })).toBe('cli_crash');
61
64
  });
62
65
  it('classifies exit code 1 with content as cli_crash', () => {
63
66
  expect(classifyError({ ...base, code: 1, stderr: 'Some unexpected internal error happened during execution blah blah' })).toBe('cli_crash');
@@ -674,9 +674,12 @@ describe('Loop integration: classifyError → getErrorAction → checkpoint', ()
674
674
  expect(classifyError({ ...base, code: 1, stderr: 'ENOTFOUND' })).toBe('network_error');
675
675
  expect(classifyError({ ...base, code: 1, stderr: 'context_length_exceeded' })).toBe('context_exhausted');
676
676
  });
677
- it('exit 1 with minimal stderr → max_turns', () => {
678
- expect(classifyError({ ...base, code: 1, stderr: '' })).toBe('max_turns');
679
- expect(classifyError({ ...base, code: 1, stderr: 'short' })).toBe('max_turns');
677
+ it('exit 1 with minimal stderr and long duration → max_turns', () => {
678
+ expect(classifyError({ ...base, code: 1, stderr: '', durationMs: 60_000 })).toBe('max_turns');
679
+ expect(classifyError({ ...base, code: 1, stderr: 'short', durationMs: 60_000 })).toBe('max_turns');
680
+ });
681
+ it('exit 1 with minimal stderr and instant exit → cli_crash (not max_turns)', () => {
682
+ expect(classifyError({ ...base, code: 1, stderr: '', durationMs: 2_000 })).toBe('cli_crash');
680
683
  });
681
684
  it('exit 1 with long stderr → cli_crash', () => {
682
685
  expect(classifyError({ ...base, code: 1, stderr: 'a'.repeat(60) })).toBe('cli_crash');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-claude-workspace",
3
- "version": "1.1.79",
3
+ "version": "1.1.80",
4
4
  "description": "Scaffold a project with Claude Code agents for autonomous AI-driven development",
5
5
  "type": "module",
6
6
  "bin": {