workflow-agent-cli 2.0.1 ā 2.2.0
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/README.md +33 -17
- package/dist/chunk-IPMSSOXR.js +293 -0
- package/dist/chunk-IPMSSOXR.js.map +1 -0
- package/dist/{chunk-X2NQJ2ZY.js ā chunk-NMHWD2GA.js} +11 -6
- package/dist/chunk-NMHWD2GA.js.map +1 -0
- package/dist/cli/index.js +777 -165
- package/dist/cli/index.js.map +1 -1
- package/dist/config/index.d.ts +8 -3
- package/dist/config/index.js +9 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.js +10 -4
- package/dist/schema-C1lmnd7L.d.ts +256 -0
- package/dist/scripts/postinstall.js +6 -2
- package/dist/scripts/postinstall.js.map +1 -1
- package/dist/validators/index.d.ts +1 -1
- package/dist/validators/index.js +1 -1
- package/package.json +17 -15
- package/templates/AGENT_EDITING_INSTRUCTIONS.md +226 -39
- package/templates/COMPONENT_LIBRARY.md +23 -17
- package/templates/CUSTOM_SCOPE_TEMPLATE.md +5 -4
- package/templates/LIBRARY_INVENTORY.md +20 -20
- package/templates/SCOPE_CREATION_WORKFLOW.md +39 -11
- package/templates/SELF_IMPROVEMENT_MANDATE.md +24 -18
- package/templates/SINGLE_SOURCE_OF_TRUTH.md +59 -42
- package/templates/TESTING_STRATEGY.md +79 -69
- package/templates/_TEMPLATE_EXAMPLE.md +2 -1
- package/LICENSE +0 -21
- package/dist/chunk-4BIDFDSR.js +0 -152
- package/dist/chunk-4BIDFDSR.js.map +0 -1
- package/dist/chunk-X2NQJ2ZY.js.map +0 -1
- package/dist/schema-RkQ91pZW.d.ts +0 -161
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
**Workflow Agent** is a portable, framework-agnostic CLI tool that brings structure and consistency to your development workflow. It enforces branch naming conventions, validates commit messages, and includes a self-improvement system that learns from community feedback.
|
|
11
11
|
|
|
12
12
|
**šÆ Perfect for:**
|
|
13
|
+
|
|
13
14
|
- AI agent development with strict workflow requirements
|
|
14
15
|
- Teams maintaining multiple repositories
|
|
15
16
|
- Open source projects enforcing contribution guidelines
|
|
@@ -94,6 +95,7 @@ This adds these scripts to your `package.json`:
|
|
|
94
95
|
### 1. Initialize Your Project
|
|
95
96
|
|
|
96
97
|
#### Interactive Mode
|
|
98
|
+
|
|
97
99
|
```bash
|
|
98
100
|
# If installed globally:
|
|
99
101
|
workflow-agent init
|
|
@@ -107,11 +109,13 @@ pnpm run workflow:init
|
|
|
107
109
|
```
|
|
108
110
|
|
|
109
111
|
Prompts you to:
|
|
112
|
+
|
|
110
113
|
1. Enter project name
|
|
111
114
|
2. Choose a preset (SaaS, Library, API, E-commerce, CMS, Custom)
|
|
112
115
|
3. Generate guidelines (optional)
|
|
113
116
|
|
|
114
117
|
#### Non-Interactive Mode
|
|
118
|
+
|
|
115
119
|
```bash
|
|
116
120
|
# Perfect for CI/CD or automation
|
|
117
121
|
|
|
@@ -134,6 +138,7 @@ workflow-agent validate commit "feat(auth): add OAuth support"
|
|
|
134
138
|
```
|
|
135
139
|
|
|
136
140
|
**Expected formats:**
|
|
141
|
+
|
|
137
142
|
- **Branch:** `<type>/<scope>/<description>`
|
|
138
143
|
- Types: `feature`, `bugfix`, `hotfix`, `chore`, `refactor`, `docs`, `test`
|
|
139
144
|
- Example: `feature/auth/implement-2fa`
|
|
@@ -172,30 +177,33 @@ workflow-agent scope:migrate
|
|
|
172
177
|
|
|
173
178
|
## š ļø Commands
|
|
174
179
|
|
|
175
|
-
| Command
|
|
176
|
-
|
|
177
|
-
| `workflow-agent init`
|
|
178
|
-
| `workflow-agent validate branch [name]`
|
|
179
|
-
| `workflow-agent validate commit [message]` | Validate commit message format
|
|
180
|
-
| `workflow-agent config get [key]`
|
|
181
|
-
| `workflow-agent config set <key> <value>`
|
|
182
|
-
| `workflow-agent suggest <idea>`
|
|
183
|
-
| `workflow-agent doctor`
|
|
184
|
-
| `workflow-agent scope:create`
|
|
185
|
-
| `workflow-agent scope:migrate`
|
|
180
|
+
| Command | Description |
|
|
181
|
+
| ------------------------------------------ | ------------------------------------------- |
|
|
182
|
+
| `workflow-agent init` | Initialize project with interactive prompts |
|
|
183
|
+
| `workflow-agent validate branch [name]` | Validate branch name format |
|
|
184
|
+
| `workflow-agent validate commit [message]` | Validate commit message format |
|
|
185
|
+
| `workflow-agent config get [key]` | View configuration values |
|
|
186
|
+
| `workflow-agent config set <key> <value>` | Update configuration |
|
|
187
|
+
| `workflow-agent suggest <idea>` | Submit improvement suggestion |
|
|
188
|
+
| `workflow-agent doctor` | Run health checks and get optimization tips |
|
|
189
|
+
| `workflow-agent scope:create` | Create a custom scope package |
|
|
190
|
+
| `workflow-agent scope:migrate` | Migrate inline scopes to package |
|
|
186
191
|
|
|
187
192
|
### Command Options
|
|
188
193
|
|
|
189
194
|
#### `init`
|
|
195
|
+
|
|
190
196
|
- `--preset <name>` - Skip preset selection (saas, library, api, ecommerce, cms, custom)
|
|
191
197
|
- `--name <name>` - Set project name without prompt
|
|
192
198
|
- `--yes` - Accept all defaults (non-interactive)
|
|
193
199
|
|
|
194
200
|
#### `validate`
|
|
201
|
+
|
|
195
202
|
- `--fix` - Apply automatic fixes (coming soon)
|
|
196
203
|
- `--json` - Output in JSON format
|
|
197
204
|
|
|
198
205
|
#### `suggest`
|
|
206
|
+
|
|
199
207
|
- `--category <type>` - Suggestion category (feature, bug, improvement, documentation)
|
|
200
208
|
- `--author <name>` - Your name or username
|
|
201
209
|
|
|
@@ -204,18 +212,23 @@ workflow-agent scope:migrate
|
|
|
204
212
|
## š¦ Preset Scope Libraries
|
|
205
213
|
|
|
206
214
|
### SaaS (17 scopes)
|
|
215
|
+
|
|
207
216
|
`auth`, `billing`, `analytics`, `notifications`, `teams`, `admin`, `api`, `integration`, `subscription`, `dashboard`, `onboarding`, `settings`, `payments`, `reports`, `support`, `webhooks`, `search`
|
|
208
217
|
|
|
209
218
|
### Library (10 scopes)
|
|
219
|
+
|
|
210
220
|
`core`, `utils`, `types`, `config`, `cli`, `api`, `docs`, `examples`, `test`, `build`
|
|
211
221
|
|
|
212
222
|
### API (13 scopes)
|
|
223
|
+
|
|
213
224
|
`routes`, `middleware`, `controllers`, `models`, `services`, `auth`, `validation`, `errors`, `logging`, `cache`, `queue`, `websocket`, `graphql`
|
|
214
225
|
|
|
215
226
|
### E-commerce (12 scopes)
|
|
227
|
+
|
|
216
228
|
`products`, `cart`, `checkout`, `orders`, `payments`, `shipping`, `inventory`, `customers`, `reviews`, `discounts`, `recommendations`, `analytics`
|
|
217
229
|
|
|
218
230
|
### CMS (13 scopes)
|
|
231
|
+
|
|
219
232
|
`content`, `media`, `pages`, `posts`, `categories`, `tags`, `users`, `comments`, `seo`, `templates`, `widgets`, `api`, `admin`
|
|
220
233
|
|
|
221
234
|
---
|
|
@@ -234,12 +247,12 @@ workflow-agent scope:create
|
|
|
234
247
|
// packages/scopes-medical/src/index.ts
|
|
235
248
|
export default {
|
|
236
249
|
scopes: [
|
|
237
|
-
{ name:
|
|
238
|
-
{ name:
|
|
239
|
-
{ name:
|
|
240
|
-
{ name:
|
|
241
|
-
{ name:
|
|
242
|
-
]
|
|
250
|
+
{ name: "patient", description: "Patient records and profiles" },
|
|
251
|
+
{ name: "appointment", description: "Scheduling and appointments" },
|
|
252
|
+
{ name: "billing", description: "Medical billing and insurance" },
|
|
253
|
+
{ name: "prescription", description: "Prescriptions and medications" },
|
|
254
|
+
{ name: "lab", description: "Laboratory tests and results" },
|
|
255
|
+
],
|
|
243
256
|
};
|
|
244
257
|
```
|
|
245
258
|
|
|
@@ -298,6 +311,7 @@ This typically means your npm token doesn't have the correct permissions. To fix
|
|
|
298
311
|
- Copy the token immediately
|
|
299
312
|
|
|
300
313
|
2. **Test the token locally:**
|
|
314
|
+
|
|
301
315
|
```bash
|
|
302
316
|
echo '//registry.npmjs.org/:_authToken=YOUR_TOKEN' > ~/.npmrc
|
|
303
317
|
npm whoami # Should show your npm username
|
|
@@ -379,6 +393,7 @@ pnpm build
|
|
|
379
393
|
If templates aren't bundled with the npm package:
|
|
380
394
|
|
|
381
395
|
1. **Ensure templates are in the package:**
|
|
396
|
+
|
|
382
397
|
```json
|
|
383
398
|
// packages/core/package.json
|
|
384
399
|
{
|
|
@@ -387,6 +402,7 @@ If templates aren't bundled with the npm package:
|
|
|
387
402
|
```
|
|
388
403
|
|
|
389
404
|
2. **Copy templates to package directory:**
|
|
405
|
+
|
|
390
406
|
```bash
|
|
391
407
|
cp -r templates packages/core/
|
|
392
408
|
```
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
// src/config/index.ts
|
|
2
|
+
import { cosmiconfig } from "cosmiconfig";
|
|
3
|
+
|
|
4
|
+
// src/config/schema.ts
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
var DEFAULT_RESERVED_SCOPE_NAMES = [
|
|
7
|
+
"init",
|
|
8
|
+
"create",
|
|
9
|
+
"build",
|
|
10
|
+
"test",
|
|
11
|
+
"config",
|
|
12
|
+
"docs",
|
|
13
|
+
"ci",
|
|
14
|
+
"deps"
|
|
15
|
+
];
|
|
16
|
+
function validateScopeName(name, reservedNames = DEFAULT_RESERVED_SCOPE_NAMES) {
|
|
17
|
+
if (reservedNames.includes(name)) {
|
|
18
|
+
const suggestions = {
|
|
19
|
+
docs: "documentation",
|
|
20
|
+
test: "testing",
|
|
21
|
+
config: "configuration",
|
|
22
|
+
build: "builds",
|
|
23
|
+
ci: "cicd",
|
|
24
|
+
deps: "dependencies"
|
|
25
|
+
};
|
|
26
|
+
return {
|
|
27
|
+
valid: false,
|
|
28
|
+
error: `Scope name "${name}" is reserved`,
|
|
29
|
+
suggestion: suggestions[name] || `${name}-scope`
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
if (!/^[a-z0-9-]+$/.test(name)) {
|
|
33
|
+
return {
|
|
34
|
+
valid: false,
|
|
35
|
+
error: "Scope name must be lowercase alphanumeric with hyphens"
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
if (name.length === 0 || name.length > 32) {
|
|
39
|
+
return {
|
|
40
|
+
valid: false,
|
|
41
|
+
error: "Scope name must be 1-32 characters"
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
return { valid: true };
|
|
45
|
+
}
|
|
46
|
+
var BranchTypeSchema = z.enum([
|
|
47
|
+
"feature",
|
|
48
|
+
"bugfix",
|
|
49
|
+
"hotfix",
|
|
50
|
+
"chore",
|
|
51
|
+
"refactor",
|
|
52
|
+
"docs",
|
|
53
|
+
"test",
|
|
54
|
+
"release"
|
|
55
|
+
]);
|
|
56
|
+
var ConventionalTypeSchema = z.enum([
|
|
57
|
+
"feat",
|
|
58
|
+
"fix",
|
|
59
|
+
"refactor",
|
|
60
|
+
"chore",
|
|
61
|
+
"docs",
|
|
62
|
+
"test",
|
|
63
|
+
"perf",
|
|
64
|
+
"style",
|
|
65
|
+
"ci",
|
|
66
|
+
"build",
|
|
67
|
+
"revert"
|
|
68
|
+
]);
|
|
69
|
+
var ScopeSchema = z.object({
|
|
70
|
+
name: z.string().min(1).max(32, "Scope name must be 32 characters or less").regex(
|
|
71
|
+
/^[a-z0-9-]+$/,
|
|
72
|
+
"Scope name must be lowercase alphanumeric with hyphens"
|
|
73
|
+
),
|
|
74
|
+
description: z.string().min(10, "Scope description must be at least 10 characters"),
|
|
75
|
+
allowedTypes: z.array(ConventionalTypeSchema).optional(),
|
|
76
|
+
mandatoryGuidelines: z.array(z.string()).optional(),
|
|
77
|
+
emoji: z.string().optional(),
|
|
78
|
+
category: z.enum([
|
|
79
|
+
"auth",
|
|
80
|
+
"features",
|
|
81
|
+
"infrastructure",
|
|
82
|
+
"documentation",
|
|
83
|
+
"testing",
|
|
84
|
+
"performance",
|
|
85
|
+
"other"
|
|
86
|
+
]).optional()
|
|
87
|
+
});
|
|
88
|
+
var EnforcementLevelSchema = z.enum([
|
|
89
|
+
"strict",
|
|
90
|
+
"advisory",
|
|
91
|
+
"learning"
|
|
92
|
+
]);
|
|
93
|
+
var AnalyticsConfigSchema = z.object({
|
|
94
|
+
enabled: z.boolean().default(false),
|
|
95
|
+
shareAnonymous: z.boolean().default(false)
|
|
96
|
+
});
|
|
97
|
+
var HookCheckSchema = z.enum([
|
|
98
|
+
"validate-branch",
|
|
99
|
+
"validate-commit",
|
|
100
|
+
"check-guidelines",
|
|
101
|
+
"validate-scopes"
|
|
102
|
+
]);
|
|
103
|
+
var HooksConfigSchema = z.object({
|
|
104
|
+
/** Whether hooks are enabled */
|
|
105
|
+
enabled: z.boolean().default(true),
|
|
106
|
+
/** Checks to run on pre-commit */
|
|
107
|
+
preCommit: z.array(HookCheckSchema).default(["validate-branch", "check-guidelines"]),
|
|
108
|
+
/** Checks to run on commit-msg */
|
|
109
|
+
commitMsg: z.array(HookCheckSchema).default(["validate-commit"])
|
|
110
|
+
});
|
|
111
|
+
var GuidelinesConfigSchema = z.object({
|
|
112
|
+
/** Additional templates to make mandatory (beyond the core set) */
|
|
113
|
+
additionalMandatory: z.array(z.string()).optional(),
|
|
114
|
+
/** Templates to make optional (override core mandatory templates) */
|
|
115
|
+
optionalOverrides: z.array(z.string()).optional()
|
|
116
|
+
});
|
|
117
|
+
var CIProviderSchema = z.enum(["github", "gitlab", "bitbucket"]);
|
|
118
|
+
var CICheckSchema = z.enum([
|
|
119
|
+
"lint",
|
|
120
|
+
"typecheck",
|
|
121
|
+
"format",
|
|
122
|
+
"test",
|
|
123
|
+
"build"
|
|
124
|
+
]);
|
|
125
|
+
var CIConfigSchema = z.object({
|
|
126
|
+
/** Whether CI setup is enabled */
|
|
127
|
+
enabled: z.boolean().default(true),
|
|
128
|
+
/** CI provider (currently only github supported) */
|
|
129
|
+
provider: CIProviderSchema.default("github"),
|
|
130
|
+
/** Checks to run in CI pipeline */
|
|
131
|
+
checks: z.array(CICheckSchema).default(["lint", "typecheck", "format", "build", "test"])
|
|
132
|
+
});
|
|
133
|
+
var WorkflowConfigSchema = z.object({
|
|
134
|
+
projectName: z.string().min(1),
|
|
135
|
+
scopes: z.array(ScopeSchema).min(1),
|
|
136
|
+
branchTypes: z.array(BranchTypeSchema).optional(),
|
|
137
|
+
conventionalTypes: z.array(ConventionalTypeSchema).optional(),
|
|
138
|
+
enforcement: EnforcementLevelSchema.default("strict"),
|
|
139
|
+
language: z.string().default("en"),
|
|
140
|
+
analytics: AnalyticsConfigSchema.optional(),
|
|
141
|
+
adapter: z.string().optional(),
|
|
142
|
+
syncRemote: z.string().optional(),
|
|
143
|
+
hooks: HooksConfigSchema.optional(),
|
|
144
|
+
guidelines: GuidelinesConfigSchema.optional(),
|
|
145
|
+
reservedScopeNames: z.array(z.string()).optional().default(DEFAULT_RESERVED_SCOPE_NAMES),
|
|
146
|
+
ci: CIConfigSchema.optional()
|
|
147
|
+
}).superRefine((config, ctx) => {
|
|
148
|
+
const reservedNames = config.reservedScopeNames || DEFAULT_RESERVED_SCOPE_NAMES;
|
|
149
|
+
config.scopes.forEach((scope, index) => {
|
|
150
|
+
const validation = validateScopeName(scope.name, reservedNames);
|
|
151
|
+
if (!validation.valid) {
|
|
152
|
+
let message = validation.error || "Invalid scope name";
|
|
153
|
+
if (validation.suggestion) {
|
|
154
|
+
message += `. Try renaming to "${validation.suggestion}"`;
|
|
155
|
+
}
|
|
156
|
+
ctx.addIssue({
|
|
157
|
+
code: z.ZodIssueCode.custom,
|
|
158
|
+
path: ["scopes", index, "name"],
|
|
159
|
+
message
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
function validateScopeDefinitions(scopes) {
|
|
165
|
+
const errors = [];
|
|
166
|
+
const seenNames = /* @__PURE__ */ new Set();
|
|
167
|
+
for (const scope of scopes) {
|
|
168
|
+
if (seenNames.has(scope.name)) {
|
|
169
|
+
errors.push(`Duplicate scope name: "${scope.name}"`);
|
|
170
|
+
}
|
|
171
|
+
seenNames.add(scope.name);
|
|
172
|
+
const result = ScopeSchema.safeParse(scope);
|
|
173
|
+
if (!result.success) {
|
|
174
|
+
result.error.errors.forEach((err) => {
|
|
175
|
+
errors.push(`Scope "${scope.name}": ${err.message}`);
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return {
|
|
180
|
+
valid: errors.length === 0,
|
|
181
|
+
errors
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// src/config/index.ts
|
|
186
|
+
import { join } from "path";
|
|
187
|
+
import { existsSync } from "fs";
|
|
188
|
+
import { z as z2 } from "zod";
|
|
189
|
+
var explorer = cosmiconfig("workflow", {
|
|
190
|
+
searchPlaces: [
|
|
191
|
+
"workflow.config.ts",
|
|
192
|
+
"workflow.config.js",
|
|
193
|
+
"workflow.config.json",
|
|
194
|
+
".workflowrc",
|
|
195
|
+
".workflowrc.json",
|
|
196
|
+
"package.json"
|
|
197
|
+
]
|
|
198
|
+
});
|
|
199
|
+
async function loadConfig(cwd = process.cwd()) {
|
|
200
|
+
try {
|
|
201
|
+
const result = await explorer.search(cwd);
|
|
202
|
+
if (!result || !result.config) {
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
const validated = WorkflowConfigSchema.parse(result.config);
|
|
206
|
+
return validated;
|
|
207
|
+
} catch (error) {
|
|
208
|
+
if (error instanceof z2.ZodError) {
|
|
209
|
+
const result = await explorer.search(cwd);
|
|
210
|
+
const formattedErrors = error.errors.map((err) => {
|
|
211
|
+
const path = err.path.join(".");
|
|
212
|
+
if (err.path[0] === "scopes" && typeof err.path[1] === "number") {
|
|
213
|
+
const scopeIndex = err.path[1];
|
|
214
|
+
const scopeName = result?.config?.scopes?.[scopeIndex]?.name || `scope at index ${scopeIndex}`;
|
|
215
|
+
const field = err.path[2] || "definition";
|
|
216
|
+
let message = err.message;
|
|
217
|
+
if (message.includes("reserved word")) {
|
|
218
|
+
const reservedMatch = message.match(
|
|
219
|
+
/Scope name "([^"]+)" is reserved/
|
|
220
|
+
);
|
|
221
|
+
if (reservedMatch) {
|
|
222
|
+
const suggestions = {
|
|
223
|
+
docs: "documentation",
|
|
224
|
+
test: "testing",
|
|
225
|
+
config: "configuration",
|
|
226
|
+
build: "builds",
|
|
227
|
+
ci: "cicd",
|
|
228
|
+
deps: "dependencies"
|
|
229
|
+
};
|
|
230
|
+
const badName = reservedMatch[1];
|
|
231
|
+
const suggestion = suggestions[badName] || `${badName}-scope`;
|
|
232
|
+
message = `${message}. Try renaming to "${suggestion}"`;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return field === "definition" ? `Scope "${scopeName}": ${message}` : `Scope "${scopeName}" ${field}: ${message}`;
|
|
236
|
+
}
|
|
237
|
+
return `${path}: ${err.message}`;
|
|
238
|
+
}).join("\n \u2022 ");
|
|
239
|
+
throw new Error(
|
|
240
|
+
`Invalid workflow configuration:
|
|
241
|
+
\u2022 ${formattedErrors}
|
|
242
|
+
|
|
243
|
+
\u{1F4A1} Fix these issues in workflow.config.json or run: workflow config validate`
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
if (error instanceof Error) {
|
|
247
|
+
throw new Error(`Failed to load workflow config: ${error.message}`);
|
|
248
|
+
}
|
|
249
|
+
throw error;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
async function validateConfig(cwd = process.cwd()) {
|
|
253
|
+
const errors = [];
|
|
254
|
+
const warnings = [];
|
|
255
|
+
try {
|
|
256
|
+
const config = await loadConfig(cwd);
|
|
257
|
+
if (!config) {
|
|
258
|
+
errors.push("No configuration file found");
|
|
259
|
+
return { valid: false, errors, warnings };
|
|
260
|
+
}
|
|
261
|
+
const scopeValidation = validateScopeDefinitions(config.scopes);
|
|
262
|
+
errors.push(...scopeValidation.errors);
|
|
263
|
+
return {
|
|
264
|
+
valid: errors.length === 0,
|
|
265
|
+
errors,
|
|
266
|
+
warnings
|
|
267
|
+
};
|
|
268
|
+
} catch (error) {
|
|
269
|
+
errors.push(error instanceof Error ? error.message : String(error));
|
|
270
|
+
return { valid: false, errors, warnings };
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
function hasConfig(cwd = process.cwd()) {
|
|
274
|
+
const configPaths = [
|
|
275
|
+
"workflow.config.ts",
|
|
276
|
+
"workflow.config.js",
|
|
277
|
+
"workflow.config.json",
|
|
278
|
+
".workflowrc",
|
|
279
|
+
".workflowrc.json"
|
|
280
|
+
];
|
|
281
|
+
return configPaths.some((path) => existsSync(join(cwd, path)));
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export {
|
|
285
|
+
DEFAULT_RESERVED_SCOPE_NAMES,
|
|
286
|
+
validateScopeName,
|
|
287
|
+
WorkflowConfigSchema,
|
|
288
|
+
validateScopeDefinitions,
|
|
289
|
+
loadConfig,
|
|
290
|
+
validateConfig,
|
|
291
|
+
hasConfig
|
|
292
|
+
};
|
|
293
|
+
//# sourceMappingURL=chunk-IPMSSOXR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/config/index.ts","../src/config/schema.ts"],"sourcesContent":["import { cosmiconfig } from \"cosmiconfig\";\nimport {\n WorkflowConfig,\n WorkflowConfigSchema,\n validateScopeDefinitions,\n} from \"./schema.js\";\nimport { join } from \"path\";\nimport { existsSync } from \"fs\";\nimport { z } from \"zod\";\n\nconst explorer = cosmiconfig(\"workflow\", {\n searchPlaces: [\n \"workflow.config.ts\",\n \"workflow.config.js\",\n \"workflow.config.json\",\n \".workflowrc\",\n \".workflowrc.json\",\n \"package.json\",\n ],\n});\n\nexport async function loadConfig(\n cwd: string = process.cwd(),\n): Promise<WorkflowConfig | null> {\n try {\n const result = await explorer.search(cwd);\n\n if (!result || !result.config) {\n return null;\n }\n\n // Validate config against schema\n const validated = WorkflowConfigSchema.parse(result.config);\n return validated;\n } catch (error) {\n if (error instanceof z.ZodError) {\n // Format Zod errors to be more user-friendly\n const result = await explorer.search(cwd);\n const formattedErrors = error.errors\n .map((err) => {\n const path = err.path.join(\".\");\n\n // If error is in scopes array, show the scope name\n if (err.path[0] === \"scopes\" && typeof err.path[1] === \"number\") {\n const scopeIndex = err.path[1];\n const scopeName =\n result?.config?.scopes?.[scopeIndex]?.name ||\n `scope at index ${scopeIndex}`;\n const field = err.path[2] || \"definition\";\n\n // Add helpful suggestions for common errors\n let message = err.message;\n if (message.includes(\"reserved word\")) {\n const reservedMatch = message.match(\n /Scope name \"([^\"]+)\" is reserved/,\n );\n if (reservedMatch) {\n const suggestions: Record<string, string> = {\n docs: \"documentation\",\n test: \"testing\",\n config: \"configuration\",\n build: \"builds\",\n ci: \"cicd\",\n deps: \"dependencies\",\n };\n const badName = reservedMatch[1];\n const suggestion = suggestions[badName] || `${badName}-scope`;\n message = `${message}. Try renaming to \"${suggestion}\"`;\n }\n }\n\n return field === \"definition\"\n ? `Scope \"${scopeName}\": ${message}`\n : `Scope \"${scopeName}\" ${field}: ${message}`;\n }\n\n return `${path}: ${err.message}`;\n })\n .join(\"\\n ⢠\");\n\n throw new Error(\n `Invalid workflow configuration:\\n ⢠${formattedErrors}\\n\\nš” Fix these issues in workflow.config.json or run: workflow config validate`,\n );\n }\n\n if (error instanceof Error) {\n throw new Error(`Failed to load workflow config: ${error.message}`);\n }\n throw error;\n }\n}\n\nexport async function validateConfig(cwd: string = process.cwd()): Promise<{\n valid: boolean;\n errors: string[];\n warnings: string[];\n}> {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n try {\n const config = await loadConfig(cwd);\n if (!config) {\n errors.push(\"No configuration file found\");\n return { valid: false, errors, warnings };\n }\n\n // Additional validation beyond schema\n const scopeValidation = validateScopeDefinitions(config.scopes);\n errors.push(...scopeValidation.errors);\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n };\n } catch (error) {\n errors.push(error instanceof Error ? error.message : String(error));\n return { valid: false, errors, warnings };\n }\n}\n\nexport function hasConfig(cwd: string = process.cwd()): boolean {\n const configPaths = [\n \"workflow.config.ts\",\n \"workflow.config.js\",\n \"workflow.config.json\",\n \".workflowrc\",\n \".workflowrc.json\",\n ];\n\n return configPaths.some((path) => existsSync(join(cwd, path)));\n}\n\nexport {\n WorkflowConfig,\n WorkflowConfigSchema,\n Scope,\n BranchType,\n ConventionalType,\n validateScopeName,\n DEFAULT_RESERVED_SCOPE_NAMES,\n} from \"./schema.js\";\n","import { z } from \"zod\";\n\n// Default reserved scope names that cannot be used\nexport const DEFAULT_RESERVED_SCOPE_NAMES = [\n \"init\",\n \"create\",\n \"build\",\n \"test\",\n \"config\",\n \"docs\",\n \"ci\",\n \"deps\",\n];\n\n/**\n * Validates a scope name against reserved words and naming rules\n */\nexport function validateScopeName(\n name: string,\n reservedNames: string[] = DEFAULT_RESERVED_SCOPE_NAMES,\n): {\n valid: boolean;\n error?: string;\n suggestion?: string;\n} {\n if (reservedNames.includes(name)) {\n // Provide suggestions for common reserved words\n const suggestions: Record<string, string> = {\n docs: \"documentation\",\n test: \"testing\",\n config: \"configuration\",\n build: \"builds\",\n ci: \"cicd\",\n deps: \"dependencies\",\n };\n\n return {\n valid: false,\n error: `Scope name \"${name}\" is reserved`,\n suggestion: suggestions[name] || `${name}-scope`,\n };\n }\n\n if (!/^[a-z0-9-]+$/.test(name)) {\n return {\n valid: false,\n error: \"Scope name must be lowercase alphanumeric with hyphens\",\n };\n }\n\n if (name.length === 0 || name.length > 32) {\n return {\n valid: false,\n error: \"Scope name must be 1-32 characters\",\n };\n }\n\n return { valid: true };\n}\n\nexport const BranchTypeSchema = z.enum([\n \"feature\",\n \"bugfix\",\n \"hotfix\",\n \"chore\",\n \"refactor\",\n \"docs\",\n \"test\",\n \"release\",\n]);\n\nexport const ConventionalTypeSchema = z.enum([\n \"feat\",\n \"fix\",\n \"refactor\",\n \"chore\",\n \"docs\",\n \"test\",\n \"perf\",\n \"style\",\n \"ci\",\n \"build\",\n \"revert\",\n]);\n\nexport const ScopeSchema = z.object({\n name: z\n .string()\n .min(1)\n .max(32, \"Scope name must be 32 characters or less\")\n .regex(\n /^[a-z0-9-]+$/,\n \"Scope name must be lowercase alphanumeric with hyphens\",\n ),\n description: z\n .string()\n .min(10, \"Scope description must be at least 10 characters\"),\n allowedTypes: z.array(ConventionalTypeSchema).optional(),\n mandatoryGuidelines: z.array(z.string()).optional(),\n emoji: z.string().optional(),\n category: z\n .enum([\n \"auth\",\n \"features\",\n \"infrastructure\",\n \"documentation\",\n \"testing\",\n \"performance\",\n \"other\",\n ])\n .optional(),\n});\n\nexport const EnforcementLevelSchema = z.enum([\n \"strict\",\n \"advisory\",\n \"learning\",\n]);\n\nexport const AnalyticsConfigSchema = z.object({\n enabled: z.boolean().default(false),\n shareAnonymous: z.boolean().default(false),\n});\n\n// Pre-commit hook check types\nexport const HookCheckSchema = z.enum([\n \"validate-branch\",\n \"validate-commit\",\n \"check-guidelines\",\n \"validate-scopes\",\n]);\n\n// Git hooks configuration\nexport const HooksConfigSchema = z.object({\n /** Whether hooks are enabled */\n enabled: z.boolean().default(true),\n /** Checks to run on pre-commit */\n preCommit: z\n .array(HookCheckSchema)\n .default([\"validate-branch\", \"check-guidelines\"]),\n /** Checks to run on commit-msg */\n commitMsg: z.array(HookCheckSchema).default([\"validate-commit\"]),\n});\n\n// Guidelines configuration with mandatory templates and user overrides\nexport const GuidelinesConfigSchema = z.object({\n /** Additional templates to make mandatory (beyond the core set) */\n additionalMandatory: z.array(z.string()).optional(),\n /** Templates to make optional (override core mandatory templates) */\n optionalOverrides: z.array(z.string()).optional(),\n});\n\n// CI provider types\nexport const CIProviderSchema = z.enum([\"github\", \"gitlab\", \"bitbucket\"]);\n\n// CI check types\nexport const CICheckSchema = z.enum([\n \"lint\",\n \"typecheck\",\n \"format\",\n \"test\",\n \"build\",\n]);\n\n// CI/CD configuration\nexport const CIConfigSchema = z.object({\n /** Whether CI setup is enabled */\n enabled: z.boolean().default(true),\n /** CI provider (currently only github supported) */\n provider: CIProviderSchema.default(\"github\"),\n /** Checks to run in CI pipeline */\n checks: z\n .array(CICheckSchema)\n .default([\"lint\", \"typecheck\", \"format\", \"build\", \"test\"]),\n});\n\nexport const WorkflowConfigSchema = z\n .object({\n projectName: z.string().min(1),\n scopes: z.array(ScopeSchema).min(1),\n branchTypes: z.array(BranchTypeSchema).optional(),\n conventionalTypes: z.array(ConventionalTypeSchema).optional(),\n enforcement: EnforcementLevelSchema.default(\"strict\"),\n language: z.string().default(\"en\"),\n analytics: AnalyticsConfigSchema.optional(),\n adapter: z.string().optional(),\n syncRemote: z.string().optional(),\n hooks: HooksConfigSchema.optional(),\n guidelines: GuidelinesConfigSchema.optional(),\n reservedScopeNames: z\n .array(z.string())\n .optional()\n .default(DEFAULT_RESERVED_SCOPE_NAMES),\n ci: CIConfigSchema.optional(),\n })\n .superRefine((config, ctx) => {\n // Validate scopes against reserved names\n const reservedNames =\n config.reservedScopeNames || DEFAULT_RESERVED_SCOPE_NAMES;\n\n config.scopes.forEach((scope, index) => {\n const validation = validateScopeName(scope.name, reservedNames);\n if (!validation.valid) {\n let message = validation.error || \"Invalid scope name\";\n if (validation.suggestion) {\n message += `. Try renaming to \"${validation.suggestion}\"`;\n }\n\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"scopes\", index, \"name\"],\n message,\n });\n }\n });\n });\n\nexport type Scope = z.infer<typeof ScopeSchema>;\nexport type BranchType = z.infer<typeof BranchTypeSchema>;\nexport type ConventionalType = z.infer<typeof ConventionalTypeSchema>;\nexport type EnforcementLevel = z.infer<typeof EnforcementLevelSchema>;\nexport type AnalyticsConfig = z.infer<typeof AnalyticsConfigSchema>;\nexport type HookCheck = z.infer<typeof HookCheckSchema>;\nexport type HooksConfig = z.infer<typeof HooksConfigSchema>;\nexport type GuidelinesConfig = z.infer<typeof GuidelinesConfigSchema>;\nexport type CIProvider = z.infer<typeof CIProviderSchema>;\nexport type CICheck = z.infer<typeof CICheckSchema>;\nexport type CIConfig = z.infer<typeof CIConfigSchema>;\nexport type WorkflowConfig = z.infer<typeof WorkflowConfigSchema>;\n\nexport const defaultBranchTypes: BranchType[] = [\n \"feature\",\n \"bugfix\",\n \"hotfix\",\n \"chore\",\n \"refactor\",\n \"docs\",\n \"test\",\n];\n\nexport const defaultConventionalTypes: ConventionalType[] = [\n \"feat\",\n \"fix\",\n \"refactor\",\n \"chore\",\n \"docs\",\n \"test\",\n \"perf\",\n \"style\",\n];\n\n/**\n * Validates scope definitions for duplicates, description quality, and category values\n * @param scopes Array of scope definitions to validate\n * @returns Object with validation result and error messages\n */\nexport function validateScopeDefinitions(scopes: Scope[]): {\n valid: boolean;\n errors: string[];\n} {\n const errors: string[] = [];\n const seenNames = new Set<string>();\n\n for (const scope of scopes) {\n // Check for duplicate names\n if (seenNames.has(scope.name)) {\n errors.push(`Duplicate scope name: \"${scope.name}\"`);\n }\n seenNames.add(scope.name);\n\n // Validate using schema (this will catch min length, reserved names, etc.)\n const result = ScopeSchema.safeParse(scope);\n if (!result.success) {\n result.error.errors.forEach((err) => {\n errors.push(`Scope \"${scope.name}\": ${err.message}`);\n });\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n };\n}\n"],"mappings":"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","names":["z"]}
|
|
@@ -12,10 +12,7 @@ async function discoverCustomScopes(workspacePath = process.cwd()) {
|
|
|
12
12
|
}
|
|
13
13
|
const discoveredScopes = [];
|
|
14
14
|
try {
|
|
15
|
-
const workspaceLocations = [
|
|
16
|
-
join(workspacePath, "packages"),
|
|
17
|
-
workspacePath
|
|
18
|
-
];
|
|
15
|
+
const workspaceLocations = [join(workspacePath, "packages"), workspacePath];
|
|
19
16
|
for (const location of workspaceLocations) {
|
|
20
17
|
try {
|
|
21
18
|
const entries = await readdir(location, { withFileTypes: true });
|
|
@@ -61,7 +58,15 @@ async function getAllScopes(config, workspacePath) {
|
|
|
61
58
|
return Array.from(scopeMap.values());
|
|
62
59
|
}
|
|
63
60
|
async function validateBranchName(branchName, config, workspacePath) {
|
|
64
|
-
const branchTypes = config.branchTypes || [
|
|
61
|
+
const branchTypes = config.branchTypes || [
|
|
62
|
+
"feature",
|
|
63
|
+
"bugfix",
|
|
64
|
+
"hotfix",
|
|
65
|
+
"chore",
|
|
66
|
+
"refactor",
|
|
67
|
+
"docs",
|
|
68
|
+
"test"
|
|
69
|
+
];
|
|
65
70
|
const allScopes = await getAllScopes(config, workspacePath);
|
|
66
71
|
const scopes = allScopes.map((s) => s.name);
|
|
67
72
|
const branchPattern = /^([a-z]+)\/([a-z0-9-]+)\/([a-z0-9-]+)$/;
|
|
@@ -167,4 +172,4 @@ export {
|
|
|
167
172
|
validateCommitMessage,
|
|
168
173
|
validatePRTitle
|
|
169
174
|
};
|
|
170
|
-
//# sourceMappingURL=chunk-
|
|
175
|
+
//# sourceMappingURL=chunk-NMHWD2GA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/validators/index.ts"],"sourcesContent":["import didYouMean from \"didyoumean2\";\nimport type { WorkflowConfig, BranchType, Scope } from \"../config/index.js\";\nimport { readdir } from \"fs/promises\";\nimport { join } from \"path\";\n\nexport interface ValidationResult {\n valid: boolean;\n error?: string;\n suggestion?: string;\n}\n\n// Cache for discovered custom scopes\nlet customScopesCache: Scope[] | null = null;\nlet cacheTimestamp: number = 0;\nconst CACHE_TTL = 5 * 60 * 1000; // 5 minutes\n\n/**\n * Discovers custom scope packages in the workspace and node_modules\n * @param workspacePath Path to workspace root\n * @returns Array of discovered scopes\n */\nexport async function discoverCustomScopes(\n workspacePath: string = process.cwd(),\n): Promise<Scope[]> {\n // Check cache validity\n const now = Date.now();\n if (customScopesCache && now - cacheTimestamp < CACHE_TTL) {\n return customScopesCache;\n }\n\n const discoveredScopes: Scope[] = [];\n\n try {\n // Search for custom scope packages in workspace\n const workspaceLocations = [join(workspacePath, \"packages\"), workspacePath];\n\n for (const location of workspaceLocations) {\n try {\n const entries = await readdir(location, { withFileTypes: true });\n\n for (const entry of entries) {\n if (entry.isDirectory() && entry.name.startsWith(\"scopes-\")) {\n const indexPath = join(location, entry.name, \"src\", \"index.ts\");\n try {\n const module = await import(indexPath);\n const scopes = module.scopes || module.default?.scopes;\n\n if (Array.isArray(scopes)) {\n discoveredScopes.push(...scopes);\n }\n } catch {\n // Silently skip packages that can't be loaded\n }\n }\n }\n } catch {\n // Directory doesn't exist or can't be read\n }\n }\n\n // Update cache\n customScopesCache = discoveredScopes;\n cacheTimestamp = now;\n } catch (error) {\n // Return empty array on error\n console.warn(\"Warning: Error discovering custom scopes:\", error);\n }\n\n return discoveredScopes;\n}\n\n/**\n * Invalidates the custom scopes cache (useful after config changes)\n */\nexport function invalidateCustomScopesCache(): void {\n customScopesCache = null;\n cacheTimestamp = 0;\n}\n\n/**\n * Gets all available scopes including custom discovered ones\n * @param config Workflow configuration\n * @param workspacePath Optional workspace path\n * @returns Combined array of scopes\n */\nexport async function getAllScopes(\n config: WorkflowConfig,\n workspacePath?: string,\n): Promise<Scope[]> {\n const configScopes = config.scopes;\n const customScopes = await discoverCustomScopes(workspacePath);\n\n // Merge and deduplicate by name\n const scopeMap = new Map<string, Scope>();\n\n // Config scopes take precedence\n for (const scope of configScopes) {\n scopeMap.set(scope.name, scope);\n }\n\n // Add custom scopes that don't conflict\n for (const scope of customScopes) {\n if (!scopeMap.has(scope.name)) {\n scopeMap.set(scope.name, scope);\n }\n }\n\n return Array.from(scopeMap.values());\n}\n\nexport async function validateBranchName(\n branchName: string,\n config: WorkflowConfig,\n workspacePath?: string,\n): Promise<ValidationResult> {\n const branchTypes = config.branchTypes || [\n \"feature\",\n \"bugfix\",\n \"hotfix\",\n \"chore\",\n \"refactor\",\n \"docs\",\n \"test\",\n ];\n const allScopes = await getAllScopes(config, workspacePath);\n const scopes = allScopes.map((s) => s.name);\n\n // Expected format: <type>/<scope>/<description>\n const branchPattern = /^([a-z]+)\\/([a-z0-9-]+)\\/([a-z0-9-]+)$/;\n const match = branchName.match(branchPattern);\n\n if (!match) {\n return {\n valid: false,\n error: `Branch name must follow format: <type>/<scope>/<description> (e.g., feature/auth/add-login)`,\n suggestion: `Current: ${branchName}. All parts must be lowercase alphanumeric with hyphens.`,\n };\n }\n\n const [, type, scope, description] = match;\n\n // Validate type\n if (!branchTypes.includes(type as BranchType)) {\n const suggestion = didYouMean(type, branchTypes);\n return {\n valid: false,\n error: `Invalid branch type '${type}'. Must be one of: ${branchTypes.join(\", \")}`,\n suggestion: suggestion ? `Did you mean '${suggestion}'?` : undefined,\n };\n }\n\n // Validate scope\n if (!scopes.includes(scope)) {\n const suggestion = didYouMean(scope, scopes);\n const scopeList =\n scopes.slice(0, 5).join(\", \") + (scopes.length > 5 ? \"...\" : \"\");\n return {\n valid: false,\n error: `Invalid scope '${scope}'. Must be one of: ${scopeList}`,\n suggestion: suggestion ? `Did you mean '${suggestion}'?` : undefined,\n };\n }\n\n // Validate description (not empty, meaningful)\n if (description.length < 3) {\n return {\n valid: false,\n error: `Branch description '${description}' is too short (minimum 3 characters)`,\n };\n }\n\n return { valid: true };\n}\n\nexport async function validateCommitMessage(\n message: string,\n config: WorkflowConfig,\n workspacePath?: string,\n): Promise<ValidationResult> {\n const conventionalTypes = config.conventionalTypes || [\n \"feat\",\n \"fix\",\n \"refactor\",\n \"chore\",\n \"docs\",\n \"test\",\n \"perf\",\n \"style\",\n ];\n const allScopes = await getAllScopes(config, workspacePath);\n const scopes = allScopes.map((s) => s.name);\n\n // Expected format: <type>(<scope>): <description>\n const commitPattern = /^([a-z]+)(?:\\(([a-z0-9-]+)\\))?: (.+)$/;\n const match = message.match(commitPattern);\n\n if (!match) {\n return {\n valid: false,\n error: `Commit message must follow conventional commits format: <type>(<scope>): <description>`,\n suggestion: `Example: feat(auth): add login validation`,\n };\n }\n\n const [, type, scope, description] = match;\n\n // Validate type\n if (!conventionalTypes.includes(type as any)) {\n const suggestion = didYouMean(type, conventionalTypes);\n return {\n valid: false,\n error: `Invalid commit type '${type}'. Must be one of: ${conventionalTypes.join(\", \")}`,\n suggestion: suggestion ? `Did you mean '${suggestion}'?` : undefined,\n };\n }\n\n // Validate scope (optional but recommended)\n if (scope && !scopes.includes(scope)) {\n const suggestion = didYouMean(scope, scopes);\n const scopeList =\n scopes.slice(0, 5).join(\", \") + (scopes.length > 5 ? \"...\" : \"\");\n return {\n valid: false,\n error: `Invalid scope '${scope}'. Must be one of: ${scopeList}`,\n suggestion: suggestion ? `Did you mean '${suggestion}'?` : undefined,\n };\n }\n\n // Validate description\n if (description.length < 10) {\n return {\n valid: false,\n error: `Commit description is too short (minimum 10 characters)`,\n suggestion: `Be more descriptive about what changed`,\n };\n }\n\n if (description[0] !== description[0].toLowerCase()) {\n return {\n valid: false,\n error: `Commit description must start with lowercase letter`,\n suggestion: `Change '${description}' to '${description[0].toLowerCase()}${description.slice(1)}'`,\n };\n }\n\n return { valid: true };\n}\n\nexport async function validatePRTitle(\n title: string,\n config: WorkflowConfig,\n workspacePath?: string,\n): Promise<ValidationResult> {\n // PR titles follow same format as commit messages\n return validateCommitMessage(title, config, workspacePath);\n}\n"],"mappings":"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","names":[]}
|