Skip to content

Commit 085985b

Browse files
authored
feat: add --json flag to all CLI commands for machine-readable output (#62)
### **User description** ## Summary All 7 CLI commands now support `--json` for machine-readable output: - **Batch commands** (`build`, `apply`, `init`, `register`, `promote`, `clear`): Output JSON envelope with results and summary - **Streaming commands** (`watch`): Output NDJSON streaming events ## Changes **New shared infrastructure:** - `BaseJsonOutput<T>` - Type-safe base envelope for all JSON output - `createBaseJsonOutput()` - Factory for consistent envelope creation - `writeJson()` - Centralized JSON output helper - `formatFatalError()` - Error handling for batch commands - `ndjsonEvent()` - NDJSON streaming for watch mode **Modified commands:** - `build`, `apply` - Use shared `output()` adapter with results/summary - `watch` - NDJSON streaming with `init`, `templateChanged`, `templateApplied`, `templateError`, `error` events - `init`, `register`, `promote`, `clear` - Per-command formatters extending `BaseJsonOutput` **DRY improvements:** - Unified command type in `RenderContext['command']` - `JsonOutput` extends `BaseJsonOutput` for stricter DRY - Eliminated duplicate JSON.stringify patterns across commands ## Test Plan - [x] All 607 tests pass - [x] TypeScript type checking passes - [x] Lint passes - [x] Manual testing of `--json` flag on all commands - [x] NDJSON streaming verified with `watch --json` ___ ### **PR Type** Enhancement, Tests, Documentation ___ ### **Description** - Added `--json` flag support to all 7 CLI commands (`build`, `apply`, `init`, `register`, `promote`, `clear`, `watch`) for machine-readable output - **Batch commands** (`build`, `apply`, `init`, `register`, `promote`, `clear`): Output JSON envelope with results and summary - **Streaming command** (`watch`): Output NDJSON streaming events (`init`, `templateChanged`, `templateApplied`, `templateError`, `error`) - Created shared JSON output infrastructure with `BaseJsonOutput<T>` type-safe envelope, `createBaseJsonOutput()` factory, `writeJson()` helper, and `formatFatalError()` for error handling - Implemented NDJSON streaming support via `ndjsonEvent()` function for watch mode - Added output adapter `output()` function for unified batch command handling - Created `toTemplateResults()` transformer for consistent result formatting across commands - Extended `RenderContext` to include all commands and `json` flag - Added comprehensive test coverage for all commands with JSON mode, including envelope structure, error handling, and branding suppression - Updated README with JSON output documentation and improved formatting - All 607 tests pass with full TypeScript type checking and lint compliance ___ ### Diagram Walkthrough ```mermaid flowchart LR CLI["CLI Commands<br/>build, apply, init, register,<br/>promote, clear, watch"] JSON["JSON Output<br/>Infrastructure"] BATCH["Batch Commands<br/>JSON Envelope"] STREAM["Watch Command<br/>NDJSON Stream"] OUTPUT["Output Adapter<br/>output()"] TRANSFORM["Result Transformer<br/>toTemplateResults()"] CLI -- "uses" --> OUTPUT CLI -- "uses" --> STREAM OUTPUT -- "uses" --> JSON STREAM -- "uses" --> JSON OUTPUT -- "uses" --> TRANSFORM JSON -- "creates" --> BATCH JSON -- "creates" --> STREAM ``` <details><summary><h3>File Walkthrough</h3></summary> <table><thead><tr><th></th><th align="left">Relevant files</th></tr></thead><tbody><tr><td><strong>Tests</strong></td><td><details><summary>11 files</summary><table> <tr> <td> <details> <summary><strong>build.test.ts</strong><dd><code>Add JSON output tests for build command</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> src/__tests__/build.test.ts <ul><li>Added mock for <code>output()</code> function to track calls instead of <br><code>renderResultsTable</code><br> <li> Updated tests to verify <code>output()</code> is called with <code>json</code> context flag<br> <li> Added comprehensive <code>--json</code> flag test suite covering JSON output, <br>branding skip, error handling, and fatal errors<br> <li> Tests verify both JSON and human-readable error modes</ul> </details> </td> <td><a href="https://github.com/t1mmen/srtd/pull/62/files#diff-02b8ff836283ba72f0a3933073f9c9cc5406a78aced205278d5f588327c7c2a6">+232/-8</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>apply.test.ts</strong><dd><code>Add JSON output tests for apply command</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> src/__tests__/apply.test.ts <ul><li>Added mock for <code>output()</code> function to track calls<br> <li> Updated existing tests to verify <code>output()</code> with <code>json</code> context flag<br> <li> Added <code>--json</code> flag test suite covering JSON output, branding skip, <br>error handling, and fatal errors<br> <li> Tests verify both JSON and human-readable error modes</ul> </details> </td> <td><a href="https://github.com/t1mmen/srtd/pull/62/files#diff-1476e03294e81bd76833f1fafc229abd893c6af95023d6799b402048b4a07e76">+167/-5</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>jsonOutput.test.ts</strong><dd><code>Add unit tests for JSON output formatting</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> src/output/jsonOutput.test.ts <ul><li>New test file for JSON output formatting functions<br> <li> Tests <code>formatJsonOutput()</code> for correct envelope structure and summary <br>calculations<br> <li> Tests <code>writeJson()</code> for proper formatting and newline handling<br> <li> Tests <code>createBaseJsonOutput()</code> for all command types<br> <li> Tests <code>formatFatalError()</code> for error envelope structure</ul> </details> </td> <td><a href="https://github.com/t1mmen/srtd/pull/62/files#diff-39ae23fb928c7b6d4c57a304798967ea00300ba304ce9d09f628e0d7231999f7">+207/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>watch.test.ts</strong><dd><code>Add JSON output tests for watch command</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> src/__tests__/watch.test.ts <ul><li>Added mock for <code>ndjsonEvent()</code> function<br> <li> Added tests for <code>--json</code> option existence and error handling<br> <li> Added new test suite for JSON mode covering no console output, no <br>rendering, no spinner<br> <li> Tests verify NDJSON error events are emitted on fatal errors</ul> </details> </td> <td><a href="https://github.com/t1mmen/srtd/pull/62/files#diff-38351fd290f501d4098211fdbf95c1be6a3e92aa47063b8e6bbbc7f0b87dd446">+103/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>clear.test.ts</strong><dd><code>Add JSON output tests for clear command</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> src/__tests__/clear.test.ts <ul><li>Added JSON output mode test suite<br> <li> Tests verify <code>--json</code> option support and JSON output structure<br> <li> Tests cover all clear modes (<code>--local</code>, <code>--shared</code>, <code>--reset</code>) with JSON<br> <li> Tests verify branding is skipped and errors are formatted as JSON<br> <li> Tests verify JSON mode requires explicit flag</ul> </details> </td> <td><a href="https://github.com/t1mmen/srtd/pull/62/files#diff-66dee6b8dc7ce05e8831e3a24b9484bf4e10fb260bb99ba1fe67c2479f6219f3">+119/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>resultTransformer.test.ts</strong><dd><code>Add unit tests for result transformation</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> src/utils/resultTransformer.test.ts <ul><li>New test file for result transformation logic<br> <li> Tests <code>toTemplateResults()</code> for both <code>apply</code> and <code>build</code> contexts<br> <li> Tests correct status mapping (success, unchanged, skipped, error)<br> <li> Tests WIP template detection and target field handling<br> <li> Tests timestamp serialization</ul> </details> </td> <td><a href="https://github.com/t1mmen/srtd/pull/62/files#diff-da5a34c5ca50bef2d193e9b16551964f469393257f091499b6d7839bab425942">+163/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>ndjsonOutput.test.ts</strong><dd><code>Add unit tests for NDJSON streaming output</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> src/output/ndjsonOutput.test.ts <ul><li>New test file for NDJSON streaming output<br> <li> Tests <code>ndjsonEvent()</code> outputs single-line JSON with newline<br> <li> Tests event structure includes type and timestamp<br> <li> Tests all event types (<code>init</code>, <code>templateChanged</code>, <code>templateApplied</code>, <br><code>templateError</code>, <code>error</code>)<br> <li> Tests data serialization including arrays and nested objects</ul> </details> </td> <td><a href="https://github.com/t1mmen/srtd/pull/62/files#diff-24c0397d9e76ec22716e542456889018c1f01d6f03ed5d9d4d2003d5211f5007">+125/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>promote.test.ts</strong><dd><code>Add JSON output tests for promote command</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> src/__tests__/promote.test.ts <ul><li>Added JSON output mode test suite<br> <li> Tests verify <code>--json</code> option support and JSON output structure<br> <li> Tests cover successful promotion and error cases with JSON<br> <li> Tests verify branding is skipped in JSON mode<br> <li> Tests verify error messages are included in JSON output</ul> </details> </td> <td><a href="https://github.com/t1mmen/srtd/pull/62/files#diff-6fd48bf3e8c56a8a8aabf7b10c84ccaf73b8110de66ceca55a1f282b1423e2b3">+96/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>register.test.ts</strong><dd><code>Add JSON output tests for register command</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> src/__tests__/register.test.ts <ul><li>Added JSON output mode test suite<br> <li> Tests verify <code>--json</code> option support and JSON output structure<br> <li> Tests cover successful registration, failures, and empty cases<br> <li> Tests verify branding is skipped in JSON mode<br> <li> Tests verify failed templates are included in JSON output</ul> </details> </td> <td><a href="https://github.com/t1mmen/srtd/pull/62/files#diff-030cfccb2c40beaae3e99e5d609a0daf38bca03ed053c276e45af846c7afab5d">+93/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>init.test.ts</strong><dd><code>Add JSON output tests for init command</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> src/__tests__/init.test.ts <ul><li>Added JSON output mode test suite<br> <li> Tests verify <code>--json</code> option support and JSON output structure<br> <li> Tests verify config object is included in JSON output<br> <li> Tests verify branding is skipped and console output is suppressed<br> <li> Tests verify error cases are formatted as JSON</ul> </details> </td> <td><a href="https://github.com/t1mmen/srtd/pull/62/files#diff-b86b43050f44f592b837394a8cf48659d25903840c130adf58beb313dd71c85e">+80/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>output.test.ts</strong><dd><code>Add unit tests for output adapter</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> src/output/output.test.ts <ul><li>New test file for output adapter function<br> <li> Tests <code>output()</code> branches to JSON when <code>context.json</code> is true<br> <li> Tests <code>output()</code> delegates to <code>renderResultsTable()</code> when JSON is false or <br>undefined<br> <li> Tests both JSON and human-readable output paths</ul> </details> </td> <td><a href="https://github.com/t1mmen/srtd/pull/62/files#diff-7951418d4b528fd8c5b5e8ecbf3975c168f5c1d8561c44a65c17b6632d5b7b21">+59/-0</a>&nbsp; &nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Enhancement</strong></td><td><details><summary>13 files</summary><table> <tr> <td> <details> <summary><strong>register.ts</strong><dd><code>Add JSON output support to register command</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> src/commands/register.ts <ul><li>Added <code>--json</code> option to command<br> <li> Refactored output to collect results in <code>RegisterResult</code> interface <br>instead of printing immediately<br> <li> Added <code>formatRegisterJsonOutput()</code> to format results as JSON envelope<br> <li> Conditional output: skips branding and console logs in JSON mode, <br>calls <code>writeJson()</code> instead<br> <li> Handles both interactive and non-interactive modes with JSON support</ul> </details> </td> <td><a href="https://github.com/t1mmen/srtd/pull/62/files#diff-46742469b545567f5f55dc3fe3bec111441af0bde75769921847280634c03fee">+146/-76</a></td> </tr> <tr> <td> <details> <summary><strong>promote.ts</strong><dd><code>Add JSON output support to promote command</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> src/commands/promote.ts <ul><li>Added <code>--json</code> option to command<br> <li> Refactored to return structured result objects instead of direct exit <br>codes<br> <li> Added <code>formatPromoteJsonOutput()</code> to format promotion results as JSON<br> <li> Conditional spinner and console output based on JSON mode<br> <li> Handles errors and success cases with JSON envelope</ul> </details> </td> <td><a href="https://github.com/t1mmen/srtd/pull/62/files#diff-ecee38a5be3a5fc38b4b9db99fbb0b069663f806030445987ec22835d59a034d">+110/-43</a></td> </tr> <tr> <td> <details> <summary><strong>watch.ts</strong><dd><code>Add NDJSON streaming output to watch command</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> src/commands/watch.ts <ul><li>Added <code>--json</code> option for NDJSON streaming output<br> <li> Skips branding, spinner, and validation warnings in JSON mode<br> <li> Emits NDJSON events for <code>init</code>, <code>templateChanged</code>, <code>templateApplied</code>, <br><code>templateError</code>, and <code>error</code><br> <li> Disables interactive keyboard handling and screen rendering in JSON <br>mode<br> <li> Conditional cleanup messages based on JSON mode</ul> </details> </td> <td><a href="https://github.com/t1mmen/srtd/pull/62/files#diff-0b5cda62a481fa4991723d74b8954950184e8f1425b36173df819200bf9b37e7">+83/-33</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>clear.ts</strong><dd><code>Add JSON output support to clear command</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> src/commands/clear.ts <ul><li>Added <code>--json</code> option to command<br> <li> Refactored to collect results and errors instead of printing <br>immediately<br> <li> Added <code>formatClearJsonOutput()</code> to format results as JSON envelope<br> <li> Conditional output: skips branding and console logs in JSON mode<br> <li> Requires explicit flag (<code>--local</code>, <code>--shared</code>, or <code>--reset</code>) in JSON mode</ul> </details> </td> <td><a href="https://github.com/t1mmen/srtd/pull/62/files#diff-ce728a666574fbfa1c5c0b3033d6bc21014e5e393e9e8df34d907628356a6baa">+100/-48</a></td> </tr> <tr> <td> <details> <summary><strong>init.ts</strong><dd><code>Add JSON output support to init command</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> src/commands/init.ts <ul><li>Added <code>--json</code> option to command<br> <li> Refactored to suppress console output in JSON mode<br> <li> Added <code>formatInitJsonOutput()</code> to format initialization results with <br>config<br> <li> Conditional output: skips branding and all status messages in JSON <br>mode<br> <li> Returns config object in JSON output</ul> </details> </td> <td><a href="https://github.com/t1mmen/srtd/pull/62/files#diff-ec8485d3117f43c2d2d8e3ec7b2830f2d6b5df3f20626a62b04f61ccd70ec93f">+92/-47</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>build.ts</strong><dd><code>Add JSON output support to build command</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> src/commands/build.ts <ul><li>Added <code>--json</code> option to command<br> <li> Replaced <code>renderResultsTable()</code> calls with new <code>output()</code> adapter function<br> <li> Imported <code>toTemplateResults()</code> transformer for unified result formatting<br> <li> Conditional branding and validation warnings based on JSON mode<br> <li> Fatal errors output as JSON when <code>--json</code> flag is used</ul> </details> </td> <td><a href="https://github.com/t1mmen/srtd/pull/62/files#diff-fb19954ad9c19d5004b1b36f4322497cb03dce0b6cf3377e2cf16873950c71a8">+30/-53</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>apply.ts</strong><dd><code>Add JSON output support to apply command</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> src/commands/apply.ts <ul><li>Added <code>--json</code> option to command<br> <li> Replaced <code>renderResultsTable()</code> calls with new <code>output()</code> adapter function<br> <li> Imported <code>toTemplateResults()</code> transformer for unified result formatting<br> <li> Conditional branding and validation warnings based on JSON mode<br> <li> Fatal errors output as JSON when <code>--json</code> flag is used</ul> </details> </td> <td><a href="https://github.com/t1mmen/srtd/pull/62/files#diff-7c83405e0f3b25c5c57c6c803b0bc371c951f5ac2950f862af11936a7f238806">+29/-47</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>jsonOutput.ts</strong><dd><code>Add shared JSON output infrastructure</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> src/output/jsonOutput.ts <ul><li>New file with shared JSON output infrastructure<br> <li> Exports <code>BaseJsonOutput<T></code> interface for type-safe envelopes<br> <li> Exports <code>createBaseJsonOutput()</code> factory for consistent envelope <br>creation<br> <li> Exports <code>writeJson()</code> for centralized JSON output with formatting<br> <li> Exports <code>formatFatalError()</code> and <code>formatJsonOutput()</code> for batch commands</ul> </details> </td> <td><a href="https://github.com/t1mmen/srtd/pull/62/files#diff-7e5d807b3933f99e78e089810977aec19548f286bf0f09a4ce3a23ff56ed13a7">+94/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>resultTransformer.ts</strong><dd><code>Add shared result transformation utility</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> src/utils/resultTransformer.ts <ul><li>New file with shared result transformation logic<br> <li> Exports <code>toTemplateResults()</code> to convert <code>ProcessedTemplateResult</code> to <br>unified <code>TemplateResult[]</code><br> <li> Handles context-specific transformations (build vs apply)<br> <li> Handles WIP template detection and status mapping<br> <li> Provides consistent ordering: skipped/unchanged first, then <br>applied/built, then errors</ul> </details> </td> <td><a href="https://github.com/t1mmen/srtd/pull/62/files#diff-f1c4b7f7833e85a9f305884fe1afc23171004ff95c4bebbd35b76863388ec476">+69/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>output.ts</strong><dd><code>Add output adapter for batch commands</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> src/output/output.ts <ul><li>New file with output adapter function for batch commands<br> <li> Exports <code>output()</code> function that branches based on <code>context.json</code> flag<br> <li> Delegates to <code>formatJsonOutput()</code> and <code>writeJson()</code> for JSON mode<br> <li> Delegates to <code>renderResultsTable()</code> for human-readable mode<br> <li> Provides unified interface for <code>build</code> and <code>apply</code> commands</ul> </details> </td> <td><a href="https://github.com/t1mmen/srtd/pull/62/files#diff-d6016bd21071be175daff90ed3a040e76adff327515b4cda00c196a8b4732272">+34/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>ndjsonOutput.ts</strong><dd><code>Add NDJSON streaming output for watch command</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> src/output/ndjsonOutput.ts <ul><li>New file with NDJSON streaming output for watch command<br> <li> Exports <code>ndjsonEvent()</code> function for emitting streaming events<br> <li> Defines <code>StreamEventType</code> union for all event types<br> <li> Defines <code>StreamEvent</code> interface with type, timestamp, and data<br> <li> Outputs single-line JSON with newline (NDJSON format)</ul> </details> </td> <td><a href="https://github.com/t1mmen/srtd/pull/62/files#diff-76c255faa9ee87a121fb225f58189aa91095464a93ebe711cc93589c64d77956">+32/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>types.ts</strong><dd><code>Extend RenderContext for all commands and JSON flag</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> src/ui/types.ts <ul><li>Extended <code>RenderContext['command']</code> to include all 7 commands: <code>init</code>, <br><code>build</code>, <code>apply</code>, <code>watch</code>, <code>register</code>, <code>promote</code>, <code>clear</code><br> <li> Added <code>json?: boolean</code> field to <code>RenderContext</code> for JSON output flag<br> <li> Updated type definitions to support unified command handling</ul> </details> </td> <td><a href="https://github.com/t1mmen/srtd/pull/62/files#diff-58661928000b918536937431da539c328fe3dad95292fc615eaa3e712b6c892e">+4/-1</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>index.ts</strong><dd><code>Add output module barrel exports</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> src/output/index.ts <ul><li>New barrel export file for output module<br> <li> Exports all JSON output functions and types from <code>jsonOutput.ts</code><br> <li> Exports NDJSON streaming functions and types from <code>ndjsonOutput.ts</code><br> <li> Exports output adapter function from <code>output.ts</code></ul> </details> </td> <td><a href="https://github.com/t1mmen/srtd/pull/62/files#diff-470b64f2c8933c9fe66461cebcc51744e4d0df1f46d8f35634af8ff4ac1e6914">+15/-0</a>&nbsp; &nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Documentation</strong></td><td><details><summary>2 files</summary><table> <tr> <td> <details> <summary><strong>json-output-mode.md</strong><dd><code>Document JSON output feature in changeset</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> .changeset/json-output-mode.md <ul><li>New changeset documenting the JSON output feature<br> <li> Describes <code>--json</code> flag support for all 7 commands<br> <li> Explains batch vs streaming output formats<br> <li> Notes shared base envelope structure<br> <li> Lists use cases for CI/CD and programmatic consumption</ul> </details> </td> <td><a href="https://github.com/t1mmen/srtd/pull/62/files#diff-f70f23925bd4736693c30083261dd561bfed7ec7ff6e74fd6c73a2cc4ec659f3">+10/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>README.md</strong><dd><code>Document JSON output support and improve formatting</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> README.md <ul><li>Removed extra blank lines throughout the document for cleaner <br>formatting<br> <li> Added comprehensive "JSON Output" section documenting <code>--json</code> flag <br>support across all commands<br> <li> Documented batch command JSON structure for <code>build</code>, <code>apply</code>, <code>register</code>, <br><code>promote</code>, <code>init</code>, and <code>clear</code><br> <li> Documented NDJSON streaming format for <code>watch --json</code> with event types <br>and examples<br> <li> Improved table formatting and alignment for Commands, Custom Migration <br>Paths, and State Tracking sections<br> <li> Added <code>srtd init</code> command to the Commands table</ul> </details> </td> <td><a href="https://github.com/t1mmen/srtd/pull/62/files#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5">+106/-40</a></td> </tr> </table></details></td></tr><tr><td><strong>Additional files</strong></td><td><details><summary>1 files</summary><table> <tr> <td><strong>Orchestrator.ts</strong></td> <td><a href="https://github.com/t1mmen/srtd/pull/62/files#diff-bb166ced099f77e72f212d1725e4059d1a376254cde0d592730534ac67203070">+0/-3</a>&nbsp; &nbsp; &nbsp; </td> </tr> </table></details></td></tr></tbody></table> </details> ___
1 parent 29d0d0e commit 085985b

27 files changed

+2364
-404
lines changed

.changeset/json-output-mode.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
"@t1mmen/srtd": minor
3+
---
4+
5+
Add `--json` flag to all commands for machine-readable output
6+
7+
- **Batch commands** (`build`, `apply`, `register`, `promote`, `clear`, `init`): Output single JSON object with command-specific structure
8+
- **Streaming commands** (`watch`): Output NDJSON (newline-delimited JSON) with events for template changes, applies, and errors
9+
- All commands share base envelope: `{ success, command, timestamp }` with command-specific fields
10+
- Useful for CI/CD pipelines, LLM integrations, and programmatic consumption

README.md

Lines changed: 46 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99

1010
[![demo](./readme-demo.gif)](./readme-demo.gif)
1111

12-
1312
## Why This Exists
1413

1514
Two things drove me crazy while building [Timely](https://www.timely.com)'s [Memory Engine](https://www.timely.com/memory-app) on Supabase:
@@ -22,7 +21,6 @@ Every function change showed up as a complete rewrite in git. Reviewers couldn't
2221

2322
After [searching](https://news.ycombinator.com/item?id=37755076) for [two years](https://news.ycombinator.com/item?id=36007640), I built `srtd`.
2423

25-
2624
## How It Works
2725

2826
Your functions, views, RLS policies, and triggers live in **template files**—plain SQL that's the source of truth.
@@ -42,7 +40,6 @@ supabase/migrations-templates/
4240
Edit template → Instantly applies locally → Build migration → Deploy
4341
```
4442

45-
4643
## Quick Start
4744

4845
```bash
@@ -71,7 +68,6 @@ srtd build # Creates supabase/migrations/20241226_srtd-hello.sql
7168
supabase migration up # Deploy with Supabase CLI
7269
```
7370

74-
7571
## The Diff Problem, Solved
7672

7773
Without templates, changing one line in a function means your PR shows a complete rewrite—the old `DROP` + `CREATE` replaced by a new one. Reviewers have to read the whole thing to spot your change.
@@ -90,38 +86,47 @@ With templates, your PR shows what you actually changed:
9086

9187
`git blame` works. Code reviews are useful. Your database logic is treated like real code.
9288

93-
9489
## Commands
9590

96-
| Command | What it does |
97-
|---------|--------------|
98-
| `srtd` | Interactive menu |
99-
| `srtd watch` | Live reload—applies templates on save |
100-
| `srtd build` | Generate migration files |
101-
| `srtd apply` | Apply all templates once (no watch) |
102-
| `srtd register` | Mark templates as already deployed |
103-
| `srtd promote` | Convert `.wip` template to buildable |
104-
| `srtd clear` | Reset build state |
91+
| Command | What it does |
92+
| --------------- | ------------------------------------- |
93+
| `srtd` | Interactive menu |
94+
| `srtd watch` | Live reload—applies templates on save |
95+
| `srtd build` | Generate migration files |
96+
| `srtd apply` | Apply all templates once (no watch) |
97+
| `srtd register` | Mark templates as already deployed |
98+
| `srtd promote` | Convert `.wip` template to buildable |
99+
| `srtd clear` | Reset build state |
100+
| `srtd init` | Initialize config file |
101+
102+
Options: `build --force` rebuilds all, `build --bundle` combines into single migration, `--no-deps` disables dependency ordering.
105103

106-
Options: `build --force` rebuilds all, `build --bundle` combines into single migration.
104+
## JSON Output
107105

106+
All commands support `--json` for machine-readable output (CI/CD, LLM integrations):
107+
108+
```bash
109+
srtd build --json # Single JSON object with results array and summary
110+
srtd watch --json # NDJSON stream (one event per line)
111+
```
112+
113+
Output includes `success`, `command`, `timestamp`, and command-specific fields. Errors use a top-level `error` field.
108114

109115
## What Works as Templates
110116

111117
Templates need to be **idempotent**—safe to run multiple times. This works great for:
112118

113-
| Object | Pattern |
114-
|--------|---------|
115-
| Functions | `DROP FUNCTION IF EXISTS` + `CREATE FUNCTION` |
116-
| Views | `CREATE OR REPLACE VIEW` |
117-
| RLS Policies | `DROP POLICY IF EXISTS` + `CREATE POLICY` |
118-
| Triggers | Drop + recreate trigger and function |
119-
| Roles | `REVOKE ALL` + `GRANT` |
120-
| Enums | `ADD VALUE IF NOT EXISTS` |
119+
| Object | Pattern |
120+
| ------------ | --------------------------------------------- |
121+
| Functions | `DROP FUNCTION IF EXISTS` + `CREATE FUNCTION` |
122+
| Views | `CREATE OR REPLACE VIEW` |
123+
| RLS Policies | `DROP POLICY IF EXISTS` + `CREATE POLICY` |
124+
| Triggers | Drop + recreate trigger and function |
125+
| Roles | `REVOKE ALL` + `GRANT` |
126+
| Enums | `ADD VALUE IF NOT EXISTS` |
121127

122128
**Not for templates:** Table structures, indexes, data modifications—use regular migrations for those.
123129

124-
125130
## WIP Templates
126131

127132
Experimenting? Add `.wip.sql` extension:
@@ -132,7 +137,6 @@ my_experiment.wip.sql → Applies locally, never builds to migration
132137

133138
When it's ready: `srtd promote my_experiment.wip.sql`
134139

135-
136140
## Template Dependencies
137141

138142
Declare dependencies between templates with `@depends-on` comments:
@@ -144,7 +148,6 @@ CREATE FUNCTION complex_calc() ...
144148

145149
During `apply` and `build`, templates are sorted so dependencies run first. Circular dependencies are detected and reported. Use `--no-deps` to disable.
146150

147-
148151
## Existing Projects
149152

150153
Already have functions in your database? Create templates for them, then:
@@ -155,7 +158,6 @@ srtd register existing_function.sql another_one.sql
155158

156159
This tells srtd "these are already deployed—don't generate migrations until they change."
157160

158-
159161
## Configuration
160162

161163
Defaults work for standard Supabase projects. Optional `srtd.config.json`:
@@ -172,67 +174,71 @@ Defaults work for standard Supabase projects. Optional `srtd.config.json`:
172174
}
173175
```
174176

175-
176177
## Custom Migration Paths
177178

178179
The `migrationFilename` option lets you match your project's existing migration structure using template variables:
179180

180-
| Variable | Description | Example |
181-
|----------|-------------|---------|
182-
| `$timestamp` | Build timestamp (YYYYMMDDHHmmss) | `20240315143022` |
183-
| `$migrationName` | Template name (without .sql) | `create_users` |
184-
| `$prefix` | Migration prefix with trailing dash | `srtd-` |
181+
| Variable | Description | Example |
182+
| ---------------- | ----------------------------------- | ---------------- |
183+
| `$timestamp` | Build timestamp (YYYYMMDDHHmmss) | `20240315143022` |
184+
| `$migrationName` | Template name (without .sql) | `create_users` |
185+
| `$prefix` | Migration prefix with trailing dash | `srtd-` |
185186

186187
### Examples
187188

188189
**Default (Supabase-style):**
190+
189191
```jsonc
190192
{ "migrationFilename": "$timestamp_$prefix$migrationName.sql" }
191193
// → migrations/20240315143022_srtd-create_users.sql
192194
```
193195

194196
**Directory per migration ([Prisma](https://prisma.io)-style):**
197+
195198
```jsonc
196199
{ "migrationFilename": "$timestamp_$migrationName/migration.sql" }
197200
// → migrations/20240315143022_create_users/migration.sql
198201
```
199202

200203
**Folder-based ([Issue #41](https://github.com/t1mmen/srtd/issues/41) request):**
204+
201205
```jsonc
202206
{ "migrationFilename": "$migrationName/migrate.sql", "migrationPrefix": "" }
203207
// → migrations/create_users/migrate.sql
204208
```
205209

206210
**Flyway-style (V prefix):**
211+
207212
```jsonc
208-
{ "migrationFilename": "V$timestamp__$migrationName.sql", "migrationPrefix": "" }
213+
{
214+
"migrationFilename": "V$timestamp__$migrationName.sql",
215+
"migrationPrefix": ""
216+
}
209217
// → migrations/V20240315143022__create_users.sql
210218
```
211219

212220
**Simple timestamp:**
221+
213222
```jsonc
214223
{ "migrationFilename": "$timestamp.sql", "migrationPrefix": "" }
215224
// → migrations/20240315143022.sql
216225
```
217226

218227
Nested directories are created automatically.
219228

220-
221229
## State Tracking
222230

223-
| File | Purpose | Git |
224-
|------|---------|-----|
225-
| `.buildlog.json` | What's been built to migrations | Commit |
231+
| File | Purpose | Git |
232+
| ---------------------- | ------------------------------- | --------- |
233+
| `.buildlog.json` | What's been built to migrations | Commit |
226234
| `.buildlog.local.json` | What's applied to your local DB | Gitignore |
227235

228-
229236
## Contributing
230237

231238
Bug fixes, docs, and test coverage welcome. See [CONTRIBUTING.md](./CONTRIBUTING.md).
232239

233240
For development: [CLAUDE.md](./CLAUDE.md).
234241

235-
236242
## More
237243

238244
- [Blog post](https://timm.stokke.me/blog/srtd-live-reloading-and-sql-templates-for-supabase)

0 commit comments

Comments
 (0)