[object Object]

← back to Cli Printing Press

fix(review): extract DefaultOutputDir helper, update main skill and docs

cf381bbb948bf71f874e68653c237002a3b40400 · 2026-03-27 16:46:05 -0700 · Trevin Chow

- Extract shared DefaultOutputDir() in pipeline package, used by all 4 call sites
- Update 13 filesystem path references in skills/printing-press/SKILL.md
- Update ONBOARDING.md path reference
- Add TestDefaultOutputDir unit test

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit cf381bbb948bf71f874e68653c237002a3b40400
Author: Trevin Chow <trevin@trevinchow.com>
Date:   Fri Mar 27 16:46:05 2026 -0700

    fix(review): extract DefaultOutputDir helper, update main skill and docs
    
    - Extract shared DefaultOutputDir() in pipeline package, used by all 4 call sites
    - Update 13 filesystem path references in skills/printing-press/SKILL.md
    - Update ONBOARDING.md path reference
    - Add TestDefaultOutputDir unit test
    
    Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---
 ONBOARDING.md                   |  2 +-
 internal/cli/root.go            |  4 ++--
 internal/cli/vision.go          |  3 ++-
 internal/pipeline/pipeline.go   |  8 +++++++-
 internal/pipeline/state_test.go | 16 ++++++++++++++++
 skills/printing-press/SKILL.md  | 26 +++++++++++++-------------
 6 files changed, 41 insertions(+), 18 deletions(-)

diff --git a/ONBOARDING.md b/ONBOARDING.md
index b1fd9d50..4df03e7d 100644
--- a/ONBOARDING.md
+++ b/ONBOARDING.md
@@ -101,7 +101,7 @@ internal/generator/generator.go (New + Generate)
   renders 30+ .tmpl files to output dir
   |
   v
-Generated CLI project at ./<name>-cli/
+Generated CLI project at ./shelf/<name>-cli/
   cmd/<name>-cli/main.go
   cmd/<name>-mcp/main.go
   internal/cli/   (per-resource commands)
diff --git a/internal/cli/root.go b/internal/cli/root.go
index b7751e83..83aa6cc1 100644
--- a/internal/cli/root.go
+++ b/internal/cli/root.go
@@ -112,7 +112,7 @@ func newGenerateCmd() *cobra.Command {
 				}
 
 				if outputDir == "" {
-					outputDir = filepath.Join("shelf", parsed.Name+"-cli")
+					outputDir = pipeline.DefaultOutputDir(parsed.Name)
 				}
 				absOut, err := filepath.Abs(outputDir)
 				if err != nil {
@@ -211,7 +211,7 @@ func newGenerateCmd() *cobra.Command {
 			}
 
 			if outputDir == "" {
-				outputDir = filepath.Join("shelf", apiSpec.Name+"-cli")
+				outputDir = pipeline.DefaultOutputDir(apiSpec.Name)
 			}
 
 			absOut, err := filepath.Abs(outputDir)
diff --git a/internal/cli/vision.go b/internal/cli/vision.go
index fc6cc63b..2531c1c7 100644
--- a/internal/cli/vision.go
+++ b/internal/cli/vision.go
@@ -6,6 +6,7 @@ import (
 	"os"
 	"path/filepath"
 
+	"github.com/mvanhorn/cli-printing-press/internal/pipeline"
 	"github.com/mvanhorn/cli-printing-press/internal/vision"
 	"github.com/spf13/cobra"
 )
@@ -31,7 +32,7 @@ The vision command produces the structure; Phase 0 fills it with intelligence.`,
 				return &ExitError{Code: ExitInputError, Err: fmt.Errorf("--api is required")}
 			}
 			if outputDir == "" {
-				outputDir = filepath.Join("shelf", apiName+"-cli")
+				outputDir = pipeline.DefaultOutputDir(apiName)
 			}
 
 			absOut, err := filepath.Abs(outputDir)
diff --git a/internal/pipeline/pipeline.go b/internal/pipeline/pipeline.go
index db9715e7..f382ebbe 100644
--- a/internal/pipeline/pipeline.go
+++ b/internal/pipeline/pipeline.go
@@ -6,6 +6,12 @@ import (
 	"path/filepath"
 )
 
+// DefaultOutputDir returns the default output directory for a given API name.
+// All commands should use this when --output is not specified.
+func DefaultOutputDir(apiName string) string {
+	return filepath.Join("shelf", apiName+"-cli")
+}
+
 // Options configures a pipeline run.
 type Options struct {
 	OutputDir string
@@ -23,7 +29,7 @@ func Init(apiName string, opts Options) (*PipelineState, error) {
 
 	outputDir := opts.OutputDir
 	if outputDir == "" {
-		outputDir = filepath.Join("shelf", apiName+"-cli")
+		outputDir = DefaultOutputDir(apiName)
 	}
 
 	absOutputDir, err := filepath.Abs(outputDir)
diff --git a/internal/pipeline/state_test.go b/internal/pipeline/state_test.go
index de52cc9a..6b5852a5 100644
--- a/internal/pipeline/state_test.go
+++ b/internal/pipeline/state_test.go
@@ -102,6 +102,22 @@ func TestIsSeedBackwardCompatible(t *testing.T) {
 	assert.False(t, s.IsSeed(PhasePreflight))
 }
 
+func TestDefaultOutputDir(t *testing.T) {
+	tests := []struct {
+		name     string
+		apiName  string
+		expected string
+	}{
+		{"simple", "stripe", "shelf/stripe-cli"},
+		{"hyphenated", "my-api", "shelf/my-api-cli"},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			assert.Equal(t, tt.expected, DefaultOutputDir(tt.apiName))
+		})
+	}
+}
+
 func TestPhaseStateJSONIncludesPlanStatus(t *testing.T) {
 	state := PhaseState{
 		Status:     StatusPlanned,
diff --git a/skills/printing-press/SKILL.md b/skills/printing-press/SKILL.md
index 528134e1..f3c7ee0d 100644
--- a/skills/printing-press/SKILL.md
+++ b/skills/printing-press/SKILL.md
@@ -1013,7 +1013,7 @@ curl -sL -o /tmp/printing-press-spec-<api>.json "<spec-url>" && head -c 200 /tmp
 ### Step 2.2: Check for existing output, remove if exists
 
 ```bash
-cd ~/cli-printing-press && rm -rf <api>-cli 2>/dev/null; echo "CLEAN"
+cd ~/cli-printing-press && rm -rf shelf/<api>-cli 2>/dev/null; echo "CLEAN"
 ```
 
 ### Step 2.3: Run the generator
@@ -1021,7 +1021,7 @@ cd ~/cli-printing-press && rm -rf <api>-cli 2>/dev/null; echo "CLEAN"
 ```bash
 cd ~/cli-printing-press && ./printing-press generate \
   --spec /tmp/printing-press-spec-<api>.json \
-  --output ./<api>-cli \
+  --output ./shelf/<api>-cli \
   --force --lenient --validate 2>&1
 ```
 
@@ -1031,7 +1031,7 @@ cd ~/cli-printing-press && ./printing-press generate \
 
 Run:
 ```bash
-cd ~/cli-printing-press && ./printing-press generate --spec /tmp/printing-press-spec-<api>.json --output ./<api>-cli --force --lenient --validate 2>&1 | grep "skipping body field"
+cd ~/cli-printing-press && ./printing-press generate --spec /tmp/printing-press-spec-<api>.json --output ./shelf/<api>-cli --force --lenient --validate 2>&1 | grep "skipping body field"
 ```
 
 Save the list of skipped fields. These are NOT acceptable limitations - they are work items for Phase 4.
@@ -1063,14 +1063,14 @@ This phase has TWO parts: (A) code review for tactical fixes, and (B) Non-Obviou
 
 You MUST **Read** these files (not just check they exist):
 
-- `<api>-cli/internal/cli/root.go`
-- `<api>-cli/README.md`
+- `shelf/<api>-cli/internal/cli/root.go`
+- `shelf/<api>-cli/README.md`
 - At least 3 resource command files
 
 ### Step 3.2: Count commands and compare to target
 
 ```bash
-cd ~/cli-printing-press/<api>-cli && grep -r "Use:" internal/cli/*.go | grep -v "root.go" | wc -l
+cd ~/cli-printing-press/shelf/<api>-cli && grep -r "Use:" internal/cli/*.go | grep -v "root.go" | wc -l
 ```
 
 Compare against target from Phase 1 research.
@@ -1103,7 +1103,7 @@ For each field skipped by the generator (from Phase 2 Step 2.4):
 Before hand-scoring, run the automated scorecard to get objective baseline numbers:
 
 ```bash
-cd ~/cli-printing-press && ./printing-press scorecard --dir ./<api>-cli
+cd ~/cli-printing-press && ./printing-press scorecard --dir ./shelf/<api>-cli
 ```
 
 Use these numbers as the baseline. The hand-scoring in Step 3.7 should explain WHY each dimension got its score, not re-guess the number.
@@ -1302,7 +1302,7 @@ func newStaleCmd(flags *rootFlags) *cobra.Command {
 Run the scorecard and fix dimensions below 10/10:
 
 ```bash
-cd ~/cli-printing-press && ./printing-press scorecard --dir ./<api>-cli
+cd ~/cli-printing-press && ./printing-press scorecard --dir ./shelf/<api>-cli
 ```
 
 For each dimension below 10/10:
@@ -1325,7 +1325,7 @@ Only after Priority 1 and 2 are complete:
 ### Step 4.4: Verify compilation
 
 ```bash
-cd ~/cli-printing-press/<api>-cli && go build ./... && go vet ./... && echo "ALL FIXES VERIFIED"
+cd ~/cli-printing-press/shelf/<api>-cli && go build ./... && go vet ./... && echo "ALL FIXES VERIFIED"
 ```
 
 ### PHASE GATE 4
@@ -1602,7 +1602,7 @@ The scorecard measures files. This phase measures behavior. Build the CLI and te
 
 ```bash
 cd ~/cli-printing-press && ./printing-press verify \
-  --dir ./<api>-cli \
+  --dir ./shelf/<api>-cli \
   --spec /tmp/<api>-spec.json \
   --threshold 80
 ```
@@ -1611,7 +1611,7 @@ If you collected an API key in Phase 0.1, add it:
 
 ```bash
 cd ~/cli-printing-press && ./printing-press verify \
-  --dir ./<api>-cli \
+  --dir ./shelf/<api>-cli \
   --spec /tmp/<api>-spec.json \
   --api-key "$<API_ENV_VAR>" \
   --env-var <API_ENV_VAR> \
@@ -1657,7 +1657,7 @@ Tell the user: "Runtime verification: [X]% pass rate ([N]/[M] commands). Data pi
 Run the automated scorecard again to measure improvement:
 
 ```bash
-cd ~/cli-printing-press && ./printing-press scorecard --dir ./<api>-cli
+cd ~/cli-printing-press && ./printing-press scorecard --dir ./shelf/<api>-cli
 ```
 
 Re-score ALL 10 dimensions. Show the DELTA from the baseline:
@@ -1717,7 +1717,7 @@ Remaining gap: <what they have that we don't, or "none">
 
 **4. Example Commands (with complex body examples):**
 ```bash
-cd ~/cli-printing-press/<api>-cli
+cd ~/cli-printing-press/shelf/<api>-cli
 go install ./cmd/<api>-cli
 
 export <AUTH_ENV_VAR>="..."

← 34e646c2 feat(generator): route generated CLI output to shelf/ direct  ·  back to Cli Printing Press  ·  refactor(generator): rename shelf/ to library/ for generated 96c28ea4 →