procedure-cli 0.1.0 → 0.1.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/CODE-REVIEW.md +32 -0
- package/dist/steps/stack-style.js +65 -22
- package/dist/steps/stack-style.js.map +1 -1
- package/package.json +1 -1
- package/src/steps/stack-style.tsx +102 -31
package/CODE-REVIEW.md
CHANGED
|
@@ -251,3 +251,35 @@
|
|
|
251
251
|
|
|
252
252
|
### Residual Risks / Testing Gaps
|
|
253
253
|
- No automated UI tests currently cover end-to-end Powerline branching (`gitCommitted` true/false).
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## Entry 2026-02-21
|
|
258
|
+
|
|
259
|
+
### Entry ID
|
|
260
|
+
- `CR-20260221-006`
|
|
261
|
+
|
|
262
|
+
### Supersedes
|
|
263
|
+
- `CR-20260221-005`
|
|
264
|
+
|
|
265
|
+
### Scope
|
|
266
|
+
- New review cycle after latest documented fix entry (`CF-20260222-003`).
|
|
267
|
+
- Validate current status of previously fixed findings and scan for regressions.
|
|
268
|
+
|
|
269
|
+
### Validation Matrix
|
|
270
|
+
| Finding # | Previous Severity | Current Status | Evidence | Notes |
|
|
271
|
+
|-----------|-------------------|----------------|----------|-------|
|
|
272
|
+
| 9 | Low | Fixed | `CODE-FIXED.md:112`, `src/steps/powerline.tsx:22`, `src/steps/powerline.tsx:127`, `src/steps/powerline.tsx:132` | Commit outcome is now reflected in UI; mixed success/warning state is resolved. |
|
|
273
|
+
| 6 | Low | Fixed | `CODE-FIXED.md:54`, `templates/docs/USER-STORIES.md.hbs:3`, `templates/docs/USER-STORIES.md.hbs:23` | USER-STORIES fallback remains present and active when story array is empty. |
|
|
274
|
+
|
|
275
|
+
### Findings (ordered by severity)
|
|
276
|
+
- No new functional findings identified in this review cycle.
|
|
277
|
+
|
|
278
|
+
### Verification Notes
|
|
279
|
+
- Procedure compliance: read latest `CODE-REVIEW.md` and latest `CODE-FIXED.md` before review.
|
|
280
|
+
- `npm run typecheck`: pass.
|
|
281
|
+
- `npm run build`: pass.
|
|
282
|
+
- Code inspection confirms `CF-20260222-003` changes remain present in `src/steps/powerline.tsx`.
|
|
283
|
+
|
|
284
|
+
### Residual Risks / Testing Gaps
|
|
285
|
+
- No automated UI tests currently cover end-to-end Powerline branching (`gitCommitted` true/false).
|
|
@@ -3,15 +3,49 @@ import { useState } from "react";
|
|
|
3
3
|
import { Text } from "ink";
|
|
4
4
|
import { TextInput } from "@inkjs/ui";
|
|
5
5
|
import { GutterLine } from "../components/gutter-line.js";
|
|
6
|
-
import { GutteredSelect } from "../components/guttered-select.js";
|
|
6
|
+
import { GutteredSelect, GutteredMultiSelect } from "../components/guttered-select.js";
|
|
7
7
|
const PRESET_OPTIONS = [
|
|
8
8
|
{ label: "TypeScript + Node.js", value: "typescript-node" },
|
|
9
9
|
];
|
|
10
|
-
const
|
|
11
|
-
{
|
|
12
|
-
{
|
|
13
|
-
{
|
|
10
|
+
const LANGUAGE_OPTIONS = [
|
|
11
|
+
{ label: "TypeScript", value: "TypeScript", description: "Typed JavaScript, strict mode recommended" },
|
|
12
|
+
{ label: "JavaScript", value: "JavaScript", description: "Dynamic, runs everywhere" },
|
|
13
|
+
{ label: "Python", value: "Python", description: "Readable, great ecosystem for AI/ML & web" },
|
|
14
|
+
{ label: "Go", value: "Go", description: "Fast compilation, built-in concurrency" },
|
|
15
|
+
{ label: "Rust", value: "Rust", description: "Memory-safe systems programming" },
|
|
16
|
+
{ label: "Java", value: "Java", description: "Enterprise-grade, JVM ecosystem" },
|
|
17
|
+
{ label: "Ruby", value: "Ruby", description: "Developer happiness, Rails ecosystem" },
|
|
18
|
+
{ label: "PHP", value: "PHP", description: "Web-native, Laravel & WordPress" },
|
|
19
|
+
{ label: "Swift", value: "Swift", description: "Apple platforms, type-safe" },
|
|
20
|
+
{ label: "Kotlin", value: "Kotlin", description: "Modern JVM, Android-first" },
|
|
14
21
|
];
|
|
22
|
+
const FRAMEWORK_OPTIONS = [
|
|
23
|
+
{ label: "Node.js", value: "Node.js", description: "JS/TS server runtime" },
|
|
24
|
+
{ label: "React", value: "React", description: "Component-based UI library" },
|
|
25
|
+
{ label: "Next.js", value: "Next.js", description: "Full-stack React framework" },
|
|
26
|
+
{ label: "Vue", value: "Vue", description: "Progressive UI framework" },
|
|
27
|
+
{ label: "Svelte", value: "Svelte", description: "Compile-time UI framework" },
|
|
28
|
+
{ label: "Express", value: "Express", description: "Minimal Node.js web framework" },
|
|
29
|
+
{ label: "Fastify", value: "Fastify", description: "Fast Node.js web framework" },
|
|
30
|
+
{ label: "Django", value: "Django", description: "Batteries-included Python web" },
|
|
31
|
+
{ label: "Flask", value: "Flask", description: "Lightweight Python web" },
|
|
32
|
+
{ label: "Rails", value: "Rails", description: "Convention-over-config Ruby web" },
|
|
33
|
+
];
|
|
34
|
+
/** Suggest frameworks based on selected languages */
|
|
35
|
+
function getFrameworkPreselect(languages) {
|
|
36
|
+
const langSet = new Set(languages.map((l) => l.toLowerCase()));
|
|
37
|
+
const preselect = [];
|
|
38
|
+
if (langSet.has("typescript") || langSet.has("javascript")) {
|
|
39
|
+
preselect.push("Node.js");
|
|
40
|
+
}
|
|
41
|
+
if (langSet.has("python")) {
|
|
42
|
+
preselect.push("Django");
|
|
43
|
+
}
|
|
44
|
+
if (langSet.has("ruby")) {
|
|
45
|
+
preselect.push("Rails");
|
|
46
|
+
}
|
|
47
|
+
return preselect;
|
|
48
|
+
}
|
|
15
49
|
const PRESETS = {
|
|
16
50
|
"typescript-node": {
|
|
17
51
|
language: "TypeScript",
|
|
@@ -30,8 +64,9 @@ const PRESETS = {
|
|
|
30
64
|
};
|
|
31
65
|
export default function StackStyle({ onComplete }) {
|
|
32
66
|
const [mode, setMode] = useState("choosing");
|
|
33
|
-
const [
|
|
34
|
-
const [
|
|
67
|
+
const [advancedStep, setAdvancedStep] = useState("language");
|
|
68
|
+
const [selectedLanguages, setSelectedLanguages] = useState([]);
|
|
69
|
+
const [selectedFrameworks, setSelectedFrameworks] = useState([]);
|
|
35
70
|
if (mode === "choosing") {
|
|
36
71
|
return (_jsxs(_Fragment, { children: [_jsx(GutterLine, { children: _jsx(Text, { bold: true, children: "Setup mode:" }) }), _jsx(GutteredSelect, { options: [
|
|
37
72
|
{ label: "QuickStart (use a preset)", value: "quickstart" },
|
|
@@ -47,21 +82,29 @@ export default function StackStyle({ onComplete }) {
|
|
|
47
82
|
} })] }));
|
|
48
83
|
}
|
|
49
84
|
// Advanced mode
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
});
|
|
63
|
-
}
|
|
85
|
+
const advStep = advancedStep;
|
|
86
|
+
if (advStep === "language") {
|
|
87
|
+
return (_jsxs(_Fragment, { children: [_jsx(GutterLine, { children: _jsx(Text, { bold: true, children: "Programming languages:" }) }), _jsx(GutteredMultiSelect, { options: LANGUAGE_OPTIONS, onSubmit: (values) => {
|
|
88
|
+
setSelectedLanguages(values);
|
|
89
|
+
setAdvancedStep("framework");
|
|
90
|
+
} })] }));
|
|
91
|
+
}
|
|
92
|
+
if (advStep === "framework") {
|
|
93
|
+
return (_jsxs(_Fragment, { children: [_jsx(GutterLine, { children: _jsxs(Text, { dimColor: true, children: ["Languages: ", selectedLanguages.join(", ")] }) }), _jsx(GutterLine, { children: _jsx(Text, { bold: true, children: "Frameworks:" }) }), _jsx(GutteredMultiSelect, { options: FRAMEWORK_OPTIONS, initialSelected: getFrameworkPreselect(selectedLanguages), onSubmit: (values) => {
|
|
94
|
+
setSelectedFrameworks(values);
|
|
95
|
+
setAdvancedStep("codeStyle");
|
|
96
|
+
} })] }));
|
|
64
97
|
}
|
|
65
|
-
|
|
98
|
+
// codeStyle step
|
|
99
|
+
return (_jsxs(_Fragment, { children: [_jsx(GutterLine, { children: _jsxs(Text, { dimColor: true, children: ["Languages: ", selectedLanguages.join(", ")] }) }), _jsx(GutterLine, { children: _jsxs(Text, { dimColor: true, children: ["Frameworks: ", selectedFrameworks.join(", ")] }) }), _jsxs(GutterLine, { children: [_jsx(Text, { bold: true, children: "Code style conventions (comma-separated): " }), _jsx(TextInput, { placeholder: "e.g. strict mode, camelCase, ESM imports", onSubmit: (value) => {
|
|
100
|
+
onComplete({
|
|
101
|
+
language: selectedLanguages.join(", "),
|
|
102
|
+
framework: selectedFrameworks.join(", "),
|
|
103
|
+
codeStyle: value
|
|
104
|
+
.split(",")
|
|
105
|
+
.map((s) => s.trim())
|
|
106
|
+
.filter(Boolean),
|
|
107
|
+
});
|
|
108
|
+
} })] })] }));
|
|
66
109
|
}
|
|
67
110
|
//# sourceMappingURL=stack-style.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stack-style.js","sourceRoot":"","sources":["../../src/steps/stack-style.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAC3B,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;
|
|
1
|
+
{"version":3,"file":"stack-style.js","sourceRoot":"","sources":["../../src/steps/stack-style.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@ import React, { useState } from "react";
|
|
|
2
2
|
import { Text } from "ink";
|
|
3
3
|
import { TextInput } from "@inkjs/ui";
|
|
4
4
|
import { GutterLine } from "../components/gutter-line.js";
|
|
5
|
-
import { GutteredSelect } from "../components/guttered-select.js";
|
|
5
|
+
import { GutteredSelect, GutteredMultiSelect } from "../components/guttered-select.js";
|
|
6
6
|
import type { WizardAnswers } from "../lib/types.js";
|
|
7
7
|
|
|
8
8
|
interface Props {
|
|
@@ -10,18 +10,54 @@ interface Props {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
type Mode = "choosing" | "quickstart" | "advanced";
|
|
13
|
-
type
|
|
13
|
+
type AdvancedStep = "language" | "framework" | "codeStyle";
|
|
14
14
|
|
|
15
15
|
const PRESET_OPTIONS = [
|
|
16
16
|
{ label: "TypeScript + Node.js", value: "typescript-node" },
|
|
17
17
|
];
|
|
18
18
|
|
|
19
|
-
const
|
|
20
|
-
{
|
|
21
|
-
{
|
|
22
|
-
{
|
|
19
|
+
const LANGUAGE_OPTIONS = [
|
|
20
|
+
{ label: "TypeScript", value: "TypeScript", description: "Typed JavaScript, strict mode recommended" },
|
|
21
|
+
{ label: "JavaScript", value: "JavaScript", description: "Dynamic, runs everywhere" },
|
|
22
|
+
{ label: "Python", value: "Python", description: "Readable, great ecosystem for AI/ML & web" },
|
|
23
|
+
{ label: "Go", value: "Go", description: "Fast compilation, built-in concurrency" },
|
|
24
|
+
{ label: "Rust", value: "Rust", description: "Memory-safe systems programming" },
|
|
25
|
+
{ label: "Java", value: "Java", description: "Enterprise-grade, JVM ecosystem" },
|
|
26
|
+
{ label: "Ruby", value: "Ruby", description: "Developer happiness, Rails ecosystem" },
|
|
27
|
+
{ label: "PHP", value: "PHP", description: "Web-native, Laravel & WordPress" },
|
|
28
|
+
{ label: "Swift", value: "Swift", description: "Apple platforms, type-safe" },
|
|
29
|
+
{ label: "Kotlin", value: "Kotlin", description: "Modern JVM, Android-first" },
|
|
23
30
|
];
|
|
24
31
|
|
|
32
|
+
const FRAMEWORK_OPTIONS = [
|
|
33
|
+
{ label: "Node.js", value: "Node.js", description: "JS/TS server runtime" },
|
|
34
|
+
{ label: "React", value: "React", description: "Component-based UI library" },
|
|
35
|
+
{ label: "Next.js", value: "Next.js", description: "Full-stack React framework" },
|
|
36
|
+
{ label: "Vue", value: "Vue", description: "Progressive UI framework" },
|
|
37
|
+
{ label: "Svelte", value: "Svelte", description: "Compile-time UI framework" },
|
|
38
|
+
{ label: "Express", value: "Express", description: "Minimal Node.js web framework" },
|
|
39
|
+
{ label: "Fastify", value: "Fastify", description: "Fast Node.js web framework" },
|
|
40
|
+
{ label: "Django", value: "Django", description: "Batteries-included Python web" },
|
|
41
|
+
{ label: "Flask", value: "Flask", description: "Lightweight Python web" },
|
|
42
|
+
{ label: "Rails", value: "Rails", description: "Convention-over-config Ruby web" },
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
/** Suggest frameworks based on selected languages */
|
|
46
|
+
function getFrameworkPreselect(languages: string[]): string[] {
|
|
47
|
+
const langSet = new Set(languages.map((l) => l.toLowerCase()));
|
|
48
|
+
const preselect: string[] = [];
|
|
49
|
+
if (langSet.has("typescript") || langSet.has("javascript")) {
|
|
50
|
+
preselect.push("Node.js");
|
|
51
|
+
}
|
|
52
|
+
if (langSet.has("python")) {
|
|
53
|
+
preselect.push("Django");
|
|
54
|
+
}
|
|
55
|
+
if (langSet.has("ruby")) {
|
|
56
|
+
preselect.push("Rails");
|
|
57
|
+
}
|
|
58
|
+
return preselect;
|
|
59
|
+
}
|
|
60
|
+
|
|
25
61
|
const PRESETS: Record<string, Partial<WizardAnswers>> = {
|
|
26
62
|
"typescript-node": {
|
|
27
63
|
language: "TypeScript",
|
|
@@ -41,8 +77,9 @@ const PRESETS: Record<string, Partial<WizardAnswers>> = {
|
|
|
41
77
|
|
|
42
78
|
export default function StackStyle({ onComplete }: Props) {
|
|
43
79
|
const [mode, setMode] = useState<Mode>("choosing");
|
|
44
|
-
const [
|
|
45
|
-
const [
|
|
80
|
+
const [advancedStep, setAdvancedStep] = useState<AdvancedStep>("language");
|
|
81
|
+
const [selectedLanguages, setSelectedLanguages] = useState<string[]>([]);
|
|
82
|
+
const [selectedFrameworks, setSelectedFrameworks] = useState<string[]>([]);
|
|
46
83
|
|
|
47
84
|
if (mode === "choosing") {
|
|
48
85
|
return (
|
|
@@ -81,36 +118,70 @@ export default function StackStyle({ onComplete }: Props) {
|
|
|
81
118
|
}
|
|
82
119
|
|
|
83
120
|
// Advanced mode
|
|
84
|
-
const
|
|
121
|
+
const advStep = advancedStep as AdvancedStep;
|
|
85
122
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
123
|
+
if (advStep === "language") {
|
|
124
|
+
return (
|
|
125
|
+
<>
|
|
126
|
+
<GutterLine>
|
|
127
|
+
<Text bold>Programming languages:</Text>
|
|
128
|
+
</GutterLine>
|
|
129
|
+
<GutteredMultiSelect
|
|
130
|
+
options={LANGUAGE_OPTIONS}
|
|
131
|
+
onSubmit={(values) => {
|
|
132
|
+
setSelectedLanguages(values);
|
|
133
|
+
setAdvancedStep("framework");
|
|
134
|
+
}}
|
|
135
|
+
/>
|
|
136
|
+
</>
|
|
137
|
+
);
|
|
138
|
+
}
|
|
89
139
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
140
|
+
if (advStep === "framework") {
|
|
141
|
+
return (
|
|
142
|
+
<>
|
|
143
|
+
<GutterLine>
|
|
144
|
+
<Text dimColor>Languages: {selectedLanguages.join(", ")}</Text>
|
|
145
|
+
</GutterLine>
|
|
146
|
+
<GutterLine>
|
|
147
|
+
<Text bold>Frameworks:</Text>
|
|
148
|
+
</GutterLine>
|
|
149
|
+
<GutteredMultiSelect
|
|
150
|
+
options={FRAMEWORK_OPTIONS}
|
|
151
|
+
initialSelected={getFrameworkPreselect(selectedLanguages)}
|
|
152
|
+
onSubmit={(values) => {
|
|
153
|
+
setSelectedFrameworks(values);
|
|
154
|
+
setAdvancedStep("codeStyle");
|
|
155
|
+
}}
|
|
156
|
+
/>
|
|
157
|
+
</>
|
|
158
|
+
);
|
|
99
159
|
}
|
|
100
160
|
|
|
161
|
+
// codeStyle step
|
|
101
162
|
return (
|
|
102
163
|
<>
|
|
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
164
|
<GutterLine>
|
|
112
|
-
<Text
|
|
113
|
-
|
|
165
|
+
<Text dimColor>Languages: {selectedLanguages.join(", ")}</Text>
|
|
166
|
+
</GutterLine>
|
|
167
|
+
<GutterLine>
|
|
168
|
+
<Text dimColor>Frameworks: {selectedFrameworks.join(", ")}</Text>
|
|
169
|
+
</GutterLine>
|
|
170
|
+
<GutterLine>
|
|
171
|
+
<Text bold>Code style conventions (comma-separated): </Text>
|
|
172
|
+
<TextInput
|
|
173
|
+
placeholder="e.g. strict mode, camelCase, ESM imports"
|
|
174
|
+
onSubmit={(value) => {
|
|
175
|
+
onComplete({
|
|
176
|
+
language: selectedLanguages.join(", "),
|
|
177
|
+
framework: selectedFrameworks.join(", "),
|
|
178
|
+
codeStyle: value
|
|
179
|
+
.split(",")
|
|
180
|
+
.map((s) => s.trim())
|
|
181
|
+
.filter(Boolean),
|
|
182
|
+
});
|
|
183
|
+
}}
|
|
184
|
+
/>
|
|
114
185
|
</GutterLine>
|
|
115
186
|
</>
|
|
116
187
|
);
|