@arvorco/relentless 0.1.20 → 0.1.22
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/relentless.implement.md +34 -7
- package/.claude/skills/implement/SKILL.md +192 -91
- package/CHANGELOG.md +269 -0
- package/package.json +1 -1
- package/relentless/constitution.md +393 -0
- package/relentless/prompt.md +192 -21
- package/src/execution/runner.ts +33 -4
- package/src/execution/story-prompt.ts +170 -0
- package/src/prd/parser.ts +1 -1
package/src/execution/runner.ts
CHANGED
|
@@ -12,8 +12,10 @@ import { getAgent, getInstalledAgents } from "../agents/registry";
|
|
|
12
12
|
import type { RelentlessConfig } from "../config/schema";
|
|
13
13
|
import { loadConstitution, validateConstitution } from "../config/loader";
|
|
14
14
|
import { loadPRD, getNextStory, isComplete, countStories } from "../prd";
|
|
15
|
+
import type { UserStory } from "../prd/types";
|
|
15
16
|
import { loadProgress, updateProgressMetadata, syncPatternsFromContent } from "../prd/progress";
|
|
16
17
|
import { routeStory } from "./router";
|
|
18
|
+
import { buildStoryPromptAddition } from "./story-prompt";
|
|
17
19
|
|
|
18
20
|
export interface RunOptions {
|
|
19
21
|
/** Agent to use (or "auto" for smart routing) */
|
|
@@ -60,7 +62,7 @@ async function buildPrompt(
|
|
|
60
62
|
promptPath: string,
|
|
61
63
|
workingDirectory: string,
|
|
62
64
|
progressPath: string,
|
|
63
|
-
story?:
|
|
65
|
+
story?: UserStory
|
|
64
66
|
): Promise<string> {
|
|
65
67
|
if (!existsSync(promptPath)) {
|
|
66
68
|
throw new Error(`Prompt file not found: ${promptPath}`);
|
|
@@ -68,8 +70,8 @@ async function buildPrompt(
|
|
|
68
70
|
|
|
69
71
|
let prompt = await Bun.file(promptPath).text();
|
|
70
72
|
|
|
71
|
-
// Load and append constitution if available
|
|
72
|
-
const constitution = await loadConstitution(
|
|
73
|
+
// Load and append constitution if available (pass undefined to auto-find)
|
|
74
|
+
const constitution = await loadConstitution();
|
|
73
75
|
if (constitution) {
|
|
74
76
|
// Validate constitution
|
|
75
77
|
const validation = validateConstitution(constitution);
|
|
@@ -88,7 +90,7 @@ async function buildPrompt(
|
|
|
88
90
|
|
|
89
91
|
// Load and append progress patterns if available
|
|
90
92
|
const progress = await loadProgress(progressPath);
|
|
91
|
-
if (progress && progress.metadata.patterns.length > 0) {
|
|
93
|
+
if (progress?.metadata?.patterns && progress.metadata.patterns.length > 0) {
|
|
92
94
|
prompt += `\n\n## Learned Patterns from Previous Iterations\n\n`;
|
|
93
95
|
prompt += `The following patterns were discovered in previous iterations:\n\n`;
|
|
94
96
|
for (const pattern of progress.metadata.patterns) {
|
|
@@ -96,6 +98,15 @@ async function buildPrompt(
|
|
|
96
98
|
}
|
|
97
99
|
}
|
|
98
100
|
|
|
101
|
+
// Load and append spec.md if available
|
|
102
|
+
const specPath = join(dirname(progressPath), "spec.md");
|
|
103
|
+
if (existsSync(specPath)) {
|
|
104
|
+
const specContent = await Bun.file(specPath).text();
|
|
105
|
+
prompt += `\n\n## Feature Specification\n\n`;
|
|
106
|
+
prompt += `The following specification defines the requirements for this feature:\n\n`;
|
|
107
|
+
prompt += specContent;
|
|
108
|
+
}
|
|
109
|
+
|
|
99
110
|
// Load and append plan.md if available
|
|
100
111
|
const planPath = join(dirname(progressPath), "plan.md");
|
|
101
112
|
if (existsSync(planPath)) {
|
|
@@ -105,6 +116,17 @@ async function buildPrompt(
|
|
|
105
116
|
prompt += planContent;
|
|
106
117
|
}
|
|
107
118
|
|
|
119
|
+
// Load and append tasks.md if available
|
|
120
|
+
const tasksPath = join(dirname(progressPath), "tasks.md");
|
|
121
|
+
if (existsSync(tasksPath)) {
|
|
122
|
+
const tasksContent = await Bun.file(tasksPath).text();
|
|
123
|
+
prompt += `\n\n## User Stories and Tasks\n\n`;
|
|
124
|
+
prompt += `The following tasks file contains all user stories with their acceptance criteria.\n`;
|
|
125
|
+
prompt += `**IMPORTANT:** Update the checkboxes in this file as you complete each criterion.\n`;
|
|
126
|
+
prompt += `Change \`- [ ]\` to \`- [x]\` for completed items.\n\n`;
|
|
127
|
+
prompt += tasksContent;
|
|
128
|
+
}
|
|
129
|
+
|
|
108
130
|
// Load and append checklist.md if available
|
|
109
131
|
const checklistPath = join(dirname(progressPath), "checklist.md");
|
|
110
132
|
if (existsSync(checklistPath)) {
|
|
@@ -126,6 +148,13 @@ async function buildPrompt(
|
|
|
126
148
|
}
|
|
127
149
|
}
|
|
128
150
|
|
|
151
|
+
// Add story-specific workflow instructions
|
|
152
|
+
if (story) {
|
|
153
|
+
const featureDir = dirname(progressPath);
|
|
154
|
+
const storyInstructions = await buildStoryPromptAddition(story, featureDir);
|
|
155
|
+
prompt += storyInstructions;
|
|
156
|
+
}
|
|
157
|
+
|
|
129
158
|
return prompt;
|
|
130
159
|
}
|
|
131
160
|
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Story-Specific Prompt Builder
|
|
3
|
+
*
|
|
4
|
+
* Generates story-specific instructions for agents, including:
|
|
5
|
+
* - Per-story acceptance criteria
|
|
6
|
+
* - Relevant checklist items
|
|
7
|
+
* - Instructions to update tasks.md and checklist.md
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { existsSync } from "node:fs";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import type { UserStory } from "../prd/types";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Extract checklist items relevant to a specific story
|
|
16
|
+
*/
|
|
17
|
+
export async function getRelevantChecklistItems(
|
|
18
|
+
checklistPath: string,
|
|
19
|
+
storyId: string
|
|
20
|
+
): Promise<string[]> {
|
|
21
|
+
if (!existsSync(checklistPath)) {
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const content = await Bun.file(checklistPath).text();
|
|
26
|
+
const lines = content.split("\n");
|
|
27
|
+
const relevantItems: string[] = [];
|
|
28
|
+
|
|
29
|
+
for (const line of lines) {
|
|
30
|
+
// Match checklist items that reference this story
|
|
31
|
+
// Format: - [ ] CHK-XXX [US-001] Description
|
|
32
|
+
// Or: - [ ] CHK-XXX [Constitution] Description (always relevant)
|
|
33
|
+
const trimmed = line.trim();
|
|
34
|
+
if (trimmed.startsWith("- [ ]") || trimmed.startsWith("- [x]")) {
|
|
35
|
+
if (
|
|
36
|
+
trimmed.includes(`[${storyId}]`) ||
|
|
37
|
+
trimmed.includes("[Constitution]") ||
|
|
38
|
+
trimmed.includes("[Edge Case]")
|
|
39
|
+
) {
|
|
40
|
+
relevantItems.push(trimmed);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return relevantItems;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Build the per-story workflow instructions
|
|
50
|
+
*/
|
|
51
|
+
export function buildStoryWorkflowInstructions(
|
|
52
|
+
story: UserStory,
|
|
53
|
+
checklistItems: string[],
|
|
54
|
+
tasksPath: string
|
|
55
|
+
): string {
|
|
56
|
+
const instructions = `
|
|
57
|
+
|
|
58
|
+
## Current Story: ${story.id} - ${story.title}
|
|
59
|
+
|
|
60
|
+
**Description:** ${story.description}
|
|
61
|
+
|
|
62
|
+
**Phase:** ${story.phase || "Implementation"}
|
|
63
|
+
**Priority:** ${story.priority}
|
|
64
|
+
${story.dependencies?.length ? `**Dependencies:** ${story.dependencies.join(", ")}` : ""}
|
|
65
|
+
${story.research ? "**Research Required:** Yes - research phase needed before implementation" : ""}
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Acceptance Criteria for ${story.id}
|
|
70
|
+
|
|
71
|
+
You MUST complete ALL of the following criteria before marking this story as done:
|
|
72
|
+
|
|
73
|
+
${story.acceptanceCriteria.map((c, i) => `${i + 1}. [ ] ${c}`).join("\n")}
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Per-Story Workflow (MANDATORY)
|
|
78
|
+
|
|
79
|
+
Follow this exact workflow for ${story.id}:
|
|
80
|
+
|
|
81
|
+
### Step 1: Read & Understand
|
|
82
|
+
1. Read the acceptance criteria above carefully
|
|
83
|
+
2. Review any existing code related to this story
|
|
84
|
+
3. Check if research findings exist in \`research/${story.id}.md\`
|
|
85
|
+
|
|
86
|
+
### Step 2: TDD - Write Tests First (RED phase)
|
|
87
|
+
\`\`\`bash
|
|
88
|
+
# Write failing tests that validate each acceptance criterion
|
|
89
|
+
bun test # Tests MUST fail initially
|
|
90
|
+
\`\`\`
|
|
91
|
+
|
|
92
|
+
### Step 3: Implement (GREEN phase)
|
|
93
|
+
\`\`\`bash
|
|
94
|
+
# Write minimum code to pass tests
|
|
95
|
+
bun test # Tests MUST pass
|
|
96
|
+
\`\`\`
|
|
97
|
+
|
|
98
|
+
### Step 4: Refactor
|
|
99
|
+
\`\`\`bash
|
|
100
|
+
# Clean up while keeping tests green
|
|
101
|
+
bun test # Tests MUST still pass
|
|
102
|
+
\`\`\`
|
|
103
|
+
|
|
104
|
+
### Step 5: Quality Checks
|
|
105
|
+
\`\`\`bash
|
|
106
|
+
bun run typecheck # 0 errors
|
|
107
|
+
bun run lint # 0 warnings
|
|
108
|
+
bun test # All pass
|
|
109
|
+
\`\`\`
|
|
110
|
+
|
|
111
|
+
### Step 6: Update Tasks & Checklist
|
|
112
|
+
**IMPORTANT:** After implementing each criterion:
|
|
113
|
+
|
|
114
|
+
1. **Update tasks.md** - Check off completed items:
|
|
115
|
+
- File: \`${tasksPath}\`
|
|
116
|
+
- Find the section for ${story.id}
|
|
117
|
+
- Change \`- [ ]\` to \`- [x]\` for completed criteria
|
|
118
|
+
|
|
119
|
+
2. **Update checklist.md** - Mark verified items:
|
|
120
|
+
- Find items tagged with [${story.id}]
|
|
121
|
+
- Change \`- [ ]\` to \`- [x]\` for verified items
|
|
122
|
+
|
|
123
|
+
### Step 7: Commit
|
|
124
|
+
\`\`\`bash
|
|
125
|
+
git add -A
|
|
126
|
+
git commit -m "feat: ${story.id} - ${story.title}"
|
|
127
|
+
\`\`\`
|
|
128
|
+
|
|
129
|
+
### Step 8: Mark Story Complete
|
|
130
|
+
Update \`prd.json\` to set \`passes: true\` for ${story.id}
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
${checklistItems.length > 0 ? `## Relevant Checklist Items for ${story.id}
|
|
135
|
+
|
|
136
|
+
Verify these items as you implement:
|
|
137
|
+
|
|
138
|
+
${checklistItems.join("\n")}
|
|
139
|
+
|
|
140
|
+
After verifying each item, update checklist.md to mark it as checked.
|
|
141
|
+
|
|
142
|
+
---` : ""}
|
|
143
|
+
|
|
144
|
+
## DO NOT Forget
|
|
145
|
+
|
|
146
|
+
1. **Update tasks.md** - Check off criteria as you complete them
|
|
147
|
+
2. **Update checklist.md** - Mark items as verified
|
|
148
|
+
3. **Run ALL quality checks** before marking story complete
|
|
149
|
+
4. **Write tests FIRST** - No exceptions to TDD
|
|
150
|
+
5. **Commit with proper message format**: \`feat: ${story.id} - ${story.title}\`
|
|
151
|
+
|
|
152
|
+
`;
|
|
153
|
+
|
|
154
|
+
return instructions;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Build complete story-specific prompt addition
|
|
159
|
+
*/
|
|
160
|
+
export async function buildStoryPromptAddition(
|
|
161
|
+
story: UserStory,
|
|
162
|
+
featureDir: string
|
|
163
|
+
): Promise<string> {
|
|
164
|
+
const checklistPath = join(featureDir, "checklist.md");
|
|
165
|
+
const tasksPath = join(featureDir, "tasks.md");
|
|
166
|
+
|
|
167
|
+
const checklistItems = await getRelevantChecklistItems(checklistPath, story.id);
|
|
168
|
+
|
|
169
|
+
return buildStoryWorkflowInstructions(story, checklistItems, tasksPath);
|
|
170
|
+
}
|
package/src/prd/parser.ts
CHANGED
|
@@ -119,7 +119,7 @@ export function parsePRDMarkdown(content: string): Partial<PRD> {
|
|
|
119
119
|
.trim()
|
|
120
120
|
.split(/[,;]/)
|
|
121
121
|
.map((d) => d.trim())
|
|
122
|
-
.filter(
|
|
122
|
+
.filter((d) => d && !["none", "n/a", "-", "na"].includes(d.toLowerCase()));
|
|
123
123
|
if (deps.length > 0) {
|
|
124
124
|
currentStory.dependencies = deps;
|
|
125
125
|
}
|