Skip to content

Commit 830adb9

Browse files
Fix default skill version and reject leading zeros in semver
- Change default skill version from 0.1.0 to 0.0.1 (skills should start at 0.0.1, distinct from the CLI's own v0.1.0 release version) - Reject leading zeros in ParseVersion per strict semver spec (e.g. "01.2.3" is now invalid) - Update all tests and docs accordingly Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 54dbc1a commit 830adb9

7 files changed

Lines changed: 60 additions & 33 deletions

File tree

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ metadata:
176176
name: author-name
177177
type: human # human | agent
178178
platform: claude-code # only when type=agent
179-
version: "0.1.0"
179+
version: "0.0.1"
180180
modified-by: # append-only provenance list
181181
- name: codex-cli
182182
type: agent

internal/cli/skill_create.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ func newSkillCreateCmd() *cobra.Command {
144144
cmd.Flags().BoolVar(&force, "force", false, "bypass overlap detection block")
145145
cmd.Flags().StringVar(&fromTemplate, "from-template", "", "path to a template file for the skill body")
146146
cmd.Flags().StringSliceVar(&tags, "tags", nil, "comma-separated tags for the skill")
147-
cmd.Flags().StringVar(&version, "version", "", "initial version (default: 0.1.0)")
147+
cmd.Flags().StringVar(&version, "version", "", "initial version (default: 0.0.1)")
148148

149149
return cmd
150150
}

internal/cli/skill_version_test.go

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ func TestSkillVersion_Show(t *testing.T) {
2323
var result output.SkillVersionResult
2424
require.NoError(t, json.Unmarshal([]byte(out), &result))
2525
assert.Equal(t, "ver-skill", result.Name)
26-
assert.Equal(t, "0.1.0", result.Version)
26+
assert.Equal(t, "0.0.1", result.Version)
2727
assert.Equal(t, "user", result.Scope)
2828
assert.False(t, result.Bumped)
2929
}
@@ -36,7 +36,7 @@ func TestSkillVersion_Show_Text(t *testing.T) {
3636

3737
out, err := runCmd(t, cc, "skill", "version", "ver-text")
3838
require.NoError(t, err)
39-
assert.Contains(t, out, "0.1.0")
39+
assert.Contains(t, out, "0.0.1")
4040
}
4141

4242
func TestSkillVersion_NotFound(t *testing.T) {
@@ -60,8 +60,8 @@ func TestSkillVersion_BumpPatch(t *testing.T) {
6060
var result output.SkillVersionResult
6161
require.NoError(t, json.Unmarshal([]byte(out), &result))
6262
assert.Equal(t, "bump-patch", result.Name)
63-
assert.Equal(t, "0.1.1", result.Version)
64-
assert.Equal(t, "0.1.0", result.PreviousVersion)
63+
assert.Equal(t, "0.0.2", result.Version)
64+
assert.Equal(t, "0.0.1", result.PreviousVersion)
6565
assert.True(t, result.Bumped)
6666

6767
// Verify the change persisted
@@ -70,7 +70,7 @@ func TestSkillVersion_BumpPatch(t *testing.T) {
7070

7171
var showResult output.SkillResult
7272
require.NoError(t, json.Unmarshal([]byte(showOut), &showResult))
73-
assert.Equal(t, "0.1.1", showResult.Version)
73+
assert.Equal(t, "0.0.2", showResult.Version)
7474
}
7575

7676
func TestSkillVersion_BumpMinor(t *testing.T) {
@@ -84,8 +84,8 @@ func TestSkillVersion_BumpMinor(t *testing.T) {
8484

8585
var result output.SkillVersionResult
8686
require.NoError(t, json.Unmarshal([]byte(out), &result))
87-
assert.Equal(t, "0.2.0", result.Version)
88-
assert.Equal(t, "0.1.0", result.PreviousVersion)
87+
assert.Equal(t, "0.1.0", result.Version)
88+
assert.Equal(t, "0.0.1", result.PreviousVersion)
8989
assert.True(t, result.Bumped)
9090
}
9191

@@ -101,7 +101,7 @@ func TestSkillVersion_BumpMajor(t *testing.T) {
101101
var result output.SkillVersionResult
102102
require.NoError(t, json.Unmarshal([]byte(out), &result))
103103
assert.Equal(t, "1.0.0", result.Version)
104-
assert.Equal(t, "0.1.0", result.PreviousVersion)
104+
assert.Equal(t, "0.0.1", result.PreviousVersion)
105105
assert.True(t, result.Bumped)
106106
}
107107

@@ -124,8 +124,8 @@ func TestSkillVersion_BumpText(t *testing.T) {
124124
out, err := runCmd(t, cc, "skill", "version", "bump-text", "--bump", "patch")
125125
require.NoError(t, err)
126126
assert.Contains(t, out, "Bumped")
127-
assert.Contains(t, out, "0.1.0")
128-
assert.Contains(t, out, "0.1.1")
127+
assert.Contains(t, out, "0.0.1")
128+
assert.Contains(t, out, "0.0.2")
129129
}
130130

131131
func TestSkillVersion_MultipleBumps(t *testing.T) {
@@ -145,7 +145,7 @@ func TestSkillVersion_MultipleBumps(t *testing.T) {
145145

146146
var result output.SkillVersionResult
147147
require.NoError(t, json.Unmarshal([]byte(out), &result))
148-
assert.Equal(t, "0.1.2", result.Version)
148+
assert.Equal(t, "0.0.3", result.Version)
149149
}
150150

151151
func TestSkillVersion_Scoped(t *testing.T) {
@@ -160,7 +160,7 @@ func TestSkillVersion_Scoped(t *testing.T) {
160160
var result output.SkillVersionResult
161161
require.NoError(t, json.Unmarshal([]byte(out), &result))
162162
assert.Equal(t, "project", result.Scope)
163-
assert.Equal(t, "0.1.0", result.Version)
163+
assert.Equal(t, "0.0.1", result.Version)
164164
}
165165

166166
// --- skill create --version ---
@@ -220,5 +220,5 @@ func TestSkillCreate_DefaultVersion(t *testing.T) {
220220

221221
var result output.SkillResult
222222
require.NoError(t, json.Unmarshal([]byte(out), &result))
223-
assert.Equal(t, "0.1.0", result.Version)
223+
assert.Equal(t, "0.0.1", result.Version)
224224
}

internal/skill/scaffold.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ func NewSkillWithBody(name, description, authorName, authorType, authorPlatform,
3131
Description: description,
3232
Metadata: Metadata{
3333
Author: author,
34-
Version: "0.1.0",
34+
Version: "0.0.1",
3535
},
3636
Body: body,
3737
}

internal/skill/scaffold_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ func TestNewSkill_NameOnly(t *testing.T) {
1111

1212
assert.Equal(t, "my-skill", s.Name)
1313
assert.Contains(t, s.Description, "TODO")
14-
assert.Equal(t, "0.1.0", s.Metadata.Version)
14+
assert.Equal(t, "0.0.1", s.Metadata.Version)
1515
assert.Contains(t, s.Body, "## Instructions")
1616
}
1717

internal/skill/version.go

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,18 @@ import (
66
"strings"
77
)
88

9+
// parseVersionPart parses a single version component, rejecting leading zeros per semver spec.
10+
func parseVersionPart(s string) (int, error) {
11+
if len(s) > 1 && s[0] == '0' {
12+
return 0, fmt.Errorf("leading zeros not allowed: %q", s)
13+
}
14+
n, err := strconv.Atoi(s)
15+
if err != nil || n < 0 {
16+
return 0, fmt.Errorf("invalid part: %q", s)
17+
}
18+
return n, nil
19+
}
20+
921
// Version represents a semantic version (MAJOR.MINOR.PATCH).
1022
type Version struct {
1123
Major int
@@ -20,19 +32,19 @@ func ParseVersion(s string) (Version, error) {
2032
return Version{}, fmt.Errorf("invalid version %q: must be MAJOR.MINOR.PATCH", s)
2133
}
2234

23-
major, err := strconv.Atoi(parts[0])
24-
if err != nil || major < 0 {
25-
return Version{}, fmt.Errorf("invalid version %q: major must be a non-negative integer", s)
35+
major, err := parseVersionPart(parts[0])
36+
if err != nil {
37+
return Version{}, fmt.Errorf("invalid version %q: major must be a non-negative integer without leading zeros", s)
2638
}
2739

28-
minor, err := strconv.Atoi(parts[1])
29-
if err != nil || minor < 0 {
30-
return Version{}, fmt.Errorf("invalid version %q: minor must be a non-negative integer", s)
40+
minor, err := parseVersionPart(parts[1])
41+
if err != nil {
42+
return Version{}, fmt.Errorf("invalid version %q: minor must be a non-negative integer without leading zeros", s)
3143
}
3244

33-
patch, err := strconv.Atoi(parts[2])
34-
if err != nil || patch < 0 {
35-
return Version{}, fmt.Errorf("invalid version %q: patch must be a non-negative integer", s)
45+
patch, err := parseVersionPart(parts[2])
46+
if err != nil {
47+
return Version{}, fmt.Errorf("invalid version %q: patch must be a non-negative integer without leading zeros", s)
3648
}
3749

3850
return Version{Major: major, Minor: minor, Patch: patch}, nil

internal/skill/version_test.go

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ func TestParseVersion(t *testing.T) {
2626
},
2727
{
2828
name: "default version",
29-
input: "0.1.0",
30-
want: Version{Major: 0, Minor: 1, Patch: 0},
29+
input: "0.0.1",
30+
want: Version{Major: 0, Minor: 0, Patch: 1},
3131
},
3232
{
3333
name: "large numbers",
@@ -79,6 +79,21 @@ func TestParseVersion(t *testing.T) {
7979
input: "1.0.-1",
8080
wantErr: true,
8181
},
82+
{
83+
name: "leading zero major",
84+
input: "01.0.0",
85+
wantErr: true,
86+
},
87+
{
88+
name: "leading zero minor",
89+
input: "1.01.0",
90+
wantErr: true,
91+
},
92+
{
93+
name: "leading zero patch",
94+
input: "1.0.01",
95+
wantErr: true,
96+
},
8297
}
8398

8499
for _, tt := range tests {
@@ -165,19 +180,19 @@ func TestBumpVersion(t *testing.T) {
165180
},
166181
{
167182
name: "bump default version patch",
168-
version: "0.1.0",
183+
version: "0.0.1",
169184
level: "patch",
170-
want: "0.1.1",
185+
want: "0.0.2",
171186
},
172187
{
173188
name: "bump default version minor",
174-
version: "0.1.0",
189+
version: "0.0.1",
175190
level: "minor",
176-
want: "0.2.0",
191+
want: "0.1.0",
177192
},
178193
{
179194
name: "bump default version major",
180-
version: "0.1.0",
195+
version: "0.0.1",
181196
level: "major",
182197
want: "1.0.0",
183198
},

0 commit comments

Comments
 (0)