← back to Cli Printing Press
feat(pipeline): copy spec into output dir after generation
a00ebdb2e41256807f15440b6eeab46af9f79ece · 2026-03-27 21:41:24 -0700 · Trevin Chow
Adds copySpecToOutput helper that saves the source spec as
<output-dir>/spec.json when specFlag is "--spec". This enables
standalone scoring without needing the original spec path.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Files touched
M internal/pipeline/fullrun.goM internal/pipeline/fullrun_test.go
Diff
commit a00ebdb2e41256807f15440b6eeab46af9f79ece
Author: Trevin Chow <trevin@trevinchow.com>
Date: Fri Mar 27 21:41:24 2026 -0700
feat(pipeline): copy spec into output dir after generation
Adds copySpecToOutput helper that saves the source spec as
<output-dir>/spec.json when specFlag is "--spec". This enables
standalone scoring without needing the original spec path.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---
internal/pipeline/fullrun.go | 23 ++++++++++++++
internal/pipeline/fullrun_test.go | 66 +++++++++++++++++++++++++++++++++++++++
2 files changed, 89 insertions(+)
diff --git a/internal/pipeline/fullrun.go b/internal/pipeline/fullrun.go
index 749a2c0c..77204e96 100644
--- a/internal/pipeline/fullrun.go
+++ b/internal/pipeline/fullrun.go
@@ -106,6 +106,11 @@ func MakeBestCLI(apiName, level, specFlag, specURL, outputDir, pressBinary strin
return result
}
+ // Step 2.1: Copy spec into output dir for standalone scoring
+ if err := copySpecToOutput(specFlag, specURL, outputDir); err != nil {
+ result.Errors = append(result.Errors, fmt.Sprintf("spec copy: %v", err))
+ }
+
// Step 2.5: LLM Polish
polishResult, polishErr := llmpolish.Polish(llmpolish.PolishRequest{
APIName: apiName,
@@ -417,6 +422,24 @@ func PrintComparisonTable(results []*FullRunResult) string {
return b.String()
}
+// copySpecToOutput copies the source spec file into <outputDir>/spec.json
+// so that standalone scoring can find it without the original spec path.
+// Only copies when specFlag is "--spec" (local file). Errors are non-fatal.
+func copySpecToOutput(specFlag, specURL, outputDir string) error {
+ if specFlag != "--spec" {
+ return nil
+ }
+ data, err := os.ReadFile(specURL)
+ if err != nil {
+ return fmt.Errorf("reading spec %s: %w", specURL, err)
+ }
+ dst := filepath.Join(outputDir, "spec.json")
+ if err := os.WriteFile(dst, data, 0o644); err != nil {
+ return fmt.Errorf("writing %s: %w", dst, err)
+ }
+ return nil
+}
+
func writeRow(b *strings.Builder, label string, results []*FullRunResult, fn func(*FullRunResult) string) {
b.WriteString(fmt.Sprintf("%-25s", label))
for _, r := range results {
diff --git a/internal/pipeline/fullrun_test.go b/internal/pipeline/fullrun_test.go
index 997433bb..5a8608db 100644
--- a/internal/pipeline/fullrun_test.go
+++ b/internal/pipeline/fullrun_test.go
@@ -62,6 +62,72 @@ func TestFullRun(t *testing.T) {
fmt.Printf("Full results at: %s\n", baseDir)
}
+func TestCopySpecToOutput(t *testing.T) {
+ tests := []struct {
+ name string
+ specFlag string
+ setup func(t *testing.T, dir string) string // returns specURL
+ wantCopy bool
+ wantError bool
+ }{
+ {
+ name: "copies spec when flag is --spec",
+ specFlag: "--spec",
+ setup: func(t *testing.T, dir string) string {
+ specPath := filepath.Join(dir, "input-spec.json")
+ require.NoError(t, os.WriteFile(specPath, []byte(`{"openapi":"3.0.0"}`), 0o644))
+ return specPath
+ },
+ wantCopy: true,
+ },
+ {
+ name: "skips when flag is --docs",
+ specFlag: "--docs",
+ setup: func(t *testing.T, dir string) string {
+ return "https://developers.notion.com/reference"
+ },
+ wantCopy: false,
+ },
+ {
+ name: "returns error when spec file missing",
+ specFlag: "--spec",
+ setup: func(t *testing.T, dir string) string {
+ return filepath.Join(dir, "nonexistent.json")
+ },
+ wantCopy: false,
+ wantError: true,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ dir := t.TempDir()
+ outputDir := filepath.Join(dir, "output")
+ require.NoError(t, os.MkdirAll(outputDir, 0o755))
+
+ specURL := tt.setup(t, dir)
+ err := copySpecToOutput(tt.specFlag, specURL, outputDir)
+
+ if tt.wantError {
+ assert.Error(t, err)
+ } else {
+ assert.NoError(t, err)
+ }
+
+ dst := filepath.Join(outputDir, "spec.json")
+ if tt.wantCopy {
+ data, readErr := os.ReadFile(dst)
+ require.NoError(t, readErr, "spec.json should exist in output dir")
+ expected, _ := os.ReadFile(specURL)
+ assert.Equal(t, expected, data, "spec.json content should match source")
+ } else if !tt.wantError {
+ _, readErr := os.ReadFile(dst)
+ assert.True(t, os.IsNotExist(readErr), "spec.json should not exist")
+ }
+ })
+ }
+}
+
func findRepoRoot() string {
dir, _ := os.Getwd()
for {
← 1bebcb0a fix(scorecard): improve accuracy for non-trivial CLIs
·
back to Cli Printing Press
·
feat(skill): add /printing-press-score for standalone CLI sc c08aedc7 →