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
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ jobs:
- name: Check native macOS picker metadata and profile transactions
run: go test ./internal/autocomplete -run '^TestPicker' -count=1 -v
- name: Check native macOS shell targeting and setup decisions
run: go test ./internal/commands/images ./pkg/custom -run '^TestImagePicker(Shell|FirstRun|TabChoice|MacOS)' -count=1 -v
run: go test ./internal/commands/images ./internal/app -run '^TestImagePicker(Shell|FirstRun|TabChoice|MacOS)' -count=1 -v

minimum-unix-shells:
name: Minimum Unix executable suggestions
Expand Down Expand Up @@ -880,7 +880,7 @@ jobs:
OPENAI_CLI_REQUIRE_NATIVE_SHELLS: cmd,powershell,pwsh
- name: Check native Windows download completion and cleanup
if: matrix.existing-checks
run: go test -p 2 ./pkg/custom ./internal/output -run '^(TestWindowsDownload|TestManagedDownload|TestDownload|TestAutomaticDownload|TestBinaryResponsePreserves|TestWriteBinaryResponse|TestReportSaveReceipt|TestSaveReceiptContext|TestResponseSaveReceipt)' -count=1 -v
run: go test -p 2 ./internal/app ./internal/output -run '^(TestWindowsDownload|TestManagedDownload|TestDownload|TestAutomaticDownload|TestBinaryResponsePreserves|TestWriteBinaryResponse|TestReportSaveReceipt|TestSaveReceiptContext|TestResponseSaveReceipt)' -count=1 -v
env:
GOMAXPROCS: '2'
- name: Check native Windows Batch stdin and private staging
Expand Down
18 changes: 9 additions & 9 deletions .github/workflows/help-compatibility.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ jobs:
GOMAXPROCS: '2'
run: |
set -euo pipefail
go test -p 1 ./cmd/openai ./internal/... ./pkg/custom -run '^$'
go test -p 1 ./cmd/openai ./internal/... -run '^$'
go build -p 1 -o "$RUNNER_TEMP/openai.exe" ./cmd/openai
go mod verify
Expand Down Expand Up @@ -503,7 +503,7 @@ jobs:
shell: bash
run: |
set -euo pipefail
go test -p 2 ./pkg/custom ./internal/output/listview -count=1 -timeout=2m -v -json -run '^(TestListPageFetcherWaitsAndPreservesPageSize|TestShowJSONPagesZeroRetainsRequestErrors|TestListPageFetcherStopsEmptyAndStalledCursors|TestListPageFetcherCancellationPreventsRequests|TestListNavigationUsesLoadedRowsBeforeOneRequest|TestListNavigationOutputPreservesFailureAndRestoresModes|TestListNavigationOutputFailureDiagnostic|TestListNavigationQuitRetainsRealFailures|TestListNavigationLargeFieldPreservedAndPrewrapped|TestListNavigationPrintPageCancelsWithoutFetching|TestListNavigationLabelsPreserveLongIDs|TestListNavigationNarrowFooterKeepsQuitAndPrintVisible|TestListNavigationErrorPresentation|TestListNavigationCompleteFirstPageFitsScreen|TestListNavigationContentFitsDisplayCells|TestListNavigationShortResultPreservesErrorsAndHistory|TestRenderListNavigationPageHintPolicy|TestRenderListNavigationPageCancelsProjection|TestRenderListTablePage|TestRenderListTablePageEmptyAndUnsupported)$' \
go test -p 2 ./internal/app ./internal/output/listview -count=1 -timeout=2m -v -json -run '^(TestListPageFetcherWaitsAndPreservesPageSize|TestShowJSONPagesZeroRetainsRequestErrors|TestListPageFetcherStopsEmptyAndStalledCursors|TestListPageFetcherCancellationPreventsRequests|TestListNavigationUsesLoadedRowsBeforeOneRequest|TestListNavigationOutputPreservesFailureAndRestoresModes|TestListNavigationOutputFailureDiagnostic|TestListNavigationQuitRetainsRealFailures|TestListNavigationLargeFieldPreservedAndPrewrapped|TestListNavigationPrintPageCancelsWithoutFetching|TestListNavigationLabelsPreserveLongIDs|TestListNavigationNarrowFooterKeepsQuitAndPrintVisible|TestListNavigationErrorPresentation|TestListNavigationCompleteFirstPageFitsScreen|TestListNavigationContentFitsDisplayCells|TestListNavigationShortResultPreservesErrorsAndHistory|TestRenderListNavigationPageHintPolicy|TestRenderListNavigationPageCancelsProjection|TestRenderListTablePage|TestRenderListTablePageEmptyAndUnsupported)$' \
| tee "$RUNNER_TEMP/list-navigation-helper-tests.jsonl"
go test -p 2 ./internal/output/transform -count=1 -timeout=2m -v -json -run '^(TestProjectListTableCanceledContext|TestListTableCountsCancellation|TestProjectListTableKnownResources|TestProjectListTableMissingOptionalFields|TestProjectListTableEmptyPagesAndRoutes|TestProjectListTablePreservesOriginalStrings|TestProjectListTableFallsBackForEntirePage|TestProjectListTablePreservesErrorsAndMalformedFields|TestProjectListTableByteSizes)$' \
| tee "$RUNNER_TEMP/list-projection-helper-tests.jsonl"
Expand All @@ -514,7 +514,7 @@ jobs:
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
go test -p 2 ./pkg/custom ./internal/output/listview -count=1 -timeout=2m -v -json -run '^(TestListPageFetcherWaitsAndPreservesPageSize|TestShowJSONPagesZeroRetainsRequestErrors|TestListPageFetcherStopsEmptyAndStalledCursors|TestListPageFetcherCancellationPreventsRequests|TestListNavigationUsesLoadedRowsBeforeOneRequest|TestListNavigationOutputPreservesFailureAndRestoresModes|TestListNavigationOutputFailureDiagnostic|TestListNavigationQuitRetainsRealFailures|TestListNavigationLargeFieldPreservedAndPrewrapped|TestListNavigationPrintPageCancelsWithoutFetching|TestListNavigationLabelsPreserveLongIDs|TestListNavigationNarrowFooterKeepsQuitAndPrintVisible|TestListNavigationErrorPresentation|TestListNavigationCompleteFirstPageFitsScreen|TestListNavigationContentFitsDisplayCells|TestListNavigationShortResultPreservesErrorsAndHistory|TestRenderListNavigationPageHintPolicy|TestRenderListNavigationPageCancelsProjection|TestRenderListTablePage|TestRenderListTablePageEmptyAndUnsupported)$' |
go test -p 2 ./internal/app ./internal/output/listview -count=1 -timeout=2m -v -json -run '^(TestListPageFetcherWaitsAndPreservesPageSize|TestShowJSONPagesZeroRetainsRequestErrors|TestListPageFetcherStopsEmptyAndStalledCursors|TestListPageFetcherCancellationPreventsRequests|TestListNavigationUsesLoadedRowsBeforeOneRequest|TestListNavigationOutputPreservesFailureAndRestoresModes|TestListNavigationOutputFailureDiagnostic|TestListNavigationQuitRetainsRealFailures|TestListNavigationLargeFieldPreservedAndPrewrapped|TestListNavigationPrintPageCancelsWithoutFetching|TestListNavigationLabelsPreserveLongIDs|TestListNavigationNarrowFooterKeepsQuitAndPrintVisible|TestListNavigationErrorPresentation|TestListNavigationCompleteFirstPageFitsScreen|TestListNavigationContentFitsDisplayCells|TestListNavigationShortResultPreservesErrorsAndHistory|TestRenderListNavigationPageHintPolicy|TestRenderListNavigationPageCancelsProjection|TestRenderListTablePage|TestRenderListTablePageEmptyAndUnsupported)$' |
Tee-Object -FilePath (Join-Path $env:RUNNER_TEMP 'list-navigation-helper-tests.jsonl') -Encoding utf8NoBOM -ErrorAction Stop
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
go test -p 2 ./internal/output/transform -count=1 -timeout=2m -v -json -run '^(TestProjectListTableCanceledContext|TestListTableCountsCancellation|TestProjectListTableKnownResources|TestProjectListTableMissingOptionalFields|TestProjectListTableEmptyPagesAndRoutes|TestProjectListTablePreservesOriginalStrings|TestProjectListTableFallsBackForEntirePage|TestProjectListTablePreservesErrorsAndMalformedFields|TestProjectListTableByteSizes)$' |
Expand Down Expand Up @@ -556,7 +556,7 @@ jobs:
set -euo pipefail
go test -p 2 ./cmd/openai -count=1 -timeout=3m -v -json -run '^(TestMainGlobalFlagsPlacementHeadersAndOutput|TestMainGlobalFlagsPrecedence|TestMainGlobalFlagsHeaderAliasesAndAuthorization|TestMainGlobalFlagsKeepEndpointFlagsAndLiterals|TestMainGlobalFlagsProjectBodyKeepsRootHeader|TestMainGlobalFlagsDeepAdminAndCustomCommands|TestMainGlobalFlagsHelpAndCompletionStayLocal|TestMainGlobalFlagsErrorOutputPlacement|TestMainGlobalFlagBaseURLOverridesMalformedEnvironment|TestMainGlobalFlagInvalidPercentBaseURLStillFails|TestMainGlobalFlagBaseURLOverridePreservesEnvironmentHeaders|TestMainGlobalFlagInvalidPercentBaseURLErrorsAreSafe|TestMainGlobalFlagEmptyBaseURLKeepsEnvironmentFallback|TestMainGlobalFlagEmptyMTLSOverridesEnvironment|TestMainGlobalFlagMTLSRejectsExplicitDuplicates|TestMainGlobalFlagCompletionScriptsIgnoreRequestConfiguration|TestMainGlobalFlagCompletionWordsRemainRequestValues|TestMainGlobalFlagRequestConfigurationStillValidates|TestMainGlobalFlagURLConfigurationPreservesOfflineHelp)$' \
| tee "$RUNNER_TEMP/request-configuration-public-tests.jsonl"
go test -p 2 ./pkg/custom -count=1 -timeout=2m -v -json -run '^(TestRootRequestFlagsPreserveOwnershipAndMetadata|TestRootRequestFlagsPreserveMTLSPlacement|TestRootRequestFlagsRetainSourcesAliasesAndValidation|TestMTLSClientFlagEnvironmentDoesNotConsumeExplicitValue)$' \
go test -p 2 ./internal/app -count=1 -timeout=2m -v -json -run '^(TestRootRequestFlagsPreserveOwnershipAndMetadata|TestRootRequestFlagsPreserveMTLSPlacement|TestRootRequestFlagsRetainSourcesAliasesAndValidation|TestMTLSClientFlagEnvironmentDoesNotConsumeExplicitValue)$' \
| tee "$RUNNER_TEMP/request-configuration-helper-tests.jsonl"
- name: Test Windows request configuration
if: matrix.request-configuration && runner.os == 'Windows' && matrix.shard != 'dispatch'
Expand All @@ -566,7 +566,7 @@ jobs:
go test -p 2 ./cmd/openai -count=1 -timeout=3m -v -json -run '^(TestMainGlobalFlagsPlacementHeadersAndOutput|TestMainGlobalFlagsPrecedence|TestMainGlobalFlagsHeaderAliasesAndAuthorization|TestMainGlobalFlagsKeepEndpointFlagsAndLiterals|TestMainGlobalFlagsProjectBodyKeepsRootHeader|TestMainGlobalFlagsDeepAdminAndCustomCommands|TestMainGlobalFlagsHelpAndCompletionStayLocal|TestMainGlobalFlagsErrorOutputPlacement|TestMainGlobalFlagBaseURLOverridesMalformedEnvironment|TestMainGlobalFlagInvalidPercentBaseURLStillFails|TestMainGlobalFlagBaseURLOverridePreservesEnvironmentHeaders|TestMainGlobalFlagInvalidPercentBaseURLErrorsAreSafe|TestMainGlobalFlagEmptyBaseURLKeepsEnvironmentFallback|TestMainGlobalFlagEmptyMTLSOverridesEnvironment|TestMainGlobalFlagMTLSRejectsExplicitDuplicates|TestMainGlobalFlagCompletionScriptsIgnoreRequestConfiguration|TestMainGlobalFlagCompletionWordsRemainRequestValues|TestMainGlobalFlagRequestConfigurationStillValidates|TestMainGlobalFlagURLConfigurationPreservesOfflineHelp)$' |
Tee-Object -FilePath (Join-Path $env:RUNNER_TEMP 'request-configuration-public-tests.jsonl') -Encoding utf8NoBOM -ErrorAction Stop
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
go test -p 2 ./pkg/custom -count=1 -timeout=2m -v -json -run '^(TestRootRequestFlagsPreserveOwnershipAndMetadata|TestRootRequestFlagsPreserveMTLSPlacement|TestRootRequestFlagsRetainSourcesAliasesAndValidation|TestMTLSClientFlagEnvironmentDoesNotConsumeExplicitValue)$' |
go test -p 2 ./internal/app -count=1 -timeout=2m -v -json -run '^(TestRootRequestFlagsPreserveOwnershipAndMetadata|TestRootRequestFlagsPreserveMTLSPlacement|TestRootRequestFlagsRetainSourcesAliasesAndValidation|TestMTLSClientFlagEnvironmentDoesNotConsumeExplicitValue)$' |
Tee-Object -FilePath (Join-Path $env:RUNNER_TEMP 'request-configuration-helper-tests.jsonl') -Encoding utf8NoBOM -ErrorAction Stop
exit $LASTEXITCODE
- name: Test Windows native shell and download contracts
Expand All @@ -580,7 +580,7 @@ jobs:
go test -p 2 ./cmd/openai -count=1 -timeout=3m -v -json -run '^TestMainShellWindowsNative$' |
Tee-Object -FilePath (Join-Path $env:RUNNER_TEMP 'windows-native-shell-tests.jsonl') -Encoding utf8NoBOM -ErrorAction Stop
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
go test -p 2 ./pkg/custom -count=1 -timeout=2m -v -json -run '^TestWindowsDownload(AutomaticConsole|PrivateCreation|PreservesExistingDACL)$' |
go test -p 2 ./internal/app -count=1 -timeout=2m -v -json -run '^TestWindowsDownload(AutomaticConsole|PrivateCreation|PreservesExistingDACL)$' |
Tee-Object -FilePath (Join-Path $env:RUNNER_TEMP 'windows-download-tests.jsonl') -Encoding utf8NoBOM -ErrorAction Stop
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
go test -p 2 ./internal/output -count=1 -timeout=2m -v -json -run '^TestWindowsDownloadPrivatePublication$' |
Expand Down Expand Up @@ -1415,7 +1415,7 @@ jobs:
suites["request-configuration-helper-tests.jsonl"] = set().union(*(
required_cases(root, children) for root, children in request_helper_cases.items()
))
strict_packages["request-configuration-helper-tests.jsonl"] = "github.com/openai/openai-cli/pkg/custom"
strict_packages["request-configuration-helper-tests.jsonl"] = "github.com/openai/openai-cli/internal/app"
if os.environ["RUNNER_OS"] == "Windows":
request_shell_root = "TestMainShellWindowsNative"
request_shell_cases = [
Expand Down Expand Up @@ -1452,7 +1452,7 @@ jobs:
suites["windows-download-tests.jsonl"] = set().union(*(
required_cases(root, children) for root, children in windows_download_cases.items()
))
strict_packages["windows-download-tests.jsonl"] = "github.com/openai/openai-cli/pkg/custom"
strict_packages["windows-download-tests.jsonl"] = "github.com/openai/openai-cli/internal/app"
suites["windows-download-publication-tests.jsonl"] = required_cases(
"TestWindowsDownloadPrivatePublication", ["hardlink", "exclusive_copy"]
)
Expand Down Expand Up @@ -1674,7 +1674,7 @@ jobs:
artifact: wsl-pager
- name: Command contracts on Ubuntu WSL 1
arguments: --command-contracts
package: ./pkg/custom
package: ./internal/app
artifact: wsl-command-contracts
steps:
- name: Reserve WSL cleanup time
Expand Down
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ it. Fix recurring generated defects in the authoritative schema or generator
when available instead of introducing broad manual changes.

Handwritten CLI features are moving into `internal/commands/<feature>/` (Codex,
tokenizer, manpages, files, batches, models, admin, audio, and images are there now); remaining behavior lives in `pkg/custom/`. API response selection
tokenizer, manpages, files, batches, models, admin, audio, and images are there now); application assembly and request/output policy live in `internal/app/`.
`pkg/custom/` is only a temporary adapter for generated callers. API response selection
and reshaping live in `internal/output/transform/`. Use cohesive `internal/` helpers for
shared implementation, including text rendering and terminal escaping.
`internal/commands/` owns command-tree routing, compatibility aliases, shortcuts,
Expand Down
12 changes: 7 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,10 @@ below describes the current layout as the migration proceeds.
guidance and request-input help annotations. It uses declared command/flag
names and escaped local invocation
metadata without echoing private operands or executing suggested corrections.
- `pkg/custom/` contains remaining handwritten CLI commands, defaults, output-format
policy, error handling, and presentation orchestration.
- `internal/app/` owns command assembly, defaults, request/output policy, error
handling, and presentation orchestration.
- `pkg/custom/` is a temporary adapter for generated callers; it contains no
independent behavior and will disappear when Castiron updates its imports.
- `internal/request/` owns root request inheritance, literal header and mTLS
flag definitions, value-safe validation, mTLS transport construction, and client
metadata setup and scalar-upload inspection/replay. Application assembly retains registration order and the local
Expand All @@ -97,8 +99,8 @@ below describes the current layout as the migration proceeds.
- `internal/testutil/console/` contains shared synthetic Windows console records
and observer fixtures used only by native feature tests.
- `internal/app/` owns invocation, request-environment scope, completion setup,
output policy, help assembly, and exit-status selection. Command assembly still uses the
existing generated tree during the layout migration.
output policy, help assembly, and exit-status selection. `cmd/openai` passes in the
generated tree; application code does not import generated commands.
- `api_reference/openapi.transformed.yml` contains the OpenAPI specification
used by the mock server.
- `scripts/` contains the local development, build, and test commands.
Expand All @@ -113,7 +115,7 @@ recurring generated problems in the authoritative schema or generator when
available.

Handwritten features are moving into `internal/commands/<feature>/`; Codex,
tokenizer, manpages, files, batches, models, admin, audio, and images are there now. Remaining behavior belongs in `pkg/custom/`,
tokenizer, manpages, files, batches, models, admin, audio, and images are there now. Application policy belongs in `internal/app/`,
`internal/output/transform/`, and focused `internal/` helpers. Keep
`cmd/openai/` focused on startup and wiring. Generated code calls handwritten
request/output helpers through a generated runtime bridge.
Expand Down
5 changes: 2 additions & 3 deletions cmd/openai/error_coverage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (
"github.com/openai/openai-cli/generated/commands"
"github.com/openai/openai-cli/internal/app"
"github.com/openai/openai-cli/internal/requestflag"
"github.com/openai/openai-cli/pkg/custom"
"github.com/urfave/cli/v3"
)

Expand Down Expand Up @@ -310,11 +309,11 @@ func TestMainErrorCoverageManifestProcess(t *testing.T) {
return
}
root := commands.Command
root.Flags = append(root.Flags, custom.NewRequestHeaderFlag())
root.Flags = append(root.Flags, app.NewRequestHeaderFlag())
if _, _, err := app.ConfigureHelp(root, []string{"openai", "--help"}); err != nil {
t.Fatal(err)
}
custom.ConfigureCommandErrors(root)
app.ConfigureCommandErrors(root)
manifest, err := buildErrorCoverageManifest(root)
if err != nil {
t.Fatal(err)
Expand Down
4 changes: 2 additions & 2 deletions cmd/openai/kitty_output_helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"github.com/openai/openai-cli/generated/commands"
batchcommands "github.com/openai/openai-cli/internal/commands/batches"
"github.com/openai/openai-cli/internal/terminalimage"
"github.com/openai/openai-cli/pkg/custom"
appconfig "github.com/openai/openai-cli/internal/app"
)

// Match executable dispatch when tests launch private helpers.
Expand Down Expand Up @@ -39,6 +39,6 @@ func TestMain(m *testing.M) {
}
os.Exit(0)
}
custom.ConfigureCommand(commands.Command)
appconfig.ConfigureCommand(commands.Command)
os.Exit(runShellTests(m))
}
3 changes: 2 additions & 1 deletion cmd/openai/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import (
"context"
"os"

"github.com/openai/openai-cli/generated/commands"
"github.com/openai/openai-cli/internal/app"
)

func main() {
if code := app.Run(context.Background(), os.Args); code != 0 {
if code := app.Run(context.Background(), os.Args, commands.Command); code != 0 {
os.Exit(code)
}
}
4 changes: 2 additions & 2 deletions cmd/openai/main_readable_errors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
"time"

"github.com/openai/openai-cli/generated/commands"
"github.com/openai/openai-cli/pkg/custom"
appconfig "github.com/openai/openai-cli/internal/app"
"github.com/urfave/cli/v3"
)

Expand Down Expand Up @@ -220,7 +220,7 @@ func TestMainWrappedExitCodeProcess(t *testing.T) {
},
}}}},
}
custom.ConfigureCommand(commands.Command)
appconfig.ConfigureCommand(commands.Command)
os.Args = []string{"openai", "synthetic", "fail"}
main()
os.Exit(0)
Expand Down
8 changes: 8 additions & 0 deletions docs/architecture/repository-layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -360,3 +360,11 @@ Optional first-run shell setup admission lives in `internal/app/`, alongside
invocation. Its existing terminal checks, synchronous audio command traversal,
argument copy, single-worker limit, and 250 ms foreground wait are unchanged.
Image shell installation mechanics remain in `internal/commands/images/`.

Application assembly, feature request/output policy, errors, and presentation
orchestration now live in `internal/app/`, with their tests. `cmd/openai` passes
the generated command tree to `app.Run`, so application policy never imports
generated commands. Shared request/output implementations keep their existing
packages. `pkg/custom/runtime.go` contains only the adapter required by current
Castiron-generated callers; remove it through the producer migration, rather than
editing generated files. No external Go import compatibility is maintained.
Loading
Loading