[object Object]

← back to Cli Printing Press

refactor(generator): rename shelf/ to library/ for generated CLI output

96c28ea4be0e2015f1edb64d049a6f5085cf63fb · 2026-03-27 16:59:34 -0700 · Trevin Chow

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

Files touched

Diff

commit 96c28ea4be0e2015f1edb64d049a6f5085cf63fb
Author: Trevin Chow <trevin@trevinchow.com>
Date:   Fri Mar 27 16:59:34 2026 -0700

    refactor(generator): rename shelf/ to library/ for generated CLI output
    
    Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---
 .gitignore                                         |  2 +-
 ONBOARDING.md                                      |  2 +-
 ...2026-03-27-020-feat-cli-output-to-shelf-plan.md | 64 +++++++++++-----------
 internal/cli/root.go                               |  4 +-
 internal/cli/vision.go                             |  2 +-
 internal/pipeline/pipeline.go                      |  2 +-
 internal/pipeline/state_test.go                    |  4 +-
 skills/printing-press-catalog/SKILL.md             |  4 +-
 skills/printing-press/SKILL.md                     | 26 ++++-----
 9 files changed, 55 insertions(+), 55 deletions(-)

diff --git a/.gitignore b/.gitignore
index 7f39cc2f..f6905792 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,3 @@
 .cache/
 printing-press
-shelf/
+library/
diff --git a/ONBOARDING.md b/ONBOARDING.md
index 4df03e7d..b13fd762 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 ./shelf/<name>-cli/
+Generated CLI project at ./library/<name>-cli/
   cmd/<name>-cli/main.go
   cmd/<name>-mcp/main.go
   internal/cli/   (per-resource commands)
diff --git a/docs/plans/2026-03-27-020-feat-cli-output-to-shelf-plan.md b/docs/plans/2026-03-27-020-feat-cli-output-to-shelf-plan.md
index af5f9510..18a1766d 100644
--- a/docs/plans/2026-03-27-020-feat-cli-output-to-shelf-plan.md
+++ b/docs/plans/2026-03-27-020-feat-cli-output-to-shelf-plan.md
@@ -1,24 +1,24 @@
 ---
-title: "feat: Route generated CLI output to shelf/ directory"
+title: "feat: Route generated CLI output to library/ directory"
 type: feat
 status: completed
 date: 2026-03-27
 ---
 
-# feat: Route generated CLI output to shelf/ directory
+# feat: Route generated CLI output to library/ directory
 
 ## Overview
 
-Change the default output directory for generated CLIs from the repo root (e.g., `./stripe-cli/`) to a `shelf/` subdirectory (e.g., `./shelf/stripe-cli/`). Create `shelf/` if it doesn't exist. This namespaces generated output, keeps the repo root clean, and establishes a consistent location for CLIs that may later be submitted via PR or published elsewhere.
+Change the default output directory for generated CLIs from the repo root (e.g., `./stripe-cli/`) to a `library/` subdirectory (e.g., `./library/stripe-cli/`). Create `library/` if it doesn't exist. This namespaces generated output, keeps the repo root clean, and establishes a consistent location for CLIs that may later be submitted via PR or published elsewhere.
 
 ## Problem Frame
 
-Generated CLIs currently land in the repo root, cluttering it alongside source code, docs, and the existing `catalog/` metadata directory. There's no single place to find all generated CLIs. The `shelf/` directory provides a printing-press-aligned namespace — CLIs are "placed on the shelf" after being printed.
+Generated CLIs currently land in the repo root, cluttering it alongside source code, docs, and the existing `catalog/` metadata directory. There's no single place to find all generated CLIs. The `library/` directory provides a printing-press-aligned namespace — CLIs are "placed on the library" after being printed.
 
 ## Requirements Trace
 
-- R1. Default output directory changes from `./<name>-cli` to `./shelf/<name>-cli` across all commands
-- R2. `shelf/` directory is created automatically if it doesn't exist
+- R1. Default output directory changes from `./<name>-cli` to `./library/<name>-cli` across all commands
+- R2. `library/` directory is created automatically if it doesn't exist
 - R3. Explicit `--output` flag still overrides the default (no behavior change for explicit paths)
 - R4. Flag help text and CLI examples reflect the new default
 - R5. Skill documentation reflects the new default path
@@ -42,9 +42,9 @@ There are **four locations** where the default output path `<name>-cli` is const
 3. **`print` command** — `internal/pipeline/pipeline.go:25-26` — `outputDir = "./" + apiName + "-cli"`
 4. **`vision` command** — `internal/cli/vision.go:33-34` — `outputDir = apiName + "-cli"`
 
-All four follow the same pattern: check if `outputDir` is empty, if so set it to `<name>-cli`. The fix is to prepend `shelf/` to each default.
+All four follow the same pattern: check if `outputDir` is empty, if so set it to `<name>-cli`. The fix is to prepend `library/` to each default.
 
-After the default is set, each location calls `filepath.Abs(outputDir)` which will resolve `shelf/<name>-cli` relative to cwd — no additional changes needed for path resolution.
+After the default is set, each location calls `filepath.Abs(outputDir)` which will resolve `library/<name>-cli` relative to cwd — no additional changes needed for path resolution.
 
 ### Documentation References
 
@@ -53,15 +53,15 @@ After the default is set, each location calls `filepath.Abs(outputDir)` which wi
 
 ## Key Technical Decisions
 
-- **Use `filepath.Join("shelf", name+"-cli")` instead of string concatenation** — Ensures correct path separators on all platforms and is consistent with Go conventions.
-- **Create `shelf/` via `os.MkdirAll` on the resolved absolute path** — `os.MkdirAll` on the full output path (e.g., `/abs/path/shelf/stripe-cli/`) will create both `shelf/` and the CLI subdirectory as needed. The generator's `Generate()` method already calls `os.MkdirAll` for its internal subdirectories, so `shelf/` creation happens naturally. No separate mkdir step is needed.
-- **Add `shelf/` to `.gitignore`** — Generated CLIs are build artifacts, not source. Keeping them gitignored prevents accidental commits of large generated trees while still allowing the directory to exist locally.
+- **Use `filepath.Join("library", name+"-cli")` instead of string concatenation** — Ensures correct path separators on all platforms and is consistent with Go conventions.
+- **Create `library/` via `os.MkdirAll` on the resolved absolute path** — `os.MkdirAll` on the full output path (e.g., `/abs/path/library/stripe-cli/`) will create both `library/` and the CLI subdirectory as needed. The generator's `Generate()` method already calls `os.MkdirAll` for its internal subdirectories, so `library/` creation happens naturally. No separate mkdir step is needed.
+- **Add `library/` to `.gitignore`** — Generated CLIs are build artifacts, not source. Keeping them gitignored prevents accidental commits of large generated trees while still allowing the directory to exist locally.
 
 ## Open Questions
 
 ### Resolved During Planning
 
-- **Should `shelf/` be gitignored?** Yes — generated CLIs are ephemeral build output. Users who want to commit a CLI can use `git add -f` or move it out. This matches the pattern where `printing-press` (the binary) is already gitignored.
+- **Should `library/` be gitignored?** Yes — generated CLIs are ephemeral build output. Users who want to commit a CLI can use `git add -f` or move it out. This matches the pattern where `printing-press` (the binary) is already gitignored.
 
 ### Deferred to Implementation
 
@@ -69,9 +69,9 @@ After the default is set, each location calls `filepath.Abs(outputDir)` which wi
 
 ## Implementation Units
 
-- [ ] **Unit 1: Change default output paths to shelf/ prefix**
+- [ ] **Unit 1: Change default output paths to library/ prefix**
 
-  **Goal:** All four default-path locations prepend `shelf/` so generated CLIs land in `shelf/<name>-cli/` by default.
+  **Goal:** All four default-path locations prepend `library/` so generated CLIs land in `library/<name>-cli/` by default.
 
   **Requirements:** R1, R2
 
@@ -83,11 +83,11 @@ After the default is set, each location calls `filepath.Abs(outputDir)` which wi
   - Modify: `internal/pipeline/pipeline.go`
 
   **Approach:**
-  - In each of the four default-path assignments, change from `<name> + "-cli"` to `filepath.Join("shelf", <name>+"-cli")`.
-  - `root.go:214`: `outputDir = filepath.Join("shelf", apiSpec.Name+"-cli")`
-  - `root.go:115`: `outputDir = filepath.Join("shelf", parsed.Name+"-cli")`
-  - `pipeline.go:26`: `outputDir = filepath.Join("shelf", apiName+"-cli")`
-  - `vision.go:34`: `outputDir = filepath.Join("shelf", apiName+"-cli")`
+  - In each of the four default-path assignments, change from `<name> + "-cli"` to `filepath.Join("library", <name>+"-cli")`.
+  - `root.go:214`: `outputDir = filepath.Join("library", apiSpec.Name+"-cli")`
+  - `root.go:115`: `outputDir = filepath.Join("library", parsed.Name+"-cli")`
+  - `pipeline.go:26`: `outputDir = filepath.Join("library", apiName+"-cli")`
+  - `vision.go:34`: `outputDir = filepath.Join("library", apiName+"-cli")`
   - The subsequent `filepath.Abs()` call handles resolution to an absolute path. `os.MkdirAll` in the generator handles directory creation. No other code changes needed for path mechanics.
 
   **Patterns to follow:**
@@ -100,13 +100,13 @@ After the default is set, each location calls `filepath.Abs(outputDir)` which wi
   - Existing tests pass (`go test ./...`) — tests use explicit `t.TempDir()` paths, not defaults
 
   **Verification:**
-  - All four default paths produce `shelf/<name>-cli` when `--output` is not specified
+  - All four default paths produce `library/<name>-cli` when `--output` is not specified
   - `--output /custom/path` still works unchanged
   - `go test ./...` passes
 
 - [ ] **Unit 2: Update flag help text and examples**
 
-  **Goal:** CLI help output reflects the new `shelf/` default so users aren't surprised.
+  **Goal:** CLI help output reflects the new `library/` default so users aren't surprised.
 
   **Requirements:** R4
 
@@ -117,9 +117,9 @@ After the default is set, each location calls `filepath.Abs(outputDir)` which wi
   - Modify: `internal/cli/vision.go`
 
   **Approach:**
-  - `root.go:279`: Change flag description to `"Output directory (default: shelf/<name>-cli)"`
-  - `root.go:472`: Change to `"Output directory (default: shelf/<api-name>-cli)"`
-  - `vision.go:93`: Change to `"Output directory (default: shelf/<api>-cli)"`
+  - `root.go:279`: Change flag description to `"Output directory (default: library/<name>-cli)"`
+  - `root.go:472`: Change to `"Output directory (default: library/<api-name>-cli)"`
+  - `vision.go:93`: Change to `"Output directory (default: library/<api>-cli)"`
 
   **Patterns to follow:**
   - Existing flag description format
@@ -129,9 +129,9 @@ After the default is set, each location calls `filepath.Abs(outputDir)` which wi
   - `printing-press print --help` shows updated default
 
   **Verification:**
-  - All three `--output` flag descriptions reference `shelf/`
+  - All three `--output` flag descriptions reference `library/`
 
-- [ ] **Unit 3: Add shelf/ to .gitignore and update skill docs**
+- [ ] **Unit 3: Add library/ to .gitignore and update skill docs**
 
   **Goal:** Generated CLIs are gitignored; skill documentation reflects the new path.
 
@@ -144,18 +144,18 @@ After the default is set, each location calls `filepath.Abs(outputDir)` which wi
   - Modify: `skills/printing-press-catalog/SKILL.md`
 
   **Approach:**
-  - Add `shelf/` line to `.gitignore`
-  - In `SKILL.md`, update the `--output ./<name>-cli` example to `--output ./shelf/<name>-cli` and update the "Try it" section paths from `cd <name>-cli` to `cd shelf/<name>-cli`
+  - Add `library/` line to `.gitignore`
+  - In `SKILL.md`, update the `--output ./<name>-cli` example to `--output ./library/<name>-cli` and update the "Try it" section paths from `cd <name>-cli` to `cd library/<name>-cli`
 
   **Patterns to follow:**
   - Existing `.gitignore` entries (`printing-press`, `.cache/`)
 
   **Test scenarios:**
-  - `git status` does not show generated CLI directories inside `shelf/`
+  - `git status` does not show generated CLI directories inside `library/`
 
   **Verification:**
-  - `.gitignore` contains `shelf/`
-  - Skill file references `shelf/` in generation and try-it instructions
+  - `.gitignore` contains `library/`
+  - Skill file references `library/` in generation and try-it instructions
 
 ## System-Wide Impact
 
@@ -166,7 +166,7 @@ After the default is set, each location calls `filepath.Abs(outputDir)` which wi
 
 ## Risks & Dependencies
 
-- **Low risk:** Users with muscle memory for `cd <name>-cli` after generation will need to use `cd shelf/<name>-cli`. Mitigated by updating the post-generation output message (which already prints the output path).
+- **Low risk:** Users with muscle memory for `cd <name>-cli` after generation will need to use `cd library/<name>-cli`. Mitigated by updating the post-generation output message (which already prints the output path).
 
 ## Sources & References
 
diff --git a/internal/cli/root.go b/internal/cli/root.go
index 83aa6cc1..07fedae2 100644
--- a/internal/cli/root.go
+++ b/internal/cli/root.go
@@ -276,7 +276,7 @@ func newGenerateCmd() *cobra.Command {
 
 	cmd.Flags().StringSliceVar(&specFiles, "spec", nil, "Path or URL to API spec (can be repeated)")
 	cmd.Flags().StringVar(&cliName, "name", "", "CLI name (required when using multiple specs)")
-	cmd.Flags().StringVar(&outputDir, "output", "", "Output directory (default: shelf/<name>-cli)")
+	cmd.Flags().StringVar(&outputDir, "output", "", "Output directory (default: library/<name>-cli)")
 	cmd.Flags().BoolVar(&validate, "validate", true, "Run quality gates on the generated project")
 	cmd.Flags().BoolVar(&refresh, "refresh", false, "Refresh cached remote spec before generating")
 	cmd.Flags().BoolVar(&force, "force", false, "Remove existing output directory before generating")
@@ -469,7 +469,7 @@ func newPrintCmd() *cobra.Command {
 		},
 	}
 
-	cmd.Flags().StringVar(&outputDir, "output", "", "Output directory (default: shelf/<api-name>-cli)")
+	cmd.Flags().StringVar(&outputDir, "output", "", "Output directory (default: library/<api-name>-cli)")
 	cmd.Flags().BoolVar(&force, "force", false, "Overwrite existing pipeline")
 	cmd.Flags().BoolVar(&resume, "resume", false, "Resume from existing checkpoint")
 	cmd.Flags().BoolVar(&asJSON, "json", false, "Output as JSON")
diff --git a/internal/cli/vision.go b/internal/cli/vision.go
index 2531c1c7..b83ca6c9 100644
--- a/internal/cli/vision.go
+++ b/internal/cli/vision.go
@@ -91,7 +91,7 @@ The vision command produces the structure; Phase 0 fills it with intelligence.`,
 	}
 
 	cmd.Flags().StringVar(&apiName, "api", "", "API name to research")
-	cmd.Flags().StringVar(&outputDir, "output", "", "Output directory (default: shelf/<api>-cli)")
+	cmd.Flags().StringVar(&outputDir, "output", "", "Output directory (default: library/<api>-cli)")
 	cmd.Flags().BoolVar(&asJSON, "json", false, "Output as JSON")
 
 	return cmd
diff --git a/internal/pipeline/pipeline.go b/internal/pipeline/pipeline.go
index f382ebbe..d8ce68ab 100644
--- a/internal/pipeline/pipeline.go
+++ b/internal/pipeline/pipeline.go
@@ -9,7 +9,7 @@ import (
 // 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")
+	return filepath.Join("library", apiName+"-cli")
 }
 
 // Options configures a pipeline run.
diff --git a/internal/pipeline/state_test.go b/internal/pipeline/state_test.go
index 6b5852a5..c132b17e 100644
--- a/internal/pipeline/state_test.go
+++ b/internal/pipeline/state_test.go
@@ -108,8 +108,8 @@ func TestDefaultOutputDir(t *testing.T) {
 		apiName  string
 		expected string
 	}{
-		{"simple", "stripe", "shelf/stripe-cli"},
-		{"hyphenated", "my-api", "shelf/my-api-cli"},
+		{"simple", "stripe", "library/stripe-cli"},
+		{"hyphenated", "my-api", "library/my-api-cli"},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
diff --git a/skills/printing-press-catalog/SKILL.md b/skills/printing-press-catalog/SKILL.md
index 7c8e781d..559de341 100644
--- a/skills/printing-press-catalog/SKILL.md
+++ b/skills/printing-press-catalog/SKILL.md
@@ -90,7 +90,7 @@ When invoked with `install <name>`:
    curl -sL -o /tmp/catalog-spec-$$.yaml "<spec_url>"
    cd ~/cli-printing-press && ./printing-press generate \
      --spec /tmp/catalog-spec-$$.yaml \
-     --output ./shelf/<name>-cli \
+     --output ./library/<name>-cli \
      --validate
    ```
 7. If all quality gates pass, present the result:
@@ -98,7 +98,7 @@ When invoked with `install <name>`:
    Generated <name>-cli with X resources.
 
    Try it:
-     cd shelf/<name>-cli
+     cd library/<name>-cli
      go install ./cmd/<name>-cli
      <name>-cli --help
      <name>-cli doctor
diff --git a/skills/printing-press/SKILL.md b/skills/printing-press/SKILL.md
index f3c7ee0d..ea0a6a17 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 shelf/<api>-cli 2>/dev/null; echo "CLEAN"
+cd ~/cli-printing-press && rm -rf library/<api>-cli 2>/dev/null; echo "CLEAN"
 ```
 
 ### Step 2.3: Run the generator
@@ -1021,7 +1021,7 @@ cd ~/cli-printing-press && rm -rf shelf/<api>-cli 2>/dev/null; echo "CLEAN"
 ```bash
 cd ~/cli-printing-press && ./printing-press generate \
   --spec /tmp/printing-press-spec-<api>.json \
-  --output ./shelf/<api>-cli \
+  --output ./library/<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 ./shelf/<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 ./library/<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):
 
-- `shelf/<api>-cli/internal/cli/root.go`
-- `shelf/<api>-cli/README.md`
+- `library/<api>-cli/internal/cli/root.go`
+- `library/<api>-cli/README.md`
 - At least 3 resource command files
 
 ### Step 3.2: Count commands and compare to target
 
 ```bash
-cd ~/cli-printing-press/shelf/<api>-cli && grep -r "Use:" internal/cli/*.go | grep -v "root.go" | wc -l
+cd ~/cli-printing-press/library/<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 ./shelf/<api>-cli
+cd ~/cli-printing-press && ./printing-press scorecard --dir ./library/<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 ./shelf/<api>-cli
+cd ~/cli-printing-press && ./printing-press scorecard --dir ./library/<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/shelf/<api>-cli && go build ./... && go vet ./... && echo "ALL FIXES VERIFIED"
+cd ~/cli-printing-press/library/<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 ./shelf/<api>-cli \
+  --dir ./library/<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 ./shelf/<api>-cli \
+  --dir ./library/<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 ./shelf/<api>-cli
+cd ~/cli-printing-press && ./printing-press scorecard --dir ./library/<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/shelf/<api>-cli
+cd ~/cli-printing-press/library/<api>-cli
 go install ./cmd/<api>-cli
 
 export <AUTH_ENV_VAR>="..."

← cf381bbb fix(review): extract DefaultOutputDir helper, update main sk  ·  back to Cli Printing Press  ·  docs: rename plan file from shelf to library 01a3c1c7 →