@alxyrgin/agent-forge 1.0.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/LICENSE +21 -0
- package/README.md +166 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +808 -0
- package/dist/index.js.map +1 -0
- package/package.json +61 -0
- package/templates/agents/core/analyst.md.ejs +56 -0
- package/templates/agents/core/architect.md.ejs +64 -0
- package/templates/agents/core/developer.md.ejs +54 -0
- package/templates/agents/core/doc-writer.md.ejs +50 -0
- package/templates/agents/core/progress-tracker.md.ejs +56 -0
- package/templates/agents/core/reviewer.md.ejs +52 -0
- package/templates/agents/core/security-auditor.md.ejs +51 -0
- package/templates/agents/core/unit-tester.md.ejs +56 -0
- package/templates/agents/extra/acceptance-tester.md.ejs +48 -0
- package/templates/agents/extra/integration-tester.md.ejs +49 -0
- package/templates/agents/extra/planner.md.ejs +89 -0
- package/templates/memory/active-context.md.ejs +26 -0
- package/templates/memory/decisions.md.ejs +20 -0
- package/templates/memory/patterns.md.ejs +45 -0
- package/templates/memory/progress.md.ejs +25 -0
- package/templates/memory/project-brief.md.ejs +36 -0
- package/templates/memory/tech-debt.md.ejs +27 -0
- package/templates/memory/tech-stack.md.ejs +23 -0
- package/templates/memory/troubleshooting.md.ejs +28 -0
- package/templates/root/CLAUDE.md.ejs +110 -0
- package/templates/root/settings.json.ejs +14 -0
- package/templates/rules/commit-conventions.md.ejs +35 -0
- package/templates/rules/development-cycle.md.ejs +54 -0
- package/templates/rules/testing-standards.md.ejs +25 -0
- package/templates/skills/core/complete-task/SKILL.md.ejs +37 -0
- package/templates/skills/core/end-session/SKILL.md.ejs +81 -0
- package/templates/skills/core/plan/SKILL.md.ejs +58 -0
- package/templates/skills/core/review/SKILL.md.ejs +29 -0
- package/templates/skills/core/start-session/SKILL.md.ejs +58 -0
- package/templates/skills/core/status/SKILL.md.ejs +38 -0
- package/templates/skills/core/take-task/SKILL.md.ejs +69 -0
- package/templates/tasks/tasks.json.ejs +7 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,808 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
|
|
6
|
+
// src/commands/init.ts
|
|
7
|
+
import chalk from "chalk";
|
|
8
|
+
import ora from "ora";
|
|
9
|
+
|
|
10
|
+
// src/prompts/project-setup.ts
|
|
11
|
+
import inquirer from "inquirer";
|
|
12
|
+
function today() {
|
|
13
|
+
return (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
14
|
+
}
|
|
15
|
+
var STACK_CHOICES = [
|
|
16
|
+
{ name: "Python", value: "python" },
|
|
17
|
+
{ name: "TypeScript / Node.js", value: "typescript" },
|
|
18
|
+
{ name: "Go", value: "go" },
|
|
19
|
+
{ name: "Rust", value: "rust" },
|
|
20
|
+
{ name: "Other", value: "other" }
|
|
21
|
+
];
|
|
22
|
+
var FRAMEWORK_MAP = {
|
|
23
|
+
python: ["FastAPI", "Django", "Flask", "None"],
|
|
24
|
+
typescript: ["Next.js", "Express", "Fastify", "None"],
|
|
25
|
+
go: ["Gin", "Echo", "Chi", "None"],
|
|
26
|
+
rust: ["Actix", "Axum", "Rocket", "None"],
|
|
27
|
+
other: ["None"]
|
|
28
|
+
};
|
|
29
|
+
var TEST_MAP = {
|
|
30
|
+
python: { framework: "pytest", command: "pytest" },
|
|
31
|
+
typescript: { framework: "vitest", command: "npx vitest run" },
|
|
32
|
+
go: { framework: "go test", command: "go test ./..." },
|
|
33
|
+
rust: { framework: "cargo test", command: "cargo test" },
|
|
34
|
+
other: { framework: "custom", command: 'echo "no tests configured"' }
|
|
35
|
+
};
|
|
36
|
+
async function promptProjectSetup(targetDir) {
|
|
37
|
+
const base = await inquirer.prompt([
|
|
38
|
+
{
|
|
39
|
+
type: "input",
|
|
40
|
+
name: "projectName",
|
|
41
|
+
message: "Project name:",
|
|
42
|
+
default: targetDir.split("/").pop()
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
type: "input",
|
|
46
|
+
name: "projectDescription",
|
|
47
|
+
message: "Project description:",
|
|
48
|
+
default: "AI-driven project"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
type: "list",
|
|
52
|
+
name: "stack",
|
|
53
|
+
message: "Technology stack:",
|
|
54
|
+
choices: STACK_CHOICES
|
|
55
|
+
}
|
|
56
|
+
]);
|
|
57
|
+
const frameworks = FRAMEWORK_MAP[base.stack] || ["None"];
|
|
58
|
+
const frameworkAnswer = await inquirer.prompt([
|
|
59
|
+
{
|
|
60
|
+
type: "list",
|
|
61
|
+
name: "framework",
|
|
62
|
+
message: "Framework:",
|
|
63
|
+
choices: frameworks
|
|
64
|
+
}
|
|
65
|
+
]);
|
|
66
|
+
const testDefaults = TEST_MAP[base.stack] || TEST_MAP.other;
|
|
67
|
+
const dirs = await inquirer.prompt([
|
|
68
|
+
{
|
|
69
|
+
type: "input",
|
|
70
|
+
name: "testFramework",
|
|
71
|
+
message: "Test framework:",
|
|
72
|
+
default: testDefaults.framework
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
type: "input",
|
|
76
|
+
name: "testCommand",
|
|
77
|
+
message: "Test command:",
|
|
78
|
+
default: testDefaults.command
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
type: "input",
|
|
82
|
+
name: "srcDir",
|
|
83
|
+
message: "Source directory:",
|
|
84
|
+
default: "src/"
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
type: "input",
|
|
88
|
+
name: "testDir",
|
|
89
|
+
message: "Test directory:",
|
|
90
|
+
default: base.stack === "python" ? "src/tests/" : "tests/"
|
|
91
|
+
}
|
|
92
|
+
]);
|
|
93
|
+
const team = [];
|
|
94
|
+
let addMore = true;
|
|
95
|
+
let first = true;
|
|
96
|
+
while (addMore) {
|
|
97
|
+
const member = await inquirer.prompt([
|
|
98
|
+
{
|
|
99
|
+
type: "input",
|
|
100
|
+
name: "name",
|
|
101
|
+
message: first ? "Team member name:" : "Next team member name (leave empty to stop):",
|
|
102
|
+
default: first ? void 0 : ""
|
|
103
|
+
}
|
|
104
|
+
]);
|
|
105
|
+
if (!member.name) break;
|
|
106
|
+
const details = await inquirer.prompt([
|
|
107
|
+
{
|
|
108
|
+
type: "input",
|
|
109
|
+
name: "role",
|
|
110
|
+
message: `${member.name}'s role:`,
|
|
111
|
+
default: "developer"
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
type: "input",
|
|
115
|
+
name: "email",
|
|
116
|
+
message: `${member.name}'s email:`
|
|
117
|
+
}
|
|
118
|
+
]);
|
|
119
|
+
team.push({ name: member.name, role: details.role, email: details.email });
|
|
120
|
+
first = false;
|
|
121
|
+
if (team.length >= 10) break;
|
|
122
|
+
const cont = await inquirer.prompt([
|
|
123
|
+
{
|
|
124
|
+
type: "confirm",
|
|
125
|
+
name: "addMore",
|
|
126
|
+
message: "Add another team member?",
|
|
127
|
+
default: false
|
|
128
|
+
}
|
|
129
|
+
]);
|
|
130
|
+
addMore = cont.addMore;
|
|
131
|
+
}
|
|
132
|
+
const milestones = [];
|
|
133
|
+
const addMilestones = await inquirer.prompt([
|
|
134
|
+
{
|
|
135
|
+
type: "confirm",
|
|
136
|
+
name: "add",
|
|
137
|
+
message: "Add milestones?",
|
|
138
|
+
default: false
|
|
139
|
+
}
|
|
140
|
+
]);
|
|
141
|
+
if (addMilestones.add) {
|
|
142
|
+
let addMoreMs = true;
|
|
143
|
+
while (addMoreMs) {
|
|
144
|
+
const ms = await inquirer.prompt([
|
|
145
|
+
{
|
|
146
|
+
type: "input",
|
|
147
|
+
name: "name",
|
|
148
|
+
message: "Milestone name:"
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
type: "input",
|
|
152
|
+
name: "deadline",
|
|
153
|
+
message: "Deadline (YYYY-MM-DD):"
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
type: "input",
|
|
157
|
+
name: "description",
|
|
158
|
+
message: "Description (optional):",
|
|
159
|
+
default: ""
|
|
160
|
+
}
|
|
161
|
+
]);
|
|
162
|
+
milestones.push({
|
|
163
|
+
name: ms.name,
|
|
164
|
+
deadline: ms.deadline,
|
|
165
|
+
description: ms.description || void 0
|
|
166
|
+
});
|
|
167
|
+
const cont = await inquirer.prompt([
|
|
168
|
+
{
|
|
169
|
+
type: "confirm",
|
|
170
|
+
name: "addMore",
|
|
171
|
+
message: "Add another milestone?",
|
|
172
|
+
default: false
|
|
173
|
+
}
|
|
174
|
+
]);
|
|
175
|
+
addMoreMs = cont.addMore;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
const config = await inquirer.prompt([
|
|
179
|
+
{
|
|
180
|
+
type: "list",
|
|
181
|
+
name: "agentPreset",
|
|
182
|
+
message: "Agent preset:",
|
|
183
|
+
choices: [
|
|
184
|
+
{ name: "Core (8 agents \u2014 recommended)", value: "core" },
|
|
185
|
+
{ name: "Full (11 agents)", value: "full" },
|
|
186
|
+
{ name: "Minimal (4 agents)", value: "minimal" }
|
|
187
|
+
]
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
type: "list",
|
|
191
|
+
name: "language",
|
|
192
|
+
message: "Instructions language:",
|
|
193
|
+
choices: [
|
|
194
|
+
{ name: "Russian", value: "ru" },
|
|
195
|
+
{ name: "English", value: "en" }
|
|
196
|
+
]
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
type: "list",
|
|
200
|
+
name: "commitStyle",
|
|
201
|
+
message: "Commit style:",
|
|
202
|
+
choices: [
|
|
203
|
+
{ name: "Standard (type(scope): description)", value: "standard" },
|
|
204
|
+
{ name: "Conventional Commits", value: "conventional" }
|
|
205
|
+
]
|
|
206
|
+
}
|
|
207
|
+
]);
|
|
208
|
+
return {
|
|
209
|
+
projectName: base.projectName,
|
|
210
|
+
projectDescription: base.projectDescription,
|
|
211
|
+
stack: base.stack,
|
|
212
|
+
framework: frameworkAnswer.framework,
|
|
213
|
+
testFramework: dirs.testFramework,
|
|
214
|
+
testCommand: dirs.testCommand,
|
|
215
|
+
srcDir: dirs.srcDir,
|
|
216
|
+
testDir: dirs.testDir,
|
|
217
|
+
team,
|
|
218
|
+
milestones,
|
|
219
|
+
agentPreset: config.agentPreset,
|
|
220
|
+
language: config.language,
|
|
221
|
+
commitStyle: config.commitStyle,
|
|
222
|
+
today: today(),
|
|
223
|
+
targetDir
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
function getDefaultContext(targetDir) {
|
|
227
|
+
return {
|
|
228
|
+
projectName: targetDir.split("/").pop() || "my-project",
|
|
229
|
+
projectDescription: "AI-driven project",
|
|
230
|
+
stack: "typescript",
|
|
231
|
+
framework: "None",
|
|
232
|
+
testFramework: "vitest",
|
|
233
|
+
testCommand: "npx vitest run",
|
|
234
|
+
srcDir: "src/",
|
|
235
|
+
testDir: "tests/",
|
|
236
|
+
team: [{ name: "Developer", role: "developer", email: "" }],
|
|
237
|
+
milestones: [],
|
|
238
|
+
agentPreset: "core",
|
|
239
|
+
language: "en",
|
|
240
|
+
commitStyle: "standard",
|
|
241
|
+
today: today(),
|
|
242
|
+
targetDir
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// src/generators/memory-bank.ts
|
|
247
|
+
import path3 from "path";
|
|
248
|
+
|
|
249
|
+
// src/utils/template.ts
|
|
250
|
+
import ejs from "ejs";
|
|
251
|
+
import path2 from "path";
|
|
252
|
+
import { fileURLToPath } from "url";
|
|
253
|
+
|
|
254
|
+
// src/utils/fs.ts
|
|
255
|
+
import fs from "fs-extra";
|
|
256
|
+
import path from "path";
|
|
257
|
+
async function ensureDir(dirPath) {
|
|
258
|
+
await fs.ensureDir(dirPath);
|
|
259
|
+
}
|
|
260
|
+
async function writeFileSafe(filePath, content, overwrite = false) {
|
|
261
|
+
const dir = path.dirname(filePath);
|
|
262
|
+
await fs.ensureDir(dir);
|
|
263
|
+
if (await fs.pathExists(filePath)) {
|
|
264
|
+
if (!overwrite) {
|
|
265
|
+
return "skipped";
|
|
266
|
+
}
|
|
267
|
+
await fs.writeFile(filePath, content, "utf-8");
|
|
268
|
+
return "overwritten";
|
|
269
|
+
}
|
|
270
|
+
await fs.writeFile(filePath, content, "utf-8");
|
|
271
|
+
return "created";
|
|
272
|
+
}
|
|
273
|
+
async function fileExists(filePath) {
|
|
274
|
+
return fs.pathExists(filePath);
|
|
275
|
+
}
|
|
276
|
+
async function readFile(filePath) {
|
|
277
|
+
return fs.readFile(filePath, "utf-8");
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// src/utils/template.ts
|
|
281
|
+
import fs2 from "fs";
|
|
282
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
283
|
+
var __dirname = path2.dirname(__filename);
|
|
284
|
+
var distPath = path2.resolve(__dirname, "../templates");
|
|
285
|
+
var devPath = path2.resolve(__dirname, "../../templates");
|
|
286
|
+
var TEMPLATES_DIR = fs2.existsSync(distPath) ? distPath : devPath;
|
|
287
|
+
async function renderTemplate(templatePath, data) {
|
|
288
|
+
const fullPath = path2.join(TEMPLATES_DIR, templatePath);
|
|
289
|
+
const template = await readFile(fullPath);
|
|
290
|
+
return ejs.render(template, data, {
|
|
291
|
+
filename: fullPath,
|
|
292
|
+
async: false
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// src/generators/memory-bank.ts
|
|
297
|
+
var MEMORY_FILES = [
|
|
298
|
+
"active-context.md",
|
|
299
|
+
"progress.md",
|
|
300
|
+
"project-brief.md",
|
|
301
|
+
"decisions.md",
|
|
302
|
+
"tech-stack.md",
|
|
303
|
+
"tech-debt.md",
|
|
304
|
+
"patterns.md",
|
|
305
|
+
"troubleshooting.md"
|
|
306
|
+
];
|
|
307
|
+
async function generateMemoryBank(ctx, overwrite) {
|
|
308
|
+
const result = {
|
|
309
|
+
filesCreated: [],
|
|
310
|
+
filesSkipped: [],
|
|
311
|
+
errors: []
|
|
312
|
+
};
|
|
313
|
+
const templateData = {
|
|
314
|
+
...ctx,
|
|
315
|
+
defaultBranch: "main"
|
|
316
|
+
};
|
|
317
|
+
for (const file of MEMORY_FILES) {
|
|
318
|
+
try {
|
|
319
|
+
const content = await renderTemplate(`memory/${file}.ejs`, templateData);
|
|
320
|
+
const outputPath = path3.join(ctx.targetDir, "dev-infra/memory", file);
|
|
321
|
+
const status = await writeFileSafe(outputPath, content, overwrite);
|
|
322
|
+
if (status === "skipped") {
|
|
323
|
+
result.filesSkipped.push(outputPath);
|
|
324
|
+
} else {
|
|
325
|
+
result.filesCreated.push(outputPath);
|
|
326
|
+
}
|
|
327
|
+
} catch (err) {
|
|
328
|
+
result.errors.push(`Memory ${file}: ${err}`);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return result;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// src/generators/agents.ts
|
|
335
|
+
import path4 from "path";
|
|
336
|
+
var CORE_AGENTS = [
|
|
337
|
+
"analyst",
|
|
338
|
+
"architect",
|
|
339
|
+
"developer",
|
|
340
|
+
"unit-tester",
|
|
341
|
+
"reviewer",
|
|
342
|
+
"security-auditor",
|
|
343
|
+
"doc-writer",
|
|
344
|
+
"progress-tracker"
|
|
345
|
+
];
|
|
346
|
+
var EXTRA_AGENTS = [
|
|
347
|
+
"planner",
|
|
348
|
+
"integration-tester",
|
|
349
|
+
"acceptance-tester"
|
|
350
|
+
];
|
|
351
|
+
var MINIMAL_AGENTS = [
|
|
352
|
+
"analyst",
|
|
353
|
+
"developer",
|
|
354
|
+
"unit-tester",
|
|
355
|
+
"reviewer"
|
|
356
|
+
];
|
|
357
|
+
function getAgentList(preset) {
|
|
358
|
+
const agents = [];
|
|
359
|
+
const coreList = preset === "minimal" ? MINIMAL_AGENTS : CORE_AGENTS;
|
|
360
|
+
for (const name of coreList) {
|
|
361
|
+
agents.push({ name, dir: "core" });
|
|
362
|
+
}
|
|
363
|
+
if (preset === "full") {
|
|
364
|
+
for (const name of EXTRA_AGENTS) {
|
|
365
|
+
agents.push({ name, dir: "extra" });
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return agents;
|
|
369
|
+
}
|
|
370
|
+
async function generateAgents(ctx, overwrite) {
|
|
371
|
+
const result = {
|
|
372
|
+
filesCreated: [],
|
|
373
|
+
filesSkipped: [],
|
|
374
|
+
errors: []
|
|
375
|
+
};
|
|
376
|
+
const agents = getAgentList(ctx.agentPreset);
|
|
377
|
+
const templateData = {
|
|
378
|
+
...ctx,
|
|
379
|
+
defaultBranch: "main"
|
|
380
|
+
};
|
|
381
|
+
for (const agent of agents) {
|
|
382
|
+
try {
|
|
383
|
+
const content = await renderTemplate(
|
|
384
|
+
`agents/${agent.dir}/${agent.name}.md.ejs`,
|
|
385
|
+
templateData
|
|
386
|
+
);
|
|
387
|
+
const outputPath = path4.join(ctx.targetDir, ".claude/agents", `${agent.name}.md`);
|
|
388
|
+
const status = await writeFileSafe(outputPath, content, overwrite);
|
|
389
|
+
if (status === "skipped") {
|
|
390
|
+
result.filesSkipped.push(outputPath);
|
|
391
|
+
} else {
|
|
392
|
+
result.filesCreated.push(outputPath);
|
|
393
|
+
}
|
|
394
|
+
} catch (err) {
|
|
395
|
+
result.errors.push(`Agent ${agent.name}: ${err}`);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
return result;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// src/generators/skills.ts
|
|
402
|
+
import path5 from "path";
|
|
403
|
+
var SKILLS = [
|
|
404
|
+
"start-session",
|
|
405
|
+
"end-session",
|
|
406
|
+
"take-task",
|
|
407
|
+
"complete-task",
|
|
408
|
+
"status",
|
|
409
|
+
"plan",
|
|
410
|
+
"review"
|
|
411
|
+
];
|
|
412
|
+
async function generateSkills(ctx, overwrite) {
|
|
413
|
+
const result = {
|
|
414
|
+
filesCreated: [],
|
|
415
|
+
filesSkipped: [],
|
|
416
|
+
errors: []
|
|
417
|
+
};
|
|
418
|
+
const templateData = {
|
|
419
|
+
...ctx,
|
|
420
|
+
defaultBranch: "main"
|
|
421
|
+
};
|
|
422
|
+
for (const skill of SKILLS) {
|
|
423
|
+
try {
|
|
424
|
+
const content = await renderTemplate(
|
|
425
|
+
`skills/core/${skill}/SKILL.md.ejs`,
|
|
426
|
+
templateData
|
|
427
|
+
);
|
|
428
|
+
const outputPath = path5.join(ctx.targetDir, `.claude/skills/${skill}`, "SKILL.md");
|
|
429
|
+
const status = await writeFileSafe(outputPath, content, overwrite);
|
|
430
|
+
if (status === "skipped") {
|
|
431
|
+
result.filesSkipped.push(outputPath);
|
|
432
|
+
} else {
|
|
433
|
+
result.filesCreated.push(outputPath);
|
|
434
|
+
}
|
|
435
|
+
} catch (err) {
|
|
436
|
+
result.errors.push(`Skill ${skill}: ${err}`);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
return result;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// src/generators/rules.ts
|
|
443
|
+
import path6 from "path";
|
|
444
|
+
var RULES = [
|
|
445
|
+
"commit-conventions",
|
|
446
|
+
"development-cycle",
|
|
447
|
+
"testing-standards"
|
|
448
|
+
];
|
|
449
|
+
async function generateRules(ctx, overwrite) {
|
|
450
|
+
const result = {
|
|
451
|
+
filesCreated: [],
|
|
452
|
+
filesSkipped: [],
|
|
453
|
+
errors: []
|
|
454
|
+
};
|
|
455
|
+
const templateData = {
|
|
456
|
+
...ctx,
|
|
457
|
+
defaultBranch: "main"
|
|
458
|
+
};
|
|
459
|
+
for (const rule of RULES) {
|
|
460
|
+
try {
|
|
461
|
+
const content = await renderTemplate(`rules/${rule}.md.ejs`, templateData);
|
|
462
|
+
const outputPath = path6.join(ctx.targetDir, ".claude/rules", `${rule}.md`);
|
|
463
|
+
const status = await writeFileSafe(outputPath, content, overwrite);
|
|
464
|
+
if (status === "skipped") {
|
|
465
|
+
result.filesSkipped.push(outputPath);
|
|
466
|
+
} else {
|
|
467
|
+
result.filesCreated.push(outputPath);
|
|
468
|
+
}
|
|
469
|
+
} catch (err) {
|
|
470
|
+
result.errors.push(`Rule ${rule}: ${err}`);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
return result;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// src/generators/claude-md.ts
|
|
477
|
+
import path7 from "path";
|
|
478
|
+
async function generateClaudeMd(ctx, overwrite) {
|
|
479
|
+
const result = {
|
|
480
|
+
filesCreated: [],
|
|
481
|
+
filesSkipped: [],
|
|
482
|
+
errors: []
|
|
483
|
+
};
|
|
484
|
+
const templateData = {
|
|
485
|
+
...ctx,
|
|
486
|
+
defaultBranch: "main"
|
|
487
|
+
};
|
|
488
|
+
try {
|
|
489
|
+
const content = await renderTemplate("root/CLAUDE.md.ejs", templateData);
|
|
490
|
+
const outputPath = path7.join(ctx.targetDir, ".claude/CLAUDE.md");
|
|
491
|
+
const status = await writeFileSafe(outputPath, content, overwrite);
|
|
492
|
+
if (status === "skipped") {
|
|
493
|
+
result.filesSkipped.push(outputPath);
|
|
494
|
+
} else {
|
|
495
|
+
result.filesCreated.push(outputPath);
|
|
496
|
+
}
|
|
497
|
+
} catch (err) {
|
|
498
|
+
result.errors.push(`CLAUDE.md: ${err}`);
|
|
499
|
+
}
|
|
500
|
+
try {
|
|
501
|
+
const content = await renderTemplate("root/settings.json.ejs", templateData);
|
|
502
|
+
const outputPath = path7.join(ctx.targetDir, ".claude/settings.json");
|
|
503
|
+
const status = await writeFileSafe(outputPath, content, overwrite);
|
|
504
|
+
if (status === "skipped") {
|
|
505
|
+
result.filesSkipped.push(outputPath);
|
|
506
|
+
} else {
|
|
507
|
+
result.filesCreated.push(outputPath);
|
|
508
|
+
}
|
|
509
|
+
} catch (err) {
|
|
510
|
+
result.errors.push(`settings.json: ${err}`);
|
|
511
|
+
}
|
|
512
|
+
return result;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// src/generators/infra.ts
|
|
516
|
+
import path8 from "path";
|
|
517
|
+
async function generateInfra(ctx, overwrite) {
|
|
518
|
+
const result = {
|
|
519
|
+
filesCreated: [],
|
|
520
|
+
filesSkipped: [],
|
|
521
|
+
errors: []
|
|
522
|
+
};
|
|
523
|
+
const templateData = {
|
|
524
|
+
...ctx,
|
|
525
|
+
defaultBranch: "main"
|
|
526
|
+
};
|
|
527
|
+
try {
|
|
528
|
+
const content = await renderTemplate("tasks/tasks.json.ejs", templateData);
|
|
529
|
+
const outputPath = path8.join(ctx.targetDir, "dev-infra/tasks/tasks.json");
|
|
530
|
+
const status = await writeFileSafe(outputPath, content, overwrite);
|
|
531
|
+
if (status === "skipped") {
|
|
532
|
+
result.filesSkipped.push(outputPath);
|
|
533
|
+
} else {
|
|
534
|
+
result.filesCreated.push(outputPath);
|
|
535
|
+
}
|
|
536
|
+
} catch (err) {
|
|
537
|
+
result.errors.push(`tasks.json: ${err}`);
|
|
538
|
+
}
|
|
539
|
+
const dirs = [
|
|
540
|
+
"dev-infra/sessions",
|
|
541
|
+
"dev-infra/tests/acceptance",
|
|
542
|
+
"dev-infra/tests/pmi",
|
|
543
|
+
"dev-infra/tests/results"
|
|
544
|
+
];
|
|
545
|
+
for (const dir of dirs) {
|
|
546
|
+
try {
|
|
547
|
+
const dirPath = path8.join(ctx.targetDir, dir);
|
|
548
|
+
await ensureDir(dirPath);
|
|
549
|
+
const gitkeepPath = path8.join(dirPath, ".gitkeep");
|
|
550
|
+
const status = await writeFileSafe(gitkeepPath, "", overwrite);
|
|
551
|
+
if (status === "skipped") {
|
|
552
|
+
result.filesSkipped.push(gitkeepPath);
|
|
553
|
+
} else {
|
|
554
|
+
result.filesCreated.push(gitkeepPath);
|
|
555
|
+
}
|
|
556
|
+
} catch (err) {
|
|
557
|
+
result.errors.push(`Dir ${dir}: ${err}`);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
try {
|
|
561
|
+
const manifest = {
|
|
562
|
+
version: "1.0.0",
|
|
563
|
+
createdAt: ctx.today,
|
|
564
|
+
projectName: ctx.projectName,
|
|
565
|
+
agentPreset: ctx.agentPreset,
|
|
566
|
+
language: ctx.language,
|
|
567
|
+
expectedFiles: getExpectedFiles(ctx)
|
|
568
|
+
};
|
|
569
|
+
const outputPath = path8.join(ctx.targetDir, ".claude-forge.json");
|
|
570
|
+
const status = await writeFileSafe(
|
|
571
|
+
outputPath,
|
|
572
|
+
JSON.stringify(manifest, null, 2) + "\n",
|
|
573
|
+
overwrite
|
|
574
|
+
);
|
|
575
|
+
if (status === "skipped") {
|
|
576
|
+
result.filesSkipped.push(outputPath);
|
|
577
|
+
} else {
|
|
578
|
+
result.filesCreated.push(outputPath);
|
|
579
|
+
}
|
|
580
|
+
} catch (err) {
|
|
581
|
+
result.errors.push(`.claude-forge.json: ${err}`);
|
|
582
|
+
}
|
|
583
|
+
return result;
|
|
584
|
+
}
|
|
585
|
+
function getExpectedFiles(ctx) {
|
|
586
|
+
const files = [
|
|
587
|
+
".claude/CLAUDE.md",
|
|
588
|
+
".claude/settings.json",
|
|
589
|
+
".claude-forge.json",
|
|
590
|
+
"dev-infra/tasks/tasks.json",
|
|
591
|
+
"dev-infra/memory/active-context.md",
|
|
592
|
+
"dev-infra/memory/progress.md",
|
|
593
|
+
"dev-infra/memory/project-brief.md",
|
|
594
|
+
"dev-infra/memory/decisions.md",
|
|
595
|
+
"dev-infra/memory/tech-stack.md",
|
|
596
|
+
"dev-infra/memory/tech-debt.md",
|
|
597
|
+
"dev-infra/memory/patterns.md",
|
|
598
|
+
"dev-infra/memory/troubleshooting.md"
|
|
599
|
+
];
|
|
600
|
+
files.push(
|
|
601
|
+
".claude/rules/commit-conventions.md",
|
|
602
|
+
".claude/rules/development-cycle.md",
|
|
603
|
+
".claude/rules/testing-standards.md"
|
|
604
|
+
);
|
|
605
|
+
const skills = [
|
|
606
|
+
"start-session",
|
|
607
|
+
"end-session",
|
|
608
|
+
"take-task",
|
|
609
|
+
"complete-task",
|
|
610
|
+
"status",
|
|
611
|
+
"plan",
|
|
612
|
+
"review"
|
|
613
|
+
];
|
|
614
|
+
for (const skill of skills) {
|
|
615
|
+
files.push(`.claude/skills/${skill}/SKILL.md`);
|
|
616
|
+
}
|
|
617
|
+
const coreAgents = [
|
|
618
|
+
"analyst",
|
|
619
|
+
"architect",
|
|
620
|
+
"developer",
|
|
621
|
+
"unit-tester",
|
|
622
|
+
"reviewer",
|
|
623
|
+
"security-auditor",
|
|
624
|
+
"doc-writer",
|
|
625
|
+
"progress-tracker"
|
|
626
|
+
];
|
|
627
|
+
const minimalAgents = ["analyst", "developer", "unit-tester", "reviewer"];
|
|
628
|
+
const extraAgents = ["planner", "integration-tester", "acceptance-tester"];
|
|
629
|
+
const agents = ctx.agentPreset === "minimal" ? minimalAgents : ctx.agentPreset === "full" ? [...coreAgents, ...extraAgents] : coreAgents;
|
|
630
|
+
for (const agent of agents) {
|
|
631
|
+
files.push(`.claude/agents/${agent}.md`);
|
|
632
|
+
}
|
|
633
|
+
return files;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// src/generators/index.ts
|
|
637
|
+
async function generateAll(ctx, overwrite = false) {
|
|
638
|
+
const result = {
|
|
639
|
+
filesCreated: [],
|
|
640
|
+
filesSkipped: [],
|
|
641
|
+
errors: []
|
|
642
|
+
};
|
|
643
|
+
const generators = [
|
|
644
|
+
generateClaudeMd,
|
|
645
|
+
generateAgents,
|
|
646
|
+
generateSkills,
|
|
647
|
+
generateRules,
|
|
648
|
+
generateMemoryBank,
|
|
649
|
+
generateInfra
|
|
650
|
+
];
|
|
651
|
+
for (const generator of generators) {
|
|
652
|
+
try {
|
|
653
|
+
const partial = await generator(ctx, overwrite);
|
|
654
|
+
result.filesCreated.push(...partial.filesCreated);
|
|
655
|
+
result.filesSkipped.push(...partial.filesSkipped);
|
|
656
|
+
result.errors.push(...partial.errors);
|
|
657
|
+
} catch (err) {
|
|
658
|
+
result.errors.push(`Generator error: ${err}`);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
return result;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// src/commands/init.ts
|
|
665
|
+
async function initCommand(options) {
|
|
666
|
+
const targetDir = process.cwd();
|
|
667
|
+
console.log();
|
|
668
|
+
console.log(chalk.bold(" agent-forge v1.0.0"));
|
|
669
|
+
console.log(chalk.dim(" AI-driven Development Framework for Claude Code"));
|
|
670
|
+
console.log();
|
|
671
|
+
let ctx;
|
|
672
|
+
if (options.yes) {
|
|
673
|
+
ctx = getDefaultContext(targetDir);
|
|
674
|
+
console.log(chalk.dim(" Using default configuration (--yes)"));
|
|
675
|
+
console.log();
|
|
676
|
+
} else {
|
|
677
|
+
ctx = await promptProjectSetup(targetDir);
|
|
678
|
+
console.log();
|
|
679
|
+
}
|
|
680
|
+
const spinner = ora("Creating project structure...").start();
|
|
681
|
+
try {
|
|
682
|
+
const result = await generateAll(ctx, options.overwrite ?? false);
|
|
683
|
+
spinner.succeed(
|
|
684
|
+
chalk.green(`${result.filesCreated.length} files created`)
|
|
685
|
+
);
|
|
686
|
+
if (result.filesSkipped.length > 0) {
|
|
687
|
+
console.log(
|
|
688
|
+
chalk.yellow(` ${result.filesSkipped.length} files skipped (already exist)`)
|
|
689
|
+
);
|
|
690
|
+
}
|
|
691
|
+
if (result.errors.length > 0) {
|
|
692
|
+
console.log(chalk.red(` ${result.errors.length} errors:`));
|
|
693
|
+
for (const err of result.errors) {
|
|
694
|
+
console.log(chalk.red(` - ${err}`));
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
console.log();
|
|
698
|
+
console.log(chalk.bold(" Next steps:"));
|
|
699
|
+
console.log(chalk.dim(" 1. Open Claude Code in this directory"));
|
|
700
|
+
console.log(chalk.dim(" 2. /start-session \u2014 begin your first session"));
|
|
701
|
+
console.log(chalk.dim(" 3. /plan init \u2014 create tasks from your documentation"));
|
|
702
|
+
console.log();
|
|
703
|
+
} catch (err) {
|
|
704
|
+
spinner.fail(chalk.red("Failed to create project structure"));
|
|
705
|
+
console.error(err);
|
|
706
|
+
process.exit(1);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
// src/commands/doctor.ts
|
|
711
|
+
import path9 from "path";
|
|
712
|
+
import chalk2 from "chalk";
|
|
713
|
+
async function doctorCommand() {
|
|
714
|
+
const targetDir = process.cwd();
|
|
715
|
+
console.log();
|
|
716
|
+
console.log(chalk2.bold(" agent-forge doctor"));
|
|
717
|
+
console.log(chalk2.dim(" Checking project structure integrity..."));
|
|
718
|
+
console.log();
|
|
719
|
+
const manifestPath = path9.join(targetDir, ".claude-forge.json");
|
|
720
|
+
const manifestExists = await fileExists(manifestPath);
|
|
721
|
+
if (!manifestExists) {
|
|
722
|
+
console.log(chalk2.red(" .claude-forge.json not found"));
|
|
723
|
+
console.log(chalk2.dim(" Run `agent-forge init` first to initialize the project."));
|
|
724
|
+
console.log();
|
|
725
|
+
process.exit(1);
|
|
726
|
+
}
|
|
727
|
+
const manifestRaw = await readFile(manifestPath);
|
|
728
|
+
let manifest;
|
|
729
|
+
try {
|
|
730
|
+
manifest = JSON.parse(manifestRaw);
|
|
731
|
+
} catch {
|
|
732
|
+
console.log(chalk2.red(" .claude-forge.json is corrupted"));
|
|
733
|
+
process.exit(1);
|
|
734
|
+
}
|
|
735
|
+
console.log(chalk2.dim(` Project: ${manifest.projectName}`));
|
|
736
|
+
console.log(chalk2.dim(` Version: ${manifest.version}`));
|
|
737
|
+
console.log();
|
|
738
|
+
const checks = [];
|
|
739
|
+
let okCount = 0;
|
|
740
|
+
let failCount = 0;
|
|
741
|
+
for (const file of manifest.expectedFiles) {
|
|
742
|
+
const filePath = path9.join(targetDir, file);
|
|
743
|
+
const exists = await fileExists(filePath);
|
|
744
|
+
const check = {
|
|
745
|
+
name: file,
|
|
746
|
+
path: filePath,
|
|
747
|
+
exists,
|
|
748
|
+
ok: exists
|
|
749
|
+
};
|
|
750
|
+
if (exists) {
|
|
751
|
+
try {
|
|
752
|
+
const content = await readFile(filePath);
|
|
753
|
+
if (content.trim().length === 0) {
|
|
754
|
+
check.ok = false;
|
|
755
|
+
check.message = "File is empty";
|
|
756
|
+
}
|
|
757
|
+
} catch {
|
|
758
|
+
check.ok = false;
|
|
759
|
+
check.message = "Cannot read file";
|
|
760
|
+
}
|
|
761
|
+
} else {
|
|
762
|
+
check.message = "File not found";
|
|
763
|
+
}
|
|
764
|
+
checks.push(check);
|
|
765
|
+
if (check.ok) okCount++;
|
|
766
|
+
else failCount++;
|
|
767
|
+
}
|
|
768
|
+
const categories = {};
|
|
769
|
+
for (const check of checks) {
|
|
770
|
+
const category = check.name.startsWith(".claude/agents") ? "Agents" : check.name.startsWith(".claude/skills") ? "Skills" : check.name.startsWith(".claude/rules") ? "Rules" : check.name.startsWith(".claude/") ? "Claude Config" : check.name.startsWith("dev-infra/memory") ? "Memory Bank" : check.name.startsWith("dev-infra/") ? "Infrastructure" : "Other";
|
|
771
|
+
if (!categories[category]) categories[category] = [];
|
|
772
|
+
categories[category].push(check);
|
|
773
|
+
}
|
|
774
|
+
for (const [category, items] of Object.entries(categories)) {
|
|
775
|
+
const allOk = items.every((c) => c.ok);
|
|
776
|
+
const icon = allOk ? chalk2.green("OK") : chalk2.red("!!");
|
|
777
|
+
console.log(` ${icon} ${chalk2.bold(category)}`);
|
|
778
|
+
for (const item of items) {
|
|
779
|
+
if (item.ok) {
|
|
780
|
+
console.log(chalk2.green(` + ${item.name}`));
|
|
781
|
+
} else {
|
|
782
|
+
console.log(chalk2.red(` - ${item.name} (${item.message})`));
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
console.log();
|
|
786
|
+
}
|
|
787
|
+
console.log(chalk2.bold(" Summary"));
|
|
788
|
+
console.log(chalk2.green(` OK: ${okCount}`));
|
|
789
|
+
if (failCount > 0) {
|
|
790
|
+
console.log(chalk2.red(` Missing/broken: ${failCount}`));
|
|
791
|
+
console.log();
|
|
792
|
+
console.log(chalk2.dim(" Run `agent-forge init --overwrite` to regenerate missing files."));
|
|
793
|
+
} else {
|
|
794
|
+
console.log(chalk2.green(" All checks passed!"));
|
|
795
|
+
}
|
|
796
|
+
console.log();
|
|
797
|
+
if (failCount > 0) {
|
|
798
|
+
process.exit(1);
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
// src/index.ts
|
|
803
|
+
var program = new Command();
|
|
804
|
+
program.name("agent-forge").description("AI-driven Development Framework for Claude Code").version("1.0.0");
|
|
805
|
+
program.command("init").description("Initialize AI-driven development infrastructure in the current project").option("-y, --yes", "Skip prompts and use defaults").option("--overwrite", "Overwrite existing files").action(initCommand);
|
|
806
|
+
program.command("doctor").description("Check integrity of the generated structure").action(doctorCommand);
|
|
807
|
+
program.parse();
|
|
808
|
+
//# sourceMappingURL=index.js.map
|