@arvorco/relentless 0.1.26 → 0.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/skills/constitution/SKILL.md +1 -1
- package/.claude/skills/plan/SKILL.md +1 -1
- package/.claude/skills/specify/SKILL.md +1 -1
- package/package.json +1 -1
- package/src/agents/amp.ts +26 -13
- package/src/agents/codex.ts +35 -1
- package/src/agents/droid.ts +35 -1
- package/src/agents/opencode.ts +35 -1
- package/src/init/scaffolder.ts +344 -118
- package/.claude/skills/constitution/templates/constitution-template.md +0 -228
- package/templates/plan.md +0 -273
- /package/.claude/skills/checklist/templates/{checklist-template.md → checklist.md} +0 -0
- /package/{templates → .claude/skills/constitution/templates}/constitution.md +0 -0
- /package/{templates → .claude/skills/constitution/templates}/prompt.md +0 -0
- /package/.claude/skills/plan/templates/{plan-template.md → plan.md} +0 -0
- /package/.claude/skills/specify/templates/{spec-template.md → spec.md} +0 -0
- /package/.claude/skills/tasks/templates/{tasks-template.md → tasks.md} +0 -0
|
@@ -54,7 +54,7 @@ Ask essential questions about the project:
|
|
|
54
54
|
|
|
55
55
|
## Step 2: Generate Constitution
|
|
56
56
|
|
|
57
|
-
Load the template from `templates/constitution
|
|
57
|
+
Load the template from `templates/constitution.md` and:
|
|
58
58
|
|
|
59
59
|
1. Replace all `[PLACEHOLDER]` tokens with concrete values from user answers
|
|
60
60
|
2. Add/remove principles based on project needs (template is a starting point)
|
|
@@ -48,7 +48,7 @@ This will output JSON with:
|
|
|
48
48
|
|
|
49
49
|
## Step 3: Generate Specification
|
|
50
50
|
|
|
51
|
-
Using the template at `templates/spec
|
|
51
|
+
Using the template at `templates/spec.md`, create a specification with:
|
|
52
52
|
|
|
53
53
|
### Required Sections:
|
|
54
54
|
|
package/package.json
CHANGED
package/src/agents/amp.ts
CHANGED
|
@@ -94,22 +94,35 @@ export const ampAdapter: AgentAdapter = {
|
|
|
94
94
|
|
|
95
95
|
async installSkills(projectPath: string): Promise<void> {
|
|
96
96
|
// Amp can install skills globally via amp skill add
|
|
97
|
-
// For project-local, we copy to the project
|
|
97
|
+
// For project-local, we copy to the project's .amp/skills/
|
|
98
98
|
const skillsDir = `${projectPath}/.amp/skills`;
|
|
99
99
|
await Bun.spawn(["mkdir", "-p", skillsDir]).exited;
|
|
100
100
|
|
|
101
101
|
const relentlessRoot = import.meta.dir.replace("/src/agents", "");
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
"
|
|
110
|
-
"
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
102
|
+
const sourceSkillsDir = `${relentlessRoot}/.claude/skills`;
|
|
103
|
+
|
|
104
|
+
// Copy all skills
|
|
105
|
+
const skills = [
|
|
106
|
+
"prd",
|
|
107
|
+
"relentless",
|
|
108
|
+
"constitution",
|
|
109
|
+
"specify",
|
|
110
|
+
"plan",
|
|
111
|
+
"tasks",
|
|
112
|
+
"checklist",
|
|
113
|
+
"clarify",
|
|
114
|
+
"analyze",
|
|
115
|
+
"implement",
|
|
116
|
+
"taskstoissues",
|
|
117
|
+
];
|
|
118
|
+
|
|
119
|
+
for (const skill of skills) {
|
|
120
|
+
await Bun.spawn([
|
|
121
|
+
"cp",
|
|
122
|
+
"-r",
|
|
123
|
+
`${sourceSkillsDir}/${skill}`,
|
|
124
|
+
`${skillsDir}/`,
|
|
125
|
+
]).exited;
|
|
126
|
+
}
|
|
114
127
|
},
|
|
115
128
|
};
|
package/src/agents/codex.ts
CHANGED
|
@@ -10,7 +10,8 @@ import type { AgentAdapter, AgentResult, InvokeOptions, RateLimitInfo } from "./
|
|
|
10
10
|
export const codexAdapter: AgentAdapter = {
|
|
11
11
|
name: "codex",
|
|
12
12
|
displayName: "OpenAI Codex",
|
|
13
|
-
hasSkillSupport:
|
|
13
|
+
hasSkillSupport: true,
|
|
14
|
+
skillInstallCommand: "codex skill add <skill-name>",
|
|
14
15
|
|
|
15
16
|
async isInstalled(): Promise<boolean> {
|
|
16
17
|
try {
|
|
@@ -86,4 +87,37 @@ export const codexAdapter: AgentAdapter = {
|
|
|
86
87
|
|
|
87
88
|
return { limited: false };
|
|
88
89
|
},
|
|
90
|
+
|
|
91
|
+
async installSkills(projectPath: string): Promise<void> {
|
|
92
|
+
// Codex uses .codex/skills/ for project-level skills
|
|
93
|
+
const skillsDir = `${projectPath}/.codex/skills`;
|
|
94
|
+
await Bun.spawn(["mkdir", "-p", skillsDir]).exited;
|
|
95
|
+
|
|
96
|
+
const relentlessRoot = import.meta.dir.replace("/src/agents", "");
|
|
97
|
+
const sourceSkillsDir = `${relentlessRoot}/.claude/skills`;
|
|
98
|
+
|
|
99
|
+
// Copy all skills
|
|
100
|
+
const skills = [
|
|
101
|
+
"prd",
|
|
102
|
+
"relentless",
|
|
103
|
+
"constitution",
|
|
104
|
+
"specify",
|
|
105
|
+
"plan",
|
|
106
|
+
"tasks",
|
|
107
|
+
"checklist",
|
|
108
|
+
"clarify",
|
|
109
|
+
"analyze",
|
|
110
|
+
"implement",
|
|
111
|
+
"taskstoissues",
|
|
112
|
+
];
|
|
113
|
+
|
|
114
|
+
for (const skill of skills) {
|
|
115
|
+
await Bun.spawn([
|
|
116
|
+
"cp",
|
|
117
|
+
"-r",
|
|
118
|
+
`${sourceSkillsDir}/${skill}`,
|
|
119
|
+
`${skillsDir}/`,
|
|
120
|
+
]).exited;
|
|
121
|
+
}
|
|
122
|
+
},
|
|
89
123
|
};
|
package/src/agents/droid.ts
CHANGED
|
@@ -10,7 +10,8 @@ import type { AgentAdapter, AgentResult, InvokeOptions, RateLimitInfo } from "./
|
|
|
10
10
|
export const droidAdapter: AgentAdapter = {
|
|
11
11
|
name: "droid",
|
|
12
12
|
displayName: "Factory Droid",
|
|
13
|
-
hasSkillSupport:
|
|
13
|
+
hasSkillSupport: true,
|
|
14
|
+
skillInstallCommand: "droid skill install <skill-name>",
|
|
14
15
|
|
|
15
16
|
async isInstalled(): Promise<boolean> {
|
|
16
17
|
try {
|
|
@@ -87,4 +88,37 @@ export const droidAdapter: AgentAdapter = {
|
|
|
87
88
|
|
|
88
89
|
return { limited: false };
|
|
89
90
|
},
|
|
91
|
+
|
|
92
|
+
async installSkills(projectPath: string): Promise<void> {
|
|
93
|
+
// Factory uses .factory/skills/ for skills (PLURAL)
|
|
94
|
+
const skillsDir = `${projectPath}/.factory/skills`;
|
|
95
|
+
await Bun.spawn(["mkdir", "-p", skillsDir]).exited;
|
|
96
|
+
|
|
97
|
+
const relentlessRoot = import.meta.dir.replace("/src/agents", "");
|
|
98
|
+
const sourceSkillsDir = `${relentlessRoot}/.claude/skills`;
|
|
99
|
+
|
|
100
|
+
// Copy all skills
|
|
101
|
+
const skills = [
|
|
102
|
+
"prd",
|
|
103
|
+
"relentless",
|
|
104
|
+
"constitution",
|
|
105
|
+
"specify",
|
|
106
|
+
"plan",
|
|
107
|
+
"tasks",
|
|
108
|
+
"checklist",
|
|
109
|
+
"clarify",
|
|
110
|
+
"analyze",
|
|
111
|
+
"implement",
|
|
112
|
+
"taskstoissues",
|
|
113
|
+
];
|
|
114
|
+
|
|
115
|
+
for (const skill of skills) {
|
|
116
|
+
await Bun.spawn([
|
|
117
|
+
"cp",
|
|
118
|
+
"-r",
|
|
119
|
+
`${sourceSkillsDir}/${skill}`,
|
|
120
|
+
`${skillsDir}/`,
|
|
121
|
+
]).exited;
|
|
122
|
+
}
|
|
123
|
+
},
|
|
90
124
|
};
|
package/src/agents/opencode.ts
CHANGED
|
@@ -10,7 +10,8 @@ import type { AgentAdapter, AgentResult, InvokeOptions, RateLimitInfo } from "./
|
|
|
10
10
|
export const opencodeAdapter: AgentAdapter = {
|
|
11
11
|
name: "opencode",
|
|
12
12
|
displayName: "OpenCode",
|
|
13
|
-
hasSkillSupport:
|
|
13
|
+
hasSkillSupport: true,
|
|
14
|
+
skillInstallCommand: "opencode skill add <skill-name>",
|
|
14
15
|
|
|
15
16
|
async isInstalled(): Promise<boolean> {
|
|
16
17
|
try {
|
|
@@ -85,4 +86,37 @@ export const opencodeAdapter: AgentAdapter = {
|
|
|
85
86
|
|
|
86
87
|
return { limited: false };
|
|
87
88
|
},
|
|
89
|
+
|
|
90
|
+
async installSkills(projectPath: string): Promise<void> {
|
|
91
|
+
// OpenCode uses .opencode/skill/ (SINGULAR!) for skills
|
|
92
|
+
const skillsDir = `${projectPath}/.opencode/skill`;
|
|
93
|
+
await Bun.spawn(["mkdir", "-p", skillsDir]).exited;
|
|
94
|
+
|
|
95
|
+
const relentlessRoot = import.meta.dir.replace("/src/agents", "");
|
|
96
|
+
const sourceSkillsDir = `${relentlessRoot}/.claude/skills`;
|
|
97
|
+
|
|
98
|
+
// Copy all skills
|
|
99
|
+
const skills = [
|
|
100
|
+
"prd",
|
|
101
|
+
"relentless",
|
|
102
|
+
"constitution",
|
|
103
|
+
"specify",
|
|
104
|
+
"plan",
|
|
105
|
+
"tasks",
|
|
106
|
+
"checklist",
|
|
107
|
+
"clarify",
|
|
108
|
+
"analyze",
|
|
109
|
+
"implement",
|
|
110
|
+
"taskstoissues",
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
for (const skill of skills) {
|
|
114
|
+
await Bun.spawn([
|
|
115
|
+
"cp",
|
|
116
|
+
"-r",
|
|
117
|
+
`${sourceSkillsDir}/${skill}`,
|
|
118
|
+
`${skillsDir}/`,
|
|
119
|
+
]).exited;
|
|
120
|
+
}
|
|
121
|
+
},
|
|
88
122
|
};
|
package/src/init/scaffolder.ts
CHANGED
|
@@ -55,76 +55,6 @@ const RELENTLESS_FILES: Record<string, () => string> = {
|
|
|
55
55
|
"config.json": () => JSON.stringify(DEFAULT_CONFIG, null, 2),
|
|
56
56
|
};
|
|
57
57
|
|
|
58
|
-
/**
|
|
59
|
-
* Files that should NEVER be overwritten, even with -f flag
|
|
60
|
-
* These are personalized files that users customize for their project
|
|
61
|
-
*/
|
|
62
|
-
const PROTECTED_FILES: Record<string, () => string> = {
|
|
63
|
-
"prompt.md": () => PROMPT_TEMPLATE,
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
const PROMPT_TEMPLATE = `# Relentless Agent Instructions
|
|
67
|
-
|
|
68
|
-
You are an autonomous coding agent. Follow these instructions exactly.
|
|
69
|
-
|
|
70
|
-
**⚠️ This is a generic template. Personalize it for your project using:**
|
|
71
|
-
\`\`\`bash
|
|
72
|
-
/relentless.prompt
|
|
73
|
-
\`\`\`
|
|
74
|
-
|
|
75
|
-
---
|
|
76
|
-
|
|
77
|
-
## Your Task (Per Iteration)
|
|
78
|
-
|
|
79
|
-
1. Read \`relentless/features/<feature>/prd.json\`
|
|
80
|
-
2. Read \`relentless/features/<feature>/progress.txt\`
|
|
81
|
-
3. Check you're on the correct branch from PRD \`branchName\`
|
|
82
|
-
4. Pick the **highest priority** story where \`passes: false\`
|
|
83
|
-
5. Review existing code to understand patterns
|
|
84
|
-
6. Implement the story
|
|
85
|
-
7. Run quality checks (typecheck, lint, test)
|
|
86
|
-
8. If ALL checks pass, commit: \`feat: [Story ID] - [Story Title]\`
|
|
87
|
-
9. Update PRD: set \`passes: true\`
|
|
88
|
-
10. Append progress to \`progress.txt\`
|
|
89
|
-
|
|
90
|
-
---
|
|
91
|
-
|
|
92
|
-
## Quality Requirements
|
|
93
|
-
|
|
94
|
-
Before marking a story complete:
|
|
95
|
-
- [ ] All quality checks pass (typecheck, lint, test)
|
|
96
|
-
- [ ] Zero errors and zero warnings
|
|
97
|
-
- [ ] No debug code (console.log, debugger)
|
|
98
|
-
- [ ] No unused imports or variables
|
|
99
|
-
- [ ] Follows existing patterns
|
|
100
|
-
|
|
101
|
-
---
|
|
102
|
-
|
|
103
|
-
## Progress Report Format
|
|
104
|
-
|
|
105
|
-
APPEND to progress.txt:
|
|
106
|
-
\`\`\`
|
|
107
|
-
## [Date/Time] - [Story ID]
|
|
108
|
-
- What was implemented
|
|
109
|
-
- Files changed
|
|
110
|
-
- Learnings for future iterations
|
|
111
|
-
---
|
|
112
|
-
\`\`\`
|
|
113
|
-
|
|
114
|
-
---
|
|
115
|
-
|
|
116
|
-
## Stop Condition
|
|
117
|
-
|
|
118
|
-
After completing a story, check if ALL stories have \`passes: true\`.
|
|
119
|
-
|
|
120
|
-
If ALL complete:
|
|
121
|
-
\`\`\`
|
|
122
|
-
<promise>COMPLETE</promise>
|
|
123
|
-
\`\`\`
|
|
124
|
-
|
|
125
|
-
Otherwise, end normally (next iteration continues).
|
|
126
|
-
`;
|
|
127
|
-
|
|
128
58
|
/**
|
|
129
59
|
* Default progress.txt content for a new feature with YAML frontmatter
|
|
130
60
|
*/
|
|
@@ -195,21 +125,9 @@ export async function initProject(projectDir: string = process.cwd(), force: boo
|
|
|
195
125
|
console.log(` ${chalk.green("✓")} relentless/${filename} ${force ? `(${action})` : ""}`);
|
|
196
126
|
}
|
|
197
127
|
|
|
198
|
-
//
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
if (existsSync(path)) {
|
|
203
|
-
console.log(` ${chalk.yellow("⚠")} relentless/${filename} already exists (protected, not overwriting)`);
|
|
204
|
-
continue;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
await Bun.write(path, contentFn());
|
|
208
|
-
console.log(` ${chalk.green("✓")} relentless/${filename} (created)`);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// Note: constitution.md is NOT copied - it should be created by /relentless.constitution command
|
|
212
|
-
// This ensures each project gets a personalized constitution
|
|
128
|
+
// Note: constitution.md and prompt.md are NOT created here
|
|
129
|
+
// They should be generated by /relentless.constitution command
|
|
130
|
+
// This ensures each project gets personalized governance and agent instructions
|
|
213
131
|
|
|
214
132
|
// Create features directory with .gitkeep
|
|
215
133
|
const gitkeepPath = join(featuresDir, ".gitkeep");
|
|
@@ -227,6 +145,13 @@ export async function initProject(projectDir: string = process.cwd(), force: boo
|
|
|
227
145
|
|
|
228
146
|
const sourceSkillsDir = join(relentlessRoot, ".claude", "skills");
|
|
229
147
|
|
|
148
|
+
// Check if we're running in the relentless project itself (source == destination)
|
|
149
|
+
// This prevents accidentally deleting our own source files with -f flag
|
|
150
|
+
const isRelentlessProject = skillsDir === sourceSkillsDir;
|
|
151
|
+
if (isRelentlessProject) {
|
|
152
|
+
console.log(chalk.yellow(" ⚠ Running in Relentless project itself - skipping skill copy to avoid self-destruction"));
|
|
153
|
+
}
|
|
154
|
+
|
|
230
155
|
if (!existsSync(sourceSkillsDir)) {
|
|
231
156
|
console.error(chalk.red(`\n❌ Error: Skills directory not found at ${sourceSkillsDir}`));
|
|
232
157
|
console.error(chalk.red(` Relentless root: ${relentlessRoot}`));
|
|
@@ -238,21 +163,22 @@ export async function initProject(projectDir: string = process.cwd(), force: boo
|
|
|
238
163
|
process.exit(1);
|
|
239
164
|
}
|
|
240
165
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
166
|
+
// List of skills to install (used for both Claude Code and Amp)
|
|
167
|
+
const skills = [
|
|
168
|
+
"prd",
|
|
169
|
+
"relentless",
|
|
170
|
+
"constitution",
|
|
171
|
+
"specify",
|
|
172
|
+
"plan",
|
|
173
|
+
"tasks",
|
|
174
|
+
"checklist",
|
|
175
|
+
"clarify",
|
|
176
|
+
"analyze",
|
|
177
|
+
"implement",
|
|
178
|
+
"taskstoissues",
|
|
179
|
+
];
|
|
180
|
+
|
|
181
|
+
if (existsSync(sourceSkillsDir) && !isRelentlessProject) {
|
|
256
182
|
for (const skill of skills) {
|
|
257
183
|
const sourcePath = join(sourceSkillsDir, skill);
|
|
258
184
|
const destPath = join(skillsDir, skill);
|
|
@@ -283,6 +209,158 @@ export async function initProject(projectDir: string = process.cwd(), force: boo
|
|
|
283
209
|
}
|
|
284
210
|
}
|
|
285
211
|
|
|
212
|
+
// Copy skills to .amp/skills/ if Amp is installed
|
|
213
|
+
const ampInstalled = installed.some((a) => a.name === "amp");
|
|
214
|
+
if (ampInstalled) {
|
|
215
|
+
console.log(chalk.dim("\nInstalling skills for Amp..."));
|
|
216
|
+
const ampSkillsDir = join(projectDir, ".amp", "skills");
|
|
217
|
+
if (!existsSync(ampSkillsDir)) {
|
|
218
|
+
mkdirSync(ampSkillsDir, { recursive: true });
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
for (const skill of skills) {
|
|
222
|
+
const sourcePath = join(sourceSkillsDir, skill);
|
|
223
|
+
const destPath = join(ampSkillsDir, skill);
|
|
224
|
+
|
|
225
|
+
if (!existsSync(sourcePath)) {
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (existsSync(destPath) && !force) {
|
|
230
|
+
console.log(` ${chalk.yellow("⚠")} .amp/skills/${skill} already exists, skipping`);
|
|
231
|
+
} else {
|
|
232
|
+
try {
|
|
233
|
+
if (existsSync(destPath) && force) {
|
|
234
|
+
await Bun.spawn(["rm", "-rf", destPath]).exited;
|
|
235
|
+
}
|
|
236
|
+
const result = await Bun.spawn(["cp", "-r", sourcePath, destPath]).exited;
|
|
237
|
+
if (result !== 0) {
|
|
238
|
+
console.log(` ${chalk.red("✗")} .amp/skills/${skill} - copy failed`);
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
const action = force ? "updated" : "created";
|
|
242
|
+
console.log(` ${chalk.green("✓")} .amp/skills/${skill} (${action})`);
|
|
243
|
+
} catch (error) {
|
|
244
|
+
console.log(` ${chalk.red("✗")} .amp/skills/${skill} - error: ${error}`);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Copy skills to .opencode/skill/ if OpenCode is installed (SINGULAR!)
|
|
251
|
+
const opencodeInstalled = installed.some((a) => a.name === "opencode");
|
|
252
|
+
if (opencodeInstalled) {
|
|
253
|
+
console.log(chalk.dim("\nInstalling skills for OpenCode..."));
|
|
254
|
+
const opencodeSkillsDir = join(projectDir, ".opencode", "skill");
|
|
255
|
+
if (!existsSync(opencodeSkillsDir)) {
|
|
256
|
+
mkdirSync(opencodeSkillsDir, { recursive: true });
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
for (const skill of skills) {
|
|
260
|
+
const sourcePath = join(sourceSkillsDir, skill);
|
|
261
|
+
const destPath = join(opencodeSkillsDir, skill);
|
|
262
|
+
|
|
263
|
+
if (!existsSync(sourcePath)) {
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (existsSync(destPath) && !force) {
|
|
268
|
+
console.log(` ${chalk.yellow("⚠")} .opencode/skill/${skill} already exists, skipping`);
|
|
269
|
+
} else {
|
|
270
|
+
try {
|
|
271
|
+
if (existsSync(destPath) && force) {
|
|
272
|
+
await Bun.spawn(["rm", "-rf", destPath]).exited;
|
|
273
|
+
}
|
|
274
|
+
const result = await Bun.spawn(["cp", "-r", sourcePath, destPath]).exited;
|
|
275
|
+
if (result !== 0) {
|
|
276
|
+
console.log(` ${chalk.red("✗")} .opencode/skill/${skill} - copy failed`);
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
const action = force ? "updated" : "created";
|
|
280
|
+
console.log(` ${chalk.green("✓")} .opencode/skill/${skill} (${action})`);
|
|
281
|
+
} catch (error) {
|
|
282
|
+
console.log(` ${chalk.red("✗")} .opencode/skill/${skill} - error: ${error}`);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Copy skills to .codex/skills/ if Codex is installed
|
|
289
|
+
const codexInstalled = installed.some((a) => a.name === "codex");
|
|
290
|
+
if (codexInstalled) {
|
|
291
|
+
console.log(chalk.dim("\nInstalling skills for Codex..."));
|
|
292
|
+
const codexSkillsDir = join(projectDir, ".codex", "skills");
|
|
293
|
+
if (!existsSync(codexSkillsDir)) {
|
|
294
|
+
mkdirSync(codexSkillsDir, { recursive: true });
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
for (const skill of skills) {
|
|
298
|
+
const sourcePath = join(sourceSkillsDir, skill);
|
|
299
|
+
const destPath = join(codexSkillsDir, skill);
|
|
300
|
+
|
|
301
|
+
if (!existsSync(sourcePath)) {
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (existsSync(destPath) && !force) {
|
|
306
|
+
console.log(` ${chalk.yellow("⚠")} .codex/skills/${skill} already exists, skipping`);
|
|
307
|
+
} else {
|
|
308
|
+
try {
|
|
309
|
+
if (existsSync(destPath) && force) {
|
|
310
|
+
await Bun.spawn(["rm", "-rf", destPath]).exited;
|
|
311
|
+
}
|
|
312
|
+
const result = await Bun.spawn(["cp", "-r", sourcePath, destPath]).exited;
|
|
313
|
+
if (result !== 0) {
|
|
314
|
+
console.log(` ${chalk.red("✗")} .codex/skills/${skill} - copy failed`);
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
const action = force ? "updated" : "created";
|
|
318
|
+
console.log(` ${chalk.green("✓")} .codex/skills/${skill} (${action})`);
|
|
319
|
+
} catch (error) {
|
|
320
|
+
console.log(` ${chalk.red("✗")} .codex/skills/${skill} - error: ${error}`);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Copy skills to .factory/skills/ if Droid (Factory) is installed
|
|
327
|
+
const droidInstalled = installed.some((a) => a.name === "droid");
|
|
328
|
+
if (droidInstalled) {
|
|
329
|
+
console.log(chalk.dim("\nInstalling skills for Droid (Factory)..."));
|
|
330
|
+
const factorySkillsDir = join(projectDir, ".factory", "skills");
|
|
331
|
+
if (!existsSync(factorySkillsDir)) {
|
|
332
|
+
mkdirSync(factorySkillsDir, { recursive: true });
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
for (const skill of skills) {
|
|
336
|
+
const sourcePath = join(sourceSkillsDir, skill);
|
|
337
|
+
const destPath = join(factorySkillsDir, skill);
|
|
338
|
+
|
|
339
|
+
if (!existsSync(sourcePath)) {
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (existsSync(destPath) && !force) {
|
|
344
|
+
console.log(` ${chalk.yellow("⚠")} .factory/skills/${skill} already exists, skipping`);
|
|
345
|
+
} else {
|
|
346
|
+
try {
|
|
347
|
+
if (existsSync(destPath) && force) {
|
|
348
|
+
await Bun.spawn(["rm", "-rf", destPath]).exited;
|
|
349
|
+
}
|
|
350
|
+
const result = await Bun.spawn(["cp", "-r", sourcePath, destPath]).exited;
|
|
351
|
+
if (result !== 0) {
|
|
352
|
+
console.log(` ${chalk.red("✗")} .factory/skills/${skill} - copy failed`);
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
const action = force ? "updated" : "created";
|
|
356
|
+
console.log(` ${chalk.green("✓")} .factory/skills/${skill} (${action})`);
|
|
357
|
+
} catch (error) {
|
|
358
|
+
console.log(` ${chalk.red("✗")} .factory/skills/${skill} - error: ${error}`);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
286
364
|
// Copy commands to .claude/commands/ (for Claude Code)
|
|
287
365
|
console.log(chalk.dim("\nInstalling commands..."));
|
|
288
366
|
const commandsDir = join(projectDir, ".claude", "commands");
|
|
@@ -292,19 +370,26 @@ export async function initProject(projectDir: string = process.cwd(), force: boo
|
|
|
292
370
|
|
|
293
371
|
const sourceCommandsDir = join(relentlessRoot, ".claude", "commands");
|
|
294
372
|
|
|
295
|
-
if (
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
"relentless.constitution.md",
|
|
301
|
-
"relentless.implement.md",
|
|
302
|
-
"relentless.plan.md",
|
|
303
|
-
"relentless.specify.md",
|
|
304
|
-
"relentless.tasks.md",
|
|
305
|
-
"relentless.taskstoissues.md",
|
|
306
|
-
];
|
|
373
|
+
// Check if we're running in the relentless project itself (source == destination)
|
|
374
|
+
const isRelentlessProjectCommands = commandsDir === sourceCommandsDir;
|
|
375
|
+
if (isRelentlessProjectCommands) {
|
|
376
|
+
console.log(chalk.yellow(" ⚠ Running in Relentless project itself - skipping command copy to avoid self-destruction"));
|
|
377
|
+
}
|
|
307
378
|
|
|
379
|
+
// List of commands to install (used for Claude, OpenCode, Factory, and Codex)
|
|
380
|
+
const commands = [
|
|
381
|
+
"relentless.analyze.md",
|
|
382
|
+
"relentless.checklist.md",
|
|
383
|
+
"relentless.clarify.md",
|
|
384
|
+
"relentless.constitution.md",
|
|
385
|
+
"relentless.implement.md",
|
|
386
|
+
"relentless.plan.md",
|
|
387
|
+
"relentless.specify.md",
|
|
388
|
+
"relentless.tasks.md",
|
|
389
|
+
"relentless.taskstoissues.md",
|
|
390
|
+
];
|
|
391
|
+
|
|
392
|
+
if (existsSync(sourceCommandsDir) && !isRelentlessProjectCommands) {
|
|
308
393
|
for (const command of commands) {
|
|
309
394
|
const sourcePath = join(sourceCommandsDir, command);
|
|
310
395
|
const destPath = join(commandsDir, command);
|
|
@@ -322,21 +407,162 @@ export async function initProject(projectDir: string = process.cwd(), force: boo
|
|
|
322
407
|
}
|
|
323
408
|
}
|
|
324
409
|
|
|
410
|
+
// Copy commands to .opencode/command/ if OpenCode is installed (SINGULAR!)
|
|
411
|
+
if (opencodeInstalled && existsSync(sourceCommandsDir)) {
|
|
412
|
+
console.log(chalk.dim("\nInstalling commands for OpenCode..."));
|
|
413
|
+
const opencodeCommandsDir = join(projectDir, ".opencode", "command");
|
|
414
|
+
if (!existsSync(opencodeCommandsDir)) {
|
|
415
|
+
mkdirSync(opencodeCommandsDir, { recursive: true });
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
for (const command of commands) {
|
|
419
|
+
const sourcePath = join(sourceCommandsDir, command);
|
|
420
|
+
const destPath = join(opencodeCommandsDir, command);
|
|
421
|
+
|
|
422
|
+
if (existsSync(sourcePath)) {
|
|
423
|
+
if (existsSync(destPath) && !force) {
|
|
424
|
+
console.log(` ${chalk.yellow("⚠")} .opencode/command/${command} already exists, skipping`);
|
|
425
|
+
} else {
|
|
426
|
+
const content = await Bun.file(sourcePath).text();
|
|
427
|
+
await Bun.write(destPath, content);
|
|
428
|
+
const action = existsSync(destPath) && force ? "updated" : "created";
|
|
429
|
+
console.log(` ${chalk.green("✓")} .opencode/command/${command} (${action})`);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// Copy commands to .factory/commands/ if Droid (Factory) is installed
|
|
436
|
+
if (droidInstalled && existsSync(sourceCommandsDir)) {
|
|
437
|
+
console.log(chalk.dim("\nInstalling commands for Droid (Factory)..."));
|
|
438
|
+
const factoryCommandsDir = join(projectDir, ".factory", "commands");
|
|
439
|
+
if (!existsSync(factoryCommandsDir)) {
|
|
440
|
+
mkdirSync(factoryCommandsDir, { recursive: true });
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
for (const command of commands) {
|
|
444
|
+
const sourcePath = join(sourceCommandsDir, command);
|
|
445
|
+
const destPath = join(factoryCommandsDir, command);
|
|
446
|
+
|
|
447
|
+
if (existsSync(sourcePath)) {
|
|
448
|
+
if (existsSync(destPath) && !force) {
|
|
449
|
+
console.log(` ${chalk.yellow("⚠")} .factory/commands/${command} already exists, skipping`);
|
|
450
|
+
} else {
|
|
451
|
+
const content = await Bun.file(sourcePath).text();
|
|
452
|
+
await Bun.write(destPath, content);
|
|
453
|
+
const action = existsSync(destPath) && force ? "updated" : "created";
|
|
454
|
+
console.log(` ${chalk.green("✓")} .factory/commands/${command} (${action})`);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// Copy prompts to ~/.codex/prompts/ if Codex is installed (user-level only)
|
|
461
|
+
if (codexInstalled && existsSync(sourceCommandsDir)) {
|
|
462
|
+
console.log(chalk.dim("\nInstalling prompts for Codex (user-level)..."));
|
|
463
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || "";
|
|
464
|
+
const codexPromptsDir = join(homeDir, ".codex", "prompts");
|
|
465
|
+
if (!existsSync(codexPromptsDir)) {
|
|
466
|
+
mkdirSync(codexPromptsDir, { recursive: true });
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
for (const command of commands) {
|
|
470
|
+
const sourcePath = join(sourceCommandsDir, command);
|
|
471
|
+
// Codex prompts are invoked as /prompts:name, so we keep the same filename
|
|
472
|
+
const destPath = join(codexPromptsDir, command);
|
|
473
|
+
|
|
474
|
+
if (existsSync(sourcePath)) {
|
|
475
|
+
if (existsSync(destPath) && !force) {
|
|
476
|
+
console.log(` ${chalk.yellow("⚠")} ~/.codex/prompts/${command} already exists, skipping`);
|
|
477
|
+
} else {
|
|
478
|
+
const content = await Bun.file(sourcePath).text();
|
|
479
|
+
await Bun.write(destPath, content);
|
|
480
|
+
const action = existsSync(destPath) && force ? "updated" : "created";
|
|
481
|
+
console.log(` ${chalk.green("✓")} ~/.codex/prompts/${command} (${action})`);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// Create .gemini/GEMINI.md context file if Gemini is installed
|
|
488
|
+
const geminiInstalled = installed.some((a) => a.name === "gemini");
|
|
489
|
+
if (geminiInstalled) {
|
|
490
|
+
console.log(chalk.dim("\nInstalling context for Gemini..."));
|
|
491
|
+
const geminiDir = join(projectDir, ".gemini");
|
|
492
|
+
if (!existsSync(geminiDir)) {
|
|
493
|
+
mkdirSync(geminiDir, { recursive: true });
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
const geminiContextPath = join(geminiDir, "GEMINI.md");
|
|
497
|
+
const geminiContextContent = `# Relentless - Universal AI Agent Orchestrator
|
|
498
|
+
|
|
499
|
+
This project uses Relentless for feature-driven development with AI agents.
|
|
500
|
+
|
|
501
|
+
## Available Skills
|
|
502
|
+
|
|
503
|
+
The following skills are available in \`.claude/skills/\`:
|
|
504
|
+
|
|
505
|
+
- **prd** - Generate Product Requirements Documents
|
|
506
|
+
- **constitution** - Create project governance and coding principles
|
|
507
|
+
- **specify** - Create feature specifications
|
|
508
|
+
- **plan** - Generate technical implementation plans
|
|
509
|
+
- **tasks** - Generate user stories and tasks
|
|
510
|
+
- **checklist** - Generate quality validation checklists
|
|
511
|
+
- **clarify** - Resolve ambiguities in specifications
|
|
512
|
+
- **analyze** - Analyze consistency across artifacts
|
|
513
|
+
- **implement** - Execute implementation workflows
|
|
514
|
+
- **taskstoissues** - Convert user stories to GitHub issues
|
|
515
|
+
|
|
516
|
+
## Workflow
|
|
517
|
+
|
|
518
|
+
1. Run \`/relentless.constitution\` to create project governance
|
|
519
|
+
2. Run \`/relentless.specify "feature description"\` to create a feature spec
|
|
520
|
+
3. Run \`/relentless.plan\` to generate technical plan
|
|
521
|
+
4. Run \`/relentless.tasks\` to generate user stories
|
|
522
|
+
5. Run \`/relentless.checklist\` to generate quality checklist
|
|
523
|
+
|
|
524
|
+
## Feature Directory Structure
|
|
525
|
+
|
|
526
|
+
\`\`\`
|
|
527
|
+
relentless/features/<feature-name>/
|
|
528
|
+
├── spec.md # Feature specification
|
|
529
|
+
├── plan.md # Technical plan
|
|
530
|
+
├── tasks.md # User stories
|
|
531
|
+
├── checklist.md # Quality checklist
|
|
532
|
+
├── prd.json # PRD JSON (for orchestrator)
|
|
533
|
+
└── progress.txt # Progress log
|
|
534
|
+
\`\`\`
|
|
535
|
+
|
|
536
|
+
For full documentation, see: https://github.com/ArvorCo/Relentless
|
|
537
|
+
`;
|
|
538
|
+
|
|
539
|
+
if (existsSync(geminiContextPath) && !force) {
|
|
540
|
+
console.log(` ${chalk.yellow("⚠")} .gemini/GEMINI.md already exists, skipping`);
|
|
541
|
+
} else {
|
|
542
|
+
await Bun.write(geminiContextPath, geminiContextContent);
|
|
543
|
+
const action = existsSync(geminiContextPath) && force ? "updated" : "created";
|
|
544
|
+
console.log(` ${chalk.green("✓")} .gemini/GEMINI.md (${action})`);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
325
548
|
// Print next steps
|
|
326
549
|
console.log(chalk.bold.green("\n✅ Relentless initialized!\n"));
|
|
327
550
|
console.log(chalk.dim("Structure:"));
|
|
328
551
|
console.log(chalk.dim(" relentless/"));
|
|
329
552
|
console.log(chalk.dim(" ├── config.json # Configuration"));
|
|
330
|
-
console.log(chalk.dim(" ├── constitution.md # Project governance"));
|
|
331
|
-
console.log(chalk.dim(" ├── prompt.md #
|
|
553
|
+
console.log(chalk.dim(" ├── constitution.md # Project governance (run /relentless.constitution)"));
|
|
554
|
+
console.log(chalk.dim(" ├── prompt.md # Agent instructions (run /relentless.constitution)"));
|
|
332
555
|
console.log(chalk.dim(" └── features/ # Feature folders"));
|
|
333
556
|
console.log(chalk.dim(" └── <feature>/ # Each feature has:"));
|
|
334
|
-
console.log(chalk.dim(" ├──
|
|
335
|
-
console.log(chalk.dim(" ├──
|
|
557
|
+
console.log(chalk.dim(" ├── spec.md # Feature specification"));
|
|
558
|
+
console.log(chalk.dim(" ├── plan.md # Technical plan"));
|
|
559
|
+
console.log(chalk.dim(" ├── tasks.md # User stories"));
|
|
560
|
+
console.log(chalk.dim(" ├── checklist.md # Quality checklist"));
|
|
561
|
+
console.log(chalk.dim(" ├── prd.json # PRD JSON (for orchestrator)"));
|
|
336
562
|
console.log(chalk.dim(" └── progress.txt # Progress log\n"));
|
|
337
563
|
|
|
338
564
|
console.log("Next steps:");
|
|
339
|
-
console.log(chalk.dim("1. Create project constitution (
|
|
565
|
+
console.log(chalk.dim("1. Create project constitution and prompt (required):"));
|
|
340
566
|
console.log(` ${chalk.cyan("/relentless.constitution")}`);
|
|
341
567
|
console.log(chalk.dim("\n2. Create a feature specification:"));
|
|
342
568
|
console.log(` ${chalk.cyan("/relentless.specify Add user authentication")}`);
|
|
@@ -414,7 +640,7 @@ export async function createFeature(
|
|
|
414
640
|
|
|
415
641
|
// Copy plan.md template if requested
|
|
416
642
|
if (options.withPlan) {
|
|
417
|
-
const planSourcePath = join(relentlessRoot, "templates", "plan.md");
|
|
643
|
+
const planSourcePath = join(relentlessRoot, ".claude", "skills", "plan", "templates", "plan.md");
|
|
418
644
|
const planDestPath = join(featureDir, "plan.md");
|
|
419
645
|
|
|
420
646
|
if (existsSync(planSourcePath)) {
|
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
# Project Constitution
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
This document defines the governing principles, patterns, and constraints for this project. All agents and developers MUST follow these guidelines when working on the codebase.
|
|
6
|
-
|
|
7
|
-
## Core Principles
|
|
8
|
-
|
|
9
|
-
### Architecture
|
|
10
|
-
|
|
11
|
-
**MUST** follow these architectural patterns:
|
|
12
|
-
- Follow existing code structure and organization patterns
|
|
13
|
-
- Keep modules focused and single-purpose
|
|
14
|
-
- Use dependency injection where appropriate
|
|
15
|
-
|
|
16
|
-
**SHOULD** consider these best practices:
|
|
17
|
-
- Prefer composition over inheritance
|
|
18
|
-
- Keep functions small and focused
|
|
19
|
-
- Write self-documenting code
|
|
20
|
-
|
|
21
|
-
### Code Quality
|
|
22
|
-
|
|
23
|
-
**MUST** maintain these quality standards:
|
|
24
|
-
- All commits MUST pass typecheck with 0 errors
|
|
25
|
-
- All commits MUST pass lint with 0 warnings
|
|
26
|
-
- All new features MUST include appropriate tests
|
|
27
|
-
- All code MUST be formatted consistently
|
|
28
|
-
|
|
29
|
-
**SHOULD** strive for:
|
|
30
|
-
- High test coverage (aim for >80%)
|
|
31
|
-
- Clear, descriptive variable and function names
|
|
32
|
-
- Comprehensive error handling
|
|
33
|
-
|
|
34
|
-
### Version Control
|
|
35
|
-
|
|
36
|
-
**MUST** follow these Git practices:
|
|
37
|
-
- Write clear, descriptive commit messages
|
|
38
|
-
- Reference user story IDs in commits: `feat: [US-XXX] - Description`
|
|
39
|
-
- Keep commits focused and atomic
|
|
40
|
-
- Do not commit broken code
|
|
41
|
-
|
|
42
|
-
**SHOULD** maintain:
|
|
43
|
-
- Clean commit history
|
|
44
|
-
- Meaningful branch names
|
|
45
|
-
- Updated documentation with code changes
|
|
46
|
-
|
|
47
|
-
## Technology Stack
|
|
48
|
-
|
|
49
|
-
### Language & Runtime
|
|
50
|
-
|
|
51
|
-
- **TypeScript** - Primary language
|
|
52
|
-
- **Bun** - Runtime environment (not Node.js)
|
|
53
|
-
- **Zod** - Schema validation
|
|
54
|
-
|
|
55
|
-
### Key Libraries
|
|
56
|
-
|
|
57
|
-
- Commander - CLI parsing
|
|
58
|
-
- Chalk - Terminal formatting
|
|
59
|
-
- Execa - Process execution
|
|
60
|
-
|
|
61
|
-
## File Organization
|
|
62
|
-
|
|
63
|
-
```
|
|
64
|
-
project/
|
|
65
|
-
├── bin/ # CLI entry points
|
|
66
|
-
├── src/ # Source code
|
|
67
|
-
│ ├── agents/ # Agent adapters
|
|
68
|
-
│ ├── config/ # Configuration
|
|
69
|
-
│ ├── execution/ # Orchestration
|
|
70
|
-
│ ├── init/ # Project scaffolding
|
|
71
|
-
│ └── prd/ # PRD handling
|
|
72
|
-
├── templates/ # Template files
|
|
73
|
-
├── skills/ # Agent skills
|
|
74
|
-
└── relentless/ # Relentless workspace
|
|
75
|
-
├── config.json
|
|
76
|
-
├── prompt.md
|
|
77
|
-
└── features/
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
## Coding Standards
|
|
81
|
-
|
|
82
|
-
### TypeScript
|
|
83
|
-
|
|
84
|
-
**MUST** follow:
|
|
85
|
-
- Use strict TypeScript mode
|
|
86
|
-
- Avoid `any` type - use `unknown` or proper types
|
|
87
|
-
- Export types alongside implementations
|
|
88
|
-
- Use Zod schemas for runtime validation
|
|
89
|
-
|
|
90
|
-
**SHOULD** prefer:
|
|
91
|
-
- Interface for public APIs
|
|
92
|
-
- Type for unions and intersections
|
|
93
|
-
- Explicit return types on public functions
|
|
94
|
-
|
|
95
|
-
### Error Handling
|
|
96
|
-
|
|
97
|
-
**MUST** implement:
|
|
98
|
-
- Descriptive error messages
|
|
99
|
-
- Proper error types
|
|
100
|
-
- Validation at system boundaries
|
|
101
|
-
- Graceful degradation where appropriate
|
|
102
|
-
|
|
103
|
-
**SHOULD** include:
|
|
104
|
-
- Context in error messages
|
|
105
|
-
- Recovery suggestions
|
|
106
|
-
- Logging for debugging
|
|
107
|
-
|
|
108
|
-
### Testing
|
|
109
|
-
|
|
110
|
-
**MUST** test:
|
|
111
|
-
- Core business logic
|
|
112
|
-
- Edge cases and error conditions
|
|
113
|
-
- Integration points
|
|
114
|
-
- CLI commands
|
|
115
|
-
|
|
116
|
-
**SHOULD** test:
|
|
117
|
-
- Helper functions
|
|
118
|
-
- Utilities
|
|
119
|
-
- Type guards
|
|
120
|
-
|
|
121
|
-
## Documentation
|
|
122
|
-
|
|
123
|
-
**MUST** document:
|
|
124
|
-
- Public APIs and interfaces
|
|
125
|
-
- Complex algorithms or logic
|
|
126
|
-
- Breaking changes in commits
|
|
127
|
-
- Setup and installation steps
|
|
128
|
-
|
|
129
|
-
**SHOULD** document:
|
|
130
|
-
- Configuration options
|
|
131
|
-
- Architecture decisions
|
|
132
|
-
- Common workflows
|
|
133
|
-
- Troubleshooting steps
|
|
134
|
-
|
|
135
|
-
## Security
|
|
136
|
-
|
|
137
|
-
**MUST** ensure:
|
|
138
|
-
- No secrets committed to git
|
|
139
|
-
- Proper input validation
|
|
140
|
-
- Safe file system operations
|
|
141
|
-
- Minimal permissions required
|
|
142
|
-
|
|
143
|
-
**SHOULD** consider:
|
|
144
|
-
- Rate limiting where appropriate
|
|
145
|
-
- Audit logs for sensitive operations
|
|
146
|
-
- Security updates for dependencies
|
|
147
|
-
|
|
148
|
-
## Performance
|
|
149
|
-
|
|
150
|
-
**MUST** maintain:
|
|
151
|
-
- Fast startup time (<1s)
|
|
152
|
-
- Responsive CLI commands
|
|
153
|
-
- Efficient file operations
|
|
154
|
-
- Minimal memory footprint
|
|
155
|
-
|
|
156
|
-
**SHOULD** optimize:
|
|
157
|
-
- Parallel operations where safe
|
|
158
|
-
- Caching for repeated operations
|
|
159
|
-
- Lazy loading of heavy modules
|
|
160
|
-
|
|
161
|
-
## Constraints
|
|
162
|
-
|
|
163
|
-
### Dependencies
|
|
164
|
-
|
|
165
|
-
**MUST NOT**:
|
|
166
|
-
- Add dependencies without justification
|
|
167
|
-
- Include deprecated packages
|
|
168
|
-
- Use packages with known security issues
|
|
169
|
-
|
|
170
|
-
**SHOULD**:
|
|
171
|
-
- Prefer built-in solutions over dependencies
|
|
172
|
-
- Keep dependencies minimal and focused
|
|
173
|
-
- Regularly update dependencies
|
|
174
|
-
|
|
175
|
-
### Backwards Compatibility
|
|
176
|
-
|
|
177
|
-
**MUST**:
|
|
178
|
-
- Maintain compatibility with existing PRDs
|
|
179
|
-
- Provide migration paths for breaking changes
|
|
180
|
-
- Version configuration formats
|
|
181
|
-
|
|
182
|
-
**SHOULD**:
|
|
183
|
-
- Deprecate features before removal
|
|
184
|
-
- Provide clear upgrade documentation
|
|
185
|
-
- Support at least 2 major versions
|
|
186
|
-
|
|
187
|
-
## Agent-Specific Guidelines
|
|
188
|
-
|
|
189
|
-
### For All Agents
|
|
190
|
-
|
|
191
|
-
**MUST**:
|
|
192
|
-
- Read progress.txt before starting work
|
|
193
|
-
- Work on ONE story per iteration
|
|
194
|
-
- Update PRD after completing a story
|
|
195
|
-
- Append learnings to progress.txt
|
|
196
|
-
- Run all quality checks before committing
|
|
197
|
-
|
|
198
|
-
**SHOULD**:
|
|
199
|
-
- Review existing code before modifying
|
|
200
|
-
- Follow established patterns
|
|
201
|
-
- Ask questions when unclear
|
|
202
|
-
- Document non-obvious decisions
|
|
203
|
-
|
|
204
|
-
### Iteration Workflow
|
|
205
|
-
|
|
206
|
-
1. Read PRD to find next incomplete story
|
|
207
|
-
2. Read progress.txt for context and patterns
|
|
208
|
-
3. Review relevant existing code
|
|
209
|
-
4. Implement the single story
|
|
210
|
-
5. Run typecheck and lint
|
|
211
|
-
6. Run tests if applicable
|
|
212
|
-
7. Commit with proper message format
|
|
213
|
-
8. Update PRD passes: true
|
|
214
|
-
9. Append to progress.txt
|
|
215
|
-
10. Check if all stories complete
|
|
216
|
-
|
|
217
|
-
## Review and Updates
|
|
218
|
-
|
|
219
|
-
This constitution should be:
|
|
220
|
-
- **Reviewed** at project milestones
|
|
221
|
-
- **Updated** when patterns emerge
|
|
222
|
-
- **Referenced** in code reviews
|
|
223
|
-
- **Enforced** in CI/CD pipelines
|
|
224
|
-
|
|
225
|
-
---
|
|
226
|
-
|
|
227
|
-
Last Updated: YYYY-MM-DD
|
|
228
|
-
Version: 1.0.0
|
package/templates/plan.md
DELETED
|
@@ -1,273 +0,0 @@
|
|
|
1
|
-
# Technical Planning Document
|
|
2
|
-
|
|
3
|
-
## Feature Overview
|
|
4
|
-
|
|
5
|
-
**Feature Name**: [Feature Name]
|
|
6
|
-
**PRD Reference**: [Link or path to PRD]
|
|
7
|
-
**Planning Date**: [YYYY-MM-DD]
|
|
8
|
-
**Author**: [Name or Agent ID]
|
|
9
|
-
|
|
10
|
-
### Problem Statement
|
|
11
|
-
|
|
12
|
-
[Describe the problem this feature solves. What pain point are we addressing?]
|
|
13
|
-
|
|
14
|
-
### Goals and Non-Goals
|
|
15
|
-
|
|
16
|
-
**Goals:**
|
|
17
|
-
- [Goal 1]
|
|
18
|
-
- [Goal 2]
|
|
19
|
-
- [Goal 3]
|
|
20
|
-
|
|
21
|
-
**Non-Goals:**
|
|
22
|
-
- [What this feature will NOT do]
|
|
23
|
-
- [Scope limitations]
|
|
24
|
-
|
|
25
|
-
---
|
|
26
|
-
|
|
27
|
-
## Technical Approach
|
|
28
|
-
|
|
29
|
-
### High-Level Architecture
|
|
30
|
-
|
|
31
|
-
[Describe the overall architectural approach. Include diagrams if helpful.]
|
|
32
|
-
|
|
33
|
-
### Technology Stack
|
|
34
|
-
|
|
35
|
-
**Languages/Frameworks:**
|
|
36
|
-
- [Technology 1]: [Justification]
|
|
37
|
-
- [Technology 2]: [Justification]
|
|
38
|
-
|
|
39
|
-
**Libraries/Dependencies:**
|
|
40
|
-
- [Dependency 1]: [Version] - [Purpose]
|
|
41
|
-
- [Dependency 2]: [Version] - [Purpose]
|
|
42
|
-
|
|
43
|
-
### Design Patterns
|
|
44
|
-
|
|
45
|
-
[Which design patterns will be used and why?]
|
|
46
|
-
- [Pattern 1]: [Rationale]
|
|
47
|
-
- [Pattern 2]: [Rationale]
|
|
48
|
-
|
|
49
|
-
---
|
|
50
|
-
|
|
51
|
-
## Implementation Details
|
|
52
|
-
|
|
53
|
-
### Data Models
|
|
54
|
-
|
|
55
|
-
```typescript
|
|
56
|
-
// Example schema definitions
|
|
57
|
-
interface Example {
|
|
58
|
-
field: string;
|
|
59
|
-
// ...
|
|
60
|
-
}
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
### API Design
|
|
64
|
-
|
|
65
|
-
**Endpoints/Functions:**
|
|
66
|
-
- `functionName(params)`: [Description]
|
|
67
|
-
- `anotherFunction(params)`: [Description]
|
|
68
|
-
|
|
69
|
-
**Request/Response Formats:**
|
|
70
|
-
```json
|
|
71
|
-
{
|
|
72
|
-
"example": "payload"
|
|
73
|
-
}
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
### File Structure
|
|
77
|
-
|
|
78
|
-
```
|
|
79
|
-
src/
|
|
80
|
-
├── feature/
|
|
81
|
-
│ ├── module1.ts
|
|
82
|
-
│ ├── module2.ts
|
|
83
|
-
│ └── types.ts
|
|
84
|
-
└── tests/
|
|
85
|
-
└── feature.test.ts
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
---
|
|
89
|
-
|
|
90
|
-
## Integration Points
|
|
91
|
-
|
|
92
|
-
### Existing Systems
|
|
93
|
-
|
|
94
|
-
- **System 1**: [How does this feature integrate?]
|
|
95
|
-
- **System 2**: [Dependencies or interactions]
|
|
96
|
-
|
|
97
|
-
### Database Changes
|
|
98
|
-
|
|
99
|
-
**Schema Updates:**
|
|
100
|
-
- [Table/Collection 1]: [Changes]
|
|
101
|
-
- [Table/Collection 2]: [Changes]
|
|
102
|
-
|
|
103
|
-
**Migration Strategy:**
|
|
104
|
-
- [Backward compatibility considerations]
|
|
105
|
-
- [Data migration approach]
|
|
106
|
-
|
|
107
|
-
---
|
|
108
|
-
|
|
109
|
-
## Testing Strategy
|
|
110
|
-
|
|
111
|
-
### Unit Tests
|
|
112
|
-
|
|
113
|
-
- [What units will be tested?]
|
|
114
|
-
- [Coverage goals]
|
|
115
|
-
|
|
116
|
-
### Integration Tests
|
|
117
|
-
|
|
118
|
-
- [Integration scenarios]
|
|
119
|
-
- [External dependencies to mock]
|
|
120
|
-
|
|
121
|
-
### E2E Tests
|
|
122
|
-
|
|
123
|
-
- [User flows to test]
|
|
124
|
-
- [Critical paths]
|
|
125
|
-
|
|
126
|
-
### Performance Tests
|
|
127
|
-
|
|
128
|
-
- [Load testing requirements]
|
|
129
|
-
- [Performance benchmarks]
|
|
130
|
-
|
|
131
|
-
---
|
|
132
|
-
|
|
133
|
-
## Security Considerations
|
|
134
|
-
|
|
135
|
-
### Authentication/Authorization
|
|
136
|
-
|
|
137
|
-
- [How is access controlled?]
|
|
138
|
-
- [Role-based permissions]
|
|
139
|
-
|
|
140
|
-
### Data Privacy
|
|
141
|
-
|
|
142
|
-
- [PII handling]
|
|
143
|
-
- [Encryption requirements]
|
|
144
|
-
|
|
145
|
-
### Vulnerability Analysis
|
|
146
|
-
|
|
147
|
-
- [Potential security risks]
|
|
148
|
-
- [Mitigation strategies]
|
|
149
|
-
|
|
150
|
-
---
|
|
151
|
-
|
|
152
|
-
## Rollout Strategy
|
|
153
|
-
|
|
154
|
-
### Feature Flags
|
|
155
|
-
|
|
156
|
-
- [Which flags to use?]
|
|
157
|
-
- [Gradual rollout plan]
|
|
158
|
-
|
|
159
|
-
### Deployment Steps
|
|
160
|
-
|
|
161
|
-
1. [Step 1]
|
|
162
|
-
2. [Step 2]
|
|
163
|
-
3. [Step 3]
|
|
164
|
-
|
|
165
|
-
### Rollback Plan
|
|
166
|
-
|
|
167
|
-
- [How to revert if needed]
|
|
168
|
-
- [Data cleanup strategy]
|
|
169
|
-
|
|
170
|
-
---
|
|
171
|
-
|
|
172
|
-
## Monitoring and Observability
|
|
173
|
-
|
|
174
|
-
### Metrics
|
|
175
|
-
|
|
176
|
-
- [Metric 1]: [What it measures]
|
|
177
|
-
- [Metric 2]: [What it measures]
|
|
178
|
-
|
|
179
|
-
### Logging
|
|
180
|
-
|
|
181
|
-
- [Key events to log]
|
|
182
|
-
- [Log levels and structured logging approach]
|
|
183
|
-
|
|
184
|
-
### Alerting
|
|
185
|
-
|
|
186
|
-
- [What triggers alerts?]
|
|
187
|
-
- [Escalation procedures]
|
|
188
|
-
|
|
189
|
-
---
|
|
190
|
-
|
|
191
|
-
## Open Questions
|
|
192
|
-
|
|
193
|
-
- [ ] [Question 1]
|
|
194
|
-
- [ ] [Question 2]
|
|
195
|
-
- [ ] [Question 3]
|
|
196
|
-
|
|
197
|
-
---
|
|
198
|
-
|
|
199
|
-
## Alternatives Considered
|
|
200
|
-
|
|
201
|
-
### Alternative 1: [Name]
|
|
202
|
-
|
|
203
|
-
**Pros:**
|
|
204
|
-
- [Pro 1]
|
|
205
|
-
- [Pro 2]
|
|
206
|
-
|
|
207
|
-
**Cons:**
|
|
208
|
-
- [Con 1]
|
|
209
|
-
- [Con 2]
|
|
210
|
-
|
|
211
|
-
**Why not chosen:** [Reasoning]
|
|
212
|
-
|
|
213
|
-
### Alternative 2: [Name]
|
|
214
|
-
|
|
215
|
-
**Pros:**
|
|
216
|
-
- [Pro 1]
|
|
217
|
-
|
|
218
|
-
**Cons:**
|
|
219
|
-
- [Con 1]
|
|
220
|
-
|
|
221
|
-
**Why not chosen:** [Reasoning]
|
|
222
|
-
|
|
223
|
-
---
|
|
224
|
-
|
|
225
|
-
## Dependencies and Risks
|
|
226
|
-
|
|
227
|
-
### External Dependencies
|
|
228
|
-
|
|
229
|
-
- [Dependency 1]: [Risk if unavailable]
|
|
230
|
-
- [Dependency 2]: [Mitigation strategy]
|
|
231
|
-
|
|
232
|
-
### Technical Risks
|
|
233
|
-
|
|
234
|
-
1. **Risk**: [Description]
|
|
235
|
-
- **Impact**: [High/Medium/Low]
|
|
236
|
-
- **Mitigation**: [Strategy]
|
|
237
|
-
|
|
238
|
-
2. **Risk**: [Description]
|
|
239
|
-
- **Impact**: [High/Medium/Low]
|
|
240
|
-
- **Mitigation**: [Strategy]
|
|
241
|
-
|
|
242
|
-
### Timeline Risks
|
|
243
|
-
|
|
244
|
-
- [Risk 1]: [Mitigation]
|
|
245
|
-
- [Risk 2]: [Mitigation]
|
|
246
|
-
|
|
247
|
-
---
|
|
248
|
-
|
|
249
|
-
## Success Criteria
|
|
250
|
-
|
|
251
|
-
### Definition of Done
|
|
252
|
-
|
|
253
|
-
- [ ] All acceptance criteria met
|
|
254
|
-
- [ ] All tests passing
|
|
255
|
-
- [ ] Code reviewed and approved
|
|
256
|
-
- [ ] Documentation complete
|
|
257
|
-
- [ ] Performance benchmarks met
|
|
258
|
-
- [ ] Security review passed
|
|
259
|
-
|
|
260
|
-
### Success Metrics
|
|
261
|
-
|
|
262
|
-
- [Metric 1]: [Target value]
|
|
263
|
-
- [Metric 2]: [Target value]
|
|
264
|
-
- [Metric 3]: [Target value]
|
|
265
|
-
|
|
266
|
-
---
|
|
267
|
-
|
|
268
|
-
## References
|
|
269
|
-
|
|
270
|
-
- [Link to PRD]
|
|
271
|
-
- [Related design docs]
|
|
272
|
-
- [External documentation]
|
|
273
|
-
- [Research papers or articles]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|