@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.
@@ -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?: { id: string; research?: boolean }
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(workingDirectory);
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(Boolean);
122
+ .filter((d) => d && !["none", "n/a", "-", "na"].includes(d.toLowerCase()));
123
123
  if (deps.length > 0) {
124
124
  currentStory.dependencies = deps;
125
125
  }