← back to Cli Printing Press
fix(cli): make firstCommandExample helper promotion-aware (#291)
6ed197c43e2c2bdcedfa66772af8f5b5f24cacbe · 2026-04-25 13:39:34 -0700 · Trevin Chow
Files touched
M .golangci.ymlM internal/authdoctor/fingerprint.goM internal/browsersniff/analysis.goM internal/browsersniff/specgen.goM internal/cli/crowd_sniff.goM internal/cli/crowd_sniff_test.goM internal/cli/lock.goM internal/cli/lock_test.goM internal/cli/mcp_audit_test.goM internal/cli/release_test.goM internal/cli/root.goM internal/cli/vision.goM internal/crowdsniff/github.goM internal/crowdsniff/params.goM internal/crowdsniff/patterns.goA internal/generator/first_command_example.goA internal/generator/first_command_example_test.goM internal/generator/generator.goM internal/generator/generator_test.goM internal/generator/planparse.goM internal/generator/root_long_test.goM internal/generator/skill_test.goM internal/graphql/parser.goM internal/llmpolish/polish.goM internal/megamcp/metatools.goM internal/openapi/parser.goM internal/openapi/parser_test.goM internal/pipeline/claim_test.goM internal/pipeline/climanifest_test.goM internal/pipeline/dogfood.goM internal/pipeline/fullrun.goM internal/pipeline/live_check.goM internal/pipeline/lock_test.goM internal/pipeline/mcp_scoring_test.goM internal/pipeline/mcp_size.goM internal/pipeline/polish.goM internal/pipeline/research.goM internal/pipeline/runtime.goM internal/pipeline/scorecard.goM internal/pipeline/spec_detect.goM internal/pipeline/verify.goM internal/pipeline/workflow_verify.goM internal/profiler/profiler.goM internal/spec/spec.goM internal/vision/vision.go
Diff
commit 6ed197c43e2c2bdcedfa66772af8f5b5f24cacbe
Author: Trevin Chow <trevin@trevinchow.com>
Date: Sat Apr 25 13:39:34 2026 -0700
fix(cli): make firstCommandExample helper promotion-aware (#291)
---
.golangci.yml | 6 ++
internal/authdoctor/fingerprint.go | 7 +-
internal/browsersniff/analysis.go | 4 +-
internal/browsersniff/specgen.go | 7 +-
internal/cli/crowd_sniff.go | 3 +-
internal/cli/crowd_sniff_test.go | 6 +-
internal/cli/lock.go | 10 +-
internal/cli/lock_test.go | 10 +-
internal/cli/mcp_audit_test.go | 2 +-
internal/cli/release_test.go | 2 +-
internal/cli/root.go | 8 +-
internal/cli/vision.go | 2 +-
internal/crowdsniff/github.go | 5 +-
internal/crowdsniff/params.go | 17 +---
internal/crowdsniff/patterns.go | 10 +-
internal/generator/first_command_example.go | 67 ++++++++++++++
internal/generator/first_command_example_test.go | 111 +++++++++++++++++++++++
internal/generator/generator.go | 49 ++--------
internal/generator/generator_test.go | 4 +-
internal/generator/planparse.go | 4 +-
internal/generator/root_long_test.go | 12 +--
internal/generator/skill_test.go | 1 -
internal/graphql/parser.go | 4 +-
internal/llmpolish/polish.go | 4 +-
internal/megamcp/metatools.go | 5 +-
internal/openapi/parser.go | 19 ++--
internal/openapi/parser_test.go | 4 +-
internal/pipeline/claim_test.go | 2 +-
internal/pipeline/climanifest_test.go | 4 +-
internal/pipeline/dogfood.go | 25 +++--
internal/pipeline/fullrun.go | 4 +-
internal/pipeline/live_check.go | 15 +--
internal/pipeline/lock_test.go | 2 +-
internal/pipeline/mcp_scoring_test.go | 2 +-
internal/pipeline/mcp_size.go | 5 +-
internal/pipeline/polish.go | 2 +-
internal/pipeline/research.go | 8 +-
internal/pipeline/runtime.go | 15 +--
internal/pipeline/scorecard.go | 33 ++-----
internal/pipeline/spec_detect.go | 4 +-
internal/pipeline/verify.go | 2 +-
internal/pipeline/workflow_verify.go | 10 +-
internal/profiler/profiler.go | 16 ++--
internal/spec/spec.go | 6 +-
internal/vision/vision.go | 8 +-
45 files changed, 318 insertions(+), 228 deletions(-)
diff --git a/.golangci.yml b/.golangci.yml
index 19cf079b..f459de8d 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -14,6 +14,12 @@ linters:
- errcheck
- staticcheck
- unused
+ # Catches accumulated "Go has a newer way to do this" patterns:
+ # interface{} → any, ad-hoc loops → slices.Contains, range-over-int,
+ # min/max builtins, strings.Builder, CutPrefix/CutSuffix, etc. Without
+ # this enabled the codebase drifts (~100 hits accumulated before this
+ # config was introduced); turning it on now keeps it clean.
+ - modernize
settings:
errcheck:
exclude-functions:
diff --git a/internal/authdoctor/fingerprint.go b/internal/authdoctor/fingerprint.go
index b6297530..21f1e8a7 100644
--- a/internal/authdoctor/fingerprint.go
+++ b/internal/authdoctor/fingerprint.go
@@ -25,12 +25,9 @@ func Fingerprint(value string) string {
var b strings.Builder
b.Grow(fingerprintLen + 3)
- limit := fingerprintLen
- if len(runes) < limit {
- limit = len(runes)
- }
+ limit := min(len(runes), fingerprintLen)
- for i := 0; i < limit; i++ {
+ for i := range limit {
r := runes[i]
if !unicode.IsPrint(r) {
b.WriteRune('?')
diff --git a/internal/browsersniff/analysis.go b/internal/browsersniff/analysis.go
index 44dd7dce..9e6c0e7e 100644
--- a/internal/browsersniff/analysis.go
+++ b/internal/browsersniff/analysis.go
@@ -94,8 +94,8 @@ type EndpointCluster struct {
Statuses []int `json:"statuses,omitempty"`
ContentTypes []string `json:"content_types,omitempty"`
SizeClass string `json:"size_class,omitempty"`
- RequestShape ShapeSummary `json:"request_shape,omitempty"`
- ResponseShape ShapeSummary `json:"response_shape,omitempty"`
+ RequestShape ShapeSummary `json:"request_shape"`
+ ResponseShape ShapeSummary `json:"response_shape"`
Evidence []EvidenceRef `json:"evidence,omitempty"`
}
diff --git a/internal/browsersniff/specgen.go b/internal/browsersniff/specgen.go
index a00e487e..9806492c 100644
--- a/internal/browsersniff/specgen.go
+++ b/internal/browsersniff/specgen.go
@@ -804,10 +804,7 @@ func inferHTMLLinkPrefixes(entries []EnrichedEntry) []string {
}
return values[i].count > values[j].count
})
- limit := len(values)
- if limit > 3 {
- limit = 3
- }
+ limit := min(len(values), 3)
prefixes := make([]string, 0, limit)
for _, value := range values[:limit] {
prefixes = append(prefixes, value.prefix)
@@ -877,7 +874,7 @@ func inferRequestBody(entries []EnrichedEntry) []spec.Param {
func inferURLParams(entries []EnrichedEntry, normalizedPath string) []spec.Param {
paramsByName := make(map[string]spec.Param)
- for _, segment := range strings.Split(normalizedPath, "/") {
+ for segment := range strings.SplitSeq(normalizedPath, "/") {
if !strings.HasPrefix(segment, "{") || !strings.HasSuffix(segment, "}") {
continue
}
diff --git a/internal/cli/crowd_sniff.go b/internal/cli/crowd_sniff.go
index 13ef03db..7da2b529 100644
--- a/internal/cli/crowd_sniff.go
+++ b/internal/cli/crowd_sniff.go
@@ -88,7 +88,6 @@ func runCrowdSniff(ctx context.Context, apiName, baseURL, outputPath string, asJ
var warningMu sync.Mutex
for i, src := range sources {
- i, src := i, src
g.Go(func() error {
result, err := src.Discover(ctx, apiName)
if err != nil {
@@ -172,7 +171,7 @@ func runCrowdSniff(ctx context.Context, apiName, baseURL, outputPath string, asJ
}
if asJSON {
- return json.NewEncoder(stdout).Encode(map[string]interface{}{
+ return json.NewEncoder(stdout).Encode(map[string]any{
"spec_path": outputPath,
"endpoints": endpointCount,
"resources": len(apiSpec.Resources),
diff --git a/internal/cli/crowd_sniff_test.go b/internal/cli/crowd_sniff_test.go
index d0571724..57131880 100644
--- a/internal/cli/crowd_sniff_test.go
+++ b/internal/cli/crowd_sniff_test.go
@@ -108,12 +108,12 @@ func TestRunCrowdSniff_JSONOutput(t *testing.T) {
err := runCrowdSniff(context.Background(), "test", "https://api.example.com", outputPath, true, opts)
require.NoError(t, err)
- var jsonOut map[string]interface{}
+ var jsonOut map[string]any
require.NoError(t, json.Unmarshal(stdout.Bytes(), &jsonOut))
assert.Equal(t, outputPath, jsonOut["spec_path"])
assert.Equal(t, float64(1), jsonOut["endpoints"])
assert.Equal(t, float64(1), jsonOut["resources"])
- tierBreakdown, ok := jsonOut["tier_breakdown"].(map[string]interface{})
+ tierBreakdown, ok := jsonOut["tier_breakdown"].(map[string]any)
require.True(t, ok, "tier_breakdown should be a map")
assert.Equal(t, float64(1), tierBreakdown[crowdsniff.TierCodeSearch])
}
@@ -381,7 +381,7 @@ func TestRunCrowdSniff_JSONOutput_IncludesParamCount(t *testing.T) {
err := runCrowdSniff(context.Background(), "test", "https://api.example.com", outputPath, true, opts)
require.NoError(t, err)
- var jsonOut map[string]interface{}
+ var jsonOut map[string]any
require.NoError(t, json.Unmarshal(stdout.Bytes(), &jsonOut))
// 2 params on first endpoint + 1 param on second endpoint = 3 total
assert.Equal(t, float64(3), jsonOut["param_count"], "expected param_count to be 3")
diff --git a/internal/cli/lock.go b/internal/cli/lock.go
index 69b74266..9ae9d382 100644
--- a/internal/cli/lock.go
+++ b/internal/cli/lock.go
@@ -58,7 +58,7 @@ func newLockAcquireCmd() *cobra.Command {
if err != nil {
// Check if it's a "lock held" error — return structured JSON + non-zero exit.
status := pipeline.LockStatus(cliName)
- result := map[string]interface{}{
+ result := map[string]any{
"acquired": false,
"blocked": true,
"error": err.Error(),
@@ -70,7 +70,7 @@ func newLockAcquireCmd() *cobra.Command {
return &ExitError{Code: ExitInputError, Err: err, Silent: true}
}
- result := map[string]interface{}{
+ result := map[string]any{
"acquired": true,
"blocked": false,
"cli": cliName,
@@ -110,7 +110,7 @@ func newLockUpdateCmd() *cobra.Command {
return &ExitError{Code: ExitInputError, Err: fmt.Errorf("updating lock: %w", err)}
}
- result := map[string]interface{}{
+ result := map[string]any{
"updated": true,
"cli": cliName,
"phase": phase,
@@ -190,7 +190,7 @@ func newLockReleaseCmd() *cobra.Command {
return &ExitError{Code: ExitInputError, Err: fmt.Errorf("releasing lock: %w", err)}
}
- result := map[string]interface{}{
+ result := map[string]any{
"released": true,
"cli": cliName,
}
@@ -242,7 +242,7 @@ func newLockPromoteCmd() *cobra.Command {
return &ExitError{Code: ExitGenerationError, Err: fmt.Errorf("promoting CLI: %w", err)}
}
- result := map[string]interface{}{
+ result := map[string]any{
"promoted": true,
"cli": cliName,
"library_dir": state.PublishedDir,
diff --git a/internal/cli/lock_test.go b/internal/cli/lock_test.go
index e788221a..d42314ce 100644
--- a/internal/cli/lock_test.go
+++ b/internal/cli/lock_test.go
@@ -56,7 +56,7 @@ func TestLockAcquire_Success(t *testing.T) {
stdout, code := runLockCmd("acquire", "--cli", "test-pp-cli", "--scope", "scope-1")
assert.Equal(t, 0, code)
- var result map[string]interface{}
+ var result map[string]any
require.NoError(t, json.Unmarshal([]byte(stdout), &result))
assert.Equal(t, true, result["acquired"])
assert.Equal(t, false, result["blocked"])
@@ -88,7 +88,7 @@ func TestLockAcquire_Blocked(t *testing.T) {
stdout, code := runLockCmd("acquire", "--cli", "test-pp-cli", "--scope", "scope-2")
assert.Equal(t, ExitInputError, code)
- var result map[string]interface{}
+ var result map[string]any
require.NoError(t, json.Unmarshal([]byte(stdout), &result))
assert.Equal(t, false, result["acquired"])
assert.Equal(t, true, result["blocked"])
@@ -118,7 +118,7 @@ func TestLockUpdate(t *testing.T) {
stdout, code := runLockCmd("update", "--cli", "test-pp-cli", "--phase", "build")
assert.Equal(t, 0, code)
- var result map[string]interface{}
+ var result map[string]any
require.NoError(t, json.Unmarshal([]byte(stdout), &result))
assert.Equal(t, true, result["updated"])
assert.Equal(t, "build", result["phase"])
@@ -132,7 +132,7 @@ func TestLockRelease(t *testing.T) {
stdout, code := runLockCmd("release", "--cli", "test-pp-cli")
assert.Equal(t, 0, code)
- var result map[string]interface{}
+ var result map[string]any
require.NoError(t, json.Unmarshal([]byte(stdout), &result))
assert.Equal(t, true, result["released"])
@@ -173,7 +173,7 @@ func TestLockPromote_Success(t *testing.T) {
stdout, code := runLockCmd("promote", "--cli", "test-pp-cli", "--dir", workDir)
assert.Equal(t, 0, code)
- var result map[string]interface{}
+ var result map[string]any
require.NoError(t, json.Unmarshal([]byte(stdout), &result))
assert.Equal(t, true, result["promoted"])
diff --git a/internal/cli/mcp_audit_test.go b/internal/cli/mcp_audit_test.go
index 00ed8f4d..7d4671c0 100644
--- a/internal/cli/mcp_audit_test.go
+++ b/internal/cli/mcp_audit_test.go
@@ -26,7 +26,7 @@ func mustWrite(t *testing.T, root, rel, body string) {
func makeMCPTools(n int) string {
var b strings.Builder
b.WriteString("package mcp\nfunc RegisterTools() {\n")
- for i := 0; i < n; i++ {
+ for range n {
b.WriteString("mcplib.NewTool(\"x\",)\n")
}
b.WriteString("}\n")
diff --git a/internal/cli/release_test.go b/internal/cli/release_test.go
index f91f4544..1b67dcd6 100644
--- a/internal/cli/release_test.go
+++ b/internal/cli/release_test.go
@@ -124,7 +124,7 @@ func TestPRTitleWorkflowAllowsReleasePleaseScope(t *testing.T) {
require.True(t, ok, "semantic pull request action should declare scopes")
allowed := map[string]bool{}
- for _, scope := range strings.Fields(scopes) {
+ for scope := range strings.FieldsSeq(scopes) {
allowed[scope] = true
}
diff --git a/internal/cli/root.go b/internal/cli/root.go
index 319cb88f..6a69e3c6 100644
--- a/internal/cli/root.go
+++ b/internal/cli/root.go
@@ -221,7 +221,7 @@ func newGenerateCmd() *cobra.Command {
fmt.Fprintf(os.Stderr, "Generated %s at %s (from docs)\n", parsed.Name, absOut)
if asJSON {
- if err := json.NewEncoder(os.Stdout).Encode(map[string]interface{}{
+ if err := json.NewEncoder(os.Stdout).Encode(map[string]any{
"name": parsed.Name,
"output_dir": absOut,
"spec_files": specFiles,
@@ -276,7 +276,7 @@ func newGenerateCmd() *cobra.Command {
fmt.Fprintf(os.Stderr, "Generated %s at %s (from plan)\n", naming.CLI(planSpec.CLIName), absOut)
if asJSON {
- if err := json.NewEncoder(os.Stdout).Encode(map[string]interface{}{
+ if err := json.NewEncoder(os.Stdout).Encode(map[string]any{
"name": planSpec.CLIName,
"output_dir": absOut,
"plan_file": planFile,
@@ -463,7 +463,7 @@ func newGenerateCmd() *cobra.Command {
fmt.Fprintf(os.Stderr, "Generated %s at %s\n", apiSpec.Name, absOut)
if asJSON {
- if err := json.NewEncoder(os.Stdout).Encode(map[string]interface{}{
+ if err := json.NewEncoder(os.Stdout).Encode(map[string]any{
"name": apiSpec.Name,
"output_dir": absOut,
"spec_files": specFiles,
@@ -899,7 +899,7 @@ func newPrintCmd() *cobra.Command {
fmt.Fprintf(os.Stderr, "\nStart with: /ce:work %s\n", state.PlanPath(pipeline.PhasePreflight))
if asJSON {
- if err := json.NewEncoder(os.Stdout).Encode(map[string]interface{}{
+ if err := json.NewEncoder(os.Stdout).Encode(map[string]any{
"api_name": apiName,
"pipeline_dir": state.PipelineDir(),
"phases_completed": countCompletedPhases(state),
diff --git a/internal/cli/vision.go b/internal/cli/vision.go
index b30b4113..f1cac433 100644
--- a/internal/cli/vision.go
+++ b/internal/cli/vision.go
@@ -85,7 +85,7 @@ The vision command produces the structure; Phase 0 fills it with intelligence.`,
fmt.Fprintf(os.Stderr, "Visionary research template written to %s/visionary-research.md\n", absOut)
fmt.Fprintf(os.Stderr, "Run Phase 0 (SKILL.md) to fill it with real research.\n")
if asJSON {
- if err := json.NewEncoder(os.Stdout).Encode(map[string]interface{}{
+ if err := json.NewEncoder(os.Stdout).Encode(map[string]any{
"api_name": apiName,
"output_file": filepath.Join(absOut, "visionary-research.md"),
}); err != nil {
diff --git a/internal/crowdsniff/github.go b/internal/crowdsniff/github.go
index 6ab36cb3..66ebb923 100644
--- a/internal/crowdsniff/github.go
+++ b/internal/crowdsniff/github.go
@@ -370,10 +370,7 @@ func inferMethod(fragment, urlMatch string) string {
}
// Check a window before the URL match for method keywords.
- start := idx - 80
- if start < 0 {
- start = 0
- }
+ start := max(idx-80, 0)
window := fragment[start : idx+len(urlMatch)]
matches := httpMethodPattern.FindAllString(window, -1)
diff --git a/internal/crowdsniff/params.go b/internal/crowdsniff/params.go
index fd020339..f65fd5ce 100644
--- a/internal/crowdsniff/params.go
+++ b/internal/crowdsniff/params.go
@@ -74,10 +74,7 @@ func EnrichWithParams(content string, endpoints []DiscoveredEndpoint) []Discover
}
// Look backward for function signature to determine required/optional.
- funcStart := methodIdx[0] - maxFuncSignatureLookback
- if funcStart < 0 {
- funcStart = 0
- }
+ funcStart := max(methodIdx[0]-maxFuncSignatureLookback, 0)
preamble := content[funcStart:methodIdx[0]]
requiredNames := extractRequiredFromSignature(preamble)
@@ -112,10 +109,7 @@ const maxFuncSignatureLookback = 1000
// Returns nil if no params object is found or if the object is malformed.
func extractParamsFromPosition(content string, urlEndPos int) []DiscoveredParam {
// Scan forward for ", {" or ",\n {" pattern.
- scanEnd := urlEndPos + maxParamScanDistance
- if scanEnd > len(content) {
- scanEnd = len(content)
- }
+ scanEnd := min(urlEndPos+maxParamScanDistance, len(content))
region := content[urlEndPos:scanEnd]
braceStart := findParamsObjectStart(region)
@@ -180,10 +174,7 @@ func extractBraceBlock(content string, pos int) string {
inLineComment := false
inBlockComment := false
- scanEnd := pos + maxParamScanDistance
- if scanEnd > len(content) {
- scanEnd = len(content)
- }
+ scanEnd := min(pos+maxParamScanDistance, len(content))
for i := pos; i < scanEnd; i++ {
ch := content[i]
@@ -642,7 +633,7 @@ func parseSignatureParams(paramList string) map[string]bool {
}
// Check for default assignment: name = value
- if eqIdx := strings.Index(part, "="); eqIdx >= 0 {
+ if found := strings.Contains(part, "="); found {
// Has a default → optional, skip.
continue
}
diff --git a/internal/crowdsniff/patterns.go b/internal/crowdsniff/patterns.go
index e9fad6b2..a55edb0e 100644
--- a/internal/crowdsniff/patterns.go
+++ b/internal/crowdsniff/patterns.go
@@ -49,9 +49,9 @@ func GrepEndpoints(content, sourceName, sourceTier string) ([]DiscoveredEndpoint
var baseURLs []string
currentOrigin := "" // most recently observed base URL in this file's scan order
- lines := strings.Split(content, "\n")
+ lines := strings.SplitSeq(content, "\n")
- for _, line := range lines {
+ for line := range lines {
trimmed := strings.TrimSpace(line)
if trimmed == "" || strings.HasPrefix(trimmed, "//") || strings.HasPrefix(trimmed, "*") {
continue
@@ -153,11 +153,11 @@ func extractInlineOrigin(path string) string {
// parseURLSilent returns just the path portion of an absolute URL, or empty
// string on failure.
func parseURLSilent(absURL string) string {
- idx := strings.Index(absURL, "://")
- if idx < 0 {
+ _, after, ok := strings.Cut(absURL, "://")
+ if !ok {
return ""
}
- rest := absURL[idx+3:]
+ rest := after
slash := strings.IndexByte(rest, '/')
if slash < 0 {
return "/"
diff --git a/internal/generator/first_command_example.go b/internal/generator/first_command_example.go
new file mode 100644
index 00000000..aa17e527
--- /dev/null
+++ b/internal/generator/first_command_example.go
@@ -0,0 +1,67 @@
+package generator
+
+import (
+ "sort"
+
+ "github.com/mvanhorn/cli-printing-press/internal/spec"
+)
+
+// firstCommandExample returns a runnable "resource [endpoint]" path for docs
+// that need a concrete example. Read-only verbs (list, get, search, query)
+// are preferred to keep examples non-destructive. Returns empty when the
+// spec has no endpoints, so callers can skip the block rather than render
+// nonsense.
+//
+// For single-endpoint resources that the generator promotes to top-level
+// commands, the returned path is just the resource name (the actual cobra
+// command path), not "resource endpoint" (the pre-promotion path). The
+// SKILL.md verifier in printing-press-library walks command references and
+// rejects pre-promotion paths because they don't exist in the shipped
+// internal/cli/*.go.
+func firstCommandExample(resources map[string]spec.Resource) string {
+ var resNames []string
+ for name := range resources {
+ resNames = append(resNames, name)
+ }
+ sort.Strings(resNames)
+ preferredVerbs := []string{"list", "get", "search", "query"}
+
+ pathFor := func(rName string, r spec.Resource, eName string) string {
+ if isPromotableSingleEndpoint(rName, r) {
+ return rName
+ }
+ return rName + " " + eName
+ }
+
+ for _, rName := range resNames {
+ r := resources[rName]
+ for _, verb := range preferredVerbs {
+ if _, ok := r.Endpoints[verb]; ok {
+ return pathFor(rName, r, verb)
+ }
+ }
+ }
+ for _, rName := range resNames {
+ r := resources[rName]
+ eNames := sortedEndpointNames(r.Endpoints)
+ if len(eNames) > 0 {
+ return pathFor(rName, r, eNames[0])
+ }
+ }
+ return ""
+}
+
+// isPromotableSingleEndpoint mirrors buildPromotedCommands's promotion
+// criterion: a resource with exactly one endpoint whose derived command
+// name does not collide with a CLI builtin (version, help, doctor, ...)
+// gets promoted to a top-level command. The dedup-against-already-promoted
+// step in buildPromotedCommands is multi-resource bookkeeping, not a
+// per-resource property, so it is intentionally omitted here; this helper
+// answers "would this resource standalone-promote?" not "does this
+// resource end up promoted in this exact spec?".
+func isPromotableSingleEndpoint(resName string, r spec.Resource) bool {
+ if len(r.Endpoints) != 1 {
+ return false
+ }
+ return !builtinCommands[toKebab(resName)]
+}
diff --git a/internal/generator/first_command_example_test.go b/internal/generator/first_command_example_test.go
new file mode 100644
index 00000000..c3c155a5
--- /dev/null
+++ b/internal/generator/first_command_example_test.go
@@ -0,0 +1,111 @@
+package generator
+
+import (
+ "testing"
+
+ "github.com/mvanhorn/cli-printing-press/internal/spec"
+ "github.com/stretchr/testify/assert"
+)
+
+// TestFirstCommandExampleHonorsPromotion covers issue #290. The Wikipedia
+// CLI's spec has a single-endpoint `feed` resource (`feed.get-on-this-day`),
+// which the generator promotes to a top-level `feed` command. The example
+// helper used to return `feed get-on-this-day` (the pre-promotion path) for
+// the SKILL.md profile-example block, which the printing-press-library
+// `Verify SKILL.md` workflow rejected because that command path doesn't
+// exist in the shipped CLI.
+func TestFirstCommandExampleHonorsPromotion(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ name string
+ resources map[string]spec.Resource
+ want string
+ }{
+ {
+ name: "single-endpoint resource gets promoted, example returns just resource name",
+ resources: map[string]spec.Resource{
+ "feed": {
+ Endpoints: map[string]spec.Endpoint{
+ "get-on-this-day": {Method: "GET", Path: "/feed/onthisday"},
+ },
+ },
+ },
+ want: "feed",
+ },
+ {
+ name: "multi-endpoint resource with preferred verb returns resource + verb",
+ resources: map[string]spec.Resource{
+ "items": {
+ Endpoints: map[string]spec.Endpoint{
+ "list": {Method: "GET", Path: "/items"},
+ "create": {Method: "POST", Path: "/items"},
+ },
+ },
+ },
+ want: "items list",
+ },
+ {
+ name: "multi-endpoint resource without preferred verb falls back to alphabetically first",
+ resources: map[string]spec.Resource{
+ "items": {
+ Endpoints: map[string]spec.Endpoint{
+ "create": {Method: "POST", Path: "/items"},
+ "register": {Method: "POST", Path: "/items/register"},
+ },
+ },
+ },
+ want: "items create",
+ },
+ {
+ name: "single-endpoint resource named after a builtin is not promoted; emits resource + endpoint",
+ resources: map[string]spec.Resource{
+ "version": {
+ Endpoints: map[string]spec.Endpoint{
+ "info": {Method: "GET", Path: "/version/info"},
+ },
+ },
+ },
+ want: "version info",
+ },
+ {
+ name: "single-endpoint resource whose only endpoint is a preferred verb emits just resource name",
+ resources: map[string]spec.Resource{
+ "reports": {
+ Endpoints: map[string]spec.Endpoint{
+ "list": {Method: "GET", Path: "/reports"},
+ },
+ },
+ },
+ want: "reports",
+ },
+ {
+ name: "preferred-verb match in any resource wins over alphabetically-first fallback",
+ resources: map[string]spec.Resource{
+ "alpha": {
+ Endpoints: map[string]spec.Endpoint{
+ "unusual-name": {Method: "GET", Path: "/alpha"},
+ },
+ },
+ "beta": {
+ Endpoints: map[string]spec.Endpoint{
+ "list": {Method: "GET", Path: "/beta"},
+ "delete": {Method: "DELETE", Path: "/beta/{id}"},
+ },
+ },
+ },
+ want: "beta list",
+ },
+ {
+ name: "empty resources returns empty string",
+ resources: map[string]spec.Resource{},
+ want: "",
+ },
+ }
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ t.Parallel()
+ assert.Equal(t, tc.want, firstCommandExample(tc.resources))
+ })
+ }
+}
diff --git a/internal/generator/generator.go b/internal/generator/generator.go
index fa069914..58b2e32d 100644
--- a/internal/generator/generator.go
+++ b/internal/generator/generator.go
@@ -9,6 +9,7 @@ import (
"os"
"os/exec"
"path/filepath"
+ "slices"
"sort"
"strconv"
"strings"
@@ -412,40 +413,7 @@ func New(s *spec.APISpec, outputDir string) *Generator {
return out
},
"whichFallbackEntries": buildWhichFallbackEntries,
- // firstCommandExample returns a real "resource endpoint" pair for use
- // in docs that need a runnable example. Prefers read-only verbs when
- // available (list, get, search, query) to keep examples non-destructive.
- // Returns empty string when the spec has no endpoints so callers can
- // skip the block rather than render nonsense like "autocomplete list"
- // when autocomplete has no list endpoint.
- "firstCommandExample": func(resources map[string]spec.Resource) string {
- var resNames []string
- for name := range resources {
- resNames = append(resNames, name)
- }
- sort.Strings(resNames)
- preferredVerbs := []string{"list", "get", "search", "query"}
- for _, rName := range resNames {
- r := resources[rName]
- for _, verb := range preferredVerbs {
- if _, ok := r.Endpoints[verb]; ok {
- return rName + " " + verb
- }
- }
- }
- for _, rName := range resNames {
- r := resources[rName]
- var eNames []string
- for eName := range r.Endpoints {
- eNames = append(eNames, eName)
- }
- sort.Strings(eNames)
- if len(eNames) > 0 {
- return rName + " " + eNames[0]
- }
- }
- return ""
- },
+ "firstCommandExample": firstCommandExample,
}
return g
}
@@ -768,12 +736,7 @@ func (g *Generator) hasTrafficAnalysisHint(hint string) bool {
if g == nil || g.TrafficAnalysis == nil {
return false
}
- for _, got := range g.TrafficAnalysis.GenerationHints {
- if got == hint {
- return true
- }
- }
- return false
+ return slices.Contains(g.TrafficAnalysis.GenerationHints, hint)
}
func appendLimited(values []string, value string, limit int) []string {
@@ -2064,13 +2027,13 @@ func configTag(format string) string {
func envVarField(envVar string) string {
// STYTCH_PROJECT_ID -> ProjectID
parts := strings.Split(strings.ToLower(envVar), "_")
- var result string
+ var result strings.Builder
for _, p := range parts {
if len(p) > 0 {
- result += strings.ToUpper(p[:1]) + p[1:]
+ result.WriteString(strings.ToUpper(p[:1]) + p[1:])
}
}
- return result
+ return result.String()
}
// builtinConfigTags lists the JSON/TOML tags of hardcoded Config struct fields
diff --git a/internal/generator/generator_test.go b/internal/generator/generator_test.go
index 9d341635..b6a1f1f2 100644
--- a/internal/generator/generator_test.go
+++ b/internal/generator/generator_test.go
@@ -1587,7 +1587,7 @@ func TestBuildPromotedCommands(t *testing.T) {
},
},
}
- for i := 0; i < 20; i++ {
+ for range 20 {
promoted := buildPromotedCommands(s)
assert.Empty(t, promoted)
}
@@ -1612,7 +1612,7 @@ func TestBuildPromotedCommands(t *testing.T) {
},
},
}
- for i := 0; i < 20; i++ {
+ for range 20 {
promoted := buildPromotedCommands(s)
require.Len(t, promoted, 2)
assert.Equal(t, "accounts", promoted[0].ResourceName)
diff --git a/internal/generator/planparse.go b/internal/generator/planparse.go
index 4b373598..4895e910 100644
--- a/internal/generator/planparse.go
+++ b/internal/generator/planparse.go
@@ -227,8 +227,8 @@ func looksLikeCommand(s string) bool {
return false
}
// Allow "auth login" style subcommands
- parts := strings.Fields(s)
- for _, part := range parts {
+ parts := strings.FieldsSeq(s)
+ for part := range parts {
for _, r := range part {
if (r < 'a' || r > 'z') && (r < '0' || r > '9') && r != '-' && r != '_' {
return false
diff --git a/internal/generator/root_long_test.go b/internal/generator/root_long_test.go
index 21c5dac9..9509db02 100644
--- a/internal/generator/root_long_test.go
+++ b/internal/generator/root_long_test.go
@@ -69,7 +69,7 @@ func TestRootLongOverflowsGracefullyAt16PlusFeatures(t *testing.T) {
apiSpec := minimalSpec("overflow")
outputDir := filepath.Join(t.TempDir(), "overflow-pp-cli")
gen := New(apiSpec, outputDir)
- for i := 0; i < 20; i++ {
+ for i := range 20 {
gen.NovelFeatures = append(gen.NovelFeatures, NovelFeature{
Command: "cmd-" + string(rune('a'+i)),
Description: "novel feature number " + string(rune('a'+i)),
@@ -82,7 +82,7 @@ func TestRootLongOverflowsGracefullyAt16PlusFeatures(t *testing.T) {
content := string(rootGo)
// First 15 features render in full.
- for i := 0; i < 15; i++ {
+ for i := range 15 {
assert.True(t, strings.Contains(content, "cmd-"+string(rune('a'+i))),
"feature cmd-%s (rank %d) should render within the 15-item cap", string(rune('a'+i)), i)
}
@@ -121,8 +121,8 @@ func TestRootLongHandlesBackticksInNarrativeText(t *testing.T) {
// Extract the Command block and assert it contains no raw backtick
// other than the string delimiters themselves. Simplest check: count
// backticks in the Short line — should be exactly 2 (the delimiters).
- lines := strings.Split(content, "\n")
- for _, line := range lines {
+ lines := strings.SplitSeq(content, "\n")
+ for line := range lines {
if strings.Contains(line, "Short:") && strings.Contains(line, "`") {
count := strings.Count(line, "`")
assert.Equal(t, 2, count,
@@ -199,7 +199,7 @@ func TestRootLongStaysUnderSizeBudget(t *testing.T) {
Headline: "A very verbose headline that exceeds the 120-rune budget: " + longStr,
}
// Ten features, each with a runaway description.
- for i := 0; i < 10; i++ {
+ for i := range 10 {
gen.NovelFeatures = append(gen.NovelFeatures, NovelFeature{
Command: "cmd" + strings.Repeat("x", i),
Description: "runaway description " + longStr,
@@ -226,7 +226,7 @@ func TestRootLongStaysUnderSizeBudget(t *testing.T) {
// All 10 features render (under the 15-item cap) — we dropped the
// top-N cap so novel capabilities aren't hidden from CLI-only agents.
- for i := 0; i < 10; i++ {
+ for i := range 10 {
assert.True(t, strings.Contains(longBody, "cmd"+strings.Repeat("x", i)),
"feature cmd%s should appear in Long (10 features is under the 15-cap)", strings.Repeat("x", i))
}
diff --git a/internal/generator/skill_test.go b/internal/generator/skill_test.go
index 2ddd0f47..ac720428 100644
--- a/internal/generator/skill_test.go
+++ b/internal/generator/skill_test.go
@@ -281,7 +281,6 @@ func TestSkillRendersAuthBranchPerType(t *testing.T) {
{"none", "none", "No authentication required"},
}
for _, tc := range cases {
- tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
apiSpec := minimalSpec("auth" + tc.name)
diff --git a/internal/graphql/parser.go b/internal/graphql/parser.go
index c0957d90..a0c7628c 100644
--- a/internal/graphql/parser.go
+++ b/internal/graphql/parser.go
@@ -212,7 +212,7 @@ func parseSDLTypes(s string) (map[string]gqlType, map[string]struct{}, error) {
func parseEnumValues(body string) []string {
var values []string
- for _, line := range strings.Split(body, "\n") {
+ for line := range strings.SplitSeq(body, "\n") {
line = strings.TrimSpace(line)
line = strings.TrimSuffix(line, ",")
if line == "" {
@@ -226,7 +226,7 @@ func parseEnumValues(body string) []string {
func parseFields(body string) []gqlField {
var fields []gqlField
- for _, rawLine := range strings.Split(body, "\n") {
+ for rawLine := range strings.SplitSeq(body, "\n") {
line := strings.TrimSpace(rawLine)
if line == "" || strings.HasPrefix(line, "@") {
continue
diff --git a/internal/llmpolish/polish.go b/internal/llmpolish/polish.go
index 566cbd6b..8bbd652f 100644
--- a/internal/llmpolish/polish.go
+++ b/internal/llmpolish/polish.go
@@ -13,8 +13,8 @@ import (
type PolishRequest struct {
APIName string
OutputDir string
- SpecSource string // OpenAPI spec URL or docs URL
- Research interface{} // *pipeline.ResearchResult - use interface to avoid circular import
+ SpecSource string // OpenAPI spec URL or docs URL
+ Research any // *pipeline.ResearchResult - use interface to avoid circular import
}
// PolishResult summarizes what the polish pass changed.
diff --git a/internal/megamcp/metatools.go b/internal/megamcp/metatools.go
index 2d1d4ce5..67249fe5 100644
--- a/internal/megamcp/metatools.go
+++ b/internal/megamcp/metatools.go
@@ -248,10 +248,7 @@ func makeActivateAPIHandler(am *ActivationManager) server.ToolHandlerFunc {
toolNames := am.toolNamesForSlug(slug)
examples := ""
if len(toolNames) > 0 {
- limit := 3
- if len(toolNames) < limit {
- limit = len(toolNames)
- }
+ limit := min(len(toolNames), 3)
examples = "\n\nExample tools:\n"
for _, name := range toolNames[:limit] {
examples += fmt.Sprintf("- %s\n", name)
diff --git a/internal/openapi/parser.go b/internal/openapi/parser.go
index 5418b3b2..60fcda7b 100644
--- a/internal/openapi/parser.go
+++ b/internal/openapi/parser.go
@@ -201,7 +201,7 @@ func parse(data []byte, lenient bool) (*spec.APISpec, error) {
var proxyRoutes map[string]string
if doc.Info != nil && doc.Info.Extensions != nil {
if raw, ok := doc.Info.Extensions["x-proxy-routes"]; ok {
- if m, ok := raw.(map[string]interface{}); ok {
+ if m, ok := raw.(map[string]any); ok {
proxyRoutes = make(map[string]string, len(m))
for k, v := range m {
if s, ok := v.(string); ok {
@@ -346,7 +346,7 @@ func mapAuth(doc *openapi3.T, name string) spec.AuthConfig {
}
}
if xCookies, ok := scheme.Extensions["x-auth-cookies"]; ok {
- if cookieList, ok := xCookies.([]interface{}); ok {
+ if cookieList, ok := xCookies.([]any); ok {
for _, c := range cookieList {
if s, ok := c.(string); ok {
auth.Cookies = append(auth.Cookies, s)
@@ -815,10 +815,7 @@ func findUnnegated(text, keyword string) bool {
// before the keyword position, catching "does not require Bearer" while avoiding
// false negation on words like "Notion" that contain "no" as a substring.
func isNegated(text string, keywordIdx int) bool {
- start := keywordIdx - 50
- if start < 0 {
- start = 0
- }
+ start := max(keywordIdx-50, 0)
preceding := text[start:keywordIdx]
for _, neg := range negationWords {
idx := strings.Index(preceding, neg)
@@ -943,7 +940,7 @@ func pathPriorityScore(path string) int {
lowPath := strings.ToLower(path)
for _, prefix := range []string{"admin", "internal", "system", "management"} {
- for _, seg := range strings.Split(strings.TrimPrefix(lowPath, "/"), "/") {
+ for seg := range strings.SplitSeq(strings.TrimPrefix(lowPath, "/"), "/") {
// Match segments that start with the prefix (catches admin, admin.users, etc.)
if strings.HasPrefix(seg, prefix) {
score -= 30
@@ -2460,8 +2457,8 @@ func stripOperationIDResourcePrefix(name string, variants []string) string {
stripped := false
for _, variant := range variants {
prefix := variant + "_"
- if strings.HasPrefix(name, prefix) {
- name = strings.TrimPrefix(name, prefix)
+ if after, ok := strings.CutPrefix(name, prefix); ok {
+ name = after
stripped = true
break
}
@@ -2718,8 +2715,8 @@ func isVersionToken(token string) bool {
return false
}
- if strings.HasPrefix(token, "v") {
- token = strings.TrimPrefix(token, "v")
+ if after, ok := strings.CutPrefix(token, "v"); ok {
+ token = after
if token == "" {
return false
}
diff --git a/internal/openapi/parser_test.go b/internal/openapi/parser_test.go
index 3a81644a..e0769f2b 100644
--- a/internal/openapi/parser_test.go
+++ b/internal/openapi/parser_test.go
@@ -949,10 +949,10 @@ func TestPathPriorityScoreSortOrder(t *testing.T) {
// Build 600 paths: 100 admin.* paths and 500 user-facing paths.
var paths []string
- for i := 0; i < 100; i++ {
+ for i := range 100 {
paths = append(paths, fmt.Sprintf("/admin.resource%d/action", i))
}
- for i := 0; i < 500; i++ {
+ for i := range 500 {
paths = append(paths, fmt.Sprintf("/resource%d", i))
}
diff --git a/internal/pipeline/claim_test.go b/internal/pipeline/claim_test.go
index 26e87f53..38cd7a8a 100644
--- a/internal/pipeline/claim_test.go
+++ b/internal/pipeline/claim_test.go
@@ -77,7 +77,7 @@ func TestClaimOutputDir_ConcurrentClaims(t *testing.T) {
errs := make(chan error, goroutines)
wg.Add(goroutines)
- for i := 0; i < goroutines; i++ {
+ for range goroutines {
go func() {
defer wg.Done()
claimed, err := ClaimOutputDir(base)
diff --git a/internal/pipeline/climanifest_test.go b/internal/pipeline/climanifest_test.go
index a5206fd5..e4b540bf 100644
--- a/internal/pipeline/climanifest_test.go
+++ b/internal/pipeline/climanifest_test.go
@@ -94,7 +94,7 @@ func TestWriteCLIManifestOmitsEmptyOptionalFields(t *testing.T) {
require.NoError(t, err)
// Verify optional fields are not present in JSON
- var raw map[string]interface{}
+ var raw map[string]any
require.NoError(t, json.Unmarshal(data, &raw))
_, hasCatalog := raw["catalog_entry"]
@@ -651,7 +651,7 @@ func TestWriteSmitheryYAML(t *testing.T) {
// Verify the file is valid YAML by re-parsing it
content, err := os.ReadFile(filepath.Join(dir, "smithery.yaml"))
require.NoError(t, err)
- var parsed map[string]interface{}
+ var parsed map[string]any
require.NoError(t, yaml.Unmarshal(content, &parsed), "smithery.yaml should be valid YAML even with special chars in description")
assert.Contains(t, parsed["description"], "Notion")
})
diff --git a/internal/pipeline/dogfood.go b/internal/pipeline/dogfood.go
index 7d73505f..a0f5f268 100644
--- a/internal/pipeline/dogfood.go
+++ b/internal/pipeline/dogfood.go
@@ -8,6 +8,7 @@ import (
"os/exec"
"path/filepath"
"regexp"
+ "slices"
"sort"
"strings"
"time"
@@ -920,11 +921,8 @@ func checkDeadFunctions(dir string) DeadCodeResult {
liveSet := make(map[string]bool)
for _, name := range sortedKeys(names) {
callRe := regexp.MustCompile(`\b` + regexp.QuoteMeta(name) + `\s*\(`)
- for _, source := range externalSources {
- if callRe.MatchString(source) {
- liveSet[name] = true
- break
- }
+ if slices.ContainsFunc(externalSources, callRe.MatchString) {
+ liveSet[name] = true
}
}
@@ -949,7 +947,7 @@ func checkDeadFunctions(dir string) DeadCodeResult {
}
// Iterative expansion: mark transitively reachable helpers as live.
- for i := 0; i < 50; i++ {
+ for range 50 {
changed := false
for _, fn := range sortedKeys(names) {
if !liveSet[fn] {
@@ -1572,7 +1570,7 @@ func sampleEvenlyCommandPaths(items [][]string, n int) [][]string {
}
step := float64(len(items)) / float64(n)
result := make([][]string, n)
- for i := 0; i < n; i++ {
+ for i := range n {
idx := int(float64(i) * step)
result[i] = items[idx]
}
@@ -1714,7 +1712,7 @@ func countDomainTables(storeSource string) int {
count := 0
for _, match := range matches {
columns := 0
- for _, line := range strings.Split(match[1], "\n") {
+ for line := range strings.SplitSeq(match[1], "\n") {
line = strings.TrimSpace(strings.TrimSuffix(line, ","))
if line == "" {
continue
@@ -1934,7 +1932,7 @@ func checkConfigConsistency(dir string) ConfigConsistResult {
}
}
// Also extract nearby token-related string literals
- for _, line := range strings.Split(content, "\n") {
+ for line := range strings.SplitSeq(content, "\n") {
if pat.MatchString(line) {
fieldMatches := fieldExtractRe.FindAllStringSubmatch(line, -1)
for _, fm := range fieldMatches {
@@ -1953,7 +1951,7 @@ func checkConfigConsistency(dir string) ConfigConsistResult {
readFields[m[1]] = struct{}{}
}
}
- for _, line := range strings.Split(content, "\n") {
+ for line := range strings.SplitSeq(content, "\n") {
if pat.MatchString(line) {
fieldMatches := fieldExtractRe.FindAllStringSubmatch(line, -1)
for _, fm := range fieldMatches {
@@ -2075,12 +2073,13 @@ func checkWorkflowCompleteness(dir string) WorkflowCompleteResult {
}
// Gather subcommand help too
- helpLower := strings.ToLower(helpOut)
+ var helpLower strings.Builder
+ helpLower.WriteString(strings.ToLower(helpOut))
topCmds := extractCommandNames(helpOut)
for _, topCmd := range topCmds {
subOut, err := runDogfoodCmd(binaryPath, 15*time.Second, topCmd, "--help")
if err == nil {
- helpLower += "\n" + strings.ToLower(subOut)
+ helpLower.WriteString("\n" + strings.ToLower(subOut))
}
}
@@ -2090,7 +2089,7 @@ func checkWorkflowCompleteness(dir string) WorkflowCompleteResult {
parts := strings.Fields(cmdLower)
found := true
for _, part := range parts {
- if !strings.Contains(helpLower, part) {
+ if !strings.Contains(helpLower.String(), part) {
found = false
break
}
diff --git a/internal/pipeline/fullrun.go b/internal/pipeline/fullrun.go
index 81ef6ac4..1fc99e55 100644
--- a/internal/pipeline/fullrun.go
+++ b/internal/pipeline/fullrun.go
@@ -139,7 +139,7 @@ func MakeBestCLI(apiName, level, specFlag, specURL, outputDir, pressBinary strin
genOut, genErr := cmd.CombinedOutput()
result.GatesOutput = string(genOut)
- for _, line := range strings.Split(result.GatesOutput, "\n") {
+ for line := range strings.SplitSeq(result.GatesOutput, "\n") {
trimmed := strings.TrimSpace(line)
if strings.Contains(trimmed, "PASS") {
result.GatesPassed++
@@ -317,7 +317,7 @@ func ensureJSON(data []byte) ([]byte, error) {
if json.Valid(data) {
return data, nil
}
- var obj interface{}
+ var obj any
if err := yaml.Unmarshal(data, &obj); err != nil {
return nil, fmt.Errorf("not valid JSON or YAML: %w", err)
}
diff --git a/internal/pipeline/live_check.go b/internal/pipeline/live_check.go
index f8fe55be..cabcbdfb 100644
--- a/internal/pipeline/live_check.go
+++ b/internal/pipeline/live_check.go
@@ -227,14 +227,12 @@ func runFeaturesConcurrent(cliDir, binaryPath string, features []NovelFeature, t
close(jobs)
var wg sync.WaitGroup
- for w := 0; w < concurrency; w++ {
- wg.Add(1)
- go func() {
- defer wg.Done()
+ for range concurrency {
+ wg.Go(func() {
for j := range jobs {
results[j.idx] = runOneFeatureCheck(cliDir, binaryPath, features[j.idx], timeout)
}
- }()
+ })
}
wg.Wait()
return results
@@ -429,10 +427,7 @@ func (lw *limitedWriter) Write(p []byte) (int, error) {
if lw.remaining <= 0 {
return len(p), nil
}
- n := len(p)
- if n > lw.remaining {
- n = lw.remaining
- }
+ n := min(len(p), lw.remaining)
if _, err := lw.w.Write(p[:n]); err != nil {
return 0, err
}
@@ -564,7 +559,7 @@ func looksLikeURLOrID(s string) bool {
// output. Mirrors the permissive relevance check used inside generated CLIs.
func outputMentionsQuery(output, query string) bool {
lowered := strings.ToLower(output)
- for _, tok := range strings.Fields(strings.ToLower(query)) {
+ for tok := range strings.FieldsSeq(strings.ToLower(query)) {
tok = strings.TrimFunc(tok, func(r rune) bool { return r == '"' || r == '\'' })
if len(tok) < 3 {
continue
diff --git a/internal/pipeline/lock_test.go b/internal/pipeline/lock_test.go
index f31cce8f..2442e11f 100644
--- a/internal/pipeline/lock_test.go
+++ b/internal/pipeline/lock_test.go
@@ -445,7 +445,7 @@ func TestConcurrentAcquire(t *testing.T) {
var wg sync.WaitGroup
successes := make(chan string, goroutines)
- for i := 0; i < goroutines; i++ {
+ for i := range goroutines {
wg.Add(1)
scope := "scope-" + string(rune('A'+i))
go func(s string) {
diff --git a/internal/pipeline/mcp_scoring_test.go b/internal/pipeline/mcp_scoring_test.go
index 78d0e873..284ba2ed 100644
--- a/internal/pipeline/mcp_scoring_test.go
+++ b/internal/pipeline/mcp_scoring_test.go
@@ -88,7 +88,7 @@ func TestScoreMCPRemoteTransport(t *testing.T) {
func buildToolsGo(n int) string {
var b strings.Builder
b.WriteString("package mcp\nfunc RegisterTools(s *server.MCPServer) {\n")
- for i := 0; i < n; i++ {
+ for i := range n {
b.WriteString("\tmcplib.NewTool(\"endpoint_")
b.WriteString(string(rune('a' + i%26)))
b.WriteString("\",)\n")
diff --git a/internal/pipeline/mcp_size.go b/internal/pipeline/mcp_size.go
index 7aa6806c..83f2ed15 100644
--- a/internal/pipeline/mcp_size.go
+++ b/internal/pipeline/mcp_size.go
@@ -104,10 +104,7 @@ func estimateMCPTokens(dir string) MCPTokenEstimate {
sort.Slice(heaviest, func(i, j int) bool {
return heaviest[i].Chars > heaviest[j].Chars
})
- top := 3
- if len(heaviest) < top {
- top = len(heaviest)
- }
+ top := min(len(heaviest), 3)
est.TopHeaviest = heaviest[:top]
}
diff --git a/internal/pipeline/polish.go b/internal/pipeline/polish.go
index efe0f7c0..5ee47805 100644
--- a/internal/pipeline/polish.go
+++ b/internal/pipeline/polish.go
@@ -43,7 +43,7 @@ func RemoveDeadCode(dir string, dryRun bool) (*PolishResult, error) {
}
const maxPasses = 3
- for pass := 0; pass < maxPasses; pass++ {
+ for pass := range maxPasses {
deadFuncs := findAllDeadFunctions(cliDir, cmdDir)
if len(deadFuncs) == 0 {
break
diff --git a/internal/pipeline/research.go b/internal/pipeline/research.go
index 89f48052..20680623 100644
--- a/internal/pipeline/research.go
+++ b/internal/pipeline/research.go
@@ -300,7 +300,7 @@ func ParseDiscoveryPages(discoveryDir string) []string {
}
var pages []string
inSection := false
- for _, line := range strings.Split(string(data), "\n") {
+ for line := range strings.SplitSeq(string(data), "\n") {
trimmed := strings.TrimSpace(line)
if strings.HasPrefix(trimmed, "## ") || strings.HasPrefix(trimmed, "# ") {
if strings.Contains(strings.ToLower(trimmed), "pages visited") {
@@ -815,12 +815,12 @@ func BuildCompetitorPrompt(owner, repo, readmeContent string, issueTitles []stri
readmeContent = readmeContent[:20000]
}
- issueList := ""
+ var issueList strings.Builder
for i, title := range issueTitles {
if i >= 20 {
break
}
- issueList += fmt.Sprintf("- %s\n", title)
+ fmt.Fprintf(&issueList, "- %s\n", title)
}
return fmt.Sprintf(`Analyze this CLI tool repository and extract intelligence.
@@ -847,7 +847,7 @@ Rules:
- commands_found: Extract ALL CLI subcommands mentioned in the README
- feature_requests: Summarize unmet user needs from issues
- pain_points: Identify user frustrations, bugs, UX issues
-- Be specific and actionable in your analysis`, owner, repo, readmeContent, issueList)
+- Be specific and actionable in your analysis`, owner, repo, readmeContent, issueList.String())
}
// parseCompetitorResponse parses the LLM JSON response into a CompetitorAnalysis.
diff --git a/internal/pipeline/runtime.go b/internal/pipeline/runtime.go
index 10765c40..fb23585b 100644
--- a/internal/pipeline/runtime.go
+++ b/internal/pipeline/runtime.go
@@ -10,6 +10,7 @@ import (
"os/exec"
"path/filepath"
"regexp"
+ "slices"
"sort"
"strings"
"time"
@@ -39,7 +40,7 @@ type VerifyReport struct {
PassRate float64 `json:"pass_rate"`
DataPipeline bool `json:"data_pipeline"`
DataPipelineDetail string `json:"data_pipeline_detail,omitempty"` // PASS, WARN, SKIP, FAIL with context
- Freshness FreshnessResult `json:"freshness,omitempty"`
+ Freshness FreshnessResult `json:"freshness"`
BrowserSessionRequired bool `json:"browser_session_required,omitempty"`
BrowserSessionProof string `json:"browser_session_proof,omitempty"`
BrowserSessionDetail string `json:"browser_session_detail,omitempty"`
@@ -153,13 +154,7 @@ func RunVerify(cfg VerifyConfig) (*VerifyReport, error) {
// what the spec declares or the API name implies.
if discovered := discoverCLIEnvVars(cfg.Dir); len(discovered) > 0 {
for _, ev := range discovered {
- found := false
- for _, existing := range authEnvVars {
- if ev == existing {
- found = true
- break
- }
- }
+ found := slices.Contains(authEnvVars, ev)
if !found {
authEnvVars = append(authEnvVars, ev)
}
@@ -973,7 +968,7 @@ func runCLIWithOutput(binary string, args []string, env []string, timeout time.D
// parseSQLOutput extracts non-empty, non-header lines from sql command output.
func parseSQLOutput(out []byte) []string {
var tables []string
- for _, line := range strings.Split(string(out), "\n") {
+ for line := range strings.SplitSeq(string(out), "\n") {
line = strings.TrimSpace(line)
if line == "" || line == "name" || strings.HasPrefix(line, "---") {
continue
@@ -1000,7 +995,7 @@ func parseSQLOutput(out []byte) []string {
// parseCountOutput extracts a numeric count from sql command output.
func parseCountOutput(out []byte) int {
- for _, line := range strings.Split(string(out), "\n") {
+ for line := range strings.SplitSeq(string(out), "\n") {
line = strings.TrimSpace(line)
if line == "" || line == "count(*)" || strings.HasPrefix(line, "---") {
continue
diff --git a/internal/pipeline/scorecard.go b/internal/pipeline/scorecard.go
index 058881ab..360769bc 100644
--- a/internal/pipeline/scorecard.go
+++ b/internal/pipeline/scorecard.go
@@ -307,12 +307,7 @@ func RunScorecard(outputDir, pipelineDir, specPath string, verifyReport *VerifyR
}
func (sc *Scorecard) IsDimensionUnscored(name string) bool {
- for _, dimension := range sc.UnscoredDimensions {
- if dimension == name {
- return true
- }
- }
- return false
+ return slices.Contains(sc.UnscoredDimensions, name)
}
func scoreOutputModes(dir string) int {
@@ -826,13 +821,7 @@ func scoreLiveAPIVerification(verifyReport *VerifyReport) (int, bool) {
return 10, true
}
// Linear scale below the cap: 0% → 0, 10% → 1, ..., 94% → 9.
- score := int(verifyReport.PassRate / 10)
- if score < 0 {
- score = 0
- }
- if score > 10 {
- score = 10
- }
+ score := min(max(int(verifyReport.PassRate/10), 0), 10)
return score, true
}
@@ -1119,10 +1108,7 @@ func scoreVision(dir string) int {
tier2 = 5
}
- score := int(tier1 + tier2)
- if score > 10 {
- score = 10
- }
+ score := min(int(tier1+tier2), 10)
return score
}
@@ -1173,7 +1159,7 @@ func registeredCommandFiles(cliDir string) map[string]bool {
continue
}
content := readFileContent(filepath.Join(cliDir, e.Name()))
- for _, line := range strings.Split(content, "\n") {
+ for line := range strings.SplitSeq(content, "\n") {
sm := defRe.FindStringSubmatch(line)
if sm == nil {
continue
@@ -1978,12 +1964,13 @@ func scoreTypeFidelity(dir string) int {
score++
}
- allCLI := ""
+ var allCLIBuilder strings.Builder
for _, content := range sampleCommandFiles(dir, 0) {
- allCLI += content
+ allCLIBuilder.WriteString(content)
}
- allCLI += readFileContent(filepath.Join(dir, "internal", "cli", "helpers.go"))
- allCLI += readFileContent(filepath.Join(dir, "internal", "cli", "root.go"))
+ allCLIBuilder.WriteString(readFileContent(filepath.Join(dir, "internal", "cli", "helpers.go")))
+ allCLIBuilder.WriteString(readFileContent(filepath.Join(dir, "internal", "cli", "root.go")))
+ allCLI := allCLIBuilder.String()
if !strings.Contains(allCLI, "var _ = strings.ReplaceAll") && !strings.Contains(allCLI, "var _ = fmt.Sprintf") {
score++
}
@@ -2126,7 +2113,7 @@ func scoreDomainTables(storeContent string) int {
columnTables := 0
for _, match := range createTableRe.FindAllStringSubmatch(storeContent, -1) {
columnCount := 0
- for _, line := range strings.Split(match[1], "\n") {
+ for line := range strings.SplitSeq(match[1], "\n") {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "--") {
continue
diff --git a/internal/pipeline/spec_detect.go b/internal/pipeline/spec_detect.go
index 6aab9f91..5f9cc456 100644
--- a/internal/pipeline/spec_detect.go
+++ b/internal/pipeline/spec_detect.go
@@ -14,8 +14,8 @@ import (
func isInternalYAMLSpec(data []byte) bool {
// Internal YAML specs start with "name:" (possibly after comments/blank lines).
// OpenAPI specs start with "openapi:" or have a top-level "paths:" key.
- lines := bytes.Split(data, []byte("\n"))
- for _, line := range lines {
+ lines := bytes.SplitSeq(data, []byte("\n"))
+ for line := range lines {
trimmed := bytes.TrimSpace(line)
if len(trimmed) == 0 || trimmed[0] == '#' {
continue
diff --git a/internal/pipeline/verify.go b/internal/pipeline/verify.go
index 04a9f206..16fa7dc3 100644
--- a/internal/pipeline/verify.go
+++ b/internal/pipeline/verify.go
@@ -520,7 +520,7 @@ func countTableColumns(storeSrc, tableName string) int {
return 0
}
columns := 0
- for _, line := range strings.Split(match[1], "\n") {
+ for line := range strings.SplitSeq(match[1], "\n") {
line = strings.TrimSpace(strings.TrimSuffix(line, ","))
if line == "" {
continue
diff --git a/internal/pipeline/workflow_verify.go b/internal/pipeline/workflow_verify.go
index 4ce8a3d4..8b24bbd2 100644
--- a/internal/pipeline/workflow_verify.go
+++ b/internal/pipeline/workflow_verify.go
@@ -178,7 +178,7 @@ func executeStep(binary string, step WorkflowStep, cmdExpanded string, dir strin
}
// Validate JSON output.
- var parsed interface{}
+ var parsed any
if err := json.Unmarshal([]byte(output), &parsed); err != nil {
sr.Status = StepStatusFailCLIBug
sr.Error = "output is not valid JSON"
@@ -187,7 +187,7 @@ func executeStep(binary string, step WorkflowStep, cmdExpanded string, dir strin
// Check expect_fields if specified.
if len(step.ExpectFields) > 0 {
- if obj, ok := parsed.(map[string]interface{}); ok {
+ if obj, ok := parsed.(map[string]any); ok {
for _, field := range step.ExpectFields {
if _, exists := obj[field]; !exists {
sr.Status = StepStatusFailCLIBug
@@ -262,7 +262,7 @@ func extractJSONField(jsonData []byte, path string) (string, error) {
return "", fmt.Errorf("empty path")
}
- var raw interface{}
+ var raw any
if err := json.Unmarshal(jsonData, &raw); err != nil {
return "", fmt.Errorf("parsing JSON: %w", err)
}
@@ -276,7 +276,7 @@ func extractJSONField(jsonData []byte, path string) (string, error) {
// Navigate into map
if name != "" {
- obj, ok := current.(map[string]interface{})
+ obj, ok := current.(map[string]any)
if !ok {
return "", fmt.Errorf("expected object at %q, got %T", seg, current)
}
@@ -289,7 +289,7 @@ func extractJSONField(jsonData []byte, path string) (string, error) {
// Navigate into array if index present
if hasIdx {
- arr, ok := current.([]interface{})
+ arr, ok := current.([]any)
if !ok {
return "", fmt.Errorf("expected array at %q, got %T", seg, current)
}
diff --git a/internal/profiler/profiler.go b/internal/profiler/profiler.go
index f3ac5618..a5318d80 100644
--- a/internal/profiler/profiler.go
+++ b/internal/profiler/profiler.go
@@ -1,6 +1,7 @@
package profiler
import (
+ "slices"
"sort"
"strings"
@@ -177,12 +178,7 @@ func Profile(s *spec.APISpec) *APIProfile {
searchParamNames := []string{"q", "query", "search", "keyword", "term", "querytext", "searchterm", "searchtext", "text"}
isSearchParam := func(name string) bool {
lower := strings.ToLower(name)
- for _, sp := range searchParamNames {
- if lower == sp {
- return true
- }
- }
- return false
+ return slices.Contains(searchParamNames, lower)
}
for _, param := range endpoint.Params {
@@ -780,11 +776,11 @@ func nameVariants(name string) []string {
if strings.HasSuffix(normalized, "ies") {
addVariant(normalized[:len(normalized)-3]+"y", seen, &variants)
}
- if strings.HasSuffix(normalized, "es") {
- addVariant(strings.TrimSuffix(normalized, "es"), seen, &variants)
+ if before, ok := strings.CutSuffix(normalized, "es"); ok {
+ addVariant(before, seen, &variants)
}
- if strings.HasSuffix(normalized, "s") {
- addVariant(strings.TrimSuffix(normalized, "s"), seen, &variants)
+ if before, ok := strings.CutSuffix(normalized, "s"); ok {
+ addVariant(before, seen, &variants)
}
return variants
diff --git a/internal/spec/spec.go b/internal/spec/spec.go
index bd977e63..3abf68a8 100644
--- a/internal/spec/spec.go
+++ b/internal/spec/spec.go
@@ -55,9 +55,9 @@ type APISpec struct {
Resources map[string]Resource `yaml:"resources" json:"resources"`
Types map[string]TypeDef `yaml:"types" json:"types"`
ExtraCommands []ExtraCommand `yaml:"extra_commands,omitempty" json:"extra_commands,omitempty"` // hand-written cobra commands declared so SKILL.md can document them; spec-only metadata, no code generated
- Cache CacheConfig `yaml:"cache,omitempty" json:"cache,omitempty"` // cache freshness + auto-refresh config; when enabled, generated read commands auto-refresh stale local data before serving
- Share ShareConfig `yaml:"share,omitempty" json:"share,omitempty"` // git-backed snapshot sharing config; when enabled, emits a `share` subcommand that publishes/subscribes to a git repo
- MCP MCPConfig `yaml:"mcp,omitempty" json:"mcp,omitempty"` // MCP server generation config; when unset, the emitted MCP binary is stdio-only (today's default). Opting into http adds a --transport/--addr flag surface so the same binary can serve cloud-hosted agents.
+ Cache CacheConfig `yaml:"cache,omitempty" json:"cache"` // cache freshness + auto-refresh config; when enabled, generated read commands auto-refresh stale local data before serving
+ Share ShareConfig `yaml:"share,omitempty" json:"share"` // git-backed snapshot sharing config; when enabled, emits a `share` subcommand that publishes/subscribes to a git repo
+ MCP MCPConfig `yaml:"mcp,omitempty" json:"mcp"` // MCP server generation config; when unset, the emitted MCP binary is stdio-only (today's default). Opting into http adds a --transport/--addr flag surface so the same binary can serve cloud-hosted agents.
}
// ExtraCommand declares a hand-written cobra command so the SKILL.md
diff --git a/internal/vision/vision.go b/internal/vision/vision.go
index fea357d2..34bfec31 100644
--- a/internal/vision/vision.go
+++ b/internal/vision/vision.go
@@ -1,6 +1,8 @@
// Package vision defines types for the visionary research system.
package vision
+import "slices"
+
type DataProfile struct {
WritePattern string
Volume string
@@ -106,10 +108,8 @@ func (v *VisionaryPlan) ShouldIncludeTemplate(templateName string) bool {
if v.Features[i].TotalScore < 8 {
continue
}
- for _, tn := range v.Features[i].TemplateNames {
- if tn == templateName {
- return true
- }
+ if slices.Contains(v.Features[i].TemplateNames, templateName) {
+ return true
}
}
return false
← 5f68784d chore(main): release 2.3.3 (#277)
·
back to Cli Printing Press
·
fix(catalog): update stale spec URLs + add download content- 4a77f46c →