procedure-cli 0.1.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/settings.local.json +23 -0
- package/.env.example +2 -0
- package/AGENTS.md +113 -0
- package/CLAUDE.md +136 -0
- package/CODE-FIXED.md +124 -0
- package/CODE-REVIEW.md +253 -0
- package/README.md +130 -0
- package/config/defaults.json +8 -0
- package/config/powerline-config.json +52 -0
- package/config/stacks/typescript-node.json +15 -0
- package/dist/app.d.ts +1 -0
- package/dist/app.js +131 -0
- package/dist/app.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +6 -0
- package/dist/cli.js.map +1 -0
- package/dist/components/banner.d.ts +1 -0
- package/dist/components/banner.js +11 -0
- package/dist/components/banner.js.map +1 -0
- package/dist/components/gutter-line.d.ts +5 -0
- package/dist/components/gutter-line.js +9 -0
- package/dist/components/gutter-line.js.map +1 -0
- package/dist/components/guttered-select.d.ts +25 -0
- package/dist/components/guttered-select.js +68 -0
- package/dist/components/guttered-select.js.map +1 -0
- package/dist/components/step-indicator.d.ts +7 -0
- package/dist/components/step-indicator.js +12 -0
- package/dist/components/step-indicator.js.map +1 -0
- package/dist/components/timeline.d.ts +13 -0
- package/dist/components/timeline.js +26 -0
- package/dist/components/timeline.js.map +1 -0
- package/dist/lib/fs.d.ts +3 -0
- package/dist/lib/fs.js +15 -0
- package/dist/lib/fs.js.map +1 -0
- package/dist/lib/git.d.ts +4 -0
- package/dist/lib/git.js +37 -0
- package/dist/lib/git.js.map +1 -0
- package/dist/lib/powerline.d.ts +1 -0
- package/dist/lib/powerline.js +30 -0
- package/dist/lib/powerline.js.map +1 -0
- package/dist/lib/template.d.ts +9 -0
- package/dist/lib/template.js +46 -0
- package/dist/lib/template.js.map +1 -0
- package/dist/lib/types.d.ts +44 -0
- package/dist/lib/types.js +2 -0
- package/dist/lib/types.js.map +1 -0
- package/dist/providers/openai.d.ts +1 -0
- package/dist/providers/openai.js +5 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/zai.d.ts +1 -0
- package/dist/providers/zai.js +7 -0
- package/dist/providers/zai.js.map +1 -0
- package/dist/steps/architecture.d.ts +6 -0
- package/dist/steps/architecture.js +39 -0
- package/dist/steps/architecture.js.map +1 -0
- package/dist/steps/build-test.d.ts +7 -0
- package/dist/steps/build-test.js +38 -0
- package/dist/steps/build-test.js.map +1 -0
- package/dist/steps/generation.d.ts +7 -0
- package/dist/steps/generation.js +60 -0
- package/dist/steps/generation.js.map +1 -0
- package/dist/steps/powerline.d.ts +7 -0
- package/dist/steps/powerline.js +62 -0
- package/dist/steps/powerline.js.map +1 -0
- package/dist/steps/product-context.d.ts +7 -0
- package/dist/steps/product-context.js +90 -0
- package/dist/steps/product-context.js.map +1 -0
- package/dist/steps/project-info.d.ts +6 -0
- package/dist/steps/project-info.js +61 -0
- package/dist/steps/project-info.js.map +1 -0
- package/dist/steps/stack-style.d.ts +6 -0
- package/dist/steps/stack-style.js +67 -0
- package/dist/steps/stack-style.js.map +1 -0
- package/docs/GIAI-THICH-CLAUDE-MD.md +206 -0
- package/docs/PRD.md +130 -0
- package/docs/USER-STORIES.md +181 -0
- package/package.json +38 -0
- package/src/app.tsx +201 -0
- package/src/cli.tsx +6 -0
- package/src/components/banner.tsx +23 -0
- package/src/components/gutter-line.tsx +10 -0
- package/src/components/guttered-select.tsx +137 -0
- package/src/components/step-indicator.tsx +19 -0
- package/src/components/timeline.tsx +49 -0
- package/src/lib/fs.ts +18 -0
- package/src/lib/git.ts +41 -0
- package/src/lib/powerline.ts +48 -0
- package/src/lib/template.ts +59 -0
- package/src/lib/types.ts +68 -0
- package/src/providers/openai.ts +5 -0
- package/src/providers/zai.ts +7 -0
- package/src/steps/architecture.tsx +71 -0
- package/src/steps/build-test.tsx +78 -0
- package/src/steps/generation.tsx +146 -0
- package/src/steps/powerline.tsx +149 -0
- package/src/steps/product-context.tsx +182 -0
- package/src/steps/project-info.tsx +135 -0
- package/src/steps/stack-style.tsx +117 -0
- package/templates/.env.example.hbs +6 -0
- package/templates/CLAUDE.md.hbs +105 -0
- package/templates/README.md.hbs +26 -0
- package/templates/docs/PRD.md.hbs +29 -0
- package/templates/docs/USER-STORIES.md.hbs +46 -0
- package/tsconfig.json +17 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { Text } from "ink";
|
|
3
|
+
import { TextInput } from "@inkjs/ui";
|
|
4
|
+
import { GutterLine } from "../components/gutter-line.js";
|
|
5
|
+
import { GutteredMultiSelect } from "../components/guttered-select.js";
|
|
6
|
+
import type { WizardAnswers } from "../lib/types.js";
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
initialValues?: Partial<WizardAnswers>;
|
|
10
|
+
onComplete: (answers: Partial<WizardAnswers>) => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type Phase = "problem" | "users" | "techStack" | "coreFeatures" | "nonGoals";
|
|
14
|
+
|
|
15
|
+
const TECH_STACK_OPTIONS = [
|
|
16
|
+
{ label: "React", value: "React", description: "UI component library" },
|
|
17
|
+
{ label: "Next.js", value: "Next.js", description: "Full-stack React framework" },
|
|
18
|
+
{ label: "Vue", value: "Vue", description: "Progressive UI framework" },
|
|
19
|
+
{ label: "Svelte", value: "Svelte", description: "Compile-time UI framework" },
|
|
20
|
+
{ label: "Node.js", value: "Node.js", description: "Server-side JavaScript runtime" },
|
|
21
|
+
{ label: "Express", value: "Express", description: "Minimal Node.js web framework" },
|
|
22
|
+
{ label: "Fastify", value: "Fastify", description: "High-performance Node.js framework" },
|
|
23
|
+
{ label: "PostgreSQL", value: "PostgreSQL", description: "Relational database" },
|
|
24
|
+
{ label: "MongoDB", value: "MongoDB", description: "Document database" },
|
|
25
|
+
{ label: "Redis", value: "Redis", description: "In-memory data store / cache" },
|
|
26
|
+
{ label: "Supabase", value: "Supabase", description: "Open-source Firebase alternative" },
|
|
27
|
+
{ label: "Prisma", value: "Prisma", description: "TypeScript ORM" },
|
|
28
|
+
{ label: "Docker", value: "Docker", description: "Container platform" },
|
|
29
|
+
{ label: "Tailwind CSS", value: "Tailwind CSS", description: "Utility-first CSS framework" },
|
|
30
|
+
{ label: "TypeScript", value: "TypeScript", description: "Typed JavaScript superset" },
|
|
31
|
+
{ label: "Python", value: "Python", description: "General-purpose language" },
|
|
32
|
+
{ label: "Go", value: "Go", description: "Systems programming language" },
|
|
33
|
+
{ label: "Rust", value: "Rust", description: "Memory-safe systems language" },
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
const PHASE_ORDER: Phase[] = ["problem", "users", "techStack", "coreFeatures", "nonGoals"];
|
|
37
|
+
|
|
38
|
+
export default function ProductContext({ initialValues, onComplete }: Props) {
|
|
39
|
+
const [phaseIndex, setPhaseIndex] = useState(0);
|
|
40
|
+
const [answers, setAnswers] = useState<Record<string, string>>({});
|
|
41
|
+
const [error, setError] = useState("");
|
|
42
|
+
|
|
43
|
+
// Prefill tech stack from Stack & Style selections (language + framework)
|
|
44
|
+
const prefillTechStack: string[] = [];
|
|
45
|
+
if (initialValues?.language) {
|
|
46
|
+
const match = TECH_STACK_OPTIONS.find(
|
|
47
|
+
(o) => o.value.toLowerCase() === initialValues.language!.toLowerCase()
|
|
48
|
+
);
|
|
49
|
+
if (match) prefillTechStack.push(match.value);
|
|
50
|
+
}
|
|
51
|
+
if (initialValues?.framework) {
|
|
52
|
+
const match = TECH_STACK_OPTIONS.find(
|
|
53
|
+
(o) => o.value.toLowerCase() === initialValues.framework!.toLowerCase()
|
|
54
|
+
);
|
|
55
|
+
if (match) prefillTechStack.push(match.value);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const currentPhase = PHASE_ORDER[phaseIndex]!;
|
|
59
|
+
|
|
60
|
+
function advanceToNext(key: string, value: string) {
|
|
61
|
+
const next = { ...answers, [key]: value };
|
|
62
|
+
setAnswers(next);
|
|
63
|
+
setError("");
|
|
64
|
+
|
|
65
|
+
if (phaseIndex < PHASE_ORDER.length - 1) {
|
|
66
|
+
setPhaseIndex(phaseIndex + 1);
|
|
67
|
+
} else {
|
|
68
|
+
onComplete({
|
|
69
|
+
problem: next.problem,
|
|
70
|
+
users: next.users,
|
|
71
|
+
techStack: next.techStack,
|
|
72
|
+
coreFeatures: (next.coreFeatures || "")
|
|
73
|
+
.split(",")
|
|
74
|
+
.map((s) => s.trim())
|
|
75
|
+
.filter(Boolean),
|
|
76
|
+
nonGoals: (next.nonGoals || "")
|
|
77
|
+
.split(",")
|
|
78
|
+
.map((s) => s.trim())
|
|
79
|
+
.filter(Boolean),
|
|
80
|
+
userStories: [],
|
|
81
|
+
envVars: [],
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function handleTextSubmit(key: string, required: boolean) {
|
|
87
|
+
return (value: string) => {
|
|
88
|
+
const val = value.trim();
|
|
89
|
+
if (required && !val) {
|
|
90
|
+
setError(`This field is required`);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
advanceToNext(key, val);
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function handleTechStackSubmit(values: string[]) {
|
|
98
|
+
advanceToNext("techStack", values.join(", "));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Build completed fields display
|
|
102
|
+
const completedFields = PHASE_ORDER.slice(0, phaseIndex).map((key) => (
|
|
103
|
+
<GutterLine key={key}>
|
|
104
|
+
<Text dimColor>
|
|
105
|
+
{key === "problem" && `Problem: ${answers[key]}`}
|
|
106
|
+
{key === "users" && `Users: ${answers[key]}`}
|
|
107
|
+
{key === "techStack" && `Tech stack: ${answers[key]}`}
|
|
108
|
+
{key === "coreFeatures" && `Core features: ${answers[key]}`}
|
|
109
|
+
{key === "nonGoals" && `Non-goals: ${answers[key]}`}
|
|
110
|
+
</Text>
|
|
111
|
+
</GutterLine>
|
|
112
|
+
));
|
|
113
|
+
|
|
114
|
+
const usersPlaceholder = answers["problem"]
|
|
115
|
+
? `People affected by: ${answers["problem"]}`
|
|
116
|
+
: "e.g. Developers, PMs, designers...";
|
|
117
|
+
|
|
118
|
+
return (
|
|
119
|
+
<>
|
|
120
|
+
{completedFields}
|
|
121
|
+
|
|
122
|
+
{currentPhase === "problem" && (
|
|
123
|
+
<GutterLine>
|
|
124
|
+
<Text bold>What problem does this solve? </Text>
|
|
125
|
+
<TextInput
|
|
126
|
+
placeholder="Describe the core problem..."
|
|
127
|
+
onSubmit={handleTextSubmit("problem", true)}
|
|
128
|
+
/>
|
|
129
|
+
</GutterLine>
|
|
130
|
+
)}
|
|
131
|
+
|
|
132
|
+
{currentPhase === "users" && (
|
|
133
|
+
<GutterLine>
|
|
134
|
+
<Text bold>Who are the users? </Text>
|
|
135
|
+
<TextInput
|
|
136
|
+
placeholder={usersPlaceholder}
|
|
137
|
+
onSubmit={handleTextSubmit("users", true)}
|
|
138
|
+
/>
|
|
139
|
+
</GutterLine>
|
|
140
|
+
)}
|
|
141
|
+
|
|
142
|
+
{currentPhase === "techStack" && (
|
|
143
|
+
<>
|
|
144
|
+
<GutterLine>
|
|
145
|
+
<Text bold>Tech stack:</Text>
|
|
146
|
+
</GutterLine>
|
|
147
|
+
<GutteredMultiSelect
|
|
148
|
+
options={TECH_STACK_OPTIONS}
|
|
149
|
+
initialSelected={prefillTechStack}
|
|
150
|
+
onSubmit={handleTechStackSubmit}
|
|
151
|
+
/>
|
|
152
|
+
</>
|
|
153
|
+
)}
|
|
154
|
+
|
|
155
|
+
{currentPhase === "coreFeatures" && (
|
|
156
|
+
<GutterLine>
|
|
157
|
+
<Text bold>Core features (comma-separated): </Text>
|
|
158
|
+
<TextInput
|
|
159
|
+
placeholder="e.g. Auth, Dashboard, API..."
|
|
160
|
+
onSubmit={handleTextSubmit("coreFeatures", true)}
|
|
161
|
+
/>
|
|
162
|
+
</GutterLine>
|
|
163
|
+
)}
|
|
164
|
+
|
|
165
|
+
{currentPhase === "nonGoals" && (
|
|
166
|
+
<GutterLine>
|
|
167
|
+
<Text bold>Non-goals (comma-separated): </Text>
|
|
168
|
+
<TextInput
|
|
169
|
+
placeholder="e.g. Mobile app, Real-time sync..."
|
|
170
|
+
onSubmit={handleTextSubmit("nonGoals", false)}
|
|
171
|
+
/>
|
|
172
|
+
</GutterLine>
|
|
173
|
+
)}
|
|
174
|
+
|
|
175
|
+
{error && (
|
|
176
|
+
<GutterLine>
|
|
177
|
+
<Text color="red">{error}</Text>
|
|
178
|
+
</GutterLine>
|
|
179
|
+
)}
|
|
180
|
+
</>
|
|
181
|
+
);
|
|
182
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { Text } from "ink";
|
|
3
|
+
import { TextInput } from "@inkjs/ui";
|
|
4
|
+
import { GutterLine } from "../components/gutter-line.js";
|
|
5
|
+
import { GutteredSelect } from "../components/guttered-select.js";
|
|
6
|
+
import type { WizardAnswers } from "../lib/types.js";
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
onComplete: (answers: Partial<WizardAnswers>) => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
type Step = "projectName" | "description" | "packageManager" | "license";
|
|
13
|
+
|
|
14
|
+
const STEP_ORDER: Step[] = [
|
|
15
|
+
"projectName",
|
|
16
|
+
"description",
|
|
17
|
+
"packageManager",
|
|
18
|
+
"license",
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
const PACKAGE_MANAGER_OPTIONS = [
|
|
22
|
+
{ label: "npm", value: "npm" },
|
|
23
|
+
{ label: "pnpm", value: "pnpm" },
|
|
24
|
+
{ label: "bun", value: "bun" },
|
|
25
|
+
{ label: "yarn", value: "yarn" },
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
const LICENSE_OPTIONS = [
|
|
29
|
+
{ label: "MIT — Permissive, allows commercial use", value: "MIT" },
|
|
30
|
+
{ label: "ISC — Simplified MIT, minimal restrictions", value: "ISC" },
|
|
31
|
+
{ label: "Apache-2.0 — Permissive with patent protection", value: "Apache-2.0" },
|
|
32
|
+
{ label: "GPL-3.0 — Copyleft, requires source disclosure", value: "GPL-3.0" },
|
|
33
|
+
{ label: "Unlicense — Public domain, no restrictions", value: "Unlicense" },
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
export default function ProjectInfo({ onComplete }: Props) {
|
|
37
|
+
const [stepIndex, setStepIndex] = useState(0);
|
|
38
|
+
const [answers, setAnswers] = useState<Record<string, string>>({});
|
|
39
|
+
const [error, setError] = useState("");
|
|
40
|
+
|
|
41
|
+
const currentStep = STEP_ORDER[stepIndex]!;
|
|
42
|
+
|
|
43
|
+
function advance(key: string, value: string) {
|
|
44
|
+
const next = { ...answers, [key]: value };
|
|
45
|
+
setAnswers(next);
|
|
46
|
+
setError("");
|
|
47
|
+
|
|
48
|
+
if (stepIndex < STEP_ORDER.length - 1) {
|
|
49
|
+
setStepIndex(stepIndex + 1);
|
|
50
|
+
} else {
|
|
51
|
+
onComplete(next as unknown as Partial<WizardAnswers>);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function handleTextSubmit(value: string) {
|
|
56
|
+
const val = value.trim();
|
|
57
|
+
if (!val) {
|
|
58
|
+
setError(
|
|
59
|
+
currentStep === "projectName"
|
|
60
|
+
? "Project name is required"
|
|
61
|
+
: "Description is required"
|
|
62
|
+
);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
advance(currentStep, val);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function handleSelectChange(key: string) {
|
|
69
|
+
return (value: string) => {
|
|
70
|
+
advance(key, value);
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Render completed fields
|
|
75
|
+
const completed = STEP_ORDER.slice(0, stepIndex).map((key) => (
|
|
76
|
+
<GutterLine key={key}>
|
|
77
|
+
<Text dimColor>
|
|
78
|
+
{key === "projectName" && `Project name: ${answers[key]}`}
|
|
79
|
+
{key === "description" && `Description: ${answers[key]}`}
|
|
80
|
+
{key === "packageManager" && `Package manager: ${answers[key]}`}
|
|
81
|
+
{key === "license" && `License: ${answers[key]}`}
|
|
82
|
+
</Text>
|
|
83
|
+
</GutterLine>
|
|
84
|
+
));
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<>
|
|
88
|
+
{completed}
|
|
89
|
+
|
|
90
|
+
{currentStep === "projectName" && (
|
|
91
|
+
<GutterLine>
|
|
92
|
+
<Text bold>Project name: </Text>
|
|
93
|
+
<TextInput placeholder="..." onSubmit={handleTextSubmit} />
|
|
94
|
+
</GutterLine>
|
|
95
|
+
)}
|
|
96
|
+
|
|
97
|
+
{currentStep === "description" && (
|
|
98
|
+
<GutterLine>
|
|
99
|
+
<Text bold>One-line description: </Text>
|
|
100
|
+
<TextInput placeholder="..." onSubmit={handleTextSubmit} />
|
|
101
|
+
</GutterLine>
|
|
102
|
+
)}
|
|
103
|
+
|
|
104
|
+
{currentStep === "packageManager" && (
|
|
105
|
+
<>
|
|
106
|
+
<GutterLine>
|
|
107
|
+
<Text bold>Package manager:</Text>
|
|
108
|
+
</GutterLine>
|
|
109
|
+
<GutteredSelect
|
|
110
|
+
options={PACKAGE_MANAGER_OPTIONS}
|
|
111
|
+
onChange={handleSelectChange("packageManager")}
|
|
112
|
+
/>
|
|
113
|
+
</>
|
|
114
|
+
)}
|
|
115
|
+
|
|
116
|
+
{currentStep === "license" && (
|
|
117
|
+
<>
|
|
118
|
+
<GutterLine>
|
|
119
|
+
<Text bold>License:</Text>
|
|
120
|
+
</GutterLine>
|
|
121
|
+
<GutteredSelect
|
|
122
|
+
options={LICENSE_OPTIONS}
|
|
123
|
+
onChange={handleSelectChange("license")}
|
|
124
|
+
/>
|
|
125
|
+
</>
|
|
126
|
+
)}
|
|
127
|
+
|
|
128
|
+
{error && (
|
|
129
|
+
<GutterLine>
|
|
130
|
+
<Text color="red">{error}</Text>
|
|
131
|
+
</GutterLine>
|
|
132
|
+
)}
|
|
133
|
+
</>
|
|
134
|
+
);
|
|
135
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { Text } from "ink";
|
|
3
|
+
import { TextInput } from "@inkjs/ui";
|
|
4
|
+
import { GutterLine } from "../components/gutter-line.js";
|
|
5
|
+
import { GutteredSelect } from "../components/guttered-select.js";
|
|
6
|
+
import type { WizardAnswers } from "../lib/types.js";
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
onComplete: (answers: Partial<WizardAnswers>) => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
type Mode = "choosing" | "quickstart" | "advanced";
|
|
13
|
+
type AdvancedField = "language" | "framework" | "codeStyle";
|
|
14
|
+
|
|
15
|
+
const PRESET_OPTIONS = [
|
|
16
|
+
{ label: "TypeScript + Node.js", value: "typescript-node" },
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
const ADVANCED_FIELDS: { key: AdvancedField; label: string }[] = [
|
|
20
|
+
{ key: "language", label: "Language" },
|
|
21
|
+
{ key: "framework", label: "Framework" },
|
|
22
|
+
{ key: "codeStyle", label: "Code style conventions (comma-separated)" },
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
const PRESETS: Record<string, Partial<WizardAnswers>> = {
|
|
26
|
+
"typescript-node": {
|
|
27
|
+
language: "TypeScript",
|
|
28
|
+
framework: "Node.js",
|
|
29
|
+
codeStyle: [
|
|
30
|
+
"TypeScript strict mode, ESM imports",
|
|
31
|
+
"camelCase for variables, PascalCase for types",
|
|
32
|
+
"stdlib -> external -> internal import ordering",
|
|
33
|
+
"Return errors, don't throw",
|
|
34
|
+
],
|
|
35
|
+
buildCommand: "npm run build",
|
|
36
|
+
testCommand: "npm run test",
|
|
37
|
+
typecheckCommand: "npm run typecheck",
|
|
38
|
+
lintCommand: "npm run lint",
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export default function StackStyle({ onComplete }: Props) {
|
|
43
|
+
const [mode, setMode] = useState<Mode>("choosing");
|
|
44
|
+
const [advancedIndex, setAdvancedIndex] = useState(0);
|
|
45
|
+
const [answers, setAnswers] = useState<Record<string, string>>({});
|
|
46
|
+
|
|
47
|
+
if (mode === "choosing") {
|
|
48
|
+
return (
|
|
49
|
+
<>
|
|
50
|
+
<GutterLine>
|
|
51
|
+
<Text bold>Setup mode:</Text>
|
|
52
|
+
</GutterLine>
|
|
53
|
+
<GutteredSelect
|
|
54
|
+
options={[
|
|
55
|
+
{ label: "QuickStart (use a preset)", value: "quickstart" },
|
|
56
|
+
{ label: "Advanced (manual config)", value: "advanced" },
|
|
57
|
+
]}
|
|
58
|
+
onChange={(val) => setMode(val as Mode)}
|
|
59
|
+
/>
|
|
60
|
+
</>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (mode === "quickstart") {
|
|
65
|
+
return (
|
|
66
|
+
<>
|
|
67
|
+
<GutterLine>
|
|
68
|
+
<Text bold>Select a stack preset:</Text>
|
|
69
|
+
</GutterLine>
|
|
70
|
+
<GutteredSelect
|
|
71
|
+
options={PRESET_OPTIONS}
|
|
72
|
+
onChange={(val) => {
|
|
73
|
+
const preset = PRESETS[val];
|
|
74
|
+
if (preset) {
|
|
75
|
+
onComplete(preset);
|
|
76
|
+
}
|
|
77
|
+
}}
|
|
78
|
+
/>
|
|
79
|
+
</>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Advanced mode
|
|
84
|
+
const current = ADVANCED_FIELDS[advancedIndex];
|
|
85
|
+
|
|
86
|
+
function handleSubmit(value: string) {
|
|
87
|
+
const next = { ...answers, [current!.key]: value };
|
|
88
|
+
setAnswers(next);
|
|
89
|
+
|
|
90
|
+
if (advancedIndex < ADVANCED_FIELDS.length - 1) {
|
|
91
|
+
setAdvancedIndex(advancedIndex + 1);
|
|
92
|
+
} else {
|
|
93
|
+
onComplete({
|
|
94
|
+
language: next.language,
|
|
95
|
+
framework: next.framework,
|
|
96
|
+
codeStyle: (next.codeStyle || "").split(",").map((s) => s.trim()),
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<>
|
|
103
|
+
{ADVANCED_FIELDS.slice(0, advancedIndex).map((f) => (
|
|
104
|
+
<GutterLine key={f.key}>
|
|
105
|
+
<Text dimColor>
|
|
106
|
+
{f.label}: {answers[f.key]}
|
|
107
|
+
</Text>
|
|
108
|
+
</GutterLine>
|
|
109
|
+
))}
|
|
110
|
+
|
|
111
|
+
<GutterLine>
|
|
112
|
+
<Text bold>{current!.label}: </Text>
|
|
113
|
+
<TextInput placeholder="..." onSubmit={handleSubmit} />
|
|
114
|
+
</GutterLine>
|
|
115
|
+
</>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# {{projectName}}
|
|
2
|
+
|
|
3
|
+
{{description}}
|
|
4
|
+
|
|
5
|
+
See docs/PRD.md for product requirements and docs/USER-STORIES.md for user stories with acceptance criteria.
|
|
6
|
+
|
|
7
|
+
## Build & Test
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
{{buildCommand}} # Build
|
|
11
|
+
{{typecheckCommand}} # Typecheck (fast — run first)
|
|
12
|
+
{{testCommand}} -- -t "name" # Test (specific)
|
|
13
|
+
{{testCommand}} # Test (full suite)
|
|
14
|
+
{{lintCommand}} # Lint
|
|
15
|
+
{{prCommand}} # Before creating a PR
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Code Style
|
|
19
|
+
|
|
20
|
+
{{#each codeStyle}}
|
|
21
|
+
- {{{this}}}
|
|
22
|
+
{{/each}}
|
|
23
|
+
|
|
24
|
+
## Architecture
|
|
25
|
+
|
|
26
|
+
{{{architecture}}}
|
|
27
|
+
|
|
28
|
+
## Workflow
|
|
29
|
+
|
|
30
|
+
### Planning
|
|
31
|
+
- Enter plan mode for ANY non-trivial task (3+ steps or architectural decisions)
|
|
32
|
+
- If something goes sideways, STOP and re-plan — don't keep pushing
|
|
33
|
+
- A good plan lets you one-shot the implementation
|
|
34
|
+
|
|
35
|
+
### Verification
|
|
36
|
+
- Never mark a task complete without proving it works
|
|
37
|
+
- Run tests, check logs, demonstrate correctness
|
|
38
|
+
- Ask yourself: "Would a staff engineer approve this?"
|
|
39
|
+
|
|
40
|
+
### Self-Improvement
|
|
41
|
+
- After ANY correction from the user: update the Lessons section below
|
|
42
|
+
- Write rules that prevent the same mistake from recurring
|
|
43
|
+
|
|
44
|
+
### Bug Fixing
|
|
45
|
+
- Given a bug report: just fix it. Don't ask for hand-holding
|
|
46
|
+
- Point at logs, errors, failing tests — then resolve them
|
|
47
|
+
|
|
48
|
+
### Elegance
|
|
49
|
+
- For non-trivial changes: pause and ask "is there a more elegant way?"
|
|
50
|
+
- If a fix feels hacky, step back and implement the clean solution
|
|
51
|
+
- Skip this for simple, obvious fixes — don't over-engineer
|
|
52
|
+
|
|
53
|
+
## Task Management
|
|
54
|
+
|
|
55
|
+
### Solo Work
|
|
56
|
+
1. Plan first (plan mode for complex tasks)
|
|
57
|
+
2. Track progress with TaskCreate/TaskUpdate for multi-step work
|
|
58
|
+
3. Verify before claiming done
|
|
59
|
+
4. Update Lessons after corrections
|
|
60
|
+
|
|
61
|
+
### Agent Teams
|
|
62
|
+
1. Plan-then-team: design approach BEFORE spawning a team
|
|
63
|
+
2. Each task = one clear deliverable with verification steps
|
|
64
|
+
3. File ownership: each teammate owns specific files — NEVER have two edit the same file
|
|
65
|
+
4. Include file paths and constraints in task descriptions — teammates don't inherit context
|
|
66
|
+
5. DMs by default, broadcast only for critical blocking issues
|
|
67
|
+
6. 3-5 teammates max
|
|
68
|
+
|
|
69
|
+
## Code Review & Fix Logging
|
|
70
|
+
|
|
71
|
+
Two tracking files live at the project root. Both are append-only logs — never delete or rewrite past entries.
|
|
72
|
+
|
|
73
|
+
### CODE-REVIEW.md — review findings
|
|
74
|
+
- Created/appended during code review sessions.
|
|
75
|
+
- Entry ID format: `CR-YYYYMMDD-###` (zero-padded sequence per day).
|
|
76
|
+
- Findings ordered by severity (`High`, `Medium`, `Low`).
|
|
77
|
+
- Every finding includes `file:line` evidence, a clear recommendation, and `Status` (`New`, `Still Open`, `Fixed`, `Regressed`, `Not Reproducible`).
|
|
78
|
+
|
|
79
|
+
### CODE-FIXED.md — fix results
|
|
80
|
+
- Created/appended when fixing findings from CODE-REVIEW.md.
|
|
81
|
+
- Entry ID format: `CF-YYYYMMDD-###`.
|
|
82
|
+
- Each entry references its source review by `CR-` Entry ID — do NOT re-describe findings, only document actions taken.
|
|
83
|
+
- Fixed findings include `file:line` for every change made.
|
|
84
|
+
- Deferred findings include rationale and a revisit trigger.
|
|
85
|
+
- Verification section must confirm `{{typecheckCommand}}` + `{{buildCommand}}` status after fixes.
|
|
86
|
+
|
|
87
|
+
### Procedure
|
|
88
|
+
1. **Reviewing**:
|
|
89
|
+
- **First**: Read CODE-REVIEW.md → Read CODE-FIXED.md → understand current state of findings and fixes.
|
|
90
|
+
- **Then**: Read codebase → append findings to CODE-REVIEW.md with a new `CR-` entry (write to CODE-REVIEW.md only, never modify CODE-FIXED.md).
|
|
91
|
+
- **Never** fix code during a reviewing process.
|
|
92
|
+
2. **Fixing**:
|
|
93
|
+
- **First**: Read CODE-REVIEW.md → Read CODE-FIXED.md → identify unfixed findings.
|
|
94
|
+
- **Then**: Fix only unfixed findings → append a new `CF-` entry to CODE-FIXED.md (write to CODE-FIXED.md only, never modify CODE-REVIEW.md).
|
|
95
|
+
- **Never** perform a code review during a fixing process.
|
|
96
|
+
|
|
97
|
+
## Core Principles
|
|
98
|
+
|
|
99
|
+
- **Simplicity First**: Make every change as simple as possible. Minimal code impact.
|
|
100
|
+
- **No Laziness**: Find root causes. No temporary fixes. Senior developer standards.
|
|
101
|
+
- **Minimal Impact**: Changes should only touch what's necessary.
|
|
102
|
+
|
|
103
|
+
## Lessons
|
|
104
|
+
|
|
105
|
+
<!-- After ANY correction, add a rule here. This section compounds over time. -->
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# {{projectName}}
|
|
2
|
+
|
|
3
|
+
{{description}}
|
|
4
|
+
|
|
5
|
+
## Getting Started
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
{{packageManager}} install
|
|
9
|
+
{{buildCommand}}
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Development
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
{{lintCommand}} # Lint
|
|
16
|
+
{{testCommand}} # Run tests
|
|
17
|
+
{{lintCommand}} && {{testCommand}} # Before creating a PR
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Architecture
|
|
21
|
+
|
|
22
|
+
{{{architectureNotes}}}
|
|
23
|
+
|
|
24
|
+
## License
|
|
25
|
+
|
|
26
|
+
{{license}}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# {{projectName}} — Product Requirements
|
|
2
|
+
|
|
3
|
+
## Vision
|
|
4
|
+
|
|
5
|
+
{{description}}
|
|
6
|
+
|
|
7
|
+
## Problem
|
|
8
|
+
|
|
9
|
+
{{problem}}
|
|
10
|
+
|
|
11
|
+
## Users
|
|
12
|
+
|
|
13
|
+
{{users}}
|
|
14
|
+
|
|
15
|
+
## Core Features
|
|
16
|
+
|
|
17
|
+
{{#each coreFeatures}}
|
|
18
|
+
- {{{this}}}
|
|
19
|
+
{{/each}}
|
|
20
|
+
|
|
21
|
+
## Non-Goals
|
|
22
|
+
|
|
23
|
+
{{#each nonGoals}}
|
|
24
|
+
- {{{this}}}
|
|
25
|
+
{{/each}}
|
|
26
|
+
|
|
27
|
+
## Tech Stack
|
|
28
|
+
|
|
29
|
+
{{techStack}}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# {{projectName}} — User Stories
|
|
2
|
+
|
|
3
|
+
{{#if userStories.length}}
|
|
4
|
+
{{#each userStories}}
|
|
5
|
+
## {{title}}
|
|
6
|
+
|
|
7
|
+
**As a** {{asA}}
|
|
8
|
+
**I want** {{iWant}}
|
|
9
|
+
**So that** {{soThat}}
|
|
10
|
+
|
|
11
|
+
### Feature: {{feature}}
|
|
12
|
+
|
|
13
|
+
{{#each scenarios}}
|
|
14
|
+
```gherkin
|
|
15
|
+
Scenario: {{this.name}}
|
|
16
|
+
Given {{this.given}}
|
|
17
|
+
When {{this.when}}
|
|
18
|
+
Then {{this.then}}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
{{/each}}
|
|
22
|
+
{{/each}}
|
|
23
|
+
{{else}}
|
|
24
|
+
<!-- TODO: Add user stories for {{projectName}} -->
|
|
25
|
+
<!-- Use the format below to define acceptance criteria in Gherkin style. -->
|
|
26
|
+
|
|
27
|
+
## Example: User can perform core action
|
|
28
|
+
|
|
29
|
+
**As a** user
|
|
30
|
+
**I want** to perform a core action
|
|
31
|
+
**So that** I can achieve my goal
|
|
32
|
+
|
|
33
|
+
### Feature: Core Action
|
|
34
|
+
|
|
35
|
+
```gherkin
|
|
36
|
+
Scenario: Successful core action
|
|
37
|
+
Given the user is authenticated
|
|
38
|
+
When the user performs the core action
|
|
39
|
+
Then the system processes the request
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
> Replace this example with real user stories for your project.
|
|
45
|
+
> Each story should follow the **As a / I want / So that** format with Gherkin scenarios.
|
|
46
|
+
{{/if}}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "Node16",
|
|
5
|
+
"moduleResolution": "Node16",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"outDir": "dist",
|
|
10
|
+
"rootDir": "src",
|
|
11
|
+
"jsx": "react-jsx",
|
|
12
|
+
"declaration": true,
|
|
13
|
+
"sourceMap": true
|
|
14
|
+
},
|
|
15
|
+
"include": ["src"],
|
|
16
|
+
"exclude": ["node_modules", "dist"]
|
|
17
|
+
}
|