aiwg 2026.1.5 → 2026.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CLAUDE.md
CHANGED
|
@@ -190,7 +190,9 @@ Before pushing a version tag:
|
|
|
190
190
|
|
|
191
191
|
### Version Format
|
|
192
192
|
|
|
193
|
-
- **CalVer**: `YYYY.
|
|
193
|
+
- **CalVer**: `YYYY.M.PATCH` (e.g., `2026.1.5`, `2026.12.0`)
|
|
194
|
+
- **CRITICAL**: No leading zeros! npm semver rejects `01`, `02`, etc.
|
|
194
195
|
- PATCH resets each month
|
|
195
|
-
- Tag format: `vYYYY.
|
|
196
|
+
- Tag format: `vYYYY.M.PATCH` (e.g., `v2026.1.5`)
|
|
197
|
+
- See `@docs/contributing/versioning.md` for full details
|
|
196
198
|
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# Versioning Guide
|
|
2
|
+
|
|
3
|
+
**Version:** 1.0
|
|
4
|
+
**Last Updated:** 2026-01-14
|
|
5
|
+
**Target Audience:** All contributors and AI agents
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
AIWG uses **Calendar Versioning (CalVer)** with npm-compatible format. This document explains the versioning scheme and critical rules to avoid npm publishing failures.
|
|
10
|
+
|
|
11
|
+
## Version Format
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
YYYY.M.PATCH
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
| Component | Description | Example |
|
|
18
|
+
|-----------|-------------|---------|
|
|
19
|
+
| `YYYY` | Four-digit year | `2026` |
|
|
20
|
+
| `M` | Month (1-12, **NO leading zeros**) | `1`, `12` |
|
|
21
|
+
| `PATCH` | Patch number within month (resets each month) | `0`, `1`, `5` |
|
|
22
|
+
|
|
23
|
+
### Examples
|
|
24
|
+
|
|
25
|
+
| Correct | Incorrect | Why |
|
|
26
|
+
|---------|-----------|-----|
|
|
27
|
+
| `2026.1.0` | `2026.01.0` | Leading zero in month |
|
|
28
|
+
| `2026.1.5` | `2026.01.05` | Leading zeros in month and patch |
|
|
29
|
+
| `2026.12.0` | `2026.12.00` | Leading zero in patch |
|
|
30
|
+
|
|
31
|
+
## Critical Rule: No Leading Zeros
|
|
32
|
+
|
|
33
|
+
**npm's semver parser rejects leading zeros.** This is per the [Semantic Versioning spec](https://semver.org/):
|
|
34
|
+
|
|
35
|
+
> A normal version number MUST take the form X.Y.Z where X, Y, and Z are non-negative integers, and **MUST NOT contain leading zeroes**.
|
|
36
|
+
|
|
37
|
+
### What Happens With Leading Zeros
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# This FAILS
|
|
41
|
+
$ npm -g update aiwg
|
|
42
|
+
npm error Invalid Version: 2026.01.4
|
|
43
|
+
|
|
44
|
+
# This WORKS (same package, different command)
|
|
45
|
+
$ npm -g install aiwg
|
|
46
|
+
# Installs successfully but update is broken
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The `npm install` command is more lenient than `npm update`. Users will be able to install but not update, causing confusion and support issues.
|
|
50
|
+
|
|
51
|
+
## Tag Format
|
|
52
|
+
|
|
53
|
+
Git tags should match the version with a `v` prefix:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# Correct
|
|
57
|
+
git tag -a v2026.1.5 -m "v2026.1.5 - Feature Name"
|
|
58
|
+
|
|
59
|
+
# Incorrect
|
|
60
|
+
git tag -a v2026.01.5 -m "v2026.01.5 - Feature Name"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Release Workflow
|
|
64
|
+
|
|
65
|
+
### 1. Update package.json
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"version": "2026.1.5"
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Validation**: Run this to check for leading zeros:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
grep '"version"' package.json | grep -E '\.[0-9]{2}\.' && echo "ERROR: Leading zero detected!" || echo "OK: No leading zeros"
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 2. Update CHANGELOG.md
|
|
80
|
+
|
|
81
|
+
```markdown
|
|
82
|
+
## [2026.1.5] - 2026-01-14 – "Release Name"
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### 3. Create and Push Tag
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
# Create annotated tag
|
|
89
|
+
git tag -a v2026.1.5 -m "v2026.1.5 - Release Name"
|
|
90
|
+
|
|
91
|
+
# Push to both remotes
|
|
92
|
+
git push origin main --tags
|
|
93
|
+
git push github main --tags
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### 4. Verify Published Version
|
|
97
|
+
|
|
98
|
+
After CI/CD completes:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
npm view aiwg version
|
|
102
|
+
# Should show: 2026.1.5
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Version Progression Examples
|
|
106
|
+
|
|
107
|
+
### Within a Month
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
2026.1.0 → First release in January 2026
|
|
111
|
+
2026.1.1 → Bug fix
|
|
112
|
+
2026.1.2 → Another fix
|
|
113
|
+
2026.1.3 → Feature addition
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Month Transitions
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
2026.1.5 → Last release in January
|
|
120
|
+
2026.2.0 → First release in February (PATCH resets)
|
|
121
|
+
2026.2.1 → Next release in February
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Year Transitions
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
2026.12.3 → December release
|
|
128
|
+
2027.1.0 → January of next year
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Automated Validation
|
|
132
|
+
|
|
133
|
+
### Pre-commit Hook (Optional)
|
|
134
|
+
|
|
135
|
+
Add to `.git/hooks/pre-commit`:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
#!/bin/bash
|
|
139
|
+
VERSION=$(grep '"version"' package.json | head -1)
|
|
140
|
+
if echo "$VERSION" | grep -qE '\.[0-9]{2}\.'; then
|
|
141
|
+
echo "ERROR: package.json version has leading zeros!"
|
|
142
|
+
echo "Found: $VERSION"
|
|
143
|
+
echo "Fix: Remove leading zeros (e.g., 2026.01.5 → 2026.1.5)"
|
|
144
|
+
exit 1
|
|
145
|
+
fi
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### CI Validation
|
|
149
|
+
|
|
150
|
+
The npm publish workflow will fail if the version has leading zeros, but it's better to catch this before pushing.
|
|
151
|
+
|
|
152
|
+
## Common Mistakes
|
|
153
|
+
|
|
154
|
+
### Mistake 1: Copy-Paste from Dates
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
# Today is January 5, 2026
|
|
158
|
+
# WRONG: Using date format
|
|
159
|
+
2026.01.05
|
|
160
|
+
|
|
161
|
+
# RIGHT: Using CalVer format
|
|
162
|
+
2026.1.5
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Mistake 2: Assuming Two-Digit Month
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
# WRONG: Padding single-digit months
|
|
169
|
+
2026.01.0, 2026.02.0, ..., 2026.09.0
|
|
170
|
+
|
|
171
|
+
# RIGHT: No padding
|
|
172
|
+
2026.1.0, 2026.2.0, ..., 2026.9.0
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Mistake 3: Incrementing Without Checking Format
|
|
176
|
+
|
|
177
|
+
When bumping versions, always verify the format:
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
# Before: 2026.1.4
|
|
181
|
+
# Bumping patch...
|
|
182
|
+
|
|
183
|
+
# WRONG (if you typed it manually)
|
|
184
|
+
"version": "2026.01.5"
|
|
185
|
+
|
|
186
|
+
# RIGHT
|
|
187
|
+
"version": "2026.1.5"
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## References
|
|
191
|
+
|
|
192
|
+
- [Semantic Versioning 2.0.0](https://semver.org/)
|
|
193
|
+
- [Calendar Versioning](https://calver.org/)
|
|
194
|
+
- [npm semver](https://docs.npmjs.com/cli/v6/using-npm/semver)
|
|
195
|
+
- @CLAUDE.md - Release Documentation Requirements
|
|
196
|
+
- @docs/contributing/ci-cd-secrets.md - CI/CD configuration
|
package/package.json
CHANGED
|
@@ -606,17 +606,24 @@ async function main() {
|
|
|
606
606
|
// Collect agent files based on mode
|
|
607
607
|
const agentFiles = [];
|
|
608
608
|
|
|
609
|
-
//
|
|
610
|
-
if (mode === 'general' || mode === 'writing' || mode === 'both' || mode === 'all') {
|
|
611
|
-
const
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
609
|
+
// All addons (dynamically discovered)
|
|
610
|
+
if (mode === 'general' || mode === 'writing' || mode === 'sdlc' || mode === 'both' || mode === 'all') {
|
|
611
|
+
const addonsRoot = path.join(srcRoot, 'agentic', 'code', 'addons');
|
|
612
|
+
if (fs.existsSync(addonsRoot)) {
|
|
613
|
+
let addonAgentCount = 0;
|
|
614
|
+
const addonDirs = fs.readdirSync(addonsRoot, { withFileTypes: true })
|
|
615
|
+
.filter(e => e.isDirectory())
|
|
616
|
+
.map(e => path.join(addonsRoot, e.name, 'agents'));
|
|
617
|
+
|
|
618
|
+
for (const addonAgentsDir of addonDirs) {
|
|
619
|
+
if (fs.existsSync(addonAgentsDir)) {
|
|
620
|
+
const files = listMdFiles(addonAgentsDir);
|
|
621
|
+
agentFiles.push(...files);
|
|
622
|
+
addonAgentCount += files.length;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
if (addonAgentCount > 0) {
|
|
626
|
+
console.log(`Found ${addonAgentCount} addon agents`);
|
|
620
627
|
}
|
|
621
628
|
}
|
|
622
629
|
}
|
|
@@ -693,6 +700,20 @@ async function main() {
|
|
|
693
700
|
// Collect skills count for .windsurfrules
|
|
694
701
|
let skillCount = 0;
|
|
695
702
|
if (deploySkills) {
|
|
703
|
+
// Addon skills (dynamically discovered)
|
|
704
|
+
const addonsRoot = path.join(srcRoot, 'agentic', 'code', 'addons');
|
|
705
|
+
if (fs.existsSync(addonsRoot)) {
|
|
706
|
+
const addonDirs = fs.readdirSync(addonsRoot, { withFileTypes: true })
|
|
707
|
+
.filter(e => e.isDirectory())
|
|
708
|
+
.map(e => path.join(addonsRoot, e.name, 'skills'));
|
|
709
|
+
|
|
710
|
+
for (const addonSkillsDir of addonDirs) {
|
|
711
|
+
if (fs.existsSync(addonSkillsDir)) {
|
|
712
|
+
skillCount += listSkillDirs(addonSkillsDir).length;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
696
717
|
// SDLC skills
|
|
697
718
|
if (mode === 'sdlc' || mode === 'both' || mode === 'all') {
|
|
698
719
|
const sdlcSkillsRoot = path.join(srcRoot, 'agentic', 'code', 'frameworks', 'sdlc-complete', 'skills');
|
|
@@ -701,16 +722,9 @@ async function main() {
|
|
|
701
722
|
}
|
|
702
723
|
}
|
|
703
724
|
|
|
704
|
-
// Utils addon skills
|
|
705
|
-
const utilsSkillsRoot = path.join(srcRoot, 'agentic', 'code', 'addons', 'aiwg-utils', 'skills');
|
|
706
|
-
if (fs.existsSync(utilsSkillsRoot)) {
|
|
707
|
-
skillCount += listSkillDirs(utilsSkillsRoot).length;
|
|
708
|
-
}
|
|
709
|
-
|
|
710
725
|
if (skillCount > 0) {
|
|
711
726
|
console.log(`\n[NOTE] Skills (${skillCount} found) are not directly deployed to Windsurf.`);
|
|
712
|
-
console.log('Reference skill files in prompts using @-mentions
|
|
713
|
-
console.log(' @~/.local/share/ai-writing-guide/agentic/code/addons/aiwg-utils/skills/<skill>/SKILL.md');
|
|
727
|
+
console.log('Reference skill files in prompts using @-mentions.');
|
|
714
728
|
}
|
|
715
729
|
}
|
|
716
730
|
|
|
@@ -201,6 +201,22 @@ function getCommandDirectories(srcRoot, mode) {
|
|
|
201
201
|
}
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
+
// Addon commands (dynamically discovered)
|
|
205
|
+
if (mode === 'general' || mode === 'sdlc' || mode === 'both' || mode === 'all') {
|
|
206
|
+
const addonsRoot = path.join(srcRoot, 'agentic', 'code', 'addons');
|
|
207
|
+
if (fs.existsSync(addonsRoot)) {
|
|
208
|
+
const addonDirs = fs.readdirSync(addonsRoot, { withFileTypes: true })
|
|
209
|
+
.filter(e => e.isDirectory())
|
|
210
|
+
.map(e => path.join(addonsRoot, e.name, 'commands'));
|
|
211
|
+
|
|
212
|
+
for (const addonCommandsDir of addonDirs) {
|
|
213
|
+
if (fs.existsSync(addonCommandsDir)) {
|
|
214
|
+
dirs.push({ dir: addonCommandsDir, label: path.basename(path.dirname(addonCommandsDir)) });
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
204
220
|
// SDLC framework commands
|
|
205
221
|
if (mode === 'sdlc' || mode === 'both' || mode === 'all') {
|
|
206
222
|
const sdlcCommandsDir = path.join(srcRoot, 'agentic', 'code', 'frameworks', 'sdlc-complete', 'commands');
|
|
@@ -241,6 +241,22 @@ function getCommandDirectories(srcRoot, mode) {
|
|
|
241
241
|
}
|
|
242
242
|
}
|
|
243
243
|
|
|
244
|
+
// Addon commands (dynamically discovered)
|
|
245
|
+
if (mode === 'general' || mode === 'sdlc' || mode === 'both' || mode === 'all') {
|
|
246
|
+
const addonsRoot = path.join(srcRoot, 'agentic', 'code', 'addons');
|
|
247
|
+
if (fs.existsSync(addonsRoot)) {
|
|
248
|
+
const addonDirs = fs.readdirSync(addonsRoot, { withFileTypes: true })
|
|
249
|
+
.filter(e => e.isDirectory())
|
|
250
|
+
.map(e => path.join(addonsRoot, e.name, 'commands'));
|
|
251
|
+
|
|
252
|
+
for (const addonCommandsDir of addonDirs) {
|
|
253
|
+
if (fs.existsSync(addonCommandsDir)) {
|
|
254
|
+
dirs.push({ dir: addonCommandsDir, label: path.basename(path.dirname(addonCommandsDir)) });
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
244
260
|
// SDLC framework commands
|
|
245
261
|
if (mode === 'sdlc' || mode === 'both' || mode === 'all') {
|
|
246
262
|
const sdlcCommandsDir = path.join(srcRoot, 'agentic', 'code', 'frameworks', 'sdlc-complete', 'commands');
|
|
@@ -279,6 +279,21 @@ function generateAIWGContent(aiwgRoot, mode) {
|
|
|
279
279
|
agentPaths.push(...listMdFiles(generalAgents));
|
|
280
280
|
}
|
|
281
281
|
}
|
|
282
|
+
|
|
283
|
+
// Addon agents (dynamically discovered)
|
|
284
|
+
const addonsRoot = path.join(aiwgRoot, 'agentic', 'code', 'addons');
|
|
285
|
+
if (fs.existsSync(addonsRoot)) {
|
|
286
|
+
const addonDirs = fs.readdirSync(addonsRoot, { withFileTypes: true })
|
|
287
|
+
.filter(e => e.isDirectory())
|
|
288
|
+
.map(e => path.join(addonsRoot, e.name, 'agents'));
|
|
289
|
+
|
|
290
|
+
for (const addonAgentsDir of addonDirs) {
|
|
291
|
+
if (fs.existsSync(addonAgentsDir)) {
|
|
292
|
+
agentPaths.push(...listMdFiles(addonAgentsDir));
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
282
297
|
if (mode === 'sdlc' || mode === 'both') {
|
|
283
298
|
const sdlcAgents = path.join(aiwgRoot, 'agentic', 'code', 'frameworks', 'sdlc-complete', 'agents');
|
|
284
299
|
if (fs.existsSync(sdlcAgents)) {
|
|
@@ -304,6 +319,20 @@ function generateAIWGContent(aiwgRoot, mode) {
|
|
|
304
319
|
commandPaths.push(...listMdFiles(generalCommands));
|
|
305
320
|
}
|
|
306
321
|
}
|
|
322
|
+
|
|
323
|
+
// Addon commands (dynamically discovered)
|
|
324
|
+
if (fs.existsSync(addonsRoot)) {
|
|
325
|
+
const addonCommandDirs = fs.readdirSync(addonsRoot, { withFileTypes: true })
|
|
326
|
+
.filter(e => e.isDirectory())
|
|
327
|
+
.map(e => path.join(addonsRoot, e.name, 'commands'));
|
|
328
|
+
|
|
329
|
+
for (const addonCommandsDir of addonCommandDirs) {
|
|
330
|
+
if (fs.existsSync(addonCommandsDir)) {
|
|
331
|
+
commandPaths.push(...listMdFiles(addonCommandsDir));
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
307
336
|
if (mode === 'sdlc' || mode === 'both') {
|
|
308
337
|
const sdlcCommands = path.join(aiwgRoot, 'agentic', 'code', 'frameworks', 'sdlc-complete', 'commands');
|
|
309
338
|
if (fs.existsSync(sdlcCommands)) {
|