forge-dev-framework 1.0.1 → 1.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/.claude/commands/forge/README.md +281 -0
- package/.claude/commands/forge/add-phase.md +90 -0
- package/.claude/commands/forge/complete-milestone.md +130 -0
- package/.claude/commands/forge/config.md +115 -0
- package/.claude/commands/forge/convert.md +31 -0
- package/.claude/commands/forge/debug.md +31 -0
- package/.claude/commands/forge/discuss.md +78 -0
- package/.claude/commands/forge/execute.md +85 -0
- package/.claude/commands/forge/generate.md +21 -0
- package/.claude/commands/forge/help.md +18 -0
- package/.claude/commands/forge/init.md +21 -0
- package/.claude/commands/forge/insert-phase.md +99 -0
- package/.claude/commands/forge/new-milestone.md +114 -0
- package/.claude/commands/forge/new-project.md +24 -0
- package/.claude/commands/forge/pause-work.md +111 -0
- package/.claude/commands/forge/plan.md +129 -0
- package/.claude/commands/forge/quick.md +41 -0
- package/.claude/commands/forge/remove-phase.md +92 -0
- package/.claude/commands/forge/resume.md +22 -0
- package/.claude/commands/forge/status.md +87 -0
- package/.claude/commands/forge/team-add.md +24 -0
- package/.claude/commands/forge/team-create.md +22 -0
- package/.claude/commands/forge/team-remove.md +24 -0
- package/.claude/commands/forge/team-start.md +22 -0
- package/.claude/commands/forge/team-view.md +18 -0
- package/.claude/commands/forge/verify.md +95 -0
- package/.claude/hooks/forge-context-cleanup.cjs +79 -0
- package/.claude/hooks/forge-event-guard.cjs +36 -0
- package/.claude/hooks/forge-size-guard.cjs +55 -0
- package/.claude/rules/api-patterns.md +13 -98
- package/.claude/rules/context-efficiency.md +10 -0
- package/.claude/rules/security-baseline.md +18 -204
- package/.claude/rules/testing-standards.md +16 -177
- package/.claude/rules/ui-conventions.md +17 -142
- package/README.md +1 -0
- package/bin/forge.js +5 -3
- package/dist/bin/forge.js +5 -3
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +15 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/convert.d.ts +6 -0
- package/dist/commands/convert.d.ts.map +1 -0
- package/dist/commands/convert.js +132 -0
- package/dist/commands/convert.js.map +1 -0
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/generate.js +3 -2
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/index.d.ts +4 -4
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +4 -4
- package/dist/commands/index.js.map +1 -1
- package/dist/generators/gsd-converter.d.ts +100 -0
- package/dist/generators/gsd-converter.d.ts.map +1 -0
- package/dist/generators/gsd-converter.js +335 -0
- package/dist/generators/gsd-converter.js.map +1 -0
- package/dist/templates/.claude/rules/api-patterns.md.template +212 -0
- package/dist/templates/.claude/rules/security-baseline.md.template +322 -0
- package/dist/templates/.claude/rules/testing-standards.md.template +280 -0
- package/dist/templates/.claude/rules/ui-conventions.md.template +264 -0
- package/dist/templates/.planning/forge.config.json.template +75 -0
- package/dist/templates/CLAUDE.md.template +161 -0
- package/dist/templates/PLAN.md.template +177 -0
- package/dist/templates/PROJECT.md.template +156 -0
- package/dist/templates/REQUIREMENTS.md.template +221 -0
- package/dist/templates/ROADMAP.md.template +130 -0
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -2
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +5 -5
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +5 -5
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/template-client.d.ts.map +1 -1
- package/dist/utils/template-client.js +3 -2
- package/dist/utils/template-client.js.map +1 -1
- package/package.json +6 -4
- package/dist/git/__tests__/worktree.test.d.ts +0 -5
- package/dist/git/__tests__/worktree.test.d.ts.map +0 -1
- package/dist/git/__tests__/worktree.test.js +0 -121
- package/dist/git/__tests__/worktree.test.js.map +0 -1
- package/dist/git/codeowners.d.ts +0 -101
- package/dist/git/codeowners.d.ts.map +0 -1
- package/dist/git/codeowners.js +0 -216
- package/dist/git/codeowners.js.map +0 -1
- package/dist/git/commit.d.ts +0 -135
- package/dist/git/commit.d.ts.map +0 -1
- package/dist/git/commit.js +0 -223
- package/dist/git/commit.js.map +0 -1
- package/dist/git/hooks/commit-msg.d.ts +0 -8
- package/dist/git/hooks/commit-msg.d.ts.map +0 -1
- package/dist/git/hooks/commit-msg.js +0 -34
- package/dist/git/hooks/commit-msg.js.map +0 -1
- package/dist/git/hooks/pre-commit.d.ts +0 -8
- package/dist/git/hooks/pre-commit.d.ts.map +0 -1
- package/dist/git/hooks/pre-commit.js +0 -34
- package/dist/git/hooks/pre-commit.js.map +0 -1
- package/dist/git/pre-commit-hooks.d.ts +0 -117
- package/dist/git/pre-commit-hooks.d.ts.map +0 -1
- package/dist/git/pre-commit-hooks.js +0 -270
- package/dist/git/pre-commit-hooks.js.map +0 -1
- package/dist/git/wipe-protocol.d.ts +0 -281
- package/dist/git/wipe-protocol.d.ts.map +0 -1
- package/dist/git/wipe-protocol.js +0 -237
- package/dist/git/wipe-protocol.js.map +0 -1
- package/dist/git/worktree.d.ts +0 -69
- package/dist/git/worktree.d.ts.map +0 -1
- package/dist/git/worktree.js +0 -202
- package/dist/git/worktree.js.map +0 -1
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GSD to FORGE Project Converter
|
|
3
|
+
*
|
|
4
|
+
* Converts existing GSD projects to FORGE format by:
|
|
5
|
+
* 1. Reading GSD artifacts (PROJECT.md, ROADMAP.md, phase plans)
|
|
6
|
+
* 2. Creating FORGE equivalents (CLAUDE.md, STATE.json)
|
|
7
|
+
* 3. Preserving roadmap and phase structure
|
|
8
|
+
*
|
|
9
|
+
* @private - Not published to NPM
|
|
10
|
+
*/
|
|
11
|
+
import { readFile, writeFile, mkdir, access, readdir } from 'fs/promises';
|
|
12
|
+
import { join, dirname } from 'path';
|
|
13
|
+
import { existsSync } from 'fs';
|
|
14
|
+
/**
|
|
15
|
+
* Detect if current directory is a GSD project
|
|
16
|
+
*/
|
|
17
|
+
export async function detectGSDProject(projectPath) {
|
|
18
|
+
const projectMdPath = join(projectPath, 'PROJECT.md');
|
|
19
|
+
const roadmapMdPath = join(projectPath, 'ROADMAP.md');
|
|
20
|
+
const planningDir = join(projectPath, '.planning');
|
|
21
|
+
const info = {
|
|
22
|
+
name: '',
|
|
23
|
+
hasProjectMd: false,
|
|
24
|
+
hasRoadmapMd: false,
|
|
25
|
+
hasPlanningDir: false,
|
|
26
|
+
phases: [],
|
|
27
|
+
};
|
|
28
|
+
// Check for PROJECT.md
|
|
29
|
+
try {
|
|
30
|
+
await access(projectMdPath);
|
|
31
|
+
info.hasProjectMd = true;
|
|
32
|
+
const content = await readFile(projectMdPath, 'utf-8');
|
|
33
|
+
// Extract project name from first line or title
|
|
34
|
+
const nameMatch = content.match(/^#\s+(.+)$/m) || content.match(/^# Project: (.+)$/m);
|
|
35
|
+
if (nameMatch) {
|
|
36
|
+
info.name = nameMatch[1].trim();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// PROJECT.md doesn't exist
|
|
41
|
+
}
|
|
42
|
+
// Check for ROADMAP.md
|
|
43
|
+
try {
|
|
44
|
+
await access(roadmapMdPath);
|
|
45
|
+
info.hasRoadmapMd = true;
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// ROADMAP.md doesn't exist
|
|
49
|
+
}
|
|
50
|
+
// Check for .planning directory and phases
|
|
51
|
+
try {
|
|
52
|
+
await access(planningDir);
|
|
53
|
+
info.hasPlanningDir = true;
|
|
54
|
+
// List phases
|
|
55
|
+
const phasesDir = join(planningDir, 'phases');
|
|
56
|
+
try {
|
|
57
|
+
await access(phasesDir);
|
|
58
|
+
const entries = await readdir(phasesDir, { withFileTypes: true });
|
|
59
|
+
info.phases = entries
|
|
60
|
+
.filter((e) => e.name.endsWith('.md') || e.isDirectory())
|
|
61
|
+
.map((e) => e.name)
|
|
62
|
+
.sort();
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// phases directory doesn't exist
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// .planning directory doesn't exist
|
|
70
|
+
}
|
|
71
|
+
return info;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Extract project metadata from PROJECT.md
|
|
75
|
+
*/
|
|
76
|
+
export async function extractProjectMetadata(projectPath) {
|
|
77
|
+
const projectMdPath = join(projectPath, 'PROJECT.md');
|
|
78
|
+
const metadata = {
|
|
79
|
+
name: '',
|
|
80
|
+
description: '',
|
|
81
|
+
techStack: [],
|
|
82
|
+
team: [],
|
|
83
|
+
};
|
|
84
|
+
try {
|
|
85
|
+
const content = await readFile(projectMdPath, 'utf-8');
|
|
86
|
+
// Extract name from title
|
|
87
|
+
const nameMatch = content.match(/^#\s+(.+)$/m);
|
|
88
|
+
if (nameMatch) {
|
|
89
|
+
metadata.name = nameMatch[1].trim();
|
|
90
|
+
}
|
|
91
|
+
// Extract description (usually after title)
|
|
92
|
+
const descMatch = content.match(/^#\s+.+\n+(.+)$/m);
|
|
93
|
+
if (descMatch) {
|
|
94
|
+
metadata.description = descMatch[1].trim();
|
|
95
|
+
}
|
|
96
|
+
// Extract tech stack (looks for "Tech Stack" or similar section)
|
|
97
|
+
const techStackMatch = content.match(/##?\s*(?:Tech Stack|Technology Stack|Stack)\n+([\s\S]+?)(?=\n##|\n*$)/i);
|
|
98
|
+
if (techStackMatch) {
|
|
99
|
+
const techLines = techStackMatch[1]
|
|
100
|
+
.split('\n')
|
|
101
|
+
.map((line) => line.replace(/^[\s\-\*]+/, '').trim())
|
|
102
|
+
.filter((line) => line.length > 0);
|
|
103
|
+
metadata.techStack = techLines;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
// PROJECT.md doesn't exist or couldn't be read
|
|
108
|
+
}
|
|
109
|
+
return metadata;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Generate CLAUDE.md from PROJECT.md content
|
|
113
|
+
*/
|
|
114
|
+
export async function generateClaudeMd(projectPath) {
|
|
115
|
+
const projectMdPath = join(projectPath, 'PROJECT.md');
|
|
116
|
+
let projectMdContent = '';
|
|
117
|
+
try {
|
|
118
|
+
projectMdContent = await readFile(projectMdPath, 'utf-8');
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
// PROJECT.md doesn't exist, use template
|
|
122
|
+
}
|
|
123
|
+
const metadata = await extractProjectMetadata(projectPath);
|
|
124
|
+
// Build CLAUDE.md content
|
|
125
|
+
let claudeMd = `# ${metadata.name || 'FORGE Project'}\n\n`;
|
|
126
|
+
claudeMd += `> **Converted from GSD.** This project was migrated from GSD to FORGE.\n`;
|
|
127
|
+
claudeMd += `> Conversion date: ${new Date().toISOString()}\n\n`;
|
|
128
|
+
// Add tech stack if available
|
|
129
|
+
if (metadata.techStack && metadata.techStack.length > 0) {
|
|
130
|
+
claudeMd += `## Tech Stack\n\n`;
|
|
131
|
+
metadata.techStack.forEach((tech) => {
|
|
132
|
+
claudeMd += `- ${tech}\n`;
|
|
133
|
+
});
|
|
134
|
+
claudeMd += `\n`;
|
|
135
|
+
}
|
|
136
|
+
// Add FORGE-specific sections
|
|
137
|
+
claudeMd += `## Core Architecture\n\n`;
|
|
138
|
+
claudeMd += `### Event-Sourced State\n\n`;
|
|
139
|
+
claudeMd += `FORGE uses an **append-only event log** with **single-writer state derivation**:\n\n`;
|
|
140
|
+
claudeMd += `1. All agents write events to \`state/events/\`\n`;
|
|
141
|
+
claudeMd += `2. State Steward (ARCHITECT) merges events → canonical \`STATE.json\`\n`;
|
|
142
|
+
claudeMd += `3. Events are **never modified**, only appended\n`;
|
|
143
|
+
claudeMd += `4. State is **deterministically replayable** from event log\n\n`;
|
|
144
|
+
claudeMd += `### Command System\n\n`;
|
|
145
|
+
claudeMd += `FORGE provides slash commands for Claude Code:\n\n`;
|
|
146
|
+
claudeMd += `\`\`\`\n`;
|
|
147
|
+
claudeMd += `/forge:new-project # Initialize new FORGE project\n`;
|
|
148
|
+
claudeMd += `/forge:init # Initialize FORGE in current directory\n`;
|
|
149
|
+
claudeMd += `/forge:status # Show project progress\n`;
|
|
150
|
+
claudeMd += `/forge:config # View/edit configuration\n`;
|
|
151
|
+
claudeMd += `/forge:discuss # Capture phase context\n`;
|
|
152
|
+
claudeMd += `/forge:plan # Generate task breakdown\n`;
|
|
153
|
+
claudeMd += `/forge:verify # Verify plan against requirements\n`;
|
|
154
|
+
claudeMd += `/forge:execute # Execute phase with agent teams\n`;
|
|
155
|
+
claudeMd += `/forge:generate # Generate artifact from template\n`;
|
|
156
|
+
claudeMd += `/forge:help # Show command reference\n`;
|
|
157
|
+
claudeMd += `\`\`\`\n\n`;
|
|
158
|
+
claudeMd += `### The Wipe Protocol\n\n`;
|
|
159
|
+
claudeMd += `Agent context is **ephemeral**, git is **persistent**:\n\n`;
|
|
160
|
+
claudeMd += `1. **Hydrate** — Agent reads CLAUDE.md + STATE.json + task + contracts\n`;
|
|
161
|
+
claudeMd += `2. **Execute** — Agent writes code, runs tests\n`;
|
|
162
|
+
claudeMd += `3. **Commit** — Atomic git commit (one task = one commit)\n`;
|
|
163
|
+
claudeMd += `4. **Terminate** — Session ends, context wiped\n`;
|
|
164
|
+
claudeMd += `5. **Reincarnate** — Next task starts with fresh context\n\n`;
|
|
165
|
+
claudeMd += `## Hard Rules\n\n`;
|
|
166
|
+
claudeMd += `### 5-6 Task Limit\n\n`;
|
|
167
|
+
claudeMd += `Each teammate gets **maximum 6 atomic tasks per phase**.\n\n`;
|
|
168
|
+
claudeMd += `### Atomic Commits\n\n`;
|
|
169
|
+
claudeMd += `**Every task = one git commit.** No exceptions.\n\n`;
|
|
170
|
+
claudeMd += `Commit format: \`type(scope): description\`\n\n`;
|
|
171
|
+
// Include original PROJECT.md content as appendix
|
|
172
|
+
if (projectMdContent) {
|
|
173
|
+
claudeMd += `\n---\n\n## Original GSD Content\n\n`;
|
|
174
|
+
claudeMd += `The following content was preserved from the original GSD PROJECT.md:\n\n`;
|
|
175
|
+
claudeMd += projectMdContent;
|
|
176
|
+
}
|
|
177
|
+
return claudeMd;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Extract tasks from GSD phase plans and convert to FORGE tasks
|
|
181
|
+
*/
|
|
182
|
+
export async function extractTasksFromPhases(projectPath) {
|
|
183
|
+
const phasesDir = join(projectPath, '.planning', 'phases');
|
|
184
|
+
const tasks = [];
|
|
185
|
+
let currentMilestone = 'v1.0-M1';
|
|
186
|
+
let taskId = 1;
|
|
187
|
+
try {
|
|
188
|
+
await access(phasesDir);
|
|
189
|
+
const entries = await readdir(phasesDir, { withFileTypes: true });
|
|
190
|
+
for (const entry of entries.filter((e) => e.isFile())) {
|
|
191
|
+
const phasePath = join(phasesDir, entry.name);
|
|
192
|
+
const phaseContent = await readFile(phasePath, 'utf-8');
|
|
193
|
+
// Extract tasks from phase plan
|
|
194
|
+
// GSD phase plans typically have tasks as list items or under "Tasks" section
|
|
195
|
+
const taskMatches = phaseContent.matchAll(/^[\s]*(?:[-*+]|\d+\.)\s+\[?\[?(?:task|Task)?\]?\]?\s*(.+?)(?:\s+\[([^\]]+)\])?$/gm);
|
|
196
|
+
for (const match of taskMatches) {
|
|
197
|
+
const taskTitle = match[1]?.trim() || '';
|
|
198
|
+
const taskOwner = match[2]?.trim() || 'unassigned';
|
|
199
|
+
if (taskTitle.length > 0 && !taskTitle.toLowerCase().includes('optional')) {
|
|
200
|
+
tasks.push({
|
|
201
|
+
id: `task-${String(taskId).padStart(3, '0')}`,
|
|
202
|
+
title: taskTitle,
|
|
203
|
+
ownerRole: taskOwner,
|
|
204
|
+
status: 'pending',
|
|
205
|
+
deps: [],
|
|
206
|
+
allowedPaths: [],
|
|
207
|
+
acceptance: [],
|
|
208
|
+
verify: [],
|
|
209
|
+
commit: null,
|
|
210
|
+
requests: [],
|
|
211
|
+
priority: 5,
|
|
212
|
+
blockedBy: [],
|
|
213
|
+
blocks: [],
|
|
214
|
+
assignedTo: null,
|
|
215
|
+
createdAt: new Date().toISOString(),
|
|
216
|
+
startedAt: null,
|
|
217
|
+
completedAt: null,
|
|
218
|
+
});
|
|
219
|
+
taskId++;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
// No phases directory or couldn't read
|
|
226
|
+
}
|
|
227
|
+
return { currentMilestone, tasks };
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Create initial STATE.json for FORGE project
|
|
231
|
+
*/
|
|
232
|
+
export async function createForgeState(projectName, tasks) {
|
|
233
|
+
const projectState = {
|
|
234
|
+
name: projectName,
|
|
235
|
+
status: 'in_progress',
|
|
236
|
+
currentMilestone: 'v1.0-M1',
|
|
237
|
+
currentPhase: 1,
|
|
238
|
+
updatedAt: new Date().toISOString(),
|
|
239
|
+
};
|
|
240
|
+
return {
|
|
241
|
+
project: projectState,
|
|
242
|
+
tasks,
|
|
243
|
+
contracts: [],
|
|
244
|
+
eventsPointer: 'state/events/',
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Main conversion function
|
|
249
|
+
*/
|
|
250
|
+
export async function convertGSDToForge(projectPath, options = {}) {
|
|
251
|
+
const result = {
|
|
252
|
+
success: false,
|
|
253
|
+
message: '',
|
|
254
|
+
filesCreated: [],
|
|
255
|
+
warnings: [],
|
|
256
|
+
};
|
|
257
|
+
const log = (msg) => {
|
|
258
|
+
if (options.verbose) {
|
|
259
|
+
console.log(`[convert] ${msg}`);
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
// Detect GSD project
|
|
263
|
+
log('Detecting GSD project...');
|
|
264
|
+
const gsdInfo = await detectGSDProject(projectPath);
|
|
265
|
+
if (!gsdInfo.hasProjectMd && !gsdInfo.hasPlanningDir) {
|
|
266
|
+
result.message = 'Not a GSD project: No PROJECT.md or .planning/ directory found.';
|
|
267
|
+
return result;
|
|
268
|
+
}
|
|
269
|
+
log(`Found GSD project: ${gsdInfo.name || projectPath}`);
|
|
270
|
+
log(` - PROJECT.md: ${gsdInfo.hasProjectMd}`);
|
|
271
|
+
log(` - ROADMAP.md: ${gsdInfo.hasRoadmapMd}`);
|
|
272
|
+
log(` - Phases: ${gsdInfo.phases.length}`);
|
|
273
|
+
if (options.dryRun) {
|
|
274
|
+
result.success = true;
|
|
275
|
+
result.message = 'Dry run completed. No files modified.';
|
|
276
|
+
return result;
|
|
277
|
+
}
|
|
278
|
+
// Create FORGE directory structure
|
|
279
|
+
const stateDir = join(projectPath, 'state');
|
|
280
|
+
const eventsDir = join(stateDir, 'events');
|
|
281
|
+
const contractsDir = join(projectPath, 'contracts');
|
|
282
|
+
const claudeRulesDir = join(projectPath, '.claude', 'rules');
|
|
283
|
+
for (const dir of [stateDir, eventsDir, contractsDir, claudeRulesDir]) {
|
|
284
|
+
if (!existsSync(dir)) {
|
|
285
|
+
await mkdir(dir, { recursive: true });
|
|
286
|
+
result.filesCreated.push(dir);
|
|
287
|
+
log(`Created directory: ${dir}`);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
// Generate CLAUDE.md
|
|
291
|
+
log('Generating CLAUDE.md...');
|
|
292
|
+
const claudeMd = await generateClaudeMd(projectPath);
|
|
293
|
+
const claudeMdPath = join(projectPath, 'CLAUDE.md');
|
|
294
|
+
await writeFile(claudeMdPath, claudeMd, 'utf-8');
|
|
295
|
+
result.filesCreated.push(claudeMdPath);
|
|
296
|
+
log(`Created: ${claudeMdPath}`);
|
|
297
|
+
// Extract tasks and create STATE.json
|
|
298
|
+
log('Extracting tasks from phase plans...');
|
|
299
|
+
const { currentMilestone, tasks } = await extractTasksFromPhases(projectPath);
|
|
300
|
+
const projectName = gsdInfo.name || 'Forge Project';
|
|
301
|
+
log(`Extracted ${tasks.length} tasks from ${gsdInfo.phases.length} phase plans`);
|
|
302
|
+
const forgeState = await createForgeState(projectName, tasks);
|
|
303
|
+
const stateJsonPath = join(stateDir, 'STATE.json');
|
|
304
|
+
await writeFile(stateJsonPath, JSON.stringify(forgeState, null, 2), 'utf-8');
|
|
305
|
+
result.filesCreated.push(stateJsonPath);
|
|
306
|
+
log(`Created: ${stateJsonPath}`);
|
|
307
|
+
// Create forge.config.json
|
|
308
|
+
log('Creating forge.config.json...');
|
|
309
|
+
const config = {
|
|
310
|
+
mode: 'interactive',
|
|
311
|
+
depth: 'standard',
|
|
312
|
+
maxTeammates: 5,
|
|
313
|
+
taskLimit: 6,
|
|
314
|
+
conventionalCommits: true,
|
|
315
|
+
worktreeIsolation: true,
|
|
316
|
+
convertedFrom: 'GSD',
|
|
317
|
+
convertedAt: new Date().toISOString(),
|
|
318
|
+
};
|
|
319
|
+
const configPath = join(projectPath, '.planning', 'forge.config.json');
|
|
320
|
+
await mkdir(dirname(configPath), { recursive: true });
|
|
321
|
+
await writeFile(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
322
|
+
result.filesCreated.push(configPath);
|
|
323
|
+
log(`Created: ${configPath}`);
|
|
324
|
+
// Warnings
|
|
325
|
+
if (!gsdInfo.hasRoadmapMd) {
|
|
326
|
+
result.warnings.push('No ROADMAP.md found. You may need to create one manually.');
|
|
327
|
+
}
|
|
328
|
+
if (tasks.length === 0) {
|
|
329
|
+
result.warnings.push('No tasks found in phase plans. You may need to run `forge plan <phase>` to create tasks.');
|
|
330
|
+
}
|
|
331
|
+
result.success = true;
|
|
332
|
+
result.message = `Successfully converted GSD project to FORGE. Created ${result.filesCreated.length} files.`;
|
|
333
|
+
return result;
|
|
334
|
+
}
|
|
335
|
+
//# sourceMappingURL=gsd-converter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gsd-converter.js","sourceRoot":"","sources":["../../src/generators/gsd-converter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# API Design Patterns — {{projectName}}
|
|
2
|
+
|
|
3
|
+
> **Scope:** Backend API development | **Loaded On-Demand**
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## RESTful Conventions
|
|
8
|
+
|
|
9
|
+
### URL Structure
|
|
10
|
+
{{#if apiUrlStructure}}
|
|
11
|
+
{{apiUrlStructure}}
|
|
12
|
+
{{else}}
|
|
13
|
+
- Use kebab-case for resource names: `/api/v1/user-profiles`
|
|
14
|
+
- Use plural for collections: `/api/v1/users` (not `/api/v1/user`)
|
|
15
|
+
- Nest resources logically: `/api/v1/users/{userId}/posts`
|
|
16
|
+
{{/if}}
|
|
17
|
+
|
|
18
|
+
### HTTP Methods
|
|
19
|
+
{{#if httpMethods}}
|
|
20
|
+
{{#each httpMethods}}
|
|
21
|
+
- **{{this.method}}** {{this.usage}}
|
|
22
|
+
{{/each}}
|
|
23
|
+
{{else}}
|
|
24
|
+
- **GET** — Retrieve resources (no side effects)
|
|
25
|
+
- **POST** — Create new resources
|
|
26
|
+
- **PATCH** — Partial updates (preferred over PUT)
|
|
27
|
+
- **PUT** — Full replacement (rarely used)
|
|
28
|
+
- **DELETE** — Resource deletion
|
|
29
|
+
{{/if}}
|
|
30
|
+
|
|
31
|
+
### Response Format
|
|
32
|
+
{{#if responseFormat}}
|
|
33
|
+
{{responseFormat}}
|
|
34
|
+
{{else}}
|
|
35
|
+
Always return consistent JSON structure:
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"data": { ... },
|
|
40
|
+
"meta": {
|
|
41
|
+
"page": 1,
|
|
42
|
+
"perPage": 20,
|
|
43
|
+
"total": 100
|
|
44
|
+
},
|
|
45
|
+
"errors": null
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
{{/if}}
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Error Handling
|
|
53
|
+
|
|
54
|
+
### Error Response Structure
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"error": {
|
|
58
|
+
"code": "VALIDATION_FAILED",
|
|
59
|
+
"message": "User-friendly message",
|
|
60
|
+
"details": [
|
|
61
|
+
{
|
|
62
|
+
"field": "email",
|
|
63
|
+
"message": "Invalid email format"
|
|
64
|
+
}
|
|
65
|
+
],
|
|
66
|
+
"requestId": "req_abc123"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### HTTP Status Codes
|
|
72
|
+
{{#if errorCodes}}
|
|
73
|
+
{{#each errorCodes}}
|
|
74
|
+
- **{{this.code}}** — {{this.description}}
|
|
75
|
+
{{/each}}
|
|
76
|
+
{{else}}
|
|
77
|
+
- **200** — Success
|
|
78
|
+
- **201** — Created
|
|
79
|
+
- **204** — No Content
|
|
80
|
+
- **400** — Bad Request (validation errors)
|
|
81
|
+
- **401** — Unauthorized (not logged in)
|
|
82
|
+
- **403** — Forbidden (logged in, no permission)
|
|
83
|
+
- **404** — Not Found
|
|
84
|
+
- **409** — Conflict (duplicate, state mismatch)
|
|
85
|
+
- **422** — Unprocessable Entity
|
|
86
|
+
- **429** — Too Many Requests (rate limit)
|
|
87
|
+
- **500** — Internal Server Error
|
|
88
|
+
- **503** — Service Unavailable
|
|
89
|
+
{{/if}}
|
|
90
|
+
|
|
91
|
+
### Error Codes Naming
|
|
92
|
+
{{#if errorNaming}}
|
|
93
|
+
Use {{errorNaming}}
|
|
94
|
+
{{else}}
|
|
95
|
+
Use SCREAMING_SNAKE_CASE for error codes:
|
|
96
|
+
- `VALIDATION_FAILED`
|
|
97
|
+
- `AUTHENTICATION_REQUIRED`
|
|
98
|
+
- `RATE_LIMIT_EXCEEDED`
|
|
99
|
+
- `RESOURCE_NOT_FOUND`
|
|
100
|
+
{{/if}}
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Authentication & Authorization
|
|
105
|
+
|
|
106
|
+
{{#if authPatterns}}
|
|
107
|
+
{{authPatterns}}
|
|
108
|
+
{{else}}
|
|
109
|
+
### Authentication
|
|
110
|
+
- Use JWT tokens with httpOnly cookies
|
|
111
|
+
- Include `expiresIn` claim
|
|
112
|
+
- Refresh token endpoint: `POST /api/v1/auth/refresh`
|
|
113
|
+
|
|
114
|
+
### Authorization
|
|
115
|
+
- Check permissions at route level
|
|
116
|
+
- Use role-based access control (RBAC)
|
|
117
|
+
- Return 403 for permission errors (not 401)
|
|
118
|
+
{{/if}}
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Pagination
|
|
123
|
+
|
|
124
|
+
### Standard Pagination
|
|
125
|
+
{{#if pagination}}
|
|
126
|
+
{{pagination}}
|
|
127
|
+
{{else}}
|
|
128
|
+
Default: page-based pagination
|
|
129
|
+
```
|
|
130
|
+
GET /api/v1/users?page=1&perPage=20
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Response:
|
|
134
|
+
```json
|
|
135
|
+
{
|
|
136
|
+
"data": [...],
|
|
137
|
+
"meta": {
|
|
138
|
+
"page": 1,
|
|
139
|
+
"perPage": 20,
|
|
140
|
+
"totalPages": 5,
|
|
141
|
+
"total": 100
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
{{/if}}
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Rate Limiting
|
|
150
|
+
|
|
151
|
+
{{#if rateLimiting}}
|
|
152
|
+
{{rateLimiting}}
|
|
153
|
+
{{else}}
|
|
154
|
+
- Standard: 100 requests/minute per IP
|
|
155
|
+
- Authenticated: 1000 requests/minute per user
|
|
156
|
+
- Headers returned:
|
|
157
|
+
- `X-RateLimit-Limit`
|
|
158
|
+
- `X-RateLimit-Remaining`
|
|
159
|
+
- `X-RateLimit-Reset`
|
|
160
|
+
{{/if}}
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Versioning
|
|
165
|
+
|
|
166
|
+
{{#if apiVersioning}}
|
|
167
|
+
{{apiVersioning}}
|
|
168
|
+
{{else}}
|
|
169
|
+
- URL-based versioning: `/api/v1/`, `/api/v2/`
|
|
170
|
+
- Maintain backward compatibility for at least one major version
|
|
171
|
+
- Document deprecation timeline
|
|
172
|
+
{{/if}}
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Validation
|
|
177
|
+
|
|
178
|
+
{{#if validationRules}}
|
|
179
|
+
{{validationRules}}
|
|
180
|
+
{{else}}
|
|
181
|
+
### Request Validation
|
|
182
|
+
- Validate all inputs at handler boundary
|
|
183
|
+
- Return detailed field-level errors
|
|
184
|
+
- Use Zod or similar schema validation
|
|
185
|
+
|
|
186
|
+
### Response Validation
|
|
187
|
+
- Validate contracts against OpenAPI schema
|
|
188
|
+
- Type-safe client generation from OpenAPI
|
|
189
|
+
{{/if}}
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## OpenAPI Contract Requirements
|
|
194
|
+
|
|
195
|
+
{{#if openApiRequirements}}
|
|
196
|
+
{{openApiRequirements}}
|
|
197
|
+
{{else}}
|
|
198
|
+
Every API must have:
|
|
199
|
+
1. OpenAPI 3.1 spec in `/contracts/`
|
|
200
|
+
2. All endpoints documented with:
|
|
201
|
+
- Summary and description
|
|
202
|
+
- Request/response schemas
|
|
203
|
+
- Error responses
|
|
204
|
+
- Authentication requirements
|
|
205
|
+
3. Auto-generated TypeScript types
|
|
206
|
+
4. Example requests/responses
|
|
207
|
+
{{/if}}
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
> **Token Budget:** ~1000 tokens max
|
|
212
|
+
> **Loaded On-Demand** — Only when working on API code
|