forge-dev-framework 1.0.1
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/rules/api-patterns.md +98 -0
- package/.claude/rules/security-baseline.md +204 -0
- package/.claude/rules/testing-standards.md +177 -0
- package/.claude/rules/ui-conventions.md +142 -0
- package/README.md +261 -0
- package/bin/forge.js +14 -0
- package/dist/bin/forge.js +14 -0
- package/dist/cli/index.d.ts +22 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +116 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/commands/base.d.ts +31 -0
- package/dist/commands/base.d.ts.map +1 -0
- package/dist/commands/base.js +31 -0
- package/dist/commands/base.js.map +1 -0
- package/dist/commands/config.d.ts +14 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +175 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/generate.d.ts +17 -0
- package/dist/commands/generate.d.ts.map +1 -0
- package/dist/commands/generate.js +159 -0
- package/dist/commands/generate.js.map +1 -0
- package/dist/commands/help.d.ts +11 -0
- package/dist/commands/help.d.ts.map +1 -0
- package/dist/commands/help.js +65 -0
- package/dist/commands/help.js.map +1 -0
- package/dist/commands/index.d.ts +8 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +8 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/init.d.ts +10 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +22 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/status.d.ts +13 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +101 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/stubs.d.ts +14 -0
- package/dist/commands/stubs.d.ts.map +1 -0
- package/dist/commands/stubs.js +30 -0
- package/dist/commands/stubs.js.map +1 -0
- package/dist/generators/index.d.ts +11 -0
- package/dist/generators/index.d.ts.map +1 -0
- package/dist/generators/index.js +10 -0
- package/dist/generators/index.js.map +1 -0
- package/dist/generators/required-fields.d.ts +74 -0
- package/dist/generators/required-fields.d.ts.map +1 -0
- package/dist/generators/required-fields.js +179 -0
- package/dist/generators/required-fields.js.map +1 -0
- package/dist/generators/template-engine.d.ts +65 -0
- package/dist/generators/template-engine.d.ts.map +1 -0
- package/dist/generators/template-engine.js +209 -0
- package/dist/generators/template-engine.js.map +1 -0
- package/dist/generators/token-validator.d.ts +51 -0
- package/dist/generators/token-validator.d.ts.map +1 -0
- package/dist/generators/token-validator.js +141 -0
- package/dist/generators/token-validator.js.map +1 -0
- package/dist/generators/types.d.ts +433 -0
- package/dist/generators/types.d.ts.map +1 -0
- package/dist/generators/types.js +5 -0
- package/dist/generators/types.js.map +1 -0
- package/dist/generators/xml-task-generator.d.ts +67 -0
- package/dist/generators/xml-task-generator.d.ts.map +1 -0
- package/dist/generators/xml-task-generator.js +297 -0
- package/dist/generators/xml-task-generator.js.map +1 -0
- package/dist/git/__tests__/worktree.test.d.ts +5 -0
- package/dist/git/__tests__/worktree.test.d.ts.map +1 -0
- package/dist/git/__tests__/worktree.test.js +121 -0
- package/dist/git/__tests__/worktree.test.js.map +1 -0
- package/dist/git/codeowners.d.ts +101 -0
- package/dist/git/codeowners.d.ts.map +1 -0
- package/dist/git/codeowners.js +216 -0
- package/dist/git/codeowners.js.map +1 -0
- package/dist/git/commit.d.ts +135 -0
- package/dist/git/commit.d.ts.map +1 -0
- package/dist/git/commit.js +223 -0
- package/dist/git/commit.js.map +1 -0
- package/dist/git/hooks/commit-msg.d.ts +8 -0
- package/dist/git/hooks/commit-msg.d.ts.map +1 -0
- package/dist/git/hooks/commit-msg.js +34 -0
- package/dist/git/hooks/commit-msg.js.map +1 -0
- package/dist/git/hooks/pre-commit.d.ts +8 -0
- package/dist/git/hooks/pre-commit.d.ts.map +1 -0
- package/dist/git/hooks/pre-commit.js +34 -0
- package/dist/git/hooks/pre-commit.js.map +1 -0
- package/dist/git/pre-commit-hooks.d.ts +117 -0
- package/dist/git/pre-commit-hooks.d.ts.map +1 -0
- package/dist/git/pre-commit-hooks.js +270 -0
- package/dist/git/pre-commit-hooks.js.map +1 -0
- package/dist/git/wipe-protocol.d.ts +281 -0
- package/dist/git/wipe-protocol.d.ts.map +1 -0
- package/dist/git/wipe-protocol.js +237 -0
- package/dist/git/wipe-protocol.js.map +1 -0
- package/dist/git/worktree.d.ts +69 -0
- package/dist/git/worktree.d.ts.map +1 -0
- package/dist/git/worktree.js +202 -0
- package/dist/git/worktree.js.map +1 -0
- package/dist/scripts/install.d.ts +8 -0
- package/dist/scripts/install.d.ts.map +1 -0
- package/dist/scripts/install.js +161 -0
- package/dist/scripts/install.js.map +1 -0
- package/dist/types/config.d.ts +30 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +23 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +6 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/state.d.ts +56 -0
- package/dist/types/state.d.ts.map +1 -0
- package/dist/types/state.js +6 -0
- package/dist/types/state.js.map +1 -0
- package/dist/utils/config.d.ts +15 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +80 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/errors.d.ts +25 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +48 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/index.d.ts +11 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +9 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logger.d.ts +34 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +73 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/state-api.d.ts +128 -0
- package/dist/utils/state-api.d.ts.map +1 -0
- package/dist/utils/state-api.js +170 -0
- package/dist/utils/state-api.js.map +1 -0
- package/dist/utils/template-client.d.ts +73 -0
- package/dist/utils/template-client.d.ts.map +1 -0
- package/dist/utils/template-client.js +151 -0
- package/dist/utils/template-client.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XML Task Spec Generator
|
|
3
|
+
*
|
|
4
|
+
* Generates XML-formatted task specifications as defined in FORGE Phase II: Plan
|
|
5
|
+
* Each task includes: id, type, owner, name, deps, files, contracts, action, acceptance, verify
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* XML Task Spec Generator class
|
|
9
|
+
*/
|
|
10
|
+
export class XmlTaskGenerator {
|
|
11
|
+
/**
|
|
12
|
+
* Generate a single task spec in XML format
|
|
13
|
+
*/
|
|
14
|
+
static generateTask(task) {
|
|
15
|
+
const lines = [];
|
|
16
|
+
lines.push(`<task id="${task.id}" type="${task.type}" owner="${task.owner}">`);
|
|
17
|
+
// Name (required)
|
|
18
|
+
lines.push(` <name>${this.escapeXml(task.name)}</name>`);
|
|
19
|
+
// Dependencies (optional)
|
|
20
|
+
if (task.deps) {
|
|
21
|
+
lines.push(` <deps>${task.deps}</deps>`);
|
|
22
|
+
}
|
|
23
|
+
// Files (optional)
|
|
24
|
+
if (task.files) {
|
|
25
|
+
lines.push(` <files>${task.files}</files>`);
|
|
26
|
+
}
|
|
27
|
+
// Contracts (optional)
|
|
28
|
+
if (task.contracts) {
|
|
29
|
+
lines.push(` <contracts>${task.contracts}</contracts>`);
|
|
30
|
+
}
|
|
31
|
+
// Action (required)
|
|
32
|
+
lines.push(' <action>');
|
|
33
|
+
lines.push(this.indentText(task.action, 4));
|
|
34
|
+
lines.push(' </action>');
|
|
35
|
+
// Acceptance criteria (required, array)
|
|
36
|
+
lines.push(' <acceptance>');
|
|
37
|
+
for (const criterion of task.acceptance) {
|
|
38
|
+
lines.push(` ${this.escapeXml(criterion)}`);
|
|
39
|
+
}
|
|
40
|
+
lines.push(' </acceptance>');
|
|
41
|
+
// Verification command (required)
|
|
42
|
+
lines.push(` <verify>${this.escapeXml(task.verify)}</verify>`);
|
|
43
|
+
lines.push('</task>');
|
|
44
|
+
return lines.join('\n');
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Generate multiple task specs
|
|
48
|
+
*/
|
|
49
|
+
static generateTasks(tasks) {
|
|
50
|
+
const lines = [
|
|
51
|
+
'<!--',
|
|
52
|
+
' FORGE Task Specifications',
|
|
53
|
+
` Generated: ${new Date().toISOString()}`,
|
|
54
|
+
' Total Tasks: ' + tasks.length,
|
|
55
|
+
'-->',
|
|
56
|
+
'',
|
|
57
|
+
];
|
|
58
|
+
for (const task of tasks) {
|
|
59
|
+
lines.push(this.generateTask(task));
|
|
60
|
+
lines.push('');
|
|
61
|
+
}
|
|
62
|
+
return lines.join('\n');
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Generate task spec with XML declaration
|
|
66
|
+
*/
|
|
67
|
+
static generateTaskWithHeader(task) {
|
|
68
|
+
return [
|
|
69
|
+
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
70
|
+
'<!--',
|
|
71
|
+
' FORGE Task Specification',
|
|
72
|
+
` Generated: ${new Date().toISOString()}`,
|
|
73
|
+
'-->',
|
|
74
|
+
'',
|
|
75
|
+
this.generateTask(task),
|
|
76
|
+
].join('\n');
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Parse a task spec from XML format
|
|
80
|
+
*/
|
|
81
|
+
static parseTask(xmlString) {
|
|
82
|
+
// Simple XML parser for task specs
|
|
83
|
+
// Note: This is a basic parser. For production, use a proper XML parser
|
|
84
|
+
const task = {};
|
|
85
|
+
// Extract task attributes
|
|
86
|
+
const taskMatch = xmlString.match(/<task\s+([^>]+)>/);
|
|
87
|
+
if (taskMatch) {
|
|
88
|
+
const attrs = taskMatch[1];
|
|
89
|
+
const idMatch = attrs.match(/id="([^"]+)"/);
|
|
90
|
+
const typeMatch = attrs.match(/type="([^"]+)"/);
|
|
91
|
+
const ownerMatch = attrs.match(/owner="([^"]+)"/);
|
|
92
|
+
if (idMatch)
|
|
93
|
+
task.id = idMatch[1];
|
|
94
|
+
if (typeMatch)
|
|
95
|
+
task.type = typeMatch[1];
|
|
96
|
+
if (ownerMatch)
|
|
97
|
+
task.owner = ownerMatch[1];
|
|
98
|
+
}
|
|
99
|
+
// Extract elements
|
|
100
|
+
const nameMatch = xmlString.match(/<name>([^<]+)<\/name>/);
|
|
101
|
+
if (nameMatch)
|
|
102
|
+
task.name = this.unescapeXml(nameMatch[1]);
|
|
103
|
+
const depsMatch = xmlString.match(/<deps>([^<]+)<\/deps>/);
|
|
104
|
+
if (depsMatch)
|
|
105
|
+
task.deps = this.unescapeXml(depsMatch[1]);
|
|
106
|
+
const filesMatch = xmlString.match(/<files>([^<]+)<\/files>/);
|
|
107
|
+
if (filesMatch)
|
|
108
|
+
task.files = this.unescapeXml(filesMatch[1]);
|
|
109
|
+
const contractsMatch = xmlString.match(/<contracts>([^<]+)<\/contracts>/);
|
|
110
|
+
if (contractsMatch)
|
|
111
|
+
task.contracts = this.unescapeXml(contractsMatch[1]);
|
|
112
|
+
const actionMatch = xmlString.match(/<action>\s*(.+?)\s*<\/action>/s);
|
|
113
|
+
if (actionMatch)
|
|
114
|
+
task.action = this.unescapeXml(actionMatch[1].trim());
|
|
115
|
+
const verifyMatch = xmlString.match(/<verify>([^<]+)<\/verify>/);
|
|
116
|
+
if (verifyMatch)
|
|
117
|
+
task.verify = this.unescapeXml(verifyMatch[1]);
|
|
118
|
+
// Extract acceptance criteria (multiple lines)
|
|
119
|
+
const acceptanceMatch = xmlString.match(/<acceptance>([\s\S]+?)<\/acceptance>/);
|
|
120
|
+
if (acceptanceMatch) {
|
|
121
|
+
const acceptanceLines = acceptanceMatch[1]
|
|
122
|
+
.split('\n')
|
|
123
|
+
.map((line) => line.trim())
|
|
124
|
+
.filter((line) => line.length > 0 && !line.startsWith('<'))
|
|
125
|
+
.map((line) => this.unescapeXml(line));
|
|
126
|
+
task.acceptance = acceptanceLines;
|
|
127
|
+
}
|
|
128
|
+
// Validate required fields
|
|
129
|
+
if (!task.id || !task.type || !task.owner || !task.name || !task.action || !task.acceptance || !task.verify) {
|
|
130
|
+
throw new Error('Invalid task spec: missing required fields');
|
|
131
|
+
}
|
|
132
|
+
return task;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Validate a task spec structure
|
|
136
|
+
*/
|
|
137
|
+
static validateTask(task) {
|
|
138
|
+
const errors = [];
|
|
139
|
+
// Required fields
|
|
140
|
+
if (!task.id) {
|
|
141
|
+
errors.push('Missing required field: id');
|
|
142
|
+
}
|
|
143
|
+
else if (!/^[a-z]+-\d{3}$/.test(task.id)) {
|
|
144
|
+
errors.push('Task ID must follow pattern: {prefix}-{number}, e.g., api-003');
|
|
145
|
+
}
|
|
146
|
+
if (!task.type) {
|
|
147
|
+
errors.push('Missing required field: type');
|
|
148
|
+
}
|
|
149
|
+
else if (!['auto', 'manual'].includes(task.type)) {
|
|
150
|
+
errors.push('Task type must be "auto" or "manual"');
|
|
151
|
+
}
|
|
152
|
+
if (!task.owner) {
|
|
153
|
+
errors.push('Missing required field: owner');
|
|
154
|
+
}
|
|
155
|
+
if (!task.name) {
|
|
156
|
+
errors.push('Missing required field: name');
|
|
157
|
+
}
|
|
158
|
+
else if (task.name.length > 100) {
|
|
159
|
+
errors.push('Task name must be less than 100 characters');
|
|
160
|
+
}
|
|
161
|
+
if (!task.action) {
|
|
162
|
+
errors.push('Missing required field: action');
|
|
163
|
+
}
|
|
164
|
+
else if (task.action.length > 2000) {
|
|
165
|
+
errors.push('Task action must be less than 2000 characters');
|
|
166
|
+
}
|
|
167
|
+
if (!task.acceptance || task.acceptance.length === 0) {
|
|
168
|
+
errors.push('Missing required field: acceptance (must have at least one criterion)');
|
|
169
|
+
}
|
|
170
|
+
else if (task.acceptance.length > 10) {
|
|
171
|
+
errors.push('Task acceptance criteria must not exceed 10 items');
|
|
172
|
+
}
|
|
173
|
+
if (!task.verify) {
|
|
174
|
+
errors.push('Missing required field: verify');
|
|
175
|
+
}
|
|
176
|
+
// Optional fields validation
|
|
177
|
+
if (task.deps && task.deps.length > 200) {
|
|
178
|
+
errors.push('Task dependencies must be less than 200 characters');
|
|
179
|
+
}
|
|
180
|
+
if (task.files && task.files.length > 500) {
|
|
181
|
+
errors.push('Task files must be less than 500 characters');
|
|
182
|
+
}
|
|
183
|
+
if (task.contracts && task.contracts.length > 200) {
|
|
184
|
+
errors.push('Task contracts must be less than 200 characters');
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
valid: errors.length === 0,
|
|
188
|
+
errors,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Escape XML special characters
|
|
193
|
+
*/
|
|
194
|
+
static escapeXml(text) {
|
|
195
|
+
return text
|
|
196
|
+
.replace(/&/g, '&')
|
|
197
|
+
.replace(/</g, '<')
|
|
198
|
+
.replace(/>/g, '>')
|
|
199
|
+
.replace(/"/g, '"')
|
|
200
|
+
.replace(/'/g, ''');
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Unescape XML special characters
|
|
204
|
+
*/
|
|
205
|
+
static unescapeXml(text) {
|
|
206
|
+
return text
|
|
207
|
+
.replace(/'/g, "'")
|
|
208
|
+
.replace(/"/g, '"')
|
|
209
|
+
.replace(/>/g, '>')
|
|
210
|
+
.replace(/</g, '<')
|
|
211
|
+
.replace(/&/g, '&');
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Indent text for XML formatting
|
|
215
|
+
*/
|
|
216
|
+
static indentText(text, spaces) {
|
|
217
|
+
const indent = ' '.repeat(spaces);
|
|
218
|
+
return text
|
|
219
|
+
.split('\n')
|
|
220
|
+
.map((line) => indent + line)
|
|
221
|
+
.join('\n');
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Create a task spec from a simple object
|
|
225
|
+
*/
|
|
226
|
+
static createTask(data) {
|
|
227
|
+
return {
|
|
228
|
+
id: data.id,
|
|
229
|
+
type: data.type,
|
|
230
|
+
owner: data.owner,
|
|
231
|
+
name: data.name,
|
|
232
|
+
deps: data.deps,
|
|
233
|
+
files: data.files,
|
|
234
|
+
contracts: data.contracts,
|
|
235
|
+
action: data.action,
|
|
236
|
+
acceptance: data.acceptance,
|
|
237
|
+
verify: data.verify,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Generate example task specs for reference
|
|
242
|
+
*/
|
|
243
|
+
static generateExamples() {
|
|
244
|
+
const examples = [
|
|
245
|
+
{
|
|
246
|
+
id: 'api-003',
|
|
247
|
+
type: 'auto',
|
|
248
|
+
owner: 'backend',
|
|
249
|
+
name: 'Create session authentication endpoint',
|
|
250
|
+
deps: 'api-001, core-002',
|
|
251
|
+
files: 'src/api/auth/sessions.ts, src/api/middleware/auth.ts',
|
|
252
|
+
contracts: 'contracts/auth.openapi.yaml',
|
|
253
|
+
action: `Implement POST /v1/sessions using jose for JWT.
|
|
254
|
+
Validate credentials against users table.
|
|
255
|
+
Return httpOnly cookie with configurable expiry.
|
|
256
|
+
Rate limit: 5 attempts per minute per IP.`,
|
|
257
|
+
acceptance: [
|
|
258
|
+
'Given valid credentials, POST /v1/sessions returns 200 + Set-Cookie',
|
|
259
|
+
'Given invalid credentials, returns 401 with generic error',
|
|
260
|
+
'Given 6th attempt in 1 minute, returns 429',
|
|
261
|
+
],
|
|
262
|
+
verify: 'npm test -- --grep "session auth"',
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
id: 'ui-002',
|
|
266
|
+
type: 'auto',
|
|
267
|
+
owner: 'frontend',
|
|
268
|
+
name: 'Implement login form with validation',
|
|
269
|
+
deps: 'ui-001',
|
|
270
|
+
files: 'src/ui/components/LoginForm.tsx, src/ui/hooks/useAuth.ts',
|
|
271
|
+
contracts: 'contracts/auth.openapi.yaml',
|
|
272
|
+
action: `Create login form component with email/password fields.
|
|
273
|
+
Implement client-side validation.
|
|
274
|
+
Show loading states during submission.
|
|
275
|
+
Display inline error messages.
|
|
276
|
+
Redirect to dashboard on successful login.`,
|
|
277
|
+
acceptance: [
|
|
278
|
+
'Given user enters invalid email, show inline validation error',
|
|
279
|
+
'Given user submits valid credentials, redirect to dashboard',
|
|
280
|
+
'Given login fails, display error message from API',
|
|
281
|
+
'Given form is submitting, disable submit button and show spinner',
|
|
282
|
+
],
|
|
283
|
+
verify: 'npm test -- --grep "LoginForm"',
|
|
284
|
+
},
|
|
285
|
+
];
|
|
286
|
+
return [
|
|
287
|
+
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
288
|
+
'<!--',
|
|
289
|
+
' FORGE Task Specification Examples',
|
|
290
|
+
` Generated: ${new Date().toISOString()}`,
|
|
291
|
+
'-->',
|
|
292
|
+
'',
|
|
293
|
+
...examples.map((task) => this.generateTask(task)),
|
|
294
|
+
].join('\n\n');
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
//# sourceMappingURL=xml-task-generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xml-task-generator.js","sourceRoot":"","sources":["../../src/generators/xml-task-generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worktree.test.d.ts","sourceRoot":"","sources":["../../../src/git/__tests__/worktree.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for Git Worktree Management
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, before, after } from "node:test";
|
|
5
|
+
import assert from "node/assert";
|
|
6
|
+
import { execa } from "execa";
|
|
7
|
+
import { createWorktree, getWorktree, mergeWorktree, cleanupWorktree, listWorktrees, WorktreeError, } from "../worktree.js";
|
|
8
|
+
describe("Git Worktree Management", () => {
|
|
9
|
+
const testTaskId = "test-task-001";
|
|
10
|
+
const testBaseBranch = "main";
|
|
11
|
+
before(async () => {
|
|
12
|
+
// Initialize a test git repository if needed
|
|
13
|
+
try {
|
|
14
|
+
await execa("git", ["status"]);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
// Not in a git repo, skip tests
|
|
18
|
+
console.log("Not in a git repository, skipping worktree tests");
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
describe("createWorktree", () => {
|
|
22
|
+
it("should create a new worktree with branch forge/{taskId}", async () => {
|
|
23
|
+
const result = await createWorktree({ taskId: testTaskId });
|
|
24
|
+
assert.ok(result.includes(".worktrees"));
|
|
25
|
+
assert.ok(result.includes(testTaskId));
|
|
26
|
+
});
|
|
27
|
+
it("should use custom branch name if provided", async () => {
|
|
28
|
+
const customBranch = "custom-branch";
|
|
29
|
+
const result = await createWorktree({
|
|
30
|
+
taskId: "custom-task",
|
|
31
|
+
branchName: customBranch,
|
|
32
|
+
});
|
|
33
|
+
assert.ok(result.includes("custom-task"));
|
|
34
|
+
});
|
|
35
|
+
it("should throw WorktreeError on failure", async () => {
|
|
36
|
+
try {
|
|
37
|
+
// Try to create duplicate worktree
|
|
38
|
+
await createWorktree({ taskId: testTaskId });
|
|
39
|
+
assert.fail("Should have thrown WorktreeError");
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
assert.ok(error instanceof WorktreeError);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
describe("getWorktree", () => {
|
|
47
|
+
it("should return worktree info for existing task", async () => {
|
|
48
|
+
const info = await getWorktree(testTaskId);
|
|
49
|
+
assert.ok(info);
|
|
50
|
+
assert.equal(info.taskId, testTaskId);
|
|
51
|
+
assert.ok(info.branchName);
|
|
52
|
+
assert.ok(info.path);
|
|
53
|
+
assert.ok(info.commit);
|
|
54
|
+
});
|
|
55
|
+
it("should return null for non-existent task", async () => {
|
|
56
|
+
const info = await getWorktree("non-existent-task");
|
|
57
|
+
assert.equal(info, null);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
describe("listWorktrees", () => {
|
|
61
|
+
it("should return array of worktree info", async () => {
|
|
62
|
+
const worktrees = await listWorktrees();
|
|
63
|
+
assert.ok(Array.isArray(worktrees));
|
|
64
|
+
assert.ok(worktrees.length > 0);
|
|
65
|
+
assert.ok(worktrees.some((w) => w.taskId === testTaskId));
|
|
66
|
+
});
|
|
67
|
+
it("should only include FORGE worktrees (.worktrees/)", async () => {
|
|
68
|
+
const worktrees = await listWorktrees();
|
|
69
|
+
for (const worktree of worktrees) {
|
|
70
|
+
assert.ok(worktree.path.includes(".worktrees"));
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
describe("cleanupWorktree", () => {
|
|
75
|
+
it("should remove an existing worktree", async () => {
|
|
76
|
+
await cleanupWorktree(testTaskId);
|
|
77
|
+
const info = await getWorktree(testTaskId);
|
|
78
|
+
assert.equal(info, null);
|
|
79
|
+
});
|
|
80
|
+
it("should not error when cleaning up non-existent worktree", async () => {
|
|
81
|
+
// Should not throw
|
|
82
|
+
await cleanupWorktree("non-existent-task");
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
describe("mergeWorktree", () => {
|
|
86
|
+
it("should merge worktree branch into target branch", async () => {
|
|
87
|
+
// Create a test worktree
|
|
88
|
+
const taskId = "merge-test-001";
|
|
89
|
+
await createWorktree({ taskId });
|
|
90
|
+
// Make a change in the worktree
|
|
91
|
+
// (This would require file operations, simplified here)
|
|
92
|
+
// Merge the worktree
|
|
93
|
+
try {
|
|
94
|
+
await mergeWorktree(taskId, "main");
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
// Merge might fail if no changes or conflicts, that's ok for test
|
|
98
|
+
assert.ok(error instanceof WorktreeError);
|
|
99
|
+
}
|
|
100
|
+
// Cleanup
|
|
101
|
+
await cleanupWorktree(taskId);
|
|
102
|
+
});
|
|
103
|
+
it("should throw WorktreeError for non-existent worktree", async () => {
|
|
104
|
+
try {
|
|
105
|
+
await mergeWorktree("non-existent-task");
|
|
106
|
+
assert.fail("Should have thrown WorktreeError");
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
assert.ok(error instanceof WorktreeError);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
after(async () => {
|
|
114
|
+
// Clean up test worktrees
|
|
115
|
+
try {
|
|
116
|
+
await cleanupWorktree(testTaskId);
|
|
117
|
+
}
|
|
118
|
+
catch { }
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
//# sourceMappingURL=worktree.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worktree.test.js","sourceRoot":"","sources":["../../../src/git/__tests__/worktree.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate .github/CODEOWNERS from task ownership maps.
|
|
3
|
+
*
|
|
4
|
+
* The CODEOWNERS file defines canonical ownership for code paths.
|
|
5
|
+
* GitHub enforces this at merge time - changes require owner approval.
|
|
6
|
+
*
|
|
7
|
+
* Generated format:
|
|
8
|
+
* # Auto-generated by FORGE
|
|
9
|
+
* src/api/** @backend-agent
|
|
10
|
+
* src/ui/** @frontend-agent
|
|
11
|
+
* src/tests/** @qa-agent
|
|
12
|
+
* docs/** @docs-agent
|
|
13
|
+
*/
|
|
14
|
+
export interface TaskOwnership {
|
|
15
|
+
id: string;
|
|
16
|
+
ownerRole: string;
|
|
17
|
+
allowedPaths: string[];
|
|
18
|
+
}
|
|
19
|
+
export interface StateForCodeowners {
|
|
20
|
+
tasks: TaskOwnership[];
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Generate CODEOWNERS content from task ownership.
|
|
24
|
+
*
|
|
25
|
+
* Aggregates allowedPaths by owner role and formats for GitHub.
|
|
26
|
+
*
|
|
27
|
+
* @param state - FORGE state with task ownership
|
|
28
|
+
* @returns CODEOWNERS file content
|
|
29
|
+
*/
|
|
30
|
+
export declare function generateCodeownersContent(state: StateForCodeowners): string;
|
|
31
|
+
/**
|
|
32
|
+
* Write CODEOWNERS file to disk.
|
|
33
|
+
*
|
|
34
|
+
* @param state - FORGE state with task ownership
|
|
35
|
+
* @param basePath - Root of the FORGE project
|
|
36
|
+
* @param forceOverwrite - Whether to overwrite existing file
|
|
37
|
+
* @throws Error if file exists and forceOverwrite is false
|
|
38
|
+
*/
|
|
39
|
+
export declare function writeCodeowners(state: StateForCodeowners, basePath?: string, forceOverwrite?: boolean): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Parse existing CODEOWNERS file.
|
|
42
|
+
*
|
|
43
|
+
* Useful for validating or diffing against generated content.
|
|
44
|
+
*
|
|
45
|
+
* @param content - CODEOWNERS file content
|
|
46
|
+
* @returns Parsed entries
|
|
47
|
+
*/
|
|
48
|
+
export interface CodeownersEntry {
|
|
49
|
+
pattern: string;
|
|
50
|
+
owner: string;
|
|
51
|
+
}
|
|
52
|
+
export declare function parseCodeowners(content: string): CodeownersEntry[];
|
|
53
|
+
/**
|
|
54
|
+
* Validate CODEOWNERS entries.
|
|
55
|
+
*
|
|
56
|
+
* Checks for:
|
|
57
|
+
* - Overlapping patterns (conflicting ownership)
|
|
58
|
+
* - Invalid patterns
|
|
59
|
+
* - Missing owners
|
|
60
|
+
*
|
|
61
|
+
* @param entries - Parsed CODEOWNERS entries
|
|
62
|
+
* @returns Validation result with errors
|
|
63
|
+
*/
|
|
64
|
+
export interface ValidationError {
|
|
65
|
+
type: "overlap" | "invalid-pattern" | "missing-owner";
|
|
66
|
+
message: string;
|
|
67
|
+
entries?: CodeownersEntry[];
|
|
68
|
+
}
|
|
69
|
+
export declare function validateCodeowners(entries: CodeownersEntry[]): {
|
|
70
|
+
isValid: boolean;
|
|
71
|
+
errors: ValidationError[];
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Diff two CODEOWNERS configurations.
|
|
75
|
+
*
|
|
76
|
+
* Useful for showing what would change before regenerating.
|
|
77
|
+
*
|
|
78
|
+
* @param current - Current CODEOWNERS content
|
|
79
|
+
* @param proposed - Proposed CODEOWNERS content
|
|
80
|
+
* @returns Diff with additions, removals, and changes
|
|
81
|
+
*/
|
|
82
|
+
export interface CodeownersDiff {
|
|
83
|
+
added: CodeownersEntry[];
|
|
84
|
+
removed: CodeownersEntry[];
|
|
85
|
+
changed: Array<{
|
|
86
|
+
from: CodeownersEntry;
|
|
87
|
+
to: CodeownersEntry;
|
|
88
|
+
}>;
|
|
89
|
+
}
|
|
90
|
+
export declare function diffCodeowners(current: string, proposed: string): CodeownersDiff;
|
|
91
|
+
/**
|
|
92
|
+
* Generate CODEOWNERS from STATE.json file.
|
|
93
|
+
*
|
|
94
|
+
* This is the main entry point for CODEOWNERS generation.
|
|
95
|
+
*
|
|
96
|
+
* @param stateJsonPath - Path to STATE.json
|
|
97
|
+
* @param basePath - Root of the FORGE project
|
|
98
|
+
* @param forceOverwrite - Whether to overwrite existing file
|
|
99
|
+
*/
|
|
100
|
+
export declare function generateCodeownersFromState(stateJsonPath: string, basePath?: string, forceOverwrite?: boolean): Promise<void>;
|
|
101
|
+
//# sourceMappingURL=codeowners.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codeowners.d.ts","sourceRoot":"","sources":["../../src/git/codeowners.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAOH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,aAAa,EAAE,CAAC;CACxB;AAcD;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,kBAAkB,GAAG,MAAM,CA+C3E;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CACnC,KAAK,EAAE,kBAAkB,EACzB,QAAQ,GAAE,MAAoC,EAC9C,cAAc,GAAE,OAAe,GAC9B,OAAO,CAAC,IAAI,CAAC,CAuBf;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,EAAE,CAqBlE;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,SAAS,GAAG,iBAAiB,GAAG,eAAe,CAAC;IACtD,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,eAAe,EAAE,CAAC;CAC7B;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,eAAe,EAAE,GAAG;IAC9D,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B,CA8CA;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,eAAe,CAAC;QAAC,EAAE,EAAE,eAAe,CAAA;KAAE,CAAC,CAAC;CAChE;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,cAAc,CAuDhF;AAED;;;;;;;;GAQG;AACH,wBAAsB,2BAA2B,CAC/C,aAAa,EAAE,MAAM,EACrB,QAAQ,GAAE,MAAoC,EAC9C,cAAc,GAAE,OAAe,GAC9B,OAAO,CAAC,IAAI,CAAC,CAMf"}
|