Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/improve-pgc-query-inputs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@khaale/postgres-cli": patch
---

Add per-query row-limit overrides and UTF-8 SQL-file input for `pgc`.
5 changes: 5 additions & 0 deletions .changeset/migrate-pgc-to-cmd-ts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@khaale/postgres-cli": patch
---

Align `pgc` command parsing and generated help with the `glc` and `ktc` CLI pattern.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-08-24
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
## Context

`pgc` currently parses all command-line options as strings, passes inline `options.sql` directly to `executeReadQuery`, and derives the effective row limit from the named session. Query execution already applies the session byte limit and statement timeout independently of the row limit. See `proposal.md` and `specs/postgres/query-inputs/spec.md` for the requested behavior.

## Goals / Non-Goals

**Goals:**

- Resolve one SQL source for `query` before opening a database connection.
- Validate a per-invocation positive row-limit override and pass it through the existing execution options.
- Read SQL files as UTF-8 in a platform-independent way, including files with a leading UTF-8 BOM.
- Keep byte and timeout limits sourced from the selected session and unchanged by the override.
- Make all input and file failures use the existing JSON error envelope and exit-code behavior.

**Non-Goals:**

- No unbounded or unlimited export mode.
- No row-limit override for schema listing `--limit` or the `compare` command in this change.
- No support for multiple statements; the existing read-only validator continues to reject them.
- No changes to PostgreSQL roles, connection settings, or output formats.

## Decisions

- Use the explicit flag name `--row-limit` rather than overloading `--limit`, because schema commands already use `--limit` for catalog pagination and the query option controls a safety bound.
- Resolve `--sql-file` in the CLI dispatch layer and pass the resulting SQL string through the existing `executeReadQuery` path. This keeps read-only validation, timeout setup, byte limiting, and truncation semantics in one execution path.
- Read files using the repository's injectable filesystem boundary where available, with UTF-8 decoding and a leading `\uFEFF` removed. This makes Windows PowerShell-generated files work without depending on shell quoting or platform-specific newline behavior.
- Validate `--row-limit` as a positive integer before `executeReadQuery` is called. The effective execution options should override only `rowLimit`; `statementTimeoutMs` and `byteLimit` continue to fall back to the session values.
- Treat `--sql` and `--sql-file` as mutually exclusive and require one source. File errors should be normalized at the CLI boundary so JSON and human-readable modes remain consistent with existing failures.

## Risks / Trade-offs

- [Risk] A larger row limit can increase memory and output size. → Keep the configured byte limit and statement timeout mandatory safety bounds, and expose truncation in the existing result shape.
- [Risk] A SQL file may contain a BOM or Windows line endings. → Decode as UTF-8 and strip only a leading BOM; leave SQL content otherwise unchanged for PostgreSQL validation.
- [Risk] File reads could become difficult to unit-test if they use global filesystem calls. → Inject the file-read capability through the existing runtime/dependency path and cover missing, UTF-8, and BOM cases with focused tests.
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
## Why

`pgc query` currently applies the configured row limit, which makes the default 1,000-row cap inconvenient for intentional larger reads even when the byte and statement-timeout safeguards are sufficient. Passing a complex SQL script through PowerShell command-line quoting is also error-prone on Windows, so users need a file-based SQL input path.

## What Changes

- Add a `query` command-line row-limit override that takes precedence over the selected session's configured `rowLimit`.
- Keep the selected session's `byteLimit` and `statementTimeoutMs` active for every query, including queries using the override.
- Add `query --sql-file PATH` as an alternative to inline `--sql`; read the file as UTF-8 and tolerate a leading UTF-8 BOM.
- Require exactly one SQL source (`--sql` or `--sql-file`) and return the existing structured error shape for invalid combinations or unreadable files.
- Preserve the existing read-only validation, truncation reporting, and secret-redaction behavior.

## Capabilities

### New Capabilities

- `postgres/query-inputs`: Define command-line row-limit overrides and UTF-8 SQL-file input for PostgreSQL queries.

### Modified Capabilities

<!-- No existing main capability spec currently owns the PostgreSQL query contract. -->

## Impact

- `packages/postgres-cli/src/cli.js` and query execution helpers for input resolution and limit precedence.
- PostgreSQL CLI README/help text and focused tests for CLI parsing, file encoding, and safety-limit preservation.
- No database schema, connection, or dependency changes.
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
## Purpose

Provide reliable, bounded ways to supply larger or complex read-only SQL queries to `pgc` across shells and operating systems.

## ADDED Requirements

### Requirement: Override the query row limit from the command line

The `query` command SHALL accept a positive integer `--row-limit` option that overrides the selected session's configured `rowLimit` for that invocation only. When the option is absent, the configured session limit SHALL remain effective.

#### Scenario: Command-line row limit overrides the session

- **WHEN** the user runs `pgc query` with `--row-limit 5000` and the selected session has `rowLimit` set to 1000
- **THEN** the query execution and result normalization use 5000 as the row limit for that invocation

#### Scenario: Configured row limit remains the default

- **WHEN** the user runs `pgc query` without `--row-limit`
- **THEN** the selected session's configured `rowLimit` is used

#### Scenario: Invalid row-limit values fail before execution

- **WHEN** `--row-limit` is missing a value or is not a positive integer
- **THEN** `pgc` returns a structured CLI error and does not connect to PostgreSQL

### Requirement: Preserve independent query safety limits

The `query` command SHALL continue applying the selected session's `byteLimit` and `statementTimeoutMs` regardless of whether `--row-limit` is supplied. A row-limit override SHALL NOT provide an option to disable or bypass either safety limit.

#### Scenario: Large row override remains byte-bounded

- **WHEN** a query is run with a row limit larger than the session's `byteLimit` can represent
- **THEN** the result is truncated at the byte limit and reports `truncated: true`

#### Scenario: Large row override remains time-bounded

- **WHEN** a query is run with any row-limit override
- **THEN** the session's configured statement timeout is still applied to the PostgreSQL transaction

### Requirement: Read SQL from a UTF-8 file

The `query` command SHALL accept `--sql-file PATH` as an alternative to `--sql`, read the referenced file as UTF-8, and execute the resulting SQL through the same read-only validation and safety limits as inline SQL. A leading UTF-8 BOM SHALL be ignored.

#### Scenario: Execute SQL from a file

- **WHEN** the user runs `pgc query --sql-file query.sql`
- **THEN** `pgc` reads the file as UTF-8 and executes its SQL using the selected session

#### Scenario: Preserve non-ASCII SQL content

- **WHEN** a UTF-8 SQL file contains non-ASCII identifiers, comments, or string literals
- **THEN** the SQL reaches validation and execution without shell-dependent re-encoding

#### Scenario: Missing or unreadable SQL file

- **WHEN** the path does not exist or cannot be read
- **THEN** `pgc` returns a structured CLI error identifying that the SQL file could not be read and does not execute a query

### Requirement: Require one SQL input source

The `query` command SHALL require exactly one of `--sql` and `--sql-file`. Supplying both or neither SHALL be rejected before PostgreSQL execution.

#### Scenario: Inline and file SQL are both supplied

- **WHEN** the user supplies both `--sql` and `--sql-file`
- **THEN** `pgc` returns a structured CLI error explaining that the SQL sources are mutually exclusive

#### Scenario: Neither SQL source is supplied

- **WHEN** the user runs `pgc query` without `--sql` or `--sql-file`
- **THEN** `pgc` returns a structured CLI error requiring one SQL source

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
## 1. Query input resolution

- [x] 1.1 Add positive-integer validation for `query --row-limit`, preserve the session value when omitted, and verify invalid values fail before a database client is created
- [x] 1.2 Add mutually exclusive `--sql`/`--sql-file` resolution with required-source validation, UTF-8 file reading, leading-BOM handling, and focused tests for inline, UTF-8, missing, unreadable, both-sources, and no-source cases

## 2. Bounded query execution

- [x] 2.1 Pass the resolved SQL and effective row limit through the existing read-only execution path, and verify the command-line limit overrides only `rowLimit`
- [x] 2.2 Verify byte-limit truncation and statement-timeout configuration remain active with a larger command-line row limit, including the existing structured result/error behavior

## 3. User experience and release metadata

- [x] 3.1 Update `pgc --help` and the PostgreSQL CLI README with `--row-limit`, `--sql-file`, Windows/UTF-8 usage, and the safety-limit behavior
- [x] 3.2 Update `skills/postgres-cli/SKILL.md` with the new query flags, a file-based SQL example, and guidance on retained byte/timeout safeguards
- [x] 3.3 Add a changeset for the user-facing `@khaale/postgres-cli` query input improvements and verify the package metadata remains publishable

## 4. Verification

- [x] 4.1 Run the PostgreSQL CLI tests and full `pnpm check`, confirming lint, tests, package smoke checks, and dry-run packaging all pass
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-08-24
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
## Context

`glc` and `ktc` already define their command trees with `cmd-ts`, use `runSafely` for parser failures, and adapt those failures to the repository's JSON/text error contract. `pgc` instead combines `createCliArgParser`, a static help string, and a hand-written dispatcher. The migration must preserve the behavior described in the new shared CLI command-interface specification and the existing PostgreSQL query-input specification.

## Goals / Non-Goals

**Goals:**

- Make `pgc` follow the same command-spec and parser-error flow as `glc` and `ktc`.
- Keep command handlers focused on PostgreSQL behavior rather than token parsing.
- Generate help from the command definitions and test parser behavior independently from database execution.
- Preserve JSON-by-default output, `--md`/`--csv`, named sessions, query safety limits, and all existing command paths.

**Non-Goals:**

- Do not redesign PostgreSQL operations, configuration, output rendering, or query safety.
- Do not change `cli-core` into a generic command-definition wrapper in this change.
- Do not migrate `glc` or `ktc`; they are the reference implementations for the pattern.

## Decisions

### Use the established direct `cmd-ts` pattern

Add `cmd-ts` as a runtime dependency of `@khaale/postgres-cli` and create a `pgc` command-spec module modeled on the existing `glc` and `ktc` modules. This keeps the command tree explicit and makes the implementation consistent with working repository examples.

An internal wrapper in `cli-core` is not introduced: the current tools do not share such a wrapper, and adding one would expand this refactor without solving a concrete `pgc` requirement. Shared config, errors, and output helpers remain in `cli-core` where they already belong.

### Keep handlers and normalize at the CLI boundary

Existing domain functions remain responsible for config resolution, PostgreSQL access, schema operations, comparisons, and output data. The command-spec handlers translate parsed arguments into the same option objects those functions already receive. The CLI entry point unwraps command results, handles `runSafely` parser failures, and preserves the existing structured JSON error envelope and text-stream routing.

### Represent option types in the command specification

Options such as positive row limits, numeric safety settings, CSV lists, and JSON query parameters will be parsed or validated at the command boundary where practical. Domain-level validation remains for rules that depend on the command semantics, such as exactly one SQL source. This separates generic argument errors from query-specific errors without changing their observable safety behavior.

### Treat generated help as the source of truth

The static help block will be removed after the command tree covers every currently supported resource, verb, alias, and option. Tests will assert key help content and that help does not initialize configuration or database access; README and skill examples will be updated only where generated usage differs materially.

## Risks / Trade-offs

- [Risk] `cmd-ts` help or parser wording differs from the current static text. → Mitigate by testing stable command/option presence and preserving exit codes and structured error fields rather than asserting incidental prose.
- [Risk] A command or flag is omitted during the hand-written command-tree migration. → Mitigate with a command inventory from the current dispatcher, focused parse tests for every resource, and the full monorepo check.
- [Risk] The runtime bundle resolves the new dependency differently from the workspace tests. → Mitigate by running the existing self-contained bundle smoke test for `pgc` and the repository-wide packaging checks.
- [Risk] Parser-level validation changes when configuration or database work starts. → Mitigate by testing invalid invocations with injected dependencies and asserting those dependencies are not called.

## Migration Plan

1. Add the command specification and map all current `pgc` commands and options to it.
2. Replace the custom parser/dispatch entry path while retaining the existing domain handlers.
3. Add parser, help, compatibility, and bundle smoke tests; update user-facing documentation and the package changeset.
4. Run strict OpenSpec validation and `pnpm check`.

Rollback is a source revert of the migration commit; no persisted configuration or database schema changes are involved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
## Why

`pgc` currently parses arguments with the low-level shared tokenizer and keeps its help text and command validation in a hand-written dispatcher. This differs from the `cmd-ts` command-spec pattern already used by `glc` and `ktc`, making help, option validation, and future command additions harder to keep consistent across the tools.

## What Changes

- Migrate `pgc` command parsing and dispatch to the repository's established `cmd-ts` command-spec pattern.
- Provide generated top-level and command-level help, including usage and option descriptions.
- Validate command arguments and options through the command specification, with the existing structured CLI error/output contract preserved.
- Preserve the current `pgc` commands, aliases, output formats, named-session behavior, and query options, including `--row-limit` and `--sql-file`.
- Add regression coverage for help, valid command invocations, invalid arguments, and the self-contained executable bundle.

## Capabilities

### New Capabilities

- `shared/cli-command-interface`: Standard command parsing, generated help, and structured validation behavior shared by the repository's CLI tools.

### Modified Capabilities

None.

## Impact

- `packages/postgres-cli/src/cli.js` and a new or updated command-spec module will own the `pgc` command tree and handlers.
- `@khaale/postgres-cli` will add or align its `cmd-ts` dependency and may have small help/error text changes as a result of generated output.
- Tests, README examples, and `skills/postgres-cli/SKILL.md` will be updated where the generated help or invocation contract is user-visible.
- The existing self-contained bundling path must continue to produce runnable `pgc`, `glc`, and `ktc` executables.
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
## Purpose

This capability gives the repository's CLI tools a predictable command interface with discoverable help, early argument validation, and errors suitable for both shell users and agents.

## ADDED Requirements

### Requirement: CLI tools SHALL expose discoverable command help

An adopting CLI tool SHALL expose top-level help through `--help` and through an invocation without a command. Nested resources and commands SHALL expose their own usage and option descriptions through the same help mechanism. Help requests MUST complete without loading a session or connecting to an external service.

#### Scenario: pgc top-level help

- **WHEN** the user runs `pgc --help` or `pgc` without a command
- **THEN** `pgc` prints its command overview and exits successfully without reading database credentials or opening a database connection

#### Scenario: pgc command help

- **WHEN** the user runs `pgc query --help` or `pgc schema search --help`
- **THEN** `pgc` prints usage and the options supported by that command and exits successfully without executing a query

### Requirement: CLI tools SHALL validate command arguments before execution

An adopting CLI tool SHALL reject unknown commands, unknown options, missing required options, and values that do not match the option type before invoking the command handler. Validation failures SHALL use exit code `2`; when JSON output is requested, they SHALL be represented by the standard structured error envelope.

#### Scenario: missing required option

- **WHEN** the user runs a command that requires a session without providing its session option
- **THEN** the CLI returns a structured validation error with exit code `2` and does not load a session or connect to PostgreSQL

#### Scenario: unknown option

- **WHEN** the user supplies an option that is not supported by the selected command
- **THEN** the CLI returns a structured validation error with exit code `2` before the command handler runs

#### Scenario: invalid typed value

- **WHEN** the user supplies a value that cannot be parsed as the selected option's type
- **THEN** the CLI returns a structured validation error with exit code `2` before the command handler runs

### Requirement: Migrated command interfaces SHALL preserve supported pgc invocations

The `pgc` command interface SHALL continue to support its existing resources and verbs, output mode flags, named-session options, query input options, schema exploration options, relationship options, and comparison options. In particular, query execution SHALL continue to accept exactly one of `--sql` and `--sql-file`, and SHALL retain the per-query `--row-limit` override.

#### Scenario: query input options remain available

- **WHEN** the user runs a valid `pgc query` command with either `--sql` or `--sql-file` and an optional `--row-limit`
- **THEN** `pgc` executes the same query-input behavior and applies the same safety limits as before the command-interface migration

#### Scenario: schema and comparison commands remain available

- **WHEN** the user runs a valid schema exploration, relationship, or two-session comparison command
- **THEN** `pgc` dispatches it to the corresponding existing functionality with the requested output format
Loading
Loading