@dedesfr/prompter 0.7.9 → 0.8.1
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/AGENTS.md +18 -0
- package/CHANGELOG.md +59 -0
- package/CLAUDE.md +17 -0
- package/dist/cli/index.js +2 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/init.d.ts +4 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +164 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +21 -0
- package/dist/commands/update.js.map +1 -1
- package/dist/core/configurators/slash/antigravity.d.ts +1 -0
- package/dist/core/configurators/slash/antigravity.d.ts.map +1 -1
- package/dist/core/configurators/slash/antigravity.js +3 -0
- package/dist/core/configurators/slash/antigravity.js.map +1 -1
- package/dist/core/configurators/slash/base.d.ts +18 -0
- package/dist/core/configurators/slash/base.d.ts.map +1 -1
- package/dist/core/configurators/slash/base.js +65 -0
- package/dist/core/configurators/slash/base.js.map +1 -1
- package/dist/core/configurators/slash/claude.d.ts +1 -0
- package/dist/core/configurators/slash/claude.d.ts.map +1 -1
- package/dist/core/configurators/slash/claude.js +3 -0
- package/dist/core/configurators/slash/claude.js.map +1 -1
- package/dist/core/configurators/slash/codex.d.ts +1 -0
- package/dist/core/configurators/slash/codex.d.ts.map +1 -1
- package/dist/core/configurators/slash/codex.js +3 -0
- package/dist/core/configurators/slash/codex.js.map +1 -1
- package/dist/core/configurators/slash/droid.d.ts +1 -0
- package/dist/core/configurators/slash/droid.d.ts.map +1 -1
- package/dist/core/configurators/slash/droid.js +3 -0
- package/dist/core/configurators/slash/droid.js.map +1 -1
- package/dist/core/configurators/slash/forge.d.ts +1 -0
- package/dist/core/configurators/slash/forge.d.ts.map +1 -1
- package/dist/core/configurators/slash/forge.js +3 -0
- package/dist/core/configurators/slash/forge.js.map +1 -1
- package/dist/core/configurators/slash/github-copilot.d.ts +1 -0
- package/dist/core/configurators/slash/github-copilot.d.ts.map +1 -1
- package/dist/core/configurators/slash/github-copilot.js +3 -0
- package/dist/core/configurators/slash/github-copilot.js.map +1 -1
- package/dist/core/configurators/slash/kilocode.d.ts +1 -0
- package/dist/core/configurators/slash/kilocode.d.ts.map +1 -1
- package/dist/core/configurators/slash/kilocode.js +3 -0
- package/dist/core/configurators/slash/kilocode.js.map +1 -1
- package/dist/core/configurators/slash/opencode.d.ts +1 -0
- package/dist/core/configurators/slash/opencode.d.ts.map +1 -1
- package/dist/core/configurators/slash/opencode.js +3 -0
- package/dist/core/configurators/slash/opencode.js.map +1 -1
- package/dist/core/skill-discovery.d.ts +12 -0
- package/dist/core/skill-discovery.d.ts.map +1 -0
- package/dist/core/skill-discovery.js +58 -0
- package/dist/core/skill-discovery.js.map +1 -0
- package/package.json +1 -1
- package/skills/agents-md-generator/SKILL.md +238 -0
- package/skills/agents-md-generator/assets/agents-md-template.md +81 -0
- package/skills/agents-md-generator/references/best-practices.md +215 -0
- package/skills/code-review/SKILL.md +373 -0
- package/skills/code-review/assets/report-template-agent.md +212 -0
- package/skills/code-review/assets/report-template-compact.md +81 -0
- package/skills/code-review/assets/report-template-full.md +264 -0
- package/skills/code-review/assets/report-template-human.md +168 -0
- package/skills/code-review/references/universal-patterns.md +495 -0
- package/skills/meeting-notes/SKILL.md +159 -0
- package/skills/meeting-notes/evals/evals.json +23 -0
- package/skills/project-orchestrator/SKILL.md +394 -0
- package/skills/project-orchestrator/assets/plan-summary-template.md +123 -0
- package/skills/prompter-specs/SKILL.md +115 -0
- package/skills/ui-ux-pro/SKILL.md +348 -0
- package/skills/ui-ux-pro/assets/design-spec-template.md +173 -0
- package/skills/ui-ux-pro/references/component-patterns.md +255 -0
- package/skills/ui-ux-pro/references/design-principles.md +167 -0
- package/src/cli/index.ts +2 -1
- package/src/commands/init.ts +182 -2
- package/src/commands/update.ts +22 -0
- package/src/core/configurators/slash/antigravity.ts +4 -0
- package/src/core/configurators/slash/base.ts +95 -0
- package/src/core/configurators/slash/claude.ts +4 -0
- package/src/core/configurators/slash/codex.ts +4 -0
- package/src/core/configurators/slash/droid.ts +4 -0
- package/src/core/configurators/slash/forge.ts +4 -0
- package/src/core/configurators/slash/github-copilot.ts +4 -0
- package/src/core/configurators/slash/kilocode.ts +4 -0
- package/src/core/configurators/slash/opencode.ts +4 -0
- package/src/core/skill-discovery.ts +68 -0
- package/.claude/settings.local.json +0 -7
- package/.github/prompts/prd-agent-generator.prompt.md +0 -133
package/src/commands/update.ts
CHANGED
|
@@ -4,6 +4,7 @@ import path from 'path';
|
|
|
4
4
|
import { PrompterConfig, AVAILABLE_PROMPTS, PROMPTER_DIR } from '../core/config.js';
|
|
5
5
|
import { registry } from '../core/configurators/slash/index.js';
|
|
6
6
|
import { PROMPT_TEMPLATES } from '../core/prompt-templates.js';
|
|
7
|
+
import { discoverSkills } from '../core/skill-discovery.js';
|
|
7
8
|
|
|
8
9
|
export class UpdateCommand {
|
|
9
10
|
async execute(): Promise<void> {
|
|
@@ -56,6 +57,27 @@ export class UpdateCommand {
|
|
|
56
57
|
updatedCount++;
|
|
57
58
|
}
|
|
58
59
|
|
|
60
|
+
// Update existing skill workflow files
|
|
61
|
+
const skillsDir = path.join(projectPath, 'skills');
|
|
62
|
+
const availableSkills = await discoverSkills(skillsDir);
|
|
63
|
+
|
|
64
|
+
if (availableSkills.length > 0) {
|
|
65
|
+
for (const toolId of configuredTools) {
|
|
66
|
+
const configurator = registry.get(toolId);
|
|
67
|
+
if (!configurator) continue;
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
const updatedSkillFiles = await configurator.updateExistingSkills(projectPath, availableSkills);
|
|
71
|
+
for (const file of updatedSkillFiles) {
|
|
72
|
+
console.log(chalk.green('✓') + ` Updated ${chalk.cyan(file)}`);
|
|
73
|
+
updatedCount++;
|
|
74
|
+
}
|
|
75
|
+
} catch (error) {
|
|
76
|
+
console.log(chalk.red('✗') + ` Failed to update skills for ${configurator.toolId}: ${error}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
59
81
|
if (updatedCount === 0) {
|
|
60
82
|
console.log(chalk.yellow('⚠️ No workflow files found to update.'));
|
|
61
83
|
console.log(chalk.gray(' Run `prompter init` to create them.\n'));
|
|
@@ -63,4 +63,8 @@ export class AntigravityConfigurator extends SlashCommandConfigurator {
|
|
|
63
63
|
const description = DESCRIPTIONS[id];
|
|
64
64
|
return `---\ndescription: ${description}\n---`;
|
|
65
65
|
}
|
|
66
|
+
|
|
67
|
+
protected getSkillTargetDir(skillName: string): string {
|
|
68
|
+
return `.agent/skills/${skillName}`;
|
|
69
|
+
}
|
|
66
70
|
}
|
|
@@ -2,6 +2,7 @@ import { promises as fs } from 'fs';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { SlashCommandId, TemplateManager } from '../../templates/index.js';
|
|
4
4
|
import { PROMPTER_MARKERS } from '../../config.js';
|
|
5
|
+
import { SkillMetadata } from '../../skill-discovery.js';
|
|
5
6
|
|
|
6
7
|
export interface SlashCommandTarget {
|
|
7
8
|
id: SlashCommandId;
|
|
@@ -9,6 +10,12 @@ export interface SlashCommandTarget {
|
|
|
9
10
|
kind: 'slash';
|
|
10
11
|
}
|
|
11
12
|
|
|
13
|
+
export interface SkillTarget {
|
|
14
|
+
name: string;
|
|
15
|
+
path: string;
|
|
16
|
+
kind: 'skill';
|
|
17
|
+
}
|
|
18
|
+
|
|
12
19
|
const ALL_COMMANDS: SlashCommandId[] = ['enhance', 'prd-generator', 'prd-agent-generator', 'product-brief', 'epic-single', 'epic-generator', 'story-single', 'story-generator', 'qa-test-scenario', 'skill-creator', 'ai-humanizer', 'api-contract-generator', 'apply', 'archive', 'design-system', 'erd-generator', 'fsd-generator', 'proposal', 'tdd-generator', 'tdd-lite-generator', 'wireframe-generator', 'document-explainer'];
|
|
13
20
|
|
|
14
21
|
export abstract class SlashCommandConfigurator {
|
|
@@ -77,10 +84,98 @@ export abstract class SlashCommandConfigurator {
|
|
|
77
84
|
protected abstract getRelativePath(id: SlashCommandId): string;
|
|
78
85
|
protected abstract getFrontmatter(id: SlashCommandId): string | undefined;
|
|
79
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Returns the relative directory path where the skill's full directory
|
|
89
|
+
* will be placed inside the tool's config dir.
|
|
90
|
+
* e.g. `.claude/skills/laravel-code-review`
|
|
91
|
+
*/
|
|
92
|
+
protected abstract getSkillTargetDir(skillName: string): string;
|
|
93
|
+
|
|
80
94
|
protected getBody(id: SlashCommandId): string {
|
|
81
95
|
return TemplateManager.getSlashCommandBody(id).trim();
|
|
82
96
|
}
|
|
83
97
|
|
|
98
|
+
// --- Skill directory management ---
|
|
99
|
+
|
|
100
|
+
getSkillTargets(skills: SkillMetadata[]): SkillTarget[] {
|
|
101
|
+
return skills.map(skill => ({
|
|
102
|
+
name: skill.name,
|
|
103
|
+
path: this.getSkillTargetDir(skill.name),
|
|
104
|
+
kind: 'skill'
|
|
105
|
+
}));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async generateSkills(projectPath: string, skills: SkillMetadata[]): Promise<string[]> {
|
|
109
|
+
const created: string[] = [];
|
|
110
|
+
|
|
111
|
+
for (const skill of skills) {
|
|
112
|
+
const targetDir = path.join(projectPath, this.getSkillTargetDir(skill.name));
|
|
113
|
+
await this.copyDir(skill.sourcePath, targetDir);
|
|
114
|
+
created.push(this.getSkillTargetDir(skill.name));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return created;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async updateExistingSkills(projectPath: string, skills: SkillMetadata[]): Promise<string[]> {
|
|
121
|
+
const updated: string[] = [];
|
|
122
|
+
|
|
123
|
+
for (const skill of skills) {
|
|
124
|
+
const relativeDir = this.getSkillTargetDir(skill.name);
|
|
125
|
+
const targetDir = path.join(projectPath, relativeDir);
|
|
126
|
+
|
|
127
|
+
if (await this.dirExists(targetDir)) {
|
|
128
|
+
await this.copyDir(skill.sourcePath, targetDir);
|
|
129
|
+
updated.push(relativeDir);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return updated;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async removeSkillFiles(projectPath: string, skillNames: string[]): Promise<string[]> {
|
|
137
|
+
const removed: string[] = [];
|
|
138
|
+
|
|
139
|
+
for (const name of skillNames) {
|
|
140
|
+
const relativeDir = this.getSkillTargetDir(name);
|
|
141
|
+
const targetDir = path.join(projectPath, relativeDir);
|
|
142
|
+
|
|
143
|
+
if (await this.dirExists(targetDir)) {
|
|
144
|
+
await fs.rm(targetDir, { recursive: true, force: true });
|
|
145
|
+
removed.push(relativeDir);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return removed;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
private async copyDir(src: string, dest: string): Promise<void> {
|
|
153
|
+
await fs.mkdir(dest, { recursive: true });
|
|
154
|
+
const entries = await fs.readdir(src, { withFileTypes: true });
|
|
155
|
+
|
|
156
|
+
for (const entry of entries) {
|
|
157
|
+
const srcPath = path.join(src, entry.name);
|
|
158
|
+
const destPath = path.join(dest, entry.name);
|
|
159
|
+
|
|
160
|
+
if (entry.isDirectory()) {
|
|
161
|
+
await this.copyDir(srcPath, destPath);
|
|
162
|
+
} else {
|
|
163
|
+
await fs.copyFile(srcPath, destPath);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
private async dirExists(dirPath: string): Promise<boolean> {
|
|
169
|
+
try {
|
|
170
|
+
const stat = await fs.stat(dirPath);
|
|
171
|
+
return stat.isDirectory();
|
|
172
|
+
} catch {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// --- Shared helpers ---
|
|
178
|
+
|
|
84
179
|
protected async updateBody(filePath: string, body: string): Promise<void> {
|
|
85
180
|
const content = await fs.readFile(filePath, 'utf-8');
|
|
86
181
|
const startIndex = content.indexOf(PROMPTER_MARKERS.start);
|
|
@@ -63,4 +63,8 @@ export class ClaudeConfigurator extends SlashCommandConfigurator {
|
|
|
63
63
|
// Claude Code uses the filename as the command name
|
|
64
64
|
return undefined;
|
|
65
65
|
}
|
|
66
|
+
|
|
67
|
+
protected getSkillTargetDir(skillName: string): string {
|
|
68
|
+
return `.claude/skills/${skillName}`;
|
|
69
|
+
}
|
|
66
70
|
}
|
|
@@ -63,4 +63,8 @@ export class CodexConfigurator extends SlashCommandConfigurator {
|
|
|
63
63
|
const description = DESCRIPTIONS[id];
|
|
64
64
|
return `---\ndescription: ${description}\n---`;
|
|
65
65
|
}
|
|
66
|
+
|
|
67
|
+
protected getSkillTargetDir(skillName: string): string {
|
|
68
|
+
return `.codex/skills/${skillName}`;
|
|
69
|
+
}
|
|
66
70
|
}
|
|
@@ -37,4 +37,8 @@ export class DroidConfigurator extends SlashCommandConfigurator {
|
|
|
37
37
|
protected getFrontmatter(id: SlashCommandId): string | undefined {
|
|
38
38
|
return undefined;
|
|
39
39
|
}
|
|
40
|
+
|
|
41
|
+
protected getSkillTargetDir(skillName: string): string {
|
|
42
|
+
return `.factory/skills/${skillName}`;
|
|
43
|
+
}
|
|
40
44
|
}
|
|
@@ -37,4 +37,8 @@ export class ForgeConfigurator extends SlashCommandConfigurator {
|
|
|
37
37
|
protected getFrontmatter(id: SlashCommandId): string | undefined {
|
|
38
38
|
return undefined;
|
|
39
39
|
}
|
|
40
|
+
|
|
41
|
+
protected getSkillTargetDir(skillName: string): string {
|
|
42
|
+
return `.forge/skills/${skillName}`;
|
|
43
|
+
}
|
|
40
44
|
}
|
|
@@ -99,6 +99,10 @@ export class GithubCopilotConfigurator extends SlashCommandConfigurator {
|
|
|
99
99
|
return createdOrUpdated;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
protected getSkillTargetDir(skillName: string): string {
|
|
103
|
+
return `.github/skills/${skillName}`;
|
|
104
|
+
}
|
|
105
|
+
|
|
102
106
|
private async checkFileExists(filePath: string): Promise<boolean> {
|
|
103
107
|
try {
|
|
104
108
|
await fs.access(filePath);
|
|
@@ -63,4 +63,8 @@ export class KiloCodeConfigurator extends SlashCommandConfigurator {
|
|
|
63
63
|
const description = DESCRIPTIONS[id];
|
|
64
64
|
return `---\ndescription: ${description}\n---`;
|
|
65
65
|
}
|
|
66
|
+
|
|
67
|
+
protected getSkillTargetDir(skillName: string): string {
|
|
68
|
+
return `.kilocode/skills/${skillName}`;
|
|
69
|
+
}
|
|
66
70
|
}
|
|
@@ -63,4 +63,8 @@ export class OpenCodeConfigurator extends SlashCommandConfigurator {
|
|
|
63
63
|
const description = DESCRIPTIONS[id];
|
|
64
64
|
return `---\nagent: build\ndescription: ${description}\n---`;
|
|
65
65
|
}
|
|
66
|
+
|
|
67
|
+
protected getSkillTargetDir(skillName: string): string {
|
|
68
|
+
return `.opencode/skills/${skillName}`;
|
|
69
|
+
}
|
|
66
70
|
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { promises as fs } from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import yaml from 'yaml';
|
|
4
|
+
|
|
5
|
+
export interface SkillMetadata {
|
|
6
|
+
name: string;
|
|
7
|
+
description: string;
|
|
8
|
+
sourcePath: string;
|
|
9
|
+
body: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Discover skills in a directory by scanning for subdirectories containing SKILL.md files.
|
|
14
|
+
* Parses YAML frontmatter (name, description) and extracts the markdown body.
|
|
15
|
+
*/
|
|
16
|
+
export async function discoverSkills(skillsDir: string): Promise<SkillMetadata[]> {
|
|
17
|
+
const skills: SkillMetadata[] = [];
|
|
18
|
+
|
|
19
|
+
let entries: import('fs').Dirent[];
|
|
20
|
+
try {
|
|
21
|
+
entries = await fs.readdir(skillsDir, { withFileTypes: true });
|
|
22
|
+
} catch {
|
|
23
|
+
return skills;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
for (const entry of entries) {
|
|
27
|
+
if (!entry.isDirectory()) continue;
|
|
28
|
+
|
|
29
|
+
const skillDir = path.join(skillsDir, entry.name);
|
|
30
|
+
const skillMdPath = path.join(skillDir, 'SKILL.md');
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const content = await fs.readFile(skillMdPath, 'utf-8');
|
|
34
|
+
const parsed = parseSkillMd(content);
|
|
35
|
+
|
|
36
|
+
if (parsed) {
|
|
37
|
+
skills.push({
|
|
38
|
+
name: parsed.name,
|
|
39
|
+
description: parsed.description,
|
|
40
|
+
sourcePath: skillDir,
|
|
41
|
+
body: parsed.body
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
} catch {
|
|
45
|
+
// SKILL.md not found or unreadable, skip this directory
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return skills.sort((a, b) => a.name.localeCompare(b.name));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function parseSkillMd(content: string): { name: string; description: string; body: string } | null {
|
|
53
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
|
|
54
|
+
if (!match) return null;
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
const meta = yaml.parse(match[1]);
|
|
58
|
+
if (!meta.name || !meta.description) return null;
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
name: String(meta.name),
|
|
62
|
+
description: String(meta.description),
|
|
63
|
+
body: match[2].trim()
|
|
64
|
+
};
|
|
65
|
+
} catch {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Generate a PRD with autonomous assumptions (non-interactive mode)
|
|
3
|
-
---
|
|
4
|
-
$ARGUMENTS
|
|
5
|
-
<!-- prompter-managed-start -->
|
|
6
|
-
# PRD Generator (Non-Interactive Mode)
|
|
7
|
-
|
|
8
|
-
Create detailed Product Requirements Documents that are clear, actionable, and suitable for implementation based solely on the user's initial input.
|
|
9
|
-
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
## The Job
|
|
13
|
-
|
|
14
|
-
1. Receive a feature description from the user
|
|
15
|
-
2. Analyze the input and make reasonable assumptions where details are missing
|
|
16
|
-
3. Generate a structured PRD based on the input
|
|
17
|
-
|
|
18
|
-
---
|
|
19
|
-
|
|
20
|
-
## Handling Ambiguity
|
|
21
|
-
|
|
22
|
-
When the user's input lacks specific details:
|
|
23
|
-
|
|
24
|
-
- **Make reasonable assumptions** based on common patterns and best practices
|
|
25
|
-
- **Document assumptions** in the PRD under "Assumptions Made"
|
|
26
|
-
- **Flag critical unknowns** in the "Open Questions" section
|
|
27
|
-
- **Err on the side of MVP scope** when scope is unclear
|
|
28
|
-
- **Default to standard patterns** (e.g., CRUD operations, standard UI components)
|
|
29
|
-
|
|
30
|
-
---
|
|
31
|
-
|
|
32
|
-
## PRD Structure
|
|
33
|
-
|
|
34
|
-
Generate the PRD with these sections:
|
|
35
|
-
|
|
36
|
-
### 1. Introduction/Overview
|
|
37
|
-
Brief description of the feature and the problem it solves.
|
|
38
|
-
|
|
39
|
-
### 2. Assumptions Made
|
|
40
|
-
List key assumptions made due to missing details in the original request:
|
|
41
|
-
- "Assumed target users are [X] based on feature context"
|
|
42
|
-
- "Assumed MVP scope since no specific scope mentioned"
|
|
43
|
-
- "Assumed standard authentication is already in place"
|
|
44
|
-
|
|
45
|
-
### 3. Goals
|
|
46
|
-
Specific, measurable objectives (bullet list).
|
|
47
|
-
|
|
48
|
-
### 4. User Stories
|
|
49
|
-
Each story needs:
|
|
50
|
-
- **Title:** Short descriptive name
|
|
51
|
-
- **Description:** "As a [user], I want [feature] so that [benefit]"
|
|
52
|
-
- **Acceptance Criteria:** Verifiable checklist of what "done" means
|
|
53
|
-
|
|
54
|
-
Each story should be small enough to implement in one focused session.
|
|
55
|
-
|
|
56
|
-
**Format:**
|
|
57
|
-
```markdown
|
|
58
|
-
### US-001: [Title]
|
|
59
|
-
**Description:** As a [user], I want [feature] so that [benefit].
|
|
60
|
-
|
|
61
|
-
**Acceptance Criteria:**
|
|
62
|
-
- [ ] Specific verifiable criterion
|
|
63
|
-
- [ ] Another criterion
|
|
64
|
-
- [ ] Typecheck/lint passes
|
|
65
|
-
- [ ] **[UI stories only]** Verify in browser using dev-browser skill
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
**Important:**
|
|
69
|
-
- Acceptance criteria must be verifiable, not vague. "Works correctly" is bad. "Button shows confirmation dialog before deleting" is good.
|
|
70
|
-
- **For any story with UI changes:** Always include "Verify in browser using dev-browser skill" as acceptance criteria. This ensures visual verification of frontend work.
|
|
71
|
-
|
|
72
|
-
### 5. Functional Requirements
|
|
73
|
-
Numbered list of specific functionalities:
|
|
74
|
-
- "FR-1: The system must allow users to..."
|
|
75
|
-
- "FR-2: When a user clicks X, the system must..."
|
|
76
|
-
|
|
77
|
-
Be explicit and unambiguous.
|
|
78
|
-
|
|
79
|
-
### 6. Non-Goals (Out of Scope)
|
|
80
|
-
What this feature will NOT include. Critical for managing scope.
|
|
81
|
-
|
|
82
|
-
### 7. Design Considerations (Optional)
|
|
83
|
-
- UI/UX requirements
|
|
84
|
-
- Link to mockups if available
|
|
85
|
-
- Relevant existing components to reuse
|
|
86
|
-
|
|
87
|
-
### 8. Technical Considerations (Optional)
|
|
88
|
-
- Known constraints or dependencies
|
|
89
|
-
- Integration points with existing systems
|
|
90
|
-
- Performance requirements
|
|
91
|
-
|
|
92
|
-
### 9. Success Metrics
|
|
93
|
-
How will success be measured?
|
|
94
|
-
- "Reduce time to complete X by 50%"
|
|
95
|
-
- "Increase conversion rate by 10%"
|
|
96
|
-
|
|
97
|
-
### 10. Open Questions
|
|
98
|
-
Remaining questions or areas needing clarification. This is where you document:
|
|
99
|
-
- Critical unknowns that affect implementation
|
|
100
|
-
- Areas where the original request was ambiguous
|
|
101
|
-
- Decisions that may need stakeholder input
|
|
102
|
-
|
|
103
|
-
---
|
|
104
|
-
|
|
105
|
-
## Writing for Junior Developers
|
|
106
|
-
|
|
107
|
-
The PRD reader may be a junior developer or AI agent. Therefore:
|
|
108
|
-
|
|
109
|
-
- Be explicit and unambiguous
|
|
110
|
-
- Avoid jargon or explain it
|
|
111
|
-
- Provide enough detail to understand purpose and core logic
|
|
112
|
-
- Number requirements for easy reference
|
|
113
|
-
- Use concrete examples where helpful
|
|
114
|
-
|
|
115
|
-
---
|
|
116
|
-
|
|
117
|
-
## Output
|
|
118
|
-
|
|
119
|
-
- **Format:** Markdown (`.md`)
|
|
120
|
-
|
|
121
|
-
---
|
|
122
|
-
|
|
123
|
-
## WORKFLOW STEPS
|
|
124
|
-
1. Read the user's input about the feature
|
|
125
|
-
2. Generate a unique, URL-friendly slug from the feature name (lowercase, hyphen-separated)
|
|
126
|
-
3. Create the directory `prompter/<slug>/` if it doesn't exist
|
|
127
|
-
4. Generate the complete PRD following all requirements above
|
|
128
|
-
5. Save the PRD to `prompter/<slug>/prd-agent.md`
|
|
129
|
-
6. Report the saved file path
|
|
130
|
-
|
|
131
|
-
## REFERENCE
|
|
132
|
-
- Read `prompter/project.md` for project context if needed
|
|
133
|
-
<!-- prompter-managed-end -->
|