aiwg 2026.1.4 → 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 +4 -2
- package/docs/contributing/ci-cd-secrets.md +188 -0
- package/docs/contributing/versioning.md +196 -0
- package/package.json +1 -1
- package/tools/agents/deploy-windsurf.mjs +33 -19
- package/tools/agents/providers/base.mjs +130 -0
- package/tools/agents/providers/claude.mjs +9 -31
- package/tools/agents/providers/codex.mjs +8 -13
- package/tools/agents/providers/copilot.mjs +8 -17
- package/tools/agents/providers/factory.mjs +7 -18
- package/tools/agents/providers/opencode.mjs +8 -17
- package/tools/agents/providers/windsurf.mjs +11 -16
- package/tools/commands/deploy-prompts-codex.mjs +16 -0
- package/tools/rules/deploy-rules-cursor.mjs +16 -0
- package/tools/warp/setup-warp.mjs +29 -0
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,188 @@
|
|
|
1
|
+
# CI/CD Secrets Configuration
|
|
2
|
+
|
|
3
|
+
**Version:** 1.0
|
|
4
|
+
**Last Updated:** 2026-01-14
|
|
5
|
+
**Target Audience:** Repository maintainers and administrators
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
This document describes the secrets required for CI/CD workflows in the AIWG repository. Secrets are used for authentication with package registries and external services.
|
|
10
|
+
|
|
11
|
+
## Required Secrets
|
|
12
|
+
|
|
13
|
+
### NPM_TOKEN
|
|
14
|
+
|
|
15
|
+
**Purpose:** Authenticate with Gitea's npm package registry for publishing.
|
|
16
|
+
|
|
17
|
+
**Required Scopes:**
|
|
18
|
+
- `package:write` - Required to publish packages
|
|
19
|
+
- `package:read` - Required to verify published packages
|
|
20
|
+
|
|
21
|
+
**Used In:**
|
|
22
|
+
- `.gitea/workflows/npm-publish.yml` - Publishing to Gitea npm registry
|
|
23
|
+
- Creating Gitea releases via API
|
|
24
|
+
|
|
25
|
+
### Setting Up NPM_TOKEN
|
|
26
|
+
|
|
27
|
+
#### Step 1: Create a Gitea Access Token
|
|
28
|
+
|
|
29
|
+
1. Log in to [git.integrolabs.net](https://git.integrolabs.net)
|
|
30
|
+
2. Navigate to **Settings** → **Applications** → **Access Tokens**
|
|
31
|
+
- Direct URL: https://git.integrolabs.net/user/settings/applications
|
|
32
|
+
3. Create a new token with:
|
|
33
|
+
- **Token Name:** `ci-npm-publish` (or descriptive name)
|
|
34
|
+
- **Select Scopes:**
|
|
35
|
+
- ✅ `write:package` (includes read:package)
|
|
36
|
+
- ✅ `read:repository` (for checkout operations)
|
|
37
|
+
- **Expiration:** Set according to your security policy (recommend 1 year max)
|
|
38
|
+
4. Click **Generate Token**
|
|
39
|
+
5. **IMPORTANT:** Copy the token immediately - it won't be shown again
|
|
40
|
+
|
|
41
|
+
#### Step 2: Add Secret to Gitea Repository
|
|
42
|
+
|
|
43
|
+
1. Navigate to the repository: https://git.integrolabs.net/roctinam/ai-writing-guide
|
|
44
|
+
2. Go to **Settings** → **Actions** → **Secrets**
|
|
45
|
+
3. Click **Add Secret**
|
|
46
|
+
4. Configure:
|
|
47
|
+
- **Name:** `NPM_TOKEN`
|
|
48
|
+
- **Value:** Paste the token from Step 1
|
|
49
|
+
5. Click **Add Secret**
|
|
50
|
+
|
|
51
|
+
#### Step 3: Verify Configuration
|
|
52
|
+
|
|
53
|
+
Trigger a manual workflow run to verify:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# Push a test tag (can be deleted after)
|
|
57
|
+
git tag v9999.99.99-test
|
|
58
|
+
git push origin v9999.99.99-test
|
|
59
|
+
|
|
60
|
+
# Watch the workflow at:
|
|
61
|
+
# https://git.integrolabs.net/roctinam/ai-writing-guide/actions
|
|
62
|
+
|
|
63
|
+
# Clean up test tag
|
|
64
|
+
git tag -d v9999.99.99-test
|
|
65
|
+
git push origin :refs/tags/v9999.99.99-test
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Or use the workflow dispatch with dry_run enabled.
|
|
69
|
+
|
|
70
|
+
## Troubleshooting
|
|
71
|
+
|
|
72
|
+
### Error: 401 Unauthorized
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
npm error code E401
|
|
76
|
+
npm error 401 Unauthorized - PUT https://git.integrolabs.net/api/packages/roctinam/npm/aiwg
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**Causes:**
|
|
80
|
+
1. **Token expired** - Create a new token and update the secret
|
|
81
|
+
2. **Token missing** - Verify NPM_TOKEN secret exists in repository settings
|
|
82
|
+
3. **Wrong scopes** - Token must have `write:package` scope
|
|
83
|
+
4. **Token revoked** - Check if token still exists in user settings
|
|
84
|
+
|
|
85
|
+
**Resolution:**
|
|
86
|
+
1. Go to https://git.integrolabs.net/user/settings/applications
|
|
87
|
+
2. Check if the token exists and hasn't expired
|
|
88
|
+
3. If expired/missing, create a new token with `write:package` scope
|
|
89
|
+
4. Update the repository secret with the new token
|
|
90
|
+
|
|
91
|
+
### Error: 403 Forbidden
|
|
92
|
+
|
|
93
|
+
**Causes:**
|
|
94
|
+
1. Token belongs to user without package write permissions
|
|
95
|
+
2. Repository doesn't allow package publishing
|
|
96
|
+
|
|
97
|
+
**Resolution:**
|
|
98
|
+
1. Ensure token owner has write access to the repository
|
|
99
|
+
2. Check organization/repository package settings
|
|
100
|
+
|
|
101
|
+
### Token Not Being Used
|
|
102
|
+
|
|
103
|
+
If the workflow isn't picking up the secret:
|
|
104
|
+
|
|
105
|
+
1. Verify secret name is exactly `NPM_TOKEN` (case-sensitive)
|
|
106
|
+
2. Check workflow file references `${{ secrets.NPM_TOKEN }}`
|
|
107
|
+
3. Ensure workflow has appropriate permissions in `permissions:` block
|
|
108
|
+
|
|
109
|
+
## Security Best Practices
|
|
110
|
+
|
|
111
|
+
### Token Management
|
|
112
|
+
|
|
113
|
+
- **Rotation:** Rotate tokens annually or when team members leave
|
|
114
|
+
- **Scope:** Use minimum required scopes (write:package, read:repository)
|
|
115
|
+
- **Naming:** Use descriptive names like `ci-npm-publish-2026`
|
|
116
|
+
- **Audit:** Periodically review active tokens
|
|
117
|
+
|
|
118
|
+
### Secret Storage
|
|
119
|
+
|
|
120
|
+
- Never commit tokens to the repository
|
|
121
|
+
- Use repository/organization secrets, not environment variables in code
|
|
122
|
+
- Don't echo or log token values in workflows
|
|
123
|
+
|
|
124
|
+
### Workflow Security
|
|
125
|
+
|
|
126
|
+
```yaml
|
|
127
|
+
# Good: Token passed via secrets
|
|
128
|
+
env:
|
|
129
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
130
|
+
|
|
131
|
+
# Bad: Token hardcoded or echoed
|
|
132
|
+
run: echo ${{ secrets.NPM_TOKEN }} # NEVER do this
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Workflow Architecture
|
|
136
|
+
|
|
137
|
+
### npm-publish.yml Flow
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
[Tag Push v*] → [Checkout] → [Configure npm] → [Build] → [Publish to Gitea] → [Verify]
|
|
141
|
+
↓
|
|
142
|
+
Uses NPM_TOKEN for:
|
|
143
|
+
- .npmrc authentication
|
|
144
|
+
- npm publish command
|
|
145
|
+
- Gitea release API
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Secret Usage in Workflow
|
|
149
|
+
|
|
150
|
+
```yaml
|
|
151
|
+
# .npmrc configuration (line 55-56)
|
|
152
|
+
//git.integrolabs.net/api/packages/roctinam/npm/:_authToken=${{ secrets.NPM_TOKEN }}
|
|
153
|
+
|
|
154
|
+
# Publish command (line 107-109)
|
|
155
|
+
npm publish --registry=${{ env.GITEA_NPM_REGISTRY }}
|
|
156
|
+
env:
|
|
157
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
158
|
+
|
|
159
|
+
# Release creation (line 137)
|
|
160
|
+
-H "Authorization: token ${{ secrets.NPM_TOKEN }}"
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Additional Secrets (Optional)
|
|
164
|
+
|
|
165
|
+
### NPMJS_TOKEN (for public npm)
|
|
166
|
+
|
|
167
|
+
If publishing to public npmjs.org:
|
|
168
|
+
|
|
169
|
+
1. Create token at https://www.npmjs.com/settings/tokens
|
|
170
|
+
2. Select "Automation" token type
|
|
171
|
+
3. Add as secret named `NPMJS_TOKEN`
|
|
172
|
+
4. Update workflow to use separate token for public registry
|
|
173
|
+
|
|
174
|
+
### GITHUB_TOKEN (for GitHub mirror)
|
|
175
|
+
|
|
176
|
+
For GitHub Actions (`.github/workflows/`):
|
|
177
|
+
|
|
178
|
+
- Automatically provided by GitHub Actions
|
|
179
|
+
- No manual configuration needed
|
|
180
|
+
- Used for GitHub Releases and npm publish to GitHub Packages
|
|
181
|
+
|
|
182
|
+
## References
|
|
183
|
+
|
|
184
|
+
- [Gitea Package Registry Documentation](https://docs.gitea.com/usage/packages/npm)
|
|
185
|
+
- [Gitea Actions Secrets](https://docs.gitea.com/usage/actions/secrets)
|
|
186
|
+
- [npm Authentication](https://docs.npmjs.com/using-private-packages-in-a-ci-cd-workflow)
|
|
187
|
+
- @.gitea/workflows/npm-publish.yml - Main publish workflow
|
|
188
|
+
- @.claude/rules/token-security.md - Token security rules
|
|
@@ -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
|
|
|
@@ -568,6 +568,136 @@ export function filterAgentFiles(files, opts) {
|
|
|
568
568
|
});
|
|
569
569
|
}
|
|
570
570
|
|
|
571
|
+
// ============================================================================
|
|
572
|
+
// Addon Discovery
|
|
573
|
+
// ============================================================================
|
|
574
|
+
|
|
575
|
+
/**
|
|
576
|
+
* Discover all addons in the agentic/code/addons directory
|
|
577
|
+
* @param {string} srcRoot - Source root directory
|
|
578
|
+
* @returns {Array<{name: string, path: string, manifest: object}>} - Array of addon info
|
|
579
|
+
*/
|
|
580
|
+
export function discoverAddons(srcRoot) {
|
|
581
|
+
const addonsDir = path.join(srcRoot, 'agentic', 'code', 'addons');
|
|
582
|
+
if (!fs.existsSync(addonsDir)) return [];
|
|
583
|
+
|
|
584
|
+
const addons = [];
|
|
585
|
+
for (const entry of fs.readdirSync(addonsDir, { withFileTypes: true })) {
|
|
586
|
+
if (!entry.isDirectory()) continue;
|
|
587
|
+
|
|
588
|
+
const addonPath = path.join(addonsDir, entry.name);
|
|
589
|
+
const manifestPath = path.join(addonPath, 'manifest.json');
|
|
590
|
+
|
|
591
|
+
let manifest = {};
|
|
592
|
+
if (fs.existsSync(manifestPath)) {
|
|
593
|
+
try {
|
|
594
|
+
manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
595
|
+
} catch (e) {
|
|
596
|
+
console.warn(`Warning: Could not parse manifest for addon ${entry.name}: ${e.message}`);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
addons.push({
|
|
601
|
+
name: entry.name,
|
|
602
|
+
path: addonPath,
|
|
603
|
+
manifest
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
return addons;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* Get all agent files from all addons
|
|
612
|
+
* @param {string} srcRoot - Source root directory
|
|
613
|
+
* @param {string[]} excludeAddons - Addon names to exclude (default: none)
|
|
614
|
+
* @returns {string[]} - Array of agent file paths
|
|
615
|
+
*/
|
|
616
|
+
export function getAddonAgentFiles(srcRoot, excludeAddons = []) {
|
|
617
|
+
const addons = discoverAddons(srcRoot);
|
|
618
|
+
const files = [];
|
|
619
|
+
|
|
620
|
+
for (const addon of addons) {
|
|
621
|
+
if (excludeAddons.includes(addon.name)) continue;
|
|
622
|
+
|
|
623
|
+
const agentsDir = path.join(addon.path, 'agents');
|
|
624
|
+
if (fs.existsSync(agentsDir)) {
|
|
625
|
+
files.push(...listMdFiles(agentsDir));
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
return files;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/**
|
|
633
|
+
* Get all command files from all addons
|
|
634
|
+
* @param {string} srcRoot - Source root directory
|
|
635
|
+
* @param {string[]} excludeAddons - Addon names to exclude (default: none)
|
|
636
|
+
* @returns {string[]} - Array of command file paths
|
|
637
|
+
*/
|
|
638
|
+
export function getAddonCommandFiles(srcRoot, excludeAddons = []) {
|
|
639
|
+
const addons = discoverAddons(srcRoot);
|
|
640
|
+
const files = [];
|
|
641
|
+
|
|
642
|
+
for (const addon of addons) {
|
|
643
|
+
if (excludeAddons.includes(addon.name)) continue;
|
|
644
|
+
|
|
645
|
+
const commandsDir = path.join(addon.path, 'commands');
|
|
646
|
+
if (fs.existsSync(commandsDir)) {
|
|
647
|
+
files.push(...listMdFiles(commandsDir));
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
return files;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
/**
|
|
655
|
+
* Get all skill directories from all addons
|
|
656
|
+
* @param {string} srcRoot - Source root directory
|
|
657
|
+
* @param {string[]} excludeAddons - Addon names to exclude (default: none)
|
|
658
|
+
* @returns {string[]} - Array of skill directory paths
|
|
659
|
+
*/
|
|
660
|
+
export function getAddonSkillDirs(srcRoot, excludeAddons = []) {
|
|
661
|
+
const addons = discoverAddons(srcRoot);
|
|
662
|
+
const dirs = [];
|
|
663
|
+
|
|
664
|
+
for (const addon of addons) {
|
|
665
|
+
if (excludeAddons.includes(addon.name)) continue;
|
|
666
|
+
|
|
667
|
+
const skillsDir = path.join(addon.path, 'skills');
|
|
668
|
+
if (fs.existsSync(skillsDir)) {
|
|
669
|
+
dirs.push(...listSkillDirs(skillsDir));
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
return dirs;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
/**
|
|
677
|
+
* Get addon files by category (agents, commands, skills)
|
|
678
|
+
* @param {string} srcRoot - Source root directory
|
|
679
|
+
* @param {object} options - Options
|
|
680
|
+
* @param {string[]} options.excludeAddons - Addon names to exclude
|
|
681
|
+
* @param {boolean} options.includeAgents - Include agent files (default: true)
|
|
682
|
+
* @param {boolean} options.includeCommands - Include command files (default: true)
|
|
683
|
+
* @param {boolean} options.includeSkills - Include skill directories (default: true)
|
|
684
|
+
* @returns {{agents: string[], commands: string[], skills: string[]}}
|
|
685
|
+
*/
|
|
686
|
+
export function getAddonFiles(srcRoot, options = {}) {
|
|
687
|
+
const {
|
|
688
|
+
excludeAddons = [],
|
|
689
|
+
includeAgents = true,
|
|
690
|
+
includeCommands = true,
|
|
691
|
+
includeSkills = true
|
|
692
|
+
} = options;
|
|
693
|
+
|
|
694
|
+
return {
|
|
695
|
+
agents: includeAgents ? getAddonAgentFiles(srcRoot, excludeAddons) : [],
|
|
696
|
+
commands: includeCommands ? getAddonCommandFiles(srcRoot, excludeAddons) : [],
|
|
697
|
+
skills: includeSkills ? getAddonSkillDirs(srcRoot, excludeAddons) : []
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
|
|
571
701
|
// ============================================================================
|
|
572
702
|
// Provider Interface
|
|
573
703
|
// ============================================================================
|
|
@@ -23,7 +23,10 @@ import {
|
|
|
23
23
|
deploySkillDir,
|
|
24
24
|
parseFrontmatter,
|
|
25
25
|
initializeFrameworkWorkspace,
|
|
26
|
-
filterAgentFiles
|
|
26
|
+
filterAgentFiles,
|
|
27
|
+
getAddonAgentFiles,
|
|
28
|
+
getAddonCommandFiles,
|
|
29
|
+
getAddonSkillDirs
|
|
27
30
|
} from './base.mjs';
|
|
28
31
|
|
|
29
32
|
// ============================================================================
|
|
@@ -281,19 +284,16 @@ export async function deploy(opts) {
|
|
|
281
284
|
}
|
|
282
285
|
}
|
|
283
286
|
|
|
284
|
-
//
|
|
285
|
-
if (mode === 'general' || mode === 'writing' || mode === 'both' || mode === 'all') {
|
|
286
|
-
|
|
287
|
-
agentFiles.push(...listMdFiles(writingAgentsDir));
|
|
287
|
+
// All addons (dynamically discovered)
|
|
288
|
+
if (mode === 'general' || mode === 'writing' || mode === 'sdlc' || mode === 'both' || mode === 'all') {
|
|
289
|
+
agentFiles.push(...getAddonAgentFiles(srcRoot));
|
|
288
290
|
|
|
289
291
|
if (shouldDeployCommands || commandsOnly) {
|
|
290
|
-
|
|
291
|
-
commandFiles.push(...listMdFiles(writingCommandsDir));
|
|
292
|
+
commandFiles.push(...getAddonCommandFiles(srcRoot));
|
|
292
293
|
}
|
|
293
294
|
|
|
294
295
|
if (shouldDeploySkills || skillsOnly) {
|
|
295
|
-
|
|
296
|
-
skillDirs.push(...listSkillDirs(writingSkillsDir));
|
|
296
|
+
skillDirs.push(...getAddonSkillDirs(srcRoot));
|
|
297
297
|
}
|
|
298
298
|
}
|
|
299
299
|
|
|
@@ -329,28 +329,6 @@ export async function deploy(opts) {
|
|
|
329
329
|
}
|
|
330
330
|
}
|
|
331
331
|
|
|
332
|
-
// Utils addon (always deployed with sdlc or all)
|
|
333
|
-
if (mode === 'sdlc' || mode === 'all') {
|
|
334
|
-
const utilsAgentsDir = path.join(srcRoot, 'agentic', 'code', 'addons', 'aiwg-utils', 'agents');
|
|
335
|
-
agentFiles.push(...listMdFiles(utilsAgentsDir));
|
|
336
|
-
|
|
337
|
-
if (shouldDeployCommands || commandsOnly) {
|
|
338
|
-
const utilsCommandsDir = path.join(srcRoot, 'agentic', 'code', 'addons', 'aiwg-utils', 'commands');
|
|
339
|
-
commandFiles.push(...listMdFiles(utilsCommandsDir));
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
if (shouldDeploySkills || skillsOnly) {
|
|
343
|
-
const utilsSkillsDir = path.join(srcRoot, 'agentic', 'code', 'addons', 'aiwg-utils', 'skills');
|
|
344
|
-
skillDirs.push(...listSkillDirs(utilsSkillsDir));
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
// Voice framework skills (always deployed)
|
|
349
|
-
if (shouldDeploySkills || skillsOnly) {
|
|
350
|
-
const voiceSkillsDir = path.join(srcRoot, 'agentic', 'code', 'addons', 'voice-framework', 'skills');
|
|
351
|
-
skillDirs.push(...listSkillDirs(voiceSkillsDir));
|
|
352
|
-
}
|
|
353
|
-
|
|
354
332
|
// Deploy based on flags
|
|
355
333
|
if (!commandsOnly && !skillsOnly) {
|
|
356
334
|
// Apply filters if specified
|
|
@@ -26,7 +26,10 @@ import {
|
|
|
26
26
|
writeFile,
|
|
27
27
|
deployFiles,
|
|
28
28
|
createAgentsMdFromTemplate,
|
|
29
|
-
initializeFrameworkWorkspace
|
|
29
|
+
initializeFrameworkWorkspace,
|
|
30
|
+
getAddonAgentFiles,
|
|
31
|
+
getAddonCommandFiles,
|
|
32
|
+
getAddonSkillDirs
|
|
30
33
|
} from './base.mjs';
|
|
31
34
|
|
|
32
35
|
// ============================================================================
|
|
@@ -298,28 +301,20 @@ export async function deploy(opts) {
|
|
|
298
301
|
// Collect source files based on mode
|
|
299
302
|
const agentFiles = [];
|
|
300
303
|
|
|
301
|
-
//
|
|
302
|
-
if (mode === 'general' || mode === 'writing' || mode === 'both' || mode === 'all') {
|
|
303
|
-
const writingAgentsDir = path.join(srcRoot, 'agentic', 'code', 'addons', 'writing-quality', 'agents');
|
|
304
|
-
agentFiles.push(...listMdFiles(writingAgentsDir));
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// SDLC framework
|
|
304
|
+
// Frameworks
|
|
308
305
|
if (mode === 'sdlc' || mode === 'both' || mode === 'all') {
|
|
309
306
|
const sdlcAgentsDir = path.join(srcRoot, 'agentic', 'code', 'frameworks', 'sdlc-complete', 'agents');
|
|
310
307
|
agentFiles.push(...listMdFiles(sdlcAgentsDir));
|
|
311
308
|
}
|
|
312
309
|
|
|
313
|
-
// Marketing framework
|
|
314
310
|
if (mode === 'marketing' || mode === 'all') {
|
|
315
311
|
const marketingAgentsDir = path.join(srcRoot, 'agentic', 'code', 'frameworks', 'media-marketing-kit', 'agents');
|
|
316
312
|
agentFiles.push(...listMdFiles(marketingAgentsDir));
|
|
317
313
|
}
|
|
318
314
|
|
|
319
|
-
//
|
|
320
|
-
if (mode === 'sdlc' || mode === 'all') {
|
|
321
|
-
|
|
322
|
-
agentFiles.push(...listMdFiles(utilsAgentsDir));
|
|
315
|
+
// All addons (dynamically discovered)
|
|
316
|
+
if (mode === 'general' || mode === 'writing' || mode === 'sdlc' || mode === 'both' || mode === 'all') {
|
|
317
|
+
agentFiles.push(...getAddonAgentFiles(srcRoot));
|
|
323
318
|
}
|
|
324
319
|
|
|
325
320
|
// Deploy based on flags
|
|
@@ -24,7 +24,9 @@ import {
|
|
|
24
24
|
deployFiles,
|
|
25
25
|
inferAgentCategory,
|
|
26
26
|
toKebabCase,
|
|
27
|
-
initializeFrameworkWorkspace
|
|
27
|
+
initializeFrameworkWorkspace,
|
|
28
|
+
getAddonAgentFiles,
|
|
29
|
+
getAddonCommandFiles
|
|
28
30
|
} from './base.mjs';
|
|
29
31
|
|
|
30
32
|
// ============================================================================
|
|
@@ -348,17 +350,16 @@ export async function deploy(opts) {
|
|
|
348
350
|
const agentFiles = [];
|
|
349
351
|
const commandFiles = [];
|
|
350
352
|
|
|
351
|
-
//
|
|
352
|
-
if (mode === 'general' || mode === 'writing' || mode === 'both' || mode === 'all') {
|
|
353
|
-
|
|
354
|
-
agentFiles.push(...listMdFiles(writingAgentsDir));
|
|
353
|
+
// All addons (dynamically discovered)
|
|
354
|
+
if (mode === 'general' || mode === 'writing' || mode === 'sdlc' || mode === 'both' || mode === 'all') {
|
|
355
|
+
agentFiles.push(...getAddonAgentFiles(srcRoot));
|
|
355
356
|
|
|
356
357
|
if (shouldDeployCommands || commandsOnly) {
|
|
357
|
-
|
|
358
|
-
commandFiles.push(...listMdFiles(writingCommandsDir));
|
|
358
|
+
commandFiles.push(...getAddonCommandFiles(srcRoot));
|
|
359
359
|
}
|
|
360
360
|
}
|
|
361
361
|
|
|
362
|
+
// Frameworks
|
|
362
363
|
if (mode === 'sdlc' || mode === 'both' || mode === 'all') {
|
|
363
364
|
const sdlcAgentsDir = path.join(srcRoot, 'agentic', 'code', 'frameworks', 'sdlc-complete', 'agents');
|
|
364
365
|
agentFiles.push(...listMdFiles(sdlcAgentsDir));
|
|
@@ -379,16 +380,6 @@ export async function deploy(opts) {
|
|
|
379
380
|
}
|
|
380
381
|
}
|
|
381
382
|
|
|
382
|
-
if (mode === 'sdlc' || mode === 'all') {
|
|
383
|
-
const utilsAgentsDir = path.join(srcRoot, 'agentic', 'code', 'addons', 'aiwg-utils', 'agents');
|
|
384
|
-
agentFiles.push(...listMdFiles(utilsAgentsDir));
|
|
385
|
-
|
|
386
|
-
if (shouldDeployCommands || commandsOnly) {
|
|
387
|
-
const utilsCommandsDir = path.join(srcRoot, 'agentic', 'code', 'addons', 'aiwg-utils', 'commands');
|
|
388
|
-
commandFiles.push(...listMdFiles(utilsCommandsDir));
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
|
|
392
383
|
// Deploy
|
|
393
384
|
if (!commandsOnly) {
|
|
394
385
|
console.log(`\nDeploying ${agentFiles.length} agents (YAML format)...`);
|
|
@@ -26,7 +26,9 @@ import {
|
|
|
26
26
|
toKebabCase,
|
|
27
27
|
stripJsonComments,
|
|
28
28
|
createAgentsMdFromTemplate,
|
|
29
|
-
initializeFrameworkWorkspace
|
|
29
|
+
initializeFrameworkWorkspace,
|
|
30
|
+
getAddonAgentFiles,
|
|
31
|
+
getAddonCommandFiles
|
|
30
32
|
} from './base.mjs';
|
|
31
33
|
|
|
32
34
|
// ============================================================================
|
|
@@ -455,14 +457,12 @@ export async function deploy(opts) {
|
|
|
455
457
|
const agentFiles = [];
|
|
456
458
|
const commandFiles = [];
|
|
457
459
|
|
|
458
|
-
//
|
|
459
|
-
if (mode === 'general' || mode === 'writing' || mode === 'both' || mode === 'all') {
|
|
460
|
-
|
|
461
|
-
agentFiles.push(...listMdFiles(writingAgentsDir));
|
|
460
|
+
// All addons (dynamically discovered)
|
|
461
|
+
if (mode === 'general' || mode === 'writing' || mode === 'sdlc' || mode === 'both' || mode === 'all') {
|
|
462
|
+
agentFiles.push(...getAddonAgentFiles(srcRoot));
|
|
462
463
|
|
|
463
464
|
if (shouldDeployCommands || commandsOnly) {
|
|
464
|
-
|
|
465
|
-
commandFiles.push(...listMdFiles(writingCommandsDir));
|
|
465
|
+
commandFiles.push(...getAddonCommandFiles(srcRoot));
|
|
466
466
|
}
|
|
467
467
|
}
|
|
468
468
|
|
|
@@ -488,17 +488,6 @@ export async function deploy(opts) {
|
|
|
488
488
|
}
|
|
489
489
|
}
|
|
490
490
|
|
|
491
|
-
// Utils addon
|
|
492
|
-
if (mode === 'sdlc' || mode === 'all') {
|
|
493
|
-
const utilsAgentsDir = path.join(srcRoot, 'agentic', 'code', 'addons', 'aiwg-utils', 'agents');
|
|
494
|
-
agentFiles.push(...listMdFiles(utilsAgentsDir));
|
|
495
|
-
|
|
496
|
-
if (shouldDeployCommands || commandsOnly) {
|
|
497
|
-
const utilsCommandsDir = path.join(srcRoot, 'agentic', 'code', 'addons', 'aiwg-utils', 'commands');
|
|
498
|
-
commandFiles.push(...listMdFiles(utilsCommandsDir));
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
|
|
502
491
|
// Deploy based on flags
|
|
503
492
|
if (!commandsOnly) {
|
|
504
493
|
console.log(`\nDeploying ${agentFiles.length} agents as droids...`);
|
|
@@ -23,7 +23,9 @@ import {
|
|
|
23
23
|
deployFiles,
|
|
24
24
|
inferAgentCategory,
|
|
25
25
|
createAgentsMdFromTemplate,
|
|
26
|
-
initializeFrameworkWorkspace
|
|
26
|
+
initializeFrameworkWorkspace,
|
|
27
|
+
getAddonAgentFiles,
|
|
28
|
+
getAddonCommandFiles
|
|
27
29
|
} from './base.mjs';
|
|
28
30
|
|
|
29
31
|
// ============================================================================
|
|
@@ -317,17 +319,16 @@ export async function deploy(opts) {
|
|
|
317
319
|
const agentFiles = [];
|
|
318
320
|
const commandFiles = [];
|
|
319
321
|
|
|
320
|
-
//
|
|
321
|
-
if (mode === 'general' || mode === 'writing' || mode === 'both' || mode === 'all') {
|
|
322
|
-
|
|
323
|
-
agentFiles.push(...listMdFiles(writingAgentsDir));
|
|
322
|
+
// All addons (dynamically discovered)
|
|
323
|
+
if (mode === 'general' || mode === 'writing' || mode === 'sdlc' || mode === 'both' || mode === 'all') {
|
|
324
|
+
agentFiles.push(...getAddonAgentFiles(srcRoot));
|
|
324
325
|
|
|
325
326
|
if (shouldDeployCommands || commandsOnly) {
|
|
326
|
-
|
|
327
|
-
commandFiles.push(...listMdFiles(writingCommandsDir));
|
|
327
|
+
commandFiles.push(...getAddonCommandFiles(srcRoot));
|
|
328
328
|
}
|
|
329
329
|
}
|
|
330
330
|
|
|
331
|
+
// Frameworks
|
|
331
332
|
if (mode === 'sdlc' || mode === 'both' || mode === 'all') {
|
|
332
333
|
const sdlcAgentsDir = path.join(srcRoot, 'agentic', 'code', 'frameworks', 'sdlc-complete', 'agents');
|
|
333
334
|
agentFiles.push(...listMdFiles(sdlcAgentsDir));
|
|
@@ -348,16 +349,6 @@ export async function deploy(opts) {
|
|
|
348
349
|
}
|
|
349
350
|
}
|
|
350
351
|
|
|
351
|
-
if (mode === 'sdlc' || mode === 'all') {
|
|
352
|
-
const utilsAgentsDir = path.join(srcRoot, 'agentic', 'code', 'addons', 'aiwg-utils', 'agents');
|
|
353
|
-
agentFiles.push(...listMdFiles(utilsAgentsDir));
|
|
354
|
-
|
|
355
|
-
if (shouldDeployCommands || commandsOnly) {
|
|
356
|
-
const utilsCommandsDir = path.join(srcRoot, 'agentic', 'code', 'addons', 'aiwg-utils', 'commands');
|
|
357
|
-
commandFiles.push(...listMdFiles(utilsCommandsDir));
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
|
|
361
352
|
// Deploy
|
|
362
353
|
if (!commandsOnly) {
|
|
363
354
|
console.log(`\nDeploying ${agentFiles.length} agents...`);
|
|
@@ -22,7 +22,9 @@ import {
|
|
|
22
22
|
ensureDir,
|
|
23
23
|
listMdFiles,
|
|
24
24
|
listMdFilesRecursive,
|
|
25
|
-
initializeFrameworkWorkspace
|
|
25
|
+
initializeFrameworkWorkspace,
|
|
26
|
+
getAddonAgentFiles,
|
|
27
|
+
getAddonCommandFiles
|
|
26
28
|
} from './base.mjs';
|
|
27
29
|
|
|
28
30
|
// ============================================================================
|
|
@@ -400,30 +402,22 @@ export async function deploy(opts) {
|
|
|
400
402
|
// Collect all agent files based on mode
|
|
401
403
|
const allAgentFiles = [];
|
|
402
404
|
|
|
403
|
-
//
|
|
404
|
-
if (mode === 'general' || mode === 'writing' || mode === 'both' || mode === 'all') {
|
|
405
|
-
|
|
406
|
-
allAgentFiles.push(...listMdFiles(writingAgentsDir));
|
|
405
|
+
// All addons (dynamically discovered)
|
|
406
|
+
if (mode === 'general' || mode === 'writing' || mode === 'sdlc' || mode === 'both' || mode === 'all') {
|
|
407
|
+
allAgentFiles.push(...getAddonAgentFiles(srcRoot));
|
|
407
408
|
}
|
|
408
409
|
|
|
409
|
-
//
|
|
410
|
+
// Frameworks
|
|
410
411
|
if (mode === 'sdlc' || mode === 'both' || mode === 'all') {
|
|
411
412
|
const sdlcAgentsDir = path.join(srcRoot, 'agentic', 'code', 'frameworks', 'sdlc-complete', 'agents');
|
|
412
413
|
allAgentFiles.push(...listMdFiles(sdlcAgentsDir));
|
|
413
414
|
}
|
|
414
415
|
|
|
415
|
-
// Marketing agents
|
|
416
416
|
if (mode === 'marketing' || mode === 'all') {
|
|
417
417
|
const marketingAgentsDir = path.join(srcRoot, 'agentic', 'code', 'frameworks', 'media-marketing-kit', 'agents');
|
|
418
418
|
allAgentFiles.push(...listMdFiles(marketingAgentsDir));
|
|
419
419
|
}
|
|
420
420
|
|
|
421
|
-
// Utils addon agents
|
|
422
|
-
if (mode === 'sdlc' || mode === 'all') {
|
|
423
|
-
const utilsAgentsDir = path.join(srcRoot, 'agentic', 'code', 'addons', 'aiwg-utils', 'agents');
|
|
424
|
-
allAgentFiles.push(...listMdFiles(utilsAgentsDir));
|
|
425
|
-
}
|
|
426
|
-
|
|
427
421
|
// Generate aggregated AGENTS.md
|
|
428
422
|
if (allAgentFiles.length > 0 && !commandsOnly && !skillsOnly) {
|
|
429
423
|
const agentsMdPath = path.join(target, 'AGENTS.md');
|
|
@@ -442,11 +436,12 @@ export async function deploy(opts) {
|
|
|
442
436
|
// Collect command files based on mode
|
|
443
437
|
const commandFiles = [];
|
|
444
438
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
commandFiles.push(...
|
|
439
|
+
// All addons (dynamically discovered)
|
|
440
|
+
if (mode === 'general' || mode === 'writing' || mode === 'sdlc' || mode === 'both' || mode === 'all') {
|
|
441
|
+
commandFiles.push(...getAddonCommandFiles(srcRoot));
|
|
448
442
|
}
|
|
449
443
|
|
|
444
|
+
// Frameworks
|
|
450
445
|
if (mode === 'sdlc' || mode === 'both' || mode === 'all') {
|
|
451
446
|
const sdlcCommandsDir = path.join(srcRoot, 'agentic', 'code', 'frameworks', 'sdlc-complete', 'commands');
|
|
452
447
|
commandFiles.push(...listMdFilesRecursive(sdlcCommandsDir));
|
|
@@ -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)) {
|