@specmarket/cli 0.0.4 → 0.0.5

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.
@@ -3,7 +3,7 @@ import { mkdir, writeFile, readFile, access } from 'fs/promises';
3
3
  import { join, resolve } from 'path';
4
4
  import { homedir } from 'os';
5
5
  import { randomUUID } from 'crypto';
6
- import { execSync, exec } from 'child_process';
6
+ import { exec } from 'child_process';
7
7
  import { promisify } from 'util';
8
8
  import type { RunReport, LoopIteration, SuccessCriterionResult } from '@specmarket/shared';
9
9
  import {
@@ -17,6 +17,24 @@ import createDebug from 'debug';
17
17
  const debug = createDebug('specmarket:runner');
18
18
  const execAsync = promisify(exec);
19
19
 
20
+ /**
21
+ * Pre-flight check: Verifies that Claude CLI is installed before attempting to run a spec.
22
+ * Throws an error with installation instructions if claude is not found.
23
+ */
24
+ export async function checkClaudeCliInstalled(): Promise<void> {
25
+ try {
26
+ // Use 'which claude' to check if claude is in PATH
27
+ await execAsync('which claude');
28
+ } catch {
29
+ throw new Error(
30
+ `Claude CLI is not installed or not in your PATH.\n\n` +
31
+ `Installation instructions:\n` +
32
+ ` npm install -g @anthropic-ai/claude-code\n\n` +
33
+ `Or visit: https://www.anthropic.com/claude-code\n`
34
+ );
35
+ }
36
+ }
37
+
20
38
  export interface RunOptions {
21
39
  maxLoops?: number;
22
40
  maxBudgetUsd?: number;
@@ -39,10 +57,10 @@ export interface RunResult {
39
57
  * 1. Creates a sandboxed working directory under ~/.specmarket/runs/<run-id>/
40
58
  * 2. Copies spec files into the working directory
41
59
  * 3. Initializes git for diff tracking
42
- * 4. Executes: `cat PROMPT.md | claude-code --print` in a loop
60
+ * 4. Executes: `cat PROMPT.md | claude --print` in a loop
43
61
  * 5. After each loop: captures tokens, duration, git diff
44
62
  * 6. Checks for completion conditions:
45
- * - SUCCESS: fix_plan.md empty + tests pass + all SUCCESS_CRITERIA.md criteria met
63
+ * - SUCCESS: TASKS.md empty + tests pass + all SUCCESS_CRITERIA.md criteria met
46
64
  * - STALL: 3 consecutive loops with no git diff
47
65
  * - FAILURE: 10 consecutive loops with same failing output
48
66
  * - BUDGET: total tokens > 2x estimated_tokens
@@ -131,7 +149,7 @@ export async function runSpec(
131
149
 
132
150
  const iterStart = Date.now();
133
151
 
134
- // Execute: cat PROMPT.md | claude-code --print
152
+ // Execute: cat PROMPT.md | claude --print
135
153
  const result = await executeClaudeLoop(runDir, opts.model);
136
154
 
137
155
  const iterDuration = Date.now() - iterStart;
@@ -295,9 +313,9 @@ async function executeClaudeLoop(dir: string, model?: string): Promise<ExecuteRe
295
313
  args.push('--model', model);
296
314
  }
297
315
 
298
- // Execute: cat PROMPT.md | claude-code --print --output-format json
316
+ // Execute: cat PROMPT.md | claude --print --output-format json
299
317
  // Using --output-format json gives us structured output with token usage metadata.
300
- const proc = spawn('sh', ['-c', `cat PROMPT.md | claude-code ${args.join(' ')}`], {
318
+ const proc = spawn('sh', ['-c', `cat PROMPT.md | claude ${args.join(' ')}`], {
301
319
  cwd: dir,
302
320
  stdio: ['inherit', 'pipe', 'pipe'],
303
321
  });
@@ -318,7 +336,7 @@ async function executeClaudeLoop(dir: string, model?: string): Promise<ExecuteRe
318
336
  });
319
337
 
320
338
  proc.on('error', (err) => {
321
- debug('claude-code spawn error: %O', err);
339
+ debug('claude spawn error: %O', err);
322
340
  resolve({ stdout: '', exitCode: 1 });
323
341
  });
324
342
  });
@@ -328,7 +346,7 @@ async function executeClaudeLoop(dir: string, model?: string): Promise<ExecuteRe
328
346
  * Extracts token count from Claude Code's output.
329
347
  *
330
348
  * Strategy (in priority order):
331
- * 1. Parse JSON output format (claude-code --output-format json) which contains
349
+ * 1. Parse JSON output format (claude --output-format json) which contains
332
350
  * structured metadata including token counts in the response.
333
351
  * 2. Match known text patterns from Claude Code's output (total_tokens, etc.)
334
352
  * 3. Estimate from output length as a last-resort heuristic (~4 chars per token).
@@ -338,7 +356,7 @@ async function executeClaudeLoop(dir: string, model?: string): Promise<ExecuteRe
338
356
  function parseTokensFromOutput(output: string): number {
339
357
  if (!output || output.trim().length === 0) return 0;
340
358
 
341
- // Strategy 1: Parse JSON output format from claude-code --output-format json
359
+ // Strategy 1: Parse JSON output format from claude --output-format json
342
360
  // Claude Code JSON output may contain token usage info in the response metadata.
343
361
  try {
344
362
  // The output might be a single JSON object or newline-delimited JSON
@@ -429,7 +447,7 @@ interface CompletionCheck {
429
447
  }
430
448
 
431
449
  async function checkCompletion(dir: string): Promise<CompletionCheck> {
432
- // Check 1: fix_plan.md should be empty or have only checked items
450
+ // Check 1: TASKS.md should be empty or have only checked items
433
451
  const fixPlanEmpty = await isFixPlanEmpty(dir);
434
452
  if (!fixPlanEmpty) {
435
453
  return {
@@ -459,37 +477,48 @@ async function checkCompletion(dir: string): Promise<CompletionCheck> {
459
477
 
460
478
  async function isFixPlanEmpty(dir: string): Promise<boolean> {
461
479
  try {
462
- const content = await readFile(join(dir, 'fix_plan.md'), 'utf-8');
480
+ const content = await readFile(join(dir, 'TASKS.md'), 'utf-8');
463
481
  // Consider empty if: no unchecked items (- [ ] lines)
464
482
  const hasUncheckedItems = /^- \[ \]/m.test(content);
465
483
  return !hasUncheckedItems;
466
484
  } catch {
467
- // No fix_plan.md = considered empty
485
+ // No TASKS.md = considered empty
468
486
  return true;
469
487
  }
470
488
  }
471
489
 
472
490
  async function runTests(dir: string): Promise<boolean> {
473
- // Try to detect and run tests
491
+ // Try to detect and run tests using known test runner config files.
492
+ // Exit code is the primary failure signal; output regex is a fallback.
474
493
  const testRunners = [
475
- { file: 'package.json', cmd: 'npm test -- --run 2>&1 || true' },
476
- { file: 'vitest.config.ts', cmd: 'npx vitest run 2>&1 || true' },
477
- { file: 'pytest.ini', cmd: 'python -m pytest --tb=no -q 2>&1 || true' },
478
- { file: 'Makefile', cmd: 'make test 2>&1 || true' },
494
+ { file: 'package.json', cmd: 'npm test -- --run 2>&1' },
495
+ { file: 'vitest.config.ts', cmd: 'npx vitest run 2>&1' },
496
+ { file: 'pytest.ini', cmd: 'python -m pytest --tb=no -q 2>&1' },
497
+ { file: 'Makefile', cmd: 'make test 2>&1' },
479
498
  ];
480
499
 
481
500
  for (const runner of testRunners) {
482
501
  try {
483
502
  await access(join(dir, runner.file));
503
+ } catch {
504
+ continue; // Config file doesn't exist — try next runner
505
+ }
506
+
507
+ try {
484
508
  const { stdout, stderr } = await execAsync(runner.cmd, {
485
509
  cwd: dir,
486
510
  timeout: 120000,
487
511
  });
512
+ // Exit code 0 — check output as secondary signal
488
513
  const combined = stdout + stderr;
489
- // Heuristic: "failed" or "error" in output means tests failed
490
- const hasFailed = /\d+ failed|\d+ error|FAILED|ERROR/i.test(combined);
514
+ const hasFailed = /\d+ failed|\d+ error/i.test(combined);
491
515
  return !hasFailed;
492
- } catch {
516
+ } catch (err: unknown) {
517
+ // Non-zero exit code means tests failed
518
+ if (err && typeof err === 'object' && 'code' in err && typeof err.code === 'number') {
519
+ return false;
520
+ }
521
+ // Timeout or other execution error — skip to next runner
493
522
  continue;
494
523
  }
495
524
  }
@@ -1,4 +1,5 @@
1
1
  import type { RunReport } from '@specmarket/shared';
2
+ import type { Id } from '@specmarket/convex/dataModel';
2
3
  import { loadCredentials } from './auth.js';
3
4
  import { loadConfig } from './config.js';
4
5
  import { getConvexClient } from './convex-client.js';
@@ -52,7 +53,7 @@ export async function submitTelemetry(
52
53
  const client = await getConvexClient(creds.token);
53
54
 
54
55
  await client.mutation(api.runs.submit, {
55
- specId: report.specId as any,
56
+ specId: report.specId as Id<'specs'>,
56
57
  specVersion: report.specVersion,
57
58
  model: report.model,
58
59
  runner: report.runner,
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/lib/config.ts","../../shared/src/validators.ts","../../shared/src/constants.ts"],"sourcesContent":["import { readFile, writeFile, mkdir } from 'fs/promises';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport type { CliConfig } from '@specmarket/shared';\nimport { CONFIG_PATHS } from '@specmarket/shared';\nimport createDebug from 'debug';\n\nconst debug = createDebug('specmarket:cli');\n\nfunction getConfigDir(): string {\n return join(homedir(), CONFIG_PATHS.DIR);\n}\n\nfunction getConfigPath(): string {\n return join(homedir(), CONFIG_PATHS.CONFIG);\n}\n\n/**\n * Ensures the ~/.specmarket directory exists.\n */\nasync function ensureConfigDir(): Promise<void> {\n await mkdir(getConfigDir(), { recursive: true });\n}\n\n/**\n * Loads CLI configuration from ~/.specmarket/config.json.\n * Returns default config if the file doesn't exist.\n */\nexport async function loadConfig(): Promise<CliConfig> {\n try {\n const raw = await readFile(getConfigPath(), 'utf-8');\n return JSON.parse(raw) as CliConfig;\n } catch {\n debug('No config file found, using defaults');\n return {};\n }\n}\n\n/**\n * Saves CLI configuration to ~/.specmarket/config.json.\n */\nexport async function saveConfig(config: CliConfig): Promise<void> {\n await ensureConfigDir();\n await writeFile(getConfigPath(), JSON.stringify(config, null, 2), 'utf-8');\n debug('Config saved to %s', getConfigPath());\n}\n\n/**\n * Gets a specific config value by key.\n */\nexport async function getConfigValue<K extends keyof CliConfig>(\n key: K\n): Promise<CliConfig[K]> {\n const config = await loadConfig();\n return config[key];\n}\n\n/**\n * Sets a specific config value and persists.\n */\nexport async function setConfigValue<K extends keyof CliConfig>(\n key: K,\n value: CliConfig[K]\n): Promise<void> {\n const config = await loadConfig();\n config[key] = value;\n await saveConfig(config);\n}\n","import { z } from 'zod';\n\nexport const outputTypeSchema = z.enum([\n 'web-app',\n 'cli-tool',\n 'api-service',\n 'library',\n 'mobile-app',\n]);\n\nexport const primaryStackSchema = z.enum([\n 'nextjs-typescript',\n 'astro-typescript',\n 'python-fastapi',\n 'go',\n 'rust',\n 'other',\n]);\n\nexport const serviceCategorySchema = z.enum([\n 'database',\n 'auth',\n 'storage',\n 'cdn',\n 'dns',\n 'email',\n 'sms',\n 'payments',\n 'search',\n 'analytics',\n 'monitoring',\n 'queue',\n 'cache',\n 'compute',\n 'pdf',\n 'ai',\n 'other',\n]);\n\n/** Provider schema (camelCase) — for Convex DB */\nexport const providerSchema = z.object({\n name: z.string().min(1),\n freeTier: z.boolean(),\n paidStartsUsd: z.number().min(0).optional(),\n notes: z.string().optional(),\n});\n\n/** Provider schema (snake_case) — for spec.yaml parsing */\nexport const providerSchemaYaml = z.object({\n name: z.string().min(1),\n free_tier: z.boolean(),\n paid_starts_usd: z.number().min(0).optional(),\n notes: z.string().optional(),\n});\n\n// snake_case for spec.yaml parsing\nexport const serviceSchemaYaml = z.object({\n category: serviceCategorySchema,\n name: z.string().min(1),\n purpose: z.string().min(1),\n required: z.boolean(),\n user_provided: z.boolean().optional(),\n providers: z.array(providerSchemaYaml).min(1),\n default_provider: z.string().optional(),\n setup_notes: z.string().optional(),\n});\n\n// camelCase for Convex DB\nexport const serviceSchema = z.object({\n category: serviceCategorySchema,\n name: z.string().min(1),\n purpose: z.string().min(1),\n required: z.boolean(),\n userProvided: z.boolean().optional(),\n providers: z.array(providerSchema).min(1),\n defaultProvider: z.string().optional(),\n setupNotes: z.string().optional(),\n});\n\n// snake_case for spec.yaml parsing\nexport const infrastructureSchemaYaml = z.object({\n monthly_cost: z.object({\n free_tier_usd: z.number().min(0),\n production_usd: z.number().min(0),\n }),\n services: z.array(serviceSchemaYaml).default([]),\n user_provided: z.array(z.string()).default([]),\n setup_time_minutes: z.number().min(0).optional(),\n deployment_targets: z\n .array(\n z.object({\n name: z.string().min(1),\n notes: z.string().optional(),\n })\n )\n .optional(),\n});\n\n// camelCase for Convex DB\nexport const infrastructureSchema = z.object({\n monthlyCost: z.object({\n freeTierUsd: z.number().min(0),\n productionUsd: z.number().min(0),\n }),\n services: z.array(serviceSchema).default([]),\n userProvided: z.array(z.string()).default([]),\n setupTimeMinutes: z.number().min(0).optional(),\n deploymentTargets: z\n .array(\n z.object({\n name: z.string().min(1),\n notes: z.string().optional(),\n })\n )\n .optional(),\n});\n\n// Full spec.yaml schema (snake_case as written by users)\nexport const specYamlSchema = z.object({\n name: z\n .string()\n .min(1)\n .regex(/^[a-z0-9-]+$/, 'name must be lowercase alphanumeric with hyphens'),\n display_name: z.string().min(1),\n description: z.string().min(10),\n replaces_saas: z.string().optional(),\n replaces_pricing: z.string().optional(),\n output_type: outputTypeSchema,\n primary_stack: primaryStackSchema,\n version: z\n .string()\n .regex(\n /^\\d+\\.\\d+\\.\\d+$/,\n 'version must be semver (e.g. 1.0.0)'\n ),\n runner: z.literal('claude-code'),\n min_model: z.string().min(1),\n estimated_tokens: z.number().int().positive(),\n estimated_cost_usd: z.number().positive(),\n estimated_time_minutes: z.number().positive(),\n tags: z.array(z.string()).default([]),\n infrastructure: infrastructureSchemaYaml.optional(),\n // Fork metadata (added by CLI when forking)\n forked_from_id: z.string().optional(),\n forked_from_version: z.string().optional(),\n});\n\nexport const runStatusSchema = z.enum([\n 'success',\n 'failure',\n 'stall',\n 'budget_exceeded',\n 'user_cancelled',\n]);\n\nexport const specStatusSchema = z.enum([\n 'published',\n 'draft',\n 'flagged',\n 'removed',\n]);\n\nexport const userRoleSchema = z.enum(['user', 'creator', 'admin']);\n\nexport const bountyFundingTypeSchema = z.enum([\n 'company',\n 'crowdfund',\n 'mixed',\n]);\n\nexport const bountyStatusSchema = z.enum([\n 'open',\n 'claimed',\n 'awarded',\n 'expired',\n 'cancelled',\n]);\n\nexport const bountySubmissionStatusSchema = z.enum([\n 'submitted',\n 'accepted',\n 'rejected',\n]);\n\nexport const successCriterionResultSchema = z.object({\n criterion: z.string().min(1),\n passed: z.boolean(),\n details: z.string().optional(),\n});\n\n/** Transform spec.yaml (snake_case infra) to Convex-friendly camelCase */\nexport function transformInfrastructure(\n yaml: z.infer<typeof infrastructureSchemaYaml>\n): z.infer<typeof infrastructureSchema> {\n return {\n monthlyCost: {\n freeTierUsd: yaml.monthly_cost.free_tier_usd,\n productionUsd: yaml.monthly_cost.production_usd,\n },\n services: yaml.services.map((s) => ({\n category: s.category,\n name: s.name,\n purpose: s.purpose,\n required: s.required,\n userProvided: s.user_provided,\n providers: s.providers.map((p) => ({\n name: p.name,\n freeTier: p.free_tier,\n paidStartsUsd: p.paid_starts_usd,\n notes: p.notes,\n })),\n defaultProvider: s.default_provider,\n setupNotes: s.setup_notes,\n })),\n userProvided: yaml.user_provided,\n setupTimeMinutes: yaml.setup_time_minutes,\n deploymentTargets: yaml.deployment_targets,\n };\n}\n","/** CLI exit codes */\nexport const EXIT_CODES = {\n /** Success */\n SUCCESS: 0,\n /** Validation error (bad spec, invalid args) */\n VALIDATION_ERROR: 1,\n /** Runtime error (loop failed, crash) */\n RUNTIME_ERROR: 2,\n /** Budget exceeded */\n BUDGET_EXCEEDED: 3,\n /** Network error (Convex unreachable, upload failed) */\n NETWORK_ERROR: 4,\n /** Auth error (not logged in, token expired) */\n AUTH_ERROR: 5,\n} as const;\n\nexport type ExitCode = (typeof EXIT_CODES)[keyof typeof EXIT_CODES];\n\n/** Default file paths (relative to $HOME) */\nexport const CONFIG_PATHS = {\n /** Directory for all SpecMarket CLI state */\n DIR: '.specmarket',\n /** Auth credentials file */\n CREDENTIALS: '.specmarket/credentials.json',\n /** CLI config file */\n CONFIG: '.specmarket/config.json',\n /** Run artifacts directory */\n RUNS_DIR: '.specmarket/runs',\n} as const;\n\n/** Default run behavior */\nexport const RUN_DEFAULTS = {\n /** Maximum loop iterations before forced stop */\n MAX_LOOPS: 50,\n /** Budget multiplier: stop if cost > estimated * this */\n BUDGET_MULTIPLIER: 2,\n /** Loops with no git diff before declaring stall */\n STALL_THRESHOLD: 3,\n /** Loops with the same failing test before declaring failure */\n FAILURE_THRESHOLD: 10,\n} as const;\n\n/** Rate limits (client-side awareness, enforced server-side too) */\nexport const RATE_LIMITS = {\n /** Max telemetry submissions per hour */\n RUNS_PER_HOUR: 10,\n /** Max spec publishes per day */\n PUBLISHES_PER_DAY: 5,\n /** Max ratings per day */\n RATINGS_PER_DAY: 20,\n} as const;\n\n/** Credential expiry */\nexport const TOKEN_EXPIRY_MS = 30 * 24 * 60 * 60 * 1000; // 30 days\n\n/** Runner identifier */\nexport const RUNNER_ID = 'claude-code' as const;\n\n/** Default Convex deployment URL (overridable via config or env) */\nexport const DEFAULT_CONVEX_URL =\n process.env['CONVEX_URL'] ?? 'https://clever-bird-665.convex.cloud';\n\n/** Required files in a valid spec directory */\nexport const REQUIRED_SPEC_FILES = [\n 'spec.yaml',\n 'PROMPT.md',\n 'SPEC.md',\n 'SUCCESS_CRITERIA.md',\n] as const;\n\n/** Required files in stdlib/ directory */\nexport const REQUIRED_STDLIB_FILES = ['STACK.md'] as const;\n\n/** Debug log namespaces */\nexport const DEBUG_NAMESPACES = {\n CLI: 'specmarket:cli',\n CONVEX: 'specmarket:convex',\n RUNNER: 'specmarket:runner',\n AUTH: 'specmarket:auth',\n TELEMETRY: 'specmarket:telemetry',\n} as const;\n"],"mappings":"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","names":[]}