← 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
M .gitignoreM ONBOARDING.mdM docs/plans/2026-03-27-020-feat-cli-output-to-shelf-plan.mdM internal/cli/root.goM internal/cli/vision.goM internal/pipeline/pipeline.goM internal/pipeline/state_test.goM skills/printing-press-catalog/SKILL.mdM skills/printing-press/SKILL.md
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 →