Skip to content

feat(cli)!: complete envelope migration — scene/timeline/pipeline/analyze (Issue #33 — 2c-sweep-3)#197

Merged
kiyeonjeon21 merged 1 commit intomainfrom
2c-sweep-3-scene-pipeline-analyze
Apr 28, 2026
Merged

feat(cli)!: complete envelope migration — scene/timeline/pipeline/analyze (Issue #33 — 2c-sweep-3)#197
kiyeonjeon21 merged 1 commit intomainfrom
2c-sweep-3-scene-pipeline-analyze

Conversation

@kiyeonjeon21
Copy link
Copy Markdown
Contributor

Summary

Final sweep of the `--json` envelope migration. After this PR, every CLI command's `--json` output uses the canonical envelope established in #194. The old `outputResult()` helper is now unused outside `output.ts` itself.

Files (8)

File Sites Quirk
`scene.ts` 16 3 failure-path `{...result}` spreads (compose-prompts, render, build) preserved with `process.exitCode = 1` from #193
`timeline.ts` 9 dry-run only; success-path JSON deferred to separate "2c-coverage" PR
`analyze.ts` 3 dropped local `--fields` filter (now handled by helper)
`pipeline.ts` 2 standard
`ai-highlights.ts` 2 standard
`ai-script-pipeline-cli.ts` 1 standard
`ai-review.ts` 1 standard
`output.ts` helper change — see below

Helper change: `--fields` now filters data keys

`outputSuccess()` now applies `--fields` filtering to keys inside `data`, leaving envelope meta (`command`, `elapsedMs`, `costUsd`, `warnings`, `dryRun`) intact. Matches the documented `--fields response,model` UX from `analyze.*` and avoids agents writing `--fields data` to keep the payload.

```

Old (envelope-level filter):

$ vibe analyze video x.mp4 "..." --fields response
{} # envelope had no top-level 'response' key

New (data-level filter):

$ vibe analyze video x.mp4 "..." --fields response
{ "command": "analyze video", "elapsedMs": ..., "costUsd": ..., "warnings": [], "data": { "response": "..." } }
```

Scene failure-path pattern (Quirk 1)

3 sites in `scene.ts` emit a result-shape JSON to stdout before setting `exitCode=1`. Metadata (which beat failed, which violation, etc.) is useful for agents diagnosing:

```ts
if (!result.success) {
if (isJsonMode()) {
outputSuccess({
command: "scene render",
startedAt,
data: { ...result }, // success: false, error, metadata
});
process.exitCode = 1;
return;
}
exitWithError(generalError(result.error ?? "Render failed"));
}
```

Function is still named `outputSuccess` even when emitting failure metadata — exit code 1 is the failure signal, `data.success: false` is metadata.

Smoke tests (verified locally)

```
$ vibe scene styles --json
{ "command": "scene styles", "costUsd": 0, "warnings": [], "data": { "count": 8, "styles": [...] } }

$ vibe timeline add-source proj.json /tmp/x.mp4 --dry-run --json
{ "command": "timeline add-source", "dryRun": true, "costUsd": 0, "warnings": [], "data": { "params": {...} } }
```

What 2c does NOT cover (deferred to 2c-coverage PR)

The audit (#192) flagged commands that lack `--json` entirely. This PR series only migrated commands that already emitted JSON. Coverage gaps still open:

  • `timeline.*` success paths emit no JSON (10 commands)
  • `scene render` / `scene build` / `scene add` partial `--json` (some paths still console.log only)
  • `export video` has no `--json` mode
  • `doctor` works but could surface more

These will go in a separate "2c-coverage" PR after the envelope is stable.

Migration summary across the whole 2c series

PR Commands migrated Sites
#194 (canary) `generate image` 5
#195 (sweep-1) 11 `generate.*` 27
#196 (sweep-2) edit/audio/detect/batch/project/export/run/init/etc. (13 files) ~55
#this (sweep-3) scene/timeline/pipeline/analyze (8 files) 34
Total ~33 files ~121 sites

Test plan

  • `pnpm -r exec tsc --noEmit` — 0 errors (full-repo, matches CI)
  • `pnpm lint` — 0 errors (8 pre-existing warnings)
  • `pnpm -F @vibeframe/cli exec vitest run` — 700 passed, 9 skipped, 0 failed
  • `grep "outputResult" packages/cli/src/commands/` — 0 hits (only `output.ts` still has the helper definition)
  • Smoke tests on scene styles + timeline add-source dry-run

…lyze (#33 — 2c-sweep-3)

BREAKING CHANGE: Final sweep of the --json envelope migration. After
this PR, every CLI command's --json output uses the canonical
outputSuccess() envelope established in #194. The old outputResult()
helper is now unused outside output.ts itself.

Files migrated (8):
- scene.ts (16 sites — 8 .action() blocks, includes 3 result-spread
  failure paths from #193 that emit metadata to stdout while exitCode=1)
- timeline.ts (9 sites — dry-run only; success path JSON deferred to
  separate "2c-coverage" PR)
- analyze.ts (3 sites — dropped local --fields filter; helper now
  handles --fields by filtering data keys)
- pipeline.ts (2 sites)
- ai-highlights.ts (2 sites)
- ai-script-pipeline-cli.ts (1 site)
- ai-review.ts (1 site)
- output.ts (helper change — see below)

## Helper change: --fields filters data keys (not envelope keys)

outputSuccess() now applies --fields filtering to keys *inside* data,
leaving envelope meta (command, elapsedMs, costUsd, warnings, dryRun)
intact. This matches the documented `--fields response,model` UX from
analyze.* and avoids agents needing `--fields data` to keep the payload.

Old (envelope-level): vibe analyze video x.mp4 "..." --fields response
  → { } (envelope had no `response` top-level key)

New (data-level):     vibe analyze video x.mp4 "..." --fields response
  → { command, elapsedMs, costUsd, warnings, data: { response } }

## Scene failure-path pattern (Quirk 1)

3 sites in scene.ts (compose-prompts L355-364, render L1170-1177, build
L1300-1306) emit a result-shape JSON to stdout *before* setting
exitCode=1. The metadata (which beat failed, which violation, etc.) is
useful for agents:

  if (!result.success) {
    if (isJsonMode()) {
      outputSuccess({
        command: "scene render",
        startedAt,
        data: { ...result },   // success: false, error, ...metadata
      });
      process.exitCode = 1;
      return;
    }
    exitWithError(generalError(result.error ?? "Render failed"));
  }

The function is still named outputSuccess even when emitting failure
metadata — exit code 1 is the failure signal, data.success: false is the
metadata. Agents should prefer exit code over data.success.

## Smoke tests (verified locally)

  $ vibe scene styles --json
  → { command: "scene styles", costUsd: 0, data: { count: 8, styles: [...] } }

  $ vibe timeline add-source proj.json /tmp/x.mp4 --dry-run --json
  → { command: "timeline add-source", dryRun: true, costUsd: 0, data: { params: {...} } }

## Known remaining work (not in this PR)

- timeline.* success paths emit no JSON (audit blocker, deferred to
  "2c-coverage" PR)
- scene render/build/styles need --json for non-error paths (most have
  it now via this sweep; styles list works, lint/install-skill have it,
  render/build still partial)
- doctor partial - works but could be richer
- export.video has no --json mode

Verification:
- pnpm -r exec tsc --noEmit: 0 errors
- pnpm lint: 0 errors (8 pre-existing warnings unrelated)
- pnpm -F @vibeframe/cli exec vitest run: 700 passed, 9 skipped, 0 failed
- grep "outputResult" packages/cli/src/commands/: 0 hits outside output.ts

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 28, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
vibeframe Ready Ready Preview, Comment Apr 28, 2026 3:34pm

Request Review

@kiyeonjeon21 kiyeonjeon21 merged commit 750681a into main Apr 28, 2026
5 checks passed
@kiyeonjeon21 kiyeonjeon21 deleted the 2c-sweep-3-scene-pipeline-analyze branch April 28, 2026 15:40
kiyeonjeon21 added a commit that referenced this pull request Apr 28, 2026
Bumps version 0.71.0 → 0.72.0 across all 7 package.json files and adds
the v0.72.0 CHANGELOG entry covering the breaking --json envelope
change shipped in #192/#193/#194/#195/#196/#197.

Auto-tag + auto-publish workflow will fire on merge:
- annotated tag v0.72.0
- @vibeframe/cli 0.72.0 → npm
- @vibeframe/mcp-server 0.72.0 → npm

Breaking summary (full table in CHANGELOG):
- jq .images / .video / .outputPath  →  jq .data.<same>
- jq .estimatedCost (string)         →  jq .costUsd (number)
- jq .success                        →  drop, use exit code

Errors unchanged (stderr StructuredError envelope).
Pre-1.0 → no transition shim, one-line migration via jq .data | …

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
kiyeonjeon21 added a commit that referenced this pull request Apr 28, 2026
…— 2e) (#200)

test(cli): snapshot tests for --describe schemas + --dry-run envelope drift (Issue #33 — 2e)

86 new snapshot tests at packages/cli/src/commands/envelope-snapshots.test.ts.
Catches drift in two channels:

1. **--describe schemas** (81 snapshots): every leaf command's JSON
   Schema, captured via `vibe <cmd> --describe` and checked against
   the committed snapshot. If you add/rename/remove an option,
   argument, or description, the snapshot diff makes the change
   explicit — accept with `vitest -u` after PR review, reject
   otherwise.
2. **--dry-run --json envelope shapes** (5 snapshots, representative
   slice): detect scenes (free), generate image (low cost), generate
   video (high cost / async), generate speech (medium), generate
   background (low). Captures the canonical envelope from #194/#197/#199
   so any future regression that re-flattens or renames keys fails CI.
   `elapsedMs` is normalized to "<elapsedMs>" before snapshotting.

Why these 5 dry-run cases (not all 81): every command goes through the
same outputSuccess() helper, so a drift in shape would trip ALL of
them at once. The 5 cover the major code paths (free/dry-run only,
low/medium/high cost annotation, async taskId pattern). Adding more
would catch the same regression with diminishing return.

Edit/audio commands skipped from dry-run cases — their `--dry-run` check
runs *after* file existence validation, so they need real fixture files
to exit cleanly. Their envelope shape comes from the same helper as
generate.* so it's already covered.

Maintenance:
- Intentional schema/envelope change: `pnpm -F @vibeframe/cli exec vitest run -u src/commands/envelope-snapshots.test.ts` updates snapshots; review the diff in PR.
- Unintentional change: failing test name tells you which command + channel.

Test runtime: ~60s (81 CLI spawns × ~120ms + overhead).
Suite total: 700 → 786 (+86), 9 skipped, 0 failed.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
kiyeonjeon21 added a commit that referenced this pull request Apr 28, 2026
…#201)

feat(cli): improve --describe enum extraction (Issue #33 — 2d)

Strengthens `extractEnumFromDescription` in commands/schema.ts so
commander descriptions surface more useful enum values in
`vibe <cmd> --describe` JSON Schema output. Agent prompt-generation
quality scales with how well the schema enumerates valid values.

## Before this PR

Only commander options whose description matched two narrow patterns
got `enum` arrays:
1. `Provider: a, b, c` (case-insensitive)
2. `^[A-Z][a-z]+:\s*a, b, c` (single-word label, lowercase values)

Most options describing valid values via parentheses or oxford-comma
prose did NOT get enums. Pre-PR survey: ~10 commands had enums.

## After this PR

42 options across 28 commands now have proper `enum` arrays. Extractor
now handles:

1. **Pattern 1 (extended)**: parentheticals are stripped before parsing
   so `Provider: openai (default ...), gemini, grok, runway` no longer
   truncates at the first `(`.
2. **Pattern 2 (extended)**: multi-word labels with up to 3 words
   (`Aspect ratio: 16:9, 9:16, 1:1`), and ratio-shaped values like
   `16:9` are accepted.
3. **Pattern 3 (new)**: parenthesized lists like
   `Aspect ratio (16:9, 9:16, 1:1)` extract correctly. Heuristic guards:
   skip `(default: ...)` annotations, reject if any value contains
   whitespace (indicates prose like `5 or 10`).
4. **Oxford comma**: `a, b, or c` properly produces `["a", "b", "c"]`
   (the `or ` prefix is stripped after trimming whitespace from the
   split-token).

## Selected new enums (full table in snapshot diff)

  generate.image --provider     ['openai', 'gemini', 'grok', 'runway']
  generate.video --provider     ['fal', 'grok', 'kling', 'runway', 'veo']
  generate.video --ratio        ['16:9', '9:16', '1:1']
  generate.video --resolution   ['720p', '1080p', '4k']
  generate.music --provider     ['elevenlabs', 'replicate']
  generate.thumbnail --style    ['youtube', 'instagram', 'tiktok', 'twitter']
  edit.caption --style          ['minimal', 'bold', 'outline', 'karaoke']
  edit.reframe --aspect         ['9:16', '1:1', '4:5']
  edit.reframe --focus          ['auto', 'face', 'center', 'action']
  edit.image --provider         ['gemini', 'openai', 'grok']
  edit.translate-srt --provider ['claude', 'openai']
  edit.upscale-video --model    ['real-esrgan', 'topaz']
  audio.transcribe --format     ['json', 'srt', 'vtt']
  pipeline.auto-shorts --aspect ['9:16', '1:1']
  project.create --ratio        ['16:9', '9:16', '1:1', '4:5']
  ... and 27 more

## Snapshot diff

The 2e snapshot tests caught 24 schema changes across 28 commands.
Updated via `vitest -u src/commands/envelope-snapshots.test.ts`. Each
diff shows exactly which option grew an enum array — perfect for PR
review.

## Issue #33 closure

This is the final sub-PR for Issue #33 (CLI UX audit). Status:

- 2a (audit baseline) — #192 ✓
- 2b (exit code enforcement) — #193 ✓
- 2c canary + sweeps — #194 / #195 / #196 / #197 ✓
- 2c-coverage (add --json to commands lacking it) — #199 ✓
- 2e (snapshot tests) — #200 ✓
- **2d (this PR — describe quality)**

After this merges, every leaf command emits a canonical envelope, has
descriptive `--describe` schemas with enum hints, and is gated against
drift by the 2e snapshot tests.

Verification:
- pnpm -r exec tsc --noEmit: 0 errors
- pnpm lint: 0 errors
- pnpm -F @vibeframe/cli exec vitest run: 786 passed, 9 skipped, 0 failed
- 24 snapshot updates accepted and verified idempotent

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant