procedure-cli 0.1.1 → 0.1.2
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 +2 -1
- package/dist/components/guttered-select.d.ts +6 -1
- package/dist/components/guttered-select.js +33 -8
- package/dist/components/guttered-select.js.map +1 -1
- package/dist/lib/fs.d.ts +1 -0
- package/dist/lib/fs.js +4 -0
- package/dist/lib/fs.js.map +1 -1
- package/dist/lib/template.d.ts +18 -0
- package/dist/lib/template.js +67 -2
- package/dist/lib/template.js.map +1 -1
- package/dist/steps/generation.js +36 -3
- package/dist/steps/generation.js.map +1 -1
- package/dist/steps/stack-style.js +19 -13
- package/dist/steps/stack-style.js.map +1 -1
- package/package.json +5 -2
- package/src/components/guttered-select.tsx +76 -9
- package/src/lib/fs.ts +5 -0
- package/src/lib/template.ts +104 -2
- package/src/steps/generation.tsx +65 -4
- package/src/steps/stack-style.tsx +28 -15
- package/templates/bin/release.sh.hbs +101 -0
|
@@ -16,10 +16,15 @@ interface GutteredMultiSelectProps {
|
|
|
16
16
|
options: Option[];
|
|
17
17
|
initialSelected?: string[];
|
|
18
18
|
onSubmit: (values: string[]) => void;
|
|
19
|
+
/** When true, adds a "✎ Other" option that opens a free text input */
|
|
20
|
+
allowCustom?: boolean;
|
|
21
|
+
/** Placeholder shown in the custom text input */
|
|
22
|
+
customPlaceholder?: string;
|
|
19
23
|
}
|
|
20
24
|
/**
|
|
21
25
|
* Multi-select with gutter. Space to toggle, Enter to confirm.
|
|
22
26
|
* Shows ● for selected, ○ for unselected. Hint at bottom.
|
|
27
|
+
* When allowCustom is true, an "Other" option lets users type custom values.
|
|
23
28
|
*/
|
|
24
|
-
export declare function GutteredMultiSelect({ options, initialSelected, onSubmit }: GutteredMultiSelectProps): import("react/jsx-runtime").JSX.Element;
|
|
29
|
+
export declare function GutteredMultiSelect({ options, initialSelected, onSubmit, allowCustom, customPlaceholder, }: GutteredMultiSelectProps): import("react/jsx-runtime").JSX.Element;
|
|
25
30
|
export {};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useState } from "react";
|
|
3
3
|
import { Box, Text, useInput } from "ink";
|
|
4
|
+
import { TextInput } from "@inkjs/ui";
|
|
4
5
|
/**
|
|
5
6
|
* A custom Select component that renders each option with the vertical
|
|
6
7
|
* gutter prefix (│) so the timeline line stays continuous.
|
|
@@ -26,22 +27,28 @@ export function GutteredSelect({ options, onChange }) {
|
|
|
26
27
|
return (_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "│ " }), isActive ? (_jsx(Text, { color: "cyan", bold: true, children: "❯ " })) : (_jsx(Text, { children: " " })), _jsx(Text, { bold: isActive, children: option.label }), option.description && (_jsx(Text, { dimColor: true, children: " " + option.description }))] }, option.value));
|
|
27
28
|
}) }));
|
|
28
29
|
}
|
|
30
|
+
const CUSTOM_SENTINEL = "__custom__";
|
|
29
31
|
/**
|
|
30
32
|
* Multi-select with gutter. Space to toggle, Enter to confirm.
|
|
31
33
|
* Shows ● for selected, ○ for unselected. Hint at bottom.
|
|
34
|
+
* When allowCustom is true, an "Other" option lets users type custom values.
|
|
32
35
|
*/
|
|
33
|
-
export function GutteredMultiSelect({ options, initialSelected, onSubmit }) {
|
|
36
|
+
export function GutteredMultiSelect({ options, initialSelected, onSubmit, allowCustom, customPlaceholder, }) {
|
|
37
|
+
const allOptions = allowCustom
|
|
38
|
+
? [...options, { label: "Other (type custom)", value: CUSTOM_SENTINEL, description: "Add your own" }]
|
|
39
|
+
: options;
|
|
34
40
|
const [activeIndex, setActiveIndex] = useState(0);
|
|
35
41
|
const [selected, setSelected] = useState(new Set(initialSelected ?? []));
|
|
42
|
+
const [phase, setPhase] = useState("selecting");
|
|
36
43
|
useInput((input, key) => {
|
|
37
44
|
if (key.upArrow) {
|
|
38
45
|
setActiveIndex((prev) => (prev > 0 ? prev - 1 : prev));
|
|
39
46
|
}
|
|
40
47
|
else if (key.downArrow) {
|
|
41
|
-
setActiveIndex((prev) => prev <
|
|
48
|
+
setActiveIndex((prev) => prev < allOptions.length - 1 ? prev + 1 : prev);
|
|
42
49
|
}
|
|
43
50
|
else if (input === " ") {
|
|
44
|
-
const option =
|
|
51
|
+
const option = allOptions[activeIndex];
|
|
45
52
|
if (option) {
|
|
46
53
|
setSelected((prev) => {
|
|
47
54
|
const next = new Set(prev);
|
|
@@ -56,13 +63,31 @@ export function GutteredMultiSelect({ options, initialSelected, onSubmit }) {
|
|
|
56
63
|
}
|
|
57
64
|
}
|
|
58
65
|
else if (key.return) {
|
|
59
|
-
|
|
66
|
+
if (selected.has(CUSTOM_SENTINEL)) {
|
|
67
|
+
setSelected((prev) => {
|
|
68
|
+
const next = new Set(prev);
|
|
69
|
+
next.delete(CUSTOM_SENTINEL);
|
|
70
|
+
return next;
|
|
71
|
+
});
|
|
72
|
+
setPhase("custom");
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
onSubmit(Array.from(selected));
|
|
76
|
+
}
|
|
60
77
|
}
|
|
61
|
-
});
|
|
62
|
-
|
|
78
|
+
}, { isActive: phase === "selecting" });
|
|
79
|
+
if (phase === "custom") {
|
|
80
|
+
const currentSelections = Array.from(selected);
|
|
81
|
+
return (_jsxs(Box, { flexDirection: "column", children: [currentSelections.length > 0 && (_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "│ " }), _jsx(Text, { color: "green", children: "Selected: " }), _jsx(Text, { children: currentSelections.join(", ") })] })), _jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "│ " }), _jsx(Text, { bold: true, children: "Add custom (comma-separated):" })] }), _jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "│ " }), _jsx(TextInput, { placeholder: customPlaceholder ?? "Type here, press enter to confirm", onSubmit: (value) => {
|
|
82
|
+
const custom = value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
83
|
+
onSubmit([...currentSelections, ...custom]);
|
|
84
|
+
} })] })] }));
|
|
85
|
+
}
|
|
86
|
+
return (_jsxs(Box, { flexDirection: "column", children: [allOptions.map((option, index) => {
|
|
63
87
|
const isActive = index === activeIndex;
|
|
64
88
|
const isSelected = selected.has(option.value);
|
|
65
|
-
|
|
66
|
-
|
|
89
|
+
const isCustomOption = option.value === CUSTOM_SENTINEL;
|
|
90
|
+
return (_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "│ " }), isActive ? (_jsx(Text, { color: "cyan", bold: true, children: "❯ " })) : (_jsx(Text, { children: " " })), isSelected ? (_jsx(Text, { color: isCustomOption ? "yellow" : "green", children: "● " })) : (_jsx(Text, { dimColor: true, children: "○ " })), _jsx(Text, { bold: isActive, children: isCustomOption ? `✎ ${option.label}` : option.label }), option.description && (_jsx(Text, { dimColor: true, children: " — " + option.description }))] }, option.value));
|
|
91
|
+
}), _jsxs(Text, { dimColor: true, children: ["│ ", " ↑↓ move, space toggle, enter confirm"] }), selected.size > 0 && !selected.has(CUSTOM_SENTINEL) && (_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "│ " }), _jsx(Text, { color: "green", children: "Selected: " }), _jsx(Text, { children: Array.from(selected).join(", ") })] })), selected.size > 0 && selected.has(CUSTOM_SENTINEL) && (_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "│ " }), _jsx(Text, { color: "green", children: "Selected: " }), _jsx(Text, { children: Array.from(selected).filter((v) => v !== CUSTOM_SENTINEL).join(", ") }), selected.size > 1 && _jsx(Text, { dimColor: true, children: " + custom" }), selected.size === 1 && _jsx(Text, { dimColor: true, children: "custom (on confirm)" })] }))] }));
|
|
67
92
|
}
|
|
68
93
|
//# sourceMappingURL=guttered-select.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guttered-select.js","sourceRoot":"","sources":["../../src/components/guttered-select.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACxC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"guttered-select.js","sourceRoot":"","sources":["../../src/components/guttered-select.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACxC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AActC;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAuB;IACvE,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAElD,QAAQ,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACtB,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;YAChB,cAAc,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACzD,CAAC;aAAM,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;YACzB,cAAc,CAAC,CAAC,IAAI,EAAE,EAAE,CACtB,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAC5C,CAAC;QACJ,CAAC;aAAM,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;YACtB,MAAM,QAAQ,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;YACtC,IAAI,QAAQ,EAAE,CAAC;gBACb,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;YAC3B,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,CACL,KAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,YACxB,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE;YAC7B,MAAM,QAAQ,GAAG,KAAK,KAAK,WAAW,CAAC;YACvC,OAAO,CACL,MAAC,IAAI,eACH,KAAC,IAAI,IAAC,QAAQ,kBAAE,KAAK,GAAQ,EAC5B,QAAQ,CAAC,CAAC,CAAC,CACV,KAAC,IAAI,IAAC,KAAK,EAAC,MAAM,EAAC,IAAI,kBAAE,IAAI,GAAQ,CACtC,CAAC,CAAC,CAAC,CACF,KAAC,IAAI,cAAE,IAAI,GAAQ,CACpB,EACD,KAAC,IAAI,IAAC,IAAI,EAAE,QAAQ,YAAG,MAAM,CAAC,KAAK,GAAQ,EAC1C,MAAM,CAAC,WAAW,IAAI,CACrB,KAAC,IAAI,IAAC,QAAQ,kBAAE,GAAG,GAAG,MAAM,CAAC,WAAW,GAAQ,CACjD,KAVQ,MAAM,CAAC,KAAK,CAWhB,CACR,CAAC;QACJ,CAAC,CAAC,GACE,CACP,CAAC;AACJ,CAAC;AAYD,MAAM,eAAe,GAAG,YAAY,CAAC;AAErC;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,EAClC,OAAO,EACP,eAAe,EACf,QAAQ,EACR,WAAW,EACX,iBAAiB,GACQ;IACzB,MAAM,UAAU,GAAG,WAAW;QAC5B,CAAC,CAAC,CAAC,GAAG,OAAO,EAAE,EAAE,KAAK,EAAE,qBAAqB,EAAE,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,cAAc,EAAE,CAAC;QACrG,CAAC,CAAC,OAAO,CAAC;IAEZ,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAClD,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CACtC,IAAI,GAAG,CAAC,eAAe,IAAI,EAAE,CAAC,CAC/B,CAAC;IACF,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAyB,WAAW,CAAC,CAAC;IAExE,QAAQ,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACtB,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;YAChB,cAAc,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACzD,CAAC;aAAM,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;YACzB,cAAc,CAAC,CAAC,IAAI,EAAE,EAAE,CACtB,IAAI,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAC/C,CAAC;QACJ,CAAC;aAAM,IAAI,KAAK,KAAK,GAAG,EAAE,CAAC;YACzB,MAAM,MAAM,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC;YACvC,IAAI,MAAM,EAAE,CAAC;gBACX,WAAW,CAAC,CAAC,IAAI,EAAE,EAAE;oBACnB,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC;oBAC3B,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;wBAC3B,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;oBAC5B,CAAC;yBAAM,CAAC;wBACN,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;oBACzB,CAAC;oBACD,OAAO,IAAI,CAAC;gBACd,CAAC,CAAC,CAAC;YACL,CAAC;QACH,CAAC;aAAM,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;YACtB,IAAI,QAAQ,CAAC,GAAG,CAAC,eAAe,CAAC,EAAE,CAAC;gBAClC,WAAW,CAAC,CAAC,IAAI,EAAE,EAAE;oBACnB,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC;oBAC3B,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;oBAC7B,OAAO,IAAI,CAAC;gBACd,CAAC,CAAC,CAAC;gBACH,QAAQ,CAAC,QAAQ,CAAC,CAAC;YACrB,CAAC;iBAAM,CAAC;gBACN,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;YACjC,CAAC;QACH,CAAC;IACH,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,KAAK,WAAW,EAAE,CAAC,CAAC;IAExC,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvB,MAAM,iBAAiB,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC/C,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,aACxB,iBAAiB,CAAC,MAAM,GAAG,CAAC,IAAI,CAC/B,MAAC,IAAI,eACH,KAAC,IAAI,IAAC,QAAQ,kBAAE,KAAK,GAAQ,EAC7B,KAAC,IAAI,IAAC,KAAK,EAAC,OAAO,YAAE,YAAY,GAAQ,EACzC,KAAC,IAAI,cAAE,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,GAAQ,IACtC,CACR,EACD,MAAC,IAAI,eACH,KAAC,IAAI,IAAC,QAAQ,kBAAE,KAAK,GAAQ,EAC7B,KAAC,IAAI,IAAC,IAAI,kBAAE,+BAA+B,GAAQ,IAC9C,EACP,MAAC,IAAI,eACH,KAAC,IAAI,IAAC,QAAQ,kBAAE,KAAK,GAAQ,EAC7B,KAAC,SAAS,IACR,WAAW,EAAE,iBAAiB,IAAI,mCAAmC,EACrE,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE;gCAClB,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;gCACrE,QAAQ,CAAC,CAAC,GAAG,iBAAiB,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC;4BAC9C,CAAC,GACD,IACG,IACH,CACP,CAAC;IACJ,CAAC;IAED,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,aACxB,UAAU,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE;gBAChC,MAAM,QAAQ,GAAG,KAAK,KAAK,WAAW,CAAC;gBACvC,MAAM,UAAU,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBAC9C,MAAM,cAAc,GAAG,MAAM,CAAC,KAAK,KAAK,eAAe,CAAC;gBACxD,OAAO,CACL,MAAC,IAAI,eACH,KAAC,IAAI,IAAC,QAAQ,kBAAE,KAAK,GAAQ,EAC5B,QAAQ,CAAC,CAAC,CAAC,CACV,KAAC,IAAI,IAAC,KAAK,EAAC,MAAM,EAAC,IAAI,kBAAE,IAAI,GAAQ,CACtC,CAAC,CAAC,CAAC,CACF,KAAC,IAAI,cAAE,IAAI,GAAQ,CACpB,EACA,UAAU,CAAC,CAAC,CAAC,CACZ,KAAC,IAAI,IAAC,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,YAAG,IAAI,GAAQ,CAChE,CAAC,CAAC,CAAC,CACF,KAAC,IAAI,IAAC,QAAQ,kBAAE,IAAI,GAAQ,CAC7B,EACD,KAAC,IAAI,IAAC,IAAI,EAAE,QAAQ,YAAG,cAAc,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,GAAQ,EACjF,MAAM,CAAC,WAAW,IAAI,CACrB,KAAC,IAAI,IAAC,QAAQ,kBAAE,KAAK,GAAG,MAAM,CAAC,WAAW,GAAQ,CACnD,KAfQ,MAAM,CAAC,KAAK,CAgBhB,CACR,CAAC;YACJ,CAAC,CAAC,EACF,MAAC,IAAI,IAAC,QAAQ,mBAAE,KAAK,EAAE,wCAAwC,IAAQ,EACtE,QAAQ,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,CACtD,MAAC,IAAI,eACH,KAAC,IAAI,IAAC,QAAQ,kBAAE,KAAK,GAAQ,EAC7B,KAAC,IAAI,IAAC,KAAK,EAAC,OAAO,YAAE,YAAY,GAAQ,EACzC,KAAC,IAAI,cAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAQ,IACzC,CACR,EACA,QAAQ,CAAC,IAAI,GAAG,CAAC,IAAI,QAAQ,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,CACrD,MAAC,IAAI,eACH,KAAC,IAAI,IAAC,QAAQ,kBAAE,KAAK,GAAQ,EAC7B,KAAC,IAAI,IAAC,KAAK,EAAC,OAAO,YAAE,YAAY,GAAQ,EACzC,KAAC,IAAI,cAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,eAAe,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAQ,EAClF,QAAQ,CAAC,IAAI,GAAG,CAAC,IAAI,KAAC,IAAI,IAAC,QAAQ,kBAAE,WAAW,GAAQ,EACxD,QAAQ,CAAC,IAAI,KAAK,CAAC,IAAI,KAAC,IAAI,IAAC,QAAQ,kBAAE,qBAAqB,GAAQ,IAChE,CACR,IACG,CACP,CAAC;AACJ,CAAC"}
|
package/dist/lib/fs.d.ts
CHANGED
package/dist/lib/fs.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { mkdirSync, existsSync } from 'node:fs';
|
|
2
2
|
import { resolve, dirname } from 'node:path';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
3
4
|
import { fileURLToPath } from 'node:url';
|
|
4
5
|
const __filename = fileURLToPath(import.meta.url);
|
|
5
6
|
const __dirname = dirname(__filename);
|
|
@@ -12,4 +13,7 @@ export function fileExists(filePath) {
|
|
|
12
13
|
export function resolveTemplatePath(templateName) {
|
|
13
14
|
return resolve(__dirname, '..', '..', 'templates', templateName);
|
|
14
15
|
}
|
|
16
|
+
export function homeBinPath(filename) {
|
|
17
|
+
return resolve(homedir(), 'bin', filename);
|
|
18
|
+
}
|
|
15
19
|
//# sourceMappingURL=fs.js.map
|
package/dist/lib/fs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fs.js","sourceRoot":"","sources":["../../src/lib/fs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtC,MAAM,UAAU,SAAS,CAAC,OAAe;IACvC,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,QAAgB;IACzC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,YAAoB;IACtD,OAAO,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC;AACnE,CAAC"}
|
|
1
|
+
{"version":3,"file":"fs.js","sourceRoot":"","sources":["../../src/lib/fs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtC,MAAM,UAAU,SAAS,CAAC,OAAe;IACvC,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,QAAgB;IACzC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,YAAoB;IACtD,OAAO,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,QAAgB;IAC1C,OAAO,OAAO,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;AAC7C,CAAC"}
|
package/dist/lib/template.d.ts
CHANGED
|
@@ -7,3 +7,21 @@ export declare function writeTemplate(templatePath: string, outputPath: string,
|
|
|
7
7
|
*/
|
|
8
8
|
export declare function checkConflicts(targetDir: string): string[];
|
|
9
9
|
export declare function scaffoldAll(targetDir: string, data: WizardAnswers): void;
|
|
10
|
+
export interface ReleaseConflicts {
|
|
11
|
+
releaseScriptExists: boolean;
|
|
12
|
+
packageJsonHasRelease: boolean;
|
|
13
|
+
packageJsonExists: boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare function checkReleaseConflicts(projectName: string, targetDir: string): ReleaseConflicts;
|
|
16
|
+
export interface ScaffoldReleaseResult {
|
|
17
|
+
scriptPath: string;
|
|
18
|
+
skipped: boolean;
|
|
19
|
+
reason?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare function scaffoldRelease(projectName: string, data: WizardAnswers): ScaffoldReleaseResult;
|
|
22
|
+
export interface PackageJsonReleaseResult {
|
|
23
|
+
created: boolean;
|
|
24
|
+
modified: boolean;
|
|
25
|
+
skipped: boolean;
|
|
26
|
+
}
|
|
27
|
+
export declare function ensurePackageJsonReleaseScripts(targetDir: string, projectName: string): PackageJsonReleaseResult;
|
package/dist/lib/template.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync, chmodSync } from 'node:fs';
|
|
2
2
|
import { resolve, dirname } from 'node:path';
|
|
3
3
|
import Handlebars from 'handlebars';
|
|
4
|
-
import { ensureDir, resolveTemplatePath } from './fs.js';
|
|
4
|
+
import { ensureDir, resolveTemplatePath, homeBinPath } from './fs.js';
|
|
5
|
+
Handlebars.registerHelper('eq', (a, b) => a === b);
|
|
5
6
|
export function renderTemplate(templatePath, data) {
|
|
6
7
|
const source = readFileSync(templatePath, 'utf-8');
|
|
7
8
|
const template = Handlebars.compile(source);
|
|
@@ -43,4 +44,68 @@ export function scaffoldAll(targetDir, data) {
|
|
|
43
44
|
ensureDir(dirname(gitignoreDest));
|
|
44
45
|
writeFileSync(gitignoreDest, content, 'utf-8');
|
|
45
46
|
}
|
|
47
|
+
export function checkReleaseConflicts(projectName, targetDir) {
|
|
48
|
+
const scriptPath = homeBinPath(`${projectName}-release`);
|
|
49
|
+
const pkgPath = resolve(targetDir, 'package.json');
|
|
50
|
+
let packageJsonExists = false;
|
|
51
|
+
let packageJsonHasRelease = false;
|
|
52
|
+
if (existsSync(pkgPath)) {
|
|
53
|
+
packageJsonExists = true;
|
|
54
|
+
try {
|
|
55
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
56
|
+
packageJsonHasRelease = !!pkg.scripts?.['release:patch'];
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// malformed package.json — treat as no release scripts
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
releaseScriptExists: existsSync(scriptPath),
|
|
64
|
+
packageJsonHasRelease,
|
|
65
|
+
packageJsonExists,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
export function scaffoldRelease(projectName, data) {
|
|
69
|
+
const scriptPath = homeBinPath(`${projectName}-release`);
|
|
70
|
+
if (existsSync(scriptPath)) {
|
|
71
|
+
return { scriptPath, skipped: true, reason: 'already exists' };
|
|
72
|
+
}
|
|
73
|
+
const templatePath = resolveTemplatePath('bin/release.sh.hbs');
|
|
74
|
+
const rendered = renderTemplate(templatePath, data);
|
|
75
|
+
ensureDir(dirname(scriptPath));
|
|
76
|
+
writeFileSync(scriptPath, rendered, 'utf-8');
|
|
77
|
+
chmodSync(scriptPath, 0o755);
|
|
78
|
+
return { scriptPath, skipped: false };
|
|
79
|
+
}
|
|
80
|
+
export function ensurePackageJsonReleaseScripts(targetDir, projectName) {
|
|
81
|
+
const pkgPath = resolve(targetDir, 'package.json');
|
|
82
|
+
const releaseScripts = {
|
|
83
|
+
'release:patch': `"$HOME/bin/${projectName}-release" patch`,
|
|
84
|
+
'release:minor': `"$HOME/bin/${projectName}-release" minor`,
|
|
85
|
+
'release:major': `"$HOME/bin/${projectName}-release" major`,
|
|
86
|
+
};
|
|
87
|
+
if (existsSync(pkgPath)) {
|
|
88
|
+
try {
|
|
89
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
90
|
+
if (pkg.scripts?.['release:patch']) {
|
|
91
|
+
return { created: false, modified: false, skipped: true };
|
|
92
|
+
}
|
|
93
|
+
pkg.scripts = { ...pkg.scripts, ...releaseScripts };
|
|
94
|
+
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf-8');
|
|
95
|
+
return { created: false, modified: true, skipped: false };
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
// malformed — skip
|
|
99
|
+
return { created: false, modified: false, skipped: true };
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const pkg = {
|
|
103
|
+
name: projectName,
|
|
104
|
+
version: '0.1.0',
|
|
105
|
+
scripts: releaseScripts,
|
|
106
|
+
};
|
|
107
|
+
ensureDir(dirname(pkgPath));
|
|
108
|
+
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf-8');
|
|
109
|
+
return { created: true, modified: false, skipped: false };
|
|
110
|
+
}
|
|
46
111
|
//# sourceMappingURL=template.js.map
|
package/dist/lib/template.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"template.js","sourceRoot":"","sources":["../../src/lib/template.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"template.js","sourceRoot":"","sources":["../../src/lib/template.ts"],"names":[],"mappings":"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"}
|
package/dist/steps/generation.js
CHANGED
|
@@ -3,7 +3,7 @@ import { useEffect, useState } from "react";
|
|
|
3
3
|
import { Text } from "ink";
|
|
4
4
|
import { ConfirmInput, Spinner } from "@inkjs/ui";
|
|
5
5
|
import { GutterLine } from "../components/gutter-line.js";
|
|
6
|
-
import { scaffoldAll, checkConflicts } from "../lib/template.js";
|
|
6
|
+
import { scaffoldAll, checkConflicts, checkReleaseConflicts, scaffoldRelease, ensurePackageJsonReleaseScripts, } from "../lib/template.js";
|
|
7
7
|
const FILES_TO_GENERATE = [
|
|
8
8
|
"CLAUDE.md",
|
|
9
9
|
"README.md",
|
|
@@ -15,12 +15,34 @@ const FILES_TO_GENERATE = [
|
|
|
15
15
|
export default function Generation({ answers, onComplete }) {
|
|
16
16
|
const [phase, setPhase] = useState("summary");
|
|
17
17
|
const [errorMsg, setErrorMsg] = useState("");
|
|
18
|
+
const [releaseResult, setReleaseResult] = useState("");
|
|
18
19
|
useEffect(() => {
|
|
19
20
|
if (phase !== "running")
|
|
20
21
|
return;
|
|
21
22
|
try {
|
|
22
23
|
const targetDir = process.cwd();
|
|
24
|
+
const projectName = answers.projectName || "untitled";
|
|
23
25
|
scaffoldAll(targetDir, answers);
|
|
26
|
+
// Release script scaffolding
|
|
27
|
+
const releaseRes = scaffoldRelease(projectName, answers);
|
|
28
|
+
const pkgRes = ensurePackageJsonReleaseScripts(targetDir, projectName);
|
|
29
|
+
const parts = [];
|
|
30
|
+
if (releaseRes.skipped) {
|
|
31
|
+
parts.push(`~/bin/${projectName}-release (skipped, ${releaseRes.reason})`);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
parts.push(`~/bin/${projectName}-release (created)`);
|
|
35
|
+
}
|
|
36
|
+
if (pkgRes.skipped) {
|
|
37
|
+
parts.push("package.json release scripts (skipped, already present)");
|
|
38
|
+
}
|
|
39
|
+
else if (pkgRes.modified) {
|
|
40
|
+
parts.push("package.json release scripts (added)");
|
|
41
|
+
}
|
|
42
|
+
else if (pkgRes.created) {
|
|
43
|
+
parts.push("package.json (created with release scripts)");
|
|
44
|
+
}
|
|
45
|
+
setReleaseResult(parts.join(", "));
|
|
24
46
|
setPhase("done");
|
|
25
47
|
const timer = setTimeout(() => onComplete({ generationSkipped: false }), 1500);
|
|
26
48
|
return () => clearTimeout(timer);
|
|
@@ -47,7 +69,18 @@ export default function Generation({ answers, onComplete }) {
|
|
|
47
69
|
.filter(Boolean)
|
|
48
70
|
.join(", ");
|
|
49
71
|
const conflicts = checkConflicts(process.cwd());
|
|
50
|
-
|
|
72
|
+
const projectName = answers.projectName || "untitled";
|
|
73
|
+
const releaseConflicts = checkReleaseConflicts(projectName, process.cwd());
|
|
74
|
+
const scriptDisplay = `~/bin/${projectName}-release`;
|
|
75
|
+
const scriptStatus = releaseConflicts.releaseScriptExists
|
|
76
|
+
? "exists, will skip"
|
|
77
|
+
: "new";
|
|
78
|
+
const pkgStatus = releaseConflicts.packageJsonHasRelease
|
|
79
|
+
? "has release scripts, will skip"
|
|
80
|
+
: releaseConflicts.packageJsonExists
|
|
81
|
+
? "will add release scripts"
|
|
82
|
+
: "will create with release scripts";
|
|
83
|
+
return (_jsxs(_Fragment, { children: [_jsx(GutterLine, { children: _jsxs(Text, { children: ["Project: ", _jsx(Text, { bold: true, children: answers.projectName || "untitled" }), " — ", answers.description || "no description"] }) }), _jsx(GutterLine, { children: _jsxs(Text, { children: ["Stack: ", stack, pmAndLicense ? ` (${pmAndLicense})` : ""] }) }), _jsx(GutterLine, { children: _jsxs(Text, { children: ["Build: ", answers.buildCommand || "N/A", " | Test:", " ", answers.testCommand || "N/A"] }) }), _jsx(GutterLine, { children: _jsxs(Text, { children: ["Architecture: ", answers.architecture || "N/A"] }) }), _jsx(GutterLine, { children: _jsxs(Text, { children: ["Features: ", answers.coreFeatures.length, " | Non-goals:", " ", answers.nonGoals.length] }) }), _jsx(GutterLine, { children: _jsx(Text, { children: " " }) }), _jsx(GutterLine, { children: _jsx(Text, { children: "Files to generate:" }) }), _jsx(GutterLine, { children: _jsxs(Text, { children: [" ", FILES_TO_GENERATE.join(", ")] }) }), conflicts.length > 0 && (_jsxs(_Fragment, { children: [_jsx(GutterLine, { children: _jsx(Text, { children: " " }) }), _jsx(GutterLine, { children: _jsxs(Text, { color: "yellow", children: ["⚠ Will overwrite: ", conflicts.join(", ")] }) })] })), _jsx(GutterLine, { children: _jsx(Text, { children: " " }) }), _jsx(GutterLine, { children: _jsx(Text, { children: "Release setup:" }) }), _jsx(GutterLine, { children: _jsxs(Text, { children: [" ", scriptDisplay, " \u2192 ", scriptStatus] }) }), _jsx(GutterLine, { children: _jsxs(Text, { children: [" package.json \u2192 ", pkgStatus] }) }), _jsx(GutterLine, { children: _jsx(Text, { children: " " }) }), _jsxs(GutterLine, { children: [_jsx(Text, { children: "Proceed? " }), _jsx(ConfirmInput, { onConfirm: handleConfirm, onCancel: handleCancel })] })] }));
|
|
51
84
|
}
|
|
52
85
|
if (phase === "running") {
|
|
53
86
|
return (_jsx(GutterLine, { children: _jsx(Spinner, { label: "Generating project files..." }) }));
|
|
@@ -55,6 +88,6 @@ export default function Generation({ answers, onComplete }) {
|
|
|
55
88
|
if (phase === "error") {
|
|
56
89
|
return (_jsx(GutterLine, { children: _jsxs(Text, { color: "red", children: ["Error generating files: ", errorMsg] }) }));
|
|
57
90
|
}
|
|
58
|
-
return (_jsx(GutterLine, { children: _jsx(Text, { color: "green", children: "All project files generated successfully." }) }));
|
|
91
|
+
return (_jsxs(_Fragment, { children: [_jsx(GutterLine, { children: _jsx(Text, { color: "green", children: "All project files generated successfully." }) }), releaseResult && (_jsx(GutterLine, { children: _jsxs(Text, { color: "green", children: ["Release: ", releaseResult] }) }))] }));
|
|
59
92
|
}
|
|
60
93
|
//# sourceMappingURL=generation.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generation.js","sourceRoot":"","sources":["../../src/steps/generation.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAC1D,OAAO,
|
|
1
|
+
{"version":3,"file":"generation.js","sourceRoot":"","sources":["../../src/steps/generation.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useState } from "react";
|
|
3
3
|
import { Text } from "ink";
|
|
4
|
-
import { TextInput } from "@inkjs/ui";
|
|
5
4
|
import { GutterLine } from "../components/gutter-line.js";
|
|
6
5
|
import { GutteredSelect, GutteredMultiSelect } from "../components/guttered-select.js";
|
|
7
6
|
const PRESET_OPTIONS = [
|
|
@@ -31,6 +30,16 @@ const FRAMEWORK_OPTIONS = [
|
|
|
31
30
|
{ label: "Flask", value: "Flask", description: "Lightweight Python web" },
|
|
32
31
|
{ label: "Rails", value: "Rails", description: "Convention-over-config Ruby web" },
|
|
33
32
|
];
|
|
33
|
+
const CODE_STYLE_OPTIONS = [
|
|
34
|
+
{ label: "Strict mode", value: "Strict mode", description: "TypeScript strict, ESLint strict" },
|
|
35
|
+
{ label: "camelCase / PascalCase", value: "camelCase for variables, PascalCase for types", description: "Standard JS/TS naming" },
|
|
36
|
+
{ label: "ESM imports", value: "ESM imports, no CommonJS require", description: "Modern module syntax" },
|
|
37
|
+
{ label: "Import ordering", value: "stdlib → external → internal import ordering", description: "Grouped imports" },
|
|
38
|
+
{ label: "Return errors, don't throw", value: "Return errors, don't throw", description: "Explicit error handling" },
|
|
39
|
+
{ label: "Functional style", value: "Prefer functional patterns", description: "Pure functions, immutability" },
|
|
40
|
+
{ label: "No any type", value: "No any type, use unknown", description: "Strict type safety" },
|
|
41
|
+
{ label: "JSDoc comments", value: "JSDoc for public APIs", description: "Documentation standard" },
|
|
42
|
+
];
|
|
34
43
|
/** Suggest frameworks based on selected languages */
|
|
35
44
|
function getFrameworkPreselect(languages) {
|
|
36
45
|
const langSet = new Set(languages.map((l) => l.toLowerCase()));
|
|
@@ -84,27 +93,24 @@ export default function StackStyle({ onComplete }) {
|
|
|
84
93
|
// Advanced mode
|
|
85
94
|
const advStep = advancedStep;
|
|
86
95
|
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) => {
|
|
96
|
+
return (_jsxs(_Fragment, { children: [_jsx(GutterLine, { children: _jsx(Text, { bold: true, children: "Programming languages:" }) }), _jsx(GutteredMultiSelect, { options: LANGUAGE_OPTIONS, allowCustom: true, customPlaceholder: "e.g. Elixir, Zig, C++", onSubmit: (values) => {
|
|
88
97
|
setSelectedLanguages(values);
|
|
89
98
|
setAdvancedStep("framework");
|
|
90
99
|
} })] }));
|
|
91
100
|
}
|
|
92
101
|
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) => {
|
|
102
|
+
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), allowCustom: true, customPlaceholder: "e.g. Hono, Remix, Astro", onSubmit: (values) => {
|
|
94
103
|
setSelectedFrameworks(values);
|
|
95
104
|
setAdvancedStep("codeStyle");
|
|
96
105
|
} })] }));
|
|
97
106
|
}
|
|
98
107
|
// 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(", ")] }) }),
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
.filter(Boolean),
|
|
107
|
-
});
|
|
108
|
-
} })] })] }));
|
|
108
|
+
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(", ")] }) }), _jsx(GutterLine, { children: _jsx(Text, { bold: true, children: "Code style conventions:" }) }), _jsx(GutteredMultiSelect, { options: CODE_STYLE_OPTIONS, allowCustom: true, customPlaceholder: "e.g. tabs over spaces, max 80 chars", onSubmit: (values) => {
|
|
109
|
+
onComplete({
|
|
110
|
+
language: selectedLanguages.join(", "),
|
|
111
|
+
framework: selectedFrameworks.join(", "),
|
|
112
|
+
codeStyle: values,
|
|
113
|
+
});
|
|
114
|
+
} })] }));
|
|
109
115
|
}
|
|
110
116
|
//# 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,
|
|
1
|
+
{"version":3,"file":"stack-style.js","sourceRoot":"","sources":["../../src/steps/stack-style.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "procedure-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "CLI-based AI Agent powered by AI SDK 6, Z.ai, and OpenAI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -10,7 +10,10 @@
|
|
|
10
10
|
"dev": "tsx src/cli.tsx",
|
|
11
11
|
"build": "tsc",
|
|
12
12
|
"typecheck": "tsc --noEmit",
|
|
13
|
-
"lint": "tsc --noEmit"
|
|
13
|
+
"lint": "tsc --noEmit",
|
|
14
|
+
"release:patch": "\"$HOME/bin/procedure-release\" patch",
|
|
15
|
+
"release:minor": "\"$HOME/bin/procedure-release\" minor",
|
|
16
|
+
"release:major": "\"$HOME/bin/procedure-release\" major"
|
|
14
17
|
},
|
|
15
18
|
"keywords": [
|
|
16
19
|
"cli",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { useState } from "react";
|
|
2
2
|
import { Box, Text, useInput } from "ink";
|
|
3
|
+
import { TextInput } from "@inkjs/ui";
|
|
3
4
|
// Note: Box flexDirection="column" kept here for multi-option rendering
|
|
4
5
|
|
|
5
6
|
interface Option {
|
|
@@ -62,27 +63,45 @@ interface GutteredMultiSelectProps {
|
|
|
62
63
|
options: Option[];
|
|
63
64
|
initialSelected?: string[];
|
|
64
65
|
onSubmit: (values: string[]) => void;
|
|
66
|
+
/** When true, adds a "✎ Other" option that opens a free text input */
|
|
67
|
+
allowCustom?: boolean;
|
|
68
|
+
/** Placeholder shown in the custom text input */
|
|
69
|
+
customPlaceholder?: string;
|
|
65
70
|
}
|
|
66
71
|
|
|
72
|
+
const CUSTOM_SENTINEL = "__custom__";
|
|
73
|
+
|
|
67
74
|
/**
|
|
68
75
|
* Multi-select with gutter. Space to toggle, Enter to confirm.
|
|
69
76
|
* Shows ● for selected, ○ for unselected. Hint at bottom.
|
|
77
|
+
* When allowCustom is true, an "Other" option lets users type custom values.
|
|
70
78
|
*/
|
|
71
|
-
export function GutteredMultiSelect({
|
|
79
|
+
export function GutteredMultiSelect({
|
|
80
|
+
options,
|
|
81
|
+
initialSelected,
|
|
82
|
+
onSubmit,
|
|
83
|
+
allowCustom,
|
|
84
|
+
customPlaceholder,
|
|
85
|
+
}: GutteredMultiSelectProps) {
|
|
86
|
+
const allOptions = allowCustom
|
|
87
|
+
? [...options, { label: "Other (type custom)", value: CUSTOM_SENTINEL, description: "Add your own" }]
|
|
88
|
+
: options;
|
|
89
|
+
|
|
72
90
|
const [activeIndex, setActiveIndex] = useState(0);
|
|
73
91
|
const [selected, setSelected] = useState<Set<string>>(
|
|
74
92
|
new Set(initialSelected ?? [])
|
|
75
93
|
);
|
|
94
|
+
const [phase, setPhase] = useState<"selecting" | "custom">("selecting");
|
|
76
95
|
|
|
77
96
|
useInput((input, key) => {
|
|
78
97
|
if (key.upArrow) {
|
|
79
98
|
setActiveIndex((prev) => (prev > 0 ? prev - 1 : prev));
|
|
80
99
|
} else if (key.downArrow) {
|
|
81
100
|
setActiveIndex((prev) =>
|
|
82
|
-
prev <
|
|
101
|
+
prev < allOptions.length - 1 ? prev + 1 : prev
|
|
83
102
|
);
|
|
84
103
|
} else if (input === " ") {
|
|
85
|
-
const option =
|
|
104
|
+
const option = allOptions[activeIndex];
|
|
86
105
|
if (option) {
|
|
87
106
|
setSelected((prev) => {
|
|
88
107
|
const next = new Set(prev);
|
|
@@ -95,15 +114,54 @@ export function GutteredMultiSelect({ options, initialSelected, onSubmit }: Gutt
|
|
|
95
114
|
});
|
|
96
115
|
}
|
|
97
116
|
} else if (key.return) {
|
|
98
|
-
|
|
117
|
+
if (selected.has(CUSTOM_SENTINEL)) {
|
|
118
|
+
setSelected((prev) => {
|
|
119
|
+
const next = new Set(prev);
|
|
120
|
+
next.delete(CUSTOM_SENTINEL);
|
|
121
|
+
return next;
|
|
122
|
+
});
|
|
123
|
+
setPhase("custom");
|
|
124
|
+
} else {
|
|
125
|
+
onSubmit(Array.from(selected));
|
|
126
|
+
}
|
|
99
127
|
}
|
|
100
|
-
});
|
|
128
|
+
}, { isActive: phase === "selecting" });
|
|
129
|
+
|
|
130
|
+
if (phase === "custom") {
|
|
131
|
+
const currentSelections = Array.from(selected);
|
|
132
|
+
return (
|
|
133
|
+
<Box flexDirection="column">
|
|
134
|
+
{currentSelections.length > 0 && (
|
|
135
|
+
<Text>
|
|
136
|
+
<Text dimColor>{"│ "}</Text>
|
|
137
|
+
<Text color="green">{"Selected: "}</Text>
|
|
138
|
+
<Text>{currentSelections.join(", ")}</Text>
|
|
139
|
+
</Text>
|
|
140
|
+
)}
|
|
141
|
+
<Text>
|
|
142
|
+
<Text dimColor>{"│ "}</Text>
|
|
143
|
+
<Text bold>{"Add custom (comma-separated):"}</Text>
|
|
144
|
+
</Text>
|
|
145
|
+
<Text>
|
|
146
|
+
<Text dimColor>{"│ "}</Text>
|
|
147
|
+
<TextInput
|
|
148
|
+
placeholder={customPlaceholder ?? "Type here, press enter to confirm"}
|
|
149
|
+
onSubmit={(value) => {
|
|
150
|
+
const custom = value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
151
|
+
onSubmit([...currentSelections, ...custom]);
|
|
152
|
+
}}
|
|
153
|
+
/>
|
|
154
|
+
</Text>
|
|
155
|
+
</Box>
|
|
156
|
+
);
|
|
157
|
+
}
|
|
101
158
|
|
|
102
159
|
return (
|
|
103
160
|
<Box flexDirection="column">
|
|
104
|
-
{
|
|
161
|
+
{allOptions.map((option, index) => {
|
|
105
162
|
const isActive = index === activeIndex;
|
|
106
163
|
const isSelected = selected.has(option.value);
|
|
164
|
+
const isCustomOption = option.value === CUSTOM_SENTINEL;
|
|
107
165
|
return (
|
|
108
166
|
<Text key={option.value}>
|
|
109
167
|
<Text dimColor>{"│ "}</Text>
|
|
@@ -113,11 +171,11 @@ export function GutteredMultiSelect({ options, initialSelected, onSubmit }: Gutt
|
|
|
113
171
|
<Text>{" "}</Text>
|
|
114
172
|
)}
|
|
115
173
|
{isSelected ? (
|
|
116
|
-
<Text color="green">{"● "}</Text>
|
|
174
|
+
<Text color={isCustomOption ? "yellow" : "green"}>{"● "}</Text>
|
|
117
175
|
) : (
|
|
118
176
|
<Text dimColor>{"○ "}</Text>
|
|
119
177
|
)}
|
|
120
|
-
<Text bold={isActive}>{option.label}</Text>
|
|
178
|
+
<Text bold={isActive}>{isCustomOption ? `✎ ${option.label}` : option.label}</Text>
|
|
121
179
|
{option.description && (
|
|
122
180
|
<Text dimColor>{" — " + option.description}</Text>
|
|
123
181
|
)}
|
|
@@ -125,13 +183,22 @@ export function GutteredMultiSelect({ options, initialSelected, onSubmit }: Gutt
|
|
|
125
183
|
);
|
|
126
184
|
})}
|
|
127
185
|
<Text dimColor>{"│ "}{" ↑↓ move, space toggle, enter confirm"}</Text>
|
|
128
|
-
{selected.size > 0 && (
|
|
186
|
+
{selected.size > 0 && !selected.has(CUSTOM_SENTINEL) && (
|
|
129
187
|
<Text>
|
|
130
188
|
<Text dimColor>{"│ "}</Text>
|
|
131
189
|
<Text color="green">{"Selected: "}</Text>
|
|
132
190
|
<Text>{Array.from(selected).join(", ")}</Text>
|
|
133
191
|
</Text>
|
|
134
192
|
)}
|
|
193
|
+
{selected.size > 0 && selected.has(CUSTOM_SENTINEL) && (
|
|
194
|
+
<Text>
|
|
195
|
+
<Text dimColor>{"│ "}</Text>
|
|
196
|
+
<Text color="green">{"Selected: "}</Text>
|
|
197
|
+
<Text>{Array.from(selected).filter((v) => v !== CUSTOM_SENTINEL).join(", ")}</Text>
|
|
198
|
+
{selected.size > 1 && <Text dimColor>{" + custom"}</Text>}
|
|
199
|
+
{selected.size === 1 && <Text dimColor>{"custom (on confirm)"}</Text>}
|
|
200
|
+
</Text>
|
|
201
|
+
)}
|
|
135
202
|
</Box>
|
|
136
203
|
);
|
|
137
204
|
}
|
package/src/lib/fs.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { mkdirSync, existsSync } from 'node:fs';
|
|
2
2
|
import { resolve, dirname } from 'node:path';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
3
4
|
import { fileURLToPath } from 'node:url';
|
|
4
5
|
|
|
5
6
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -16,3 +17,7 @@ export function fileExists(filePath: string): boolean {
|
|
|
16
17
|
export function resolveTemplatePath(templateName: string): string {
|
|
17
18
|
return resolve(__dirname, '..', '..', 'templates', templateName);
|
|
18
19
|
}
|
|
20
|
+
|
|
21
|
+
export function homeBinPath(filename: string): string {
|
|
22
|
+
return resolve(homedir(), 'bin', filename);
|
|
23
|
+
}
|
package/src/lib/template.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync, chmodSync } from 'node:fs';
|
|
2
2
|
import { resolve, dirname } from 'node:path';
|
|
3
3
|
import Handlebars from 'handlebars';
|
|
4
|
-
import { ensureDir, resolveTemplatePath } from './fs.js';
|
|
4
|
+
import { ensureDir, resolveTemplatePath, homeBinPath } from './fs.js';
|
|
5
5
|
import type { WizardAnswers } from './types.js';
|
|
6
6
|
|
|
7
|
+
Handlebars.registerHelper('eq', (a: unknown, b: unknown) => a === b);
|
|
8
|
+
|
|
7
9
|
export function renderTemplate(
|
|
8
10
|
templatePath: string,
|
|
9
11
|
data: Record<string, unknown>,
|
|
@@ -57,3 +59,103 @@ export function scaffoldAll(targetDir: string, data: WizardAnswers): void {
|
|
|
57
59
|
ensureDir(dirname(gitignoreDest));
|
|
58
60
|
writeFileSync(gitignoreDest, content, 'utf-8');
|
|
59
61
|
}
|
|
62
|
+
|
|
63
|
+
export interface ReleaseConflicts {
|
|
64
|
+
releaseScriptExists: boolean;
|
|
65
|
+
packageJsonHasRelease: boolean;
|
|
66
|
+
packageJsonExists: boolean;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function checkReleaseConflicts(
|
|
70
|
+
projectName: string,
|
|
71
|
+
targetDir: string,
|
|
72
|
+
): ReleaseConflicts {
|
|
73
|
+
const scriptPath = homeBinPath(`${projectName}-release`);
|
|
74
|
+
const pkgPath = resolve(targetDir, 'package.json');
|
|
75
|
+
|
|
76
|
+
let packageJsonExists = false;
|
|
77
|
+
let packageJsonHasRelease = false;
|
|
78
|
+
|
|
79
|
+
if (existsSync(pkgPath)) {
|
|
80
|
+
packageJsonExists = true;
|
|
81
|
+
try {
|
|
82
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
83
|
+
packageJsonHasRelease = !!pkg.scripts?.['release:patch'];
|
|
84
|
+
} catch {
|
|
85
|
+
// malformed package.json — treat as no release scripts
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
releaseScriptExists: existsSync(scriptPath),
|
|
91
|
+
packageJsonHasRelease,
|
|
92
|
+
packageJsonExists,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface ScaffoldReleaseResult {
|
|
97
|
+
scriptPath: string;
|
|
98
|
+
skipped: boolean;
|
|
99
|
+
reason?: string;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function scaffoldRelease(
|
|
103
|
+
projectName: string,
|
|
104
|
+
data: WizardAnswers,
|
|
105
|
+
): ScaffoldReleaseResult {
|
|
106
|
+
const scriptPath = homeBinPath(`${projectName}-release`);
|
|
107
|
+
|
|
108
|
+
if (existsSync(scriptPath)) {
|
|
109
|
+
return { scriptPath, skipped: true, reason: 'already exists' };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const templatePath = resolveTemplatePath('bin/release.sh.hbs');
|
|
113
|
+
const rendered = renderTemplate(templatePath, data as unknown as Record<string, unknown>);
|
|
114
|
+
ensureDir(dirname(scriptPath));
|
|
115
|
+
writeFileSync(scriptPath, rendered, 'utf-8');
|
|
116
|
+
chmodSync(scriptPath, 0o755);
|
|
117
|
+
|
|
118
|
+
return { scriptPath, skipped: false };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export interface PackageJsonReleaseResult {
|
|
122
|
+
created: boolean;
|
|
123
|
+
modified: boolean;
|
|
124
|
+
skipped: boolean;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function ensurePackageJsonReleaseScripts(
|
|
128
|
+
targetDir: string,
|
|
129
|
+
projectName: string,
|
|
130
|
+
): PackageJsonReleaseResult {
|
|
131
|
+
const pkgPath = resolve(targetDir, 'package.json');
|
|
132
|
+
const releaseScripts = {
|
|
133
|
+
'release:patch': `"$HOME/bin/${projectName}-release" patch`,
|
|
134
|
+
'release:minor': `"$HOME/bin/${projectName}-release" minor`,
|
|
135
|
+
'release:major': `"$HOME/bin/${projectName}-release" major`,
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
if (existsSync(pkgPath)) {
|
|
139
|
+
try {
|
|
140
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
141
|
+
if (pkg.scripts?.['release:patch']) {
|
|
142
|
+
return { created: false, modified: false, skipped: true };
|
|
143
|
+
}
|
|
144
|
+
pkg.scripts = { ...pkg.scripts, ...releaseScripts };
|
|
145
|
+
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf-8');
|
|
146
|
+
return { created: false, modified: true, skipped: false };
|
|
147
|
+
} catch {
|
|
148
|
+
// malformed — skip
|
|
149
|
+
return { created: false, modified: false, skipped: true };
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const pkg = {
|
|
154
|
+
name: projectName,
|
|
155
|
+
version: '0.1.0',
|
|
156
|
+
scripts: releaseScripts,
|
|
157
|
+
};
|
|
158
|
+
ensureDir(dirname(pkgPath));
|
|
159
|
+
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf-8');
|
|
160
|
+
return { created: true, modified: false, skipped: false };
|
|
161
|
+
}
|
package/src/steps/generation.tsx
CHANGED
|
@@ -2,7 +2,14 @@ import React, { useEffect, useState } from "react";
|
|
|
2
2
|
import { Text } from "ink";
|
|
3
3
|
import { ConfirmInput, Spinner } from "@inkjs/ui";
|
|
4
4
|
import { GutterLine } from "../components/gutter-line.js";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
scaffoldAll,
|
|
7
|
+
checkConflicts,
|
|
8
|
+
checkReleaseConflicts,
|
|
9
|
+
scaffoldRelease,
|
|
10
|
+
ensurePackageJsonReleaseScripts,
|
|
11
|
+
} from "../lib/template.js";
|
|
12
|
+
import { homeBinPath } from "../lib/fs.js";
|
|
6
13
|
import type { WizardAnswers } from "../lib/types.js";
|
|
7
14
|
|
|
8
15
|
interface Props {
|
|
@@ -24,13 +31,36 @@ export default function Generation({ answers, onComplete }: Props) {
|
|
|
24
31
|
"summary" | "running" | "done" | "error"
|
|
25
32
|
>("summary");
|
|
26
33
|
const [errorMsg, setErrorMsg] = useState("");
|
|
34
|
+
const [releaseResult, setReleaseResult] = useState("");
|
|
27
35
|
|
|
28
36
|
useEffect(() => {
|
|
29
37
|
if (phase !== "running") return;
|
|
30
38
|
|
|
31
39
|
try {
|
|
32
40
|
const targetDir = process.cwd();
|
|
41
|
+
const projectName = answers.projectName || "untitled";
|
|
42
|
+
|
|
33
43
|
scaffoldAll(targetDir, answers);
|
|
44
|
+
|
|
45
|
+
// Release script scaffolding
|
|
46
|
+
const releaseRes = scaffoldRelease(projectName, answers);
|
|
47
|
+
const pkgRes = ensurePackageJsonReleaseScripts(targetDir, projectName);
|
|
48
|
+
|
|
49
|
+
const parts: string[] = [];
|
|
50
|
+
if (releaseRes.skipped) {
|
|
51
|
+
parts.push(`~/bin/${projectName}-release (skipped, ${releaseRes.reason})`);
|
|
52
|
+
} else {
|
|
53
|
+
parts.push(`~/bin/${projectName}-release (created)`);
|
|
54
|
+
}
|
|
55
|
+
if (pkgRes.skipped) {
|
|
56
|
+
parts.push("package.json release scripts (skipped, already present)");
|
|
57
|
+
} else if (pkgRes.modified) {
|
|
58
|
+
parts.push("package.json release scripts (added)");
|
|
59
|
+
} else if (pkgRes.created) {
|
|
60
|
+
parts.push("package.json (created with release scripts)");
|
|
61
|
+
}
|
|
62
|
+
setReleaseResult(parts.join(", "));
|
|
63
|
+
|
|
34
64
|
setPhase("done");
|
|
35
65
|
const timer = setTimeout(() => onComplete({ generationSkipped: false }), 1500);
|
|
36
66
|
return () => clearTimeout(timer);
|
|
@@ -59,6 +89,18 @@ export default function Generation({ answers, onComplete }: Props) {
|
|
|
59
89
|
.join(", ");
|
|
60
90
|
|
|
61
91
|
const conflicts = checkConflicts(process.cwd());
|
|
92
|
+
const projectName = answers.projectName || "untitled";
|
|
93
|
+
const releaseConflicts = checkReleaseConflicts(projectName, process.cwd());
|
|
94
|
+
|
|
95
|
+
const scriptDisplay = `~/bin/${projectName}-release`;
|
|
96
|
+
const scriptStatus = releaseConflicts.releaseScriptExists
|
|
97
|
+
? "exists, will skip"
|
|
98
|
+
: "new";
|
|
99
|
+
const pkgStatus = releaseConflicts.packageJsonHasRelease
|
|
100
|
+
? "has release scripts, will skip"
|
|
101
|
+
: releaseConflicts.packageJsonExists
|
|
102
|
+
? "will add release scripts"
|
|
103
|
+
: "will create with release scripts";
|
|
62
104
|
|
|
63
105
|
return (
|
|
64
106
|
<>
|
|
@@ -114,6 +156,18 @@ export default function Generation({ answers, onComplete }: Props) {
|
|
|
114
156
|
<GutterLine>
|
|
115
157
|
<Text> </Text>
|
|
116
158
|
</GutterLine>
|
|
159
|
+
<GutterLine>
|
|
160
|
+
<Text>Release setup:</Text>
|
|
161
|
+
</GutterLine>
|
|
162
|
+
<GutterLine>
|
|
163
|
+
<Text> {scriptDisplay} → {scriptStatus}</Text>
|
|
164
|
+
</GutterLine>
|
|
165
|
+
<GutterLine>
|
|
166
|
+
<Text> package.json → {pkgStatus}</Text>
|
|
167
|
+
</GutterLine>
|
|
168
|
+
<GutterLine>
|
|
169
|
+
<Text> </Text>
|
|
170
|
+
</GutterLine>
|
|
117
171
|
<GutterLine>
|
|
118
172
|
<Text>Proceed? </Text>
|
|
119
173
|
<ConfirmInput onConfirm={handleConfirm} onCancel={handleCancel} />
|
|
@@ -139,8 +193,15 @@ export default function Generation({ answers, onComplete }: Props) {
|
|
|
139
193
|
}
|
|
140
194
|
|
|
141
195
|
return (
|
|
142
|
-
|
|
143
|
-
<
|
|
144
|
-
|
|
196
|
+
<>
|
|
197
|
+
<GutterLine>
|
|
198
|
+
<Text color="green">All project files generated successfully.</Text>
|
|
199
|
+
</GutterLine>
|
|
200
|
+
{releaseResult && (
|
|
201
|
+
<GutterLine>
|
|
202
|
+
<Text color="green">Release: {releaseResult}</Text>
|
|
203
|
+
</GutterLine>
|
|
204
|
+
)}
|
|
205
|
+
</>
|
|
145
206
|
);
|
|
146
207
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import React, { useState } from "react";
|
|
2
2
|
import { Text } from "ink";
|
|
3
|
-
import { TextInput } from "@inkjs/ui";
|
|
4
3
|
import { GutterLine } from "../components/gutter-line.js";
|
|
5
4
|
import { GutteredSelect, GutteredMultiSelect } from "../components/guttered-select.js";
|
|
6
5
|
import type { WizardAnswers } from "../lib/types.js";
|
|
@@ -42,6 +41,17 @@ const FRAMEWORK_OPTIONS = [
|
|
|
42
41
|
{ label: "Rails", value: "Rails", description: "Convention-over-config Ruby web" },
|
|
43
42
|
];
|
|
44
43
|
|
|
44
|
+
const CODE_STYLE_OPTIONS = [
|
|
45
|
+
{ label: "Strict mode", value: "Strict mode", description: "TypeScript strict, ESLint strict" },
|
|
46
|
+
{ label: "camelCase / PascalCase", value: "camelCase for variables, PascalCase for types", description: "Standard JS/TS naming" },
|
|
47
|
+
{ label: "ESM imports", value: "ESM imports, no CommonJS require", description: "Modern module syntax" },
|
|
48
|
+
{ label: "Import ordering", value: "stdlib → external → internal import ordering", description: "Grouped imports" },
|
|
49
|
+
{ label: "Return errors, don't throw", value: "Return errors, don't throw", description: "Explicit error handling" },
|
|
50
|
+
{ label: "Functional style", value: "Prefer functional patterns", description: "Pure functions, immutability" },
|
|
51
|
+
{ label: "No any type", value: "No any type, use unknown", description: "Strict type safety" },
|
|
52
|
+
{ label: "JSDoc comments", value: "JSDoc for public APIs", description: "Documentation standard" },
|
|
53
|
+
];
|
|
54
|
+
|
|
45
55
|
/** Suggest frameworks based on selected languages */
|
|
46
56
|
function getFrameworkPreselect(languages: string[]): string[] {
|
|
47
57
|
const langSet = new Set(languages.map((l) => l.toLowerCase()));
|
|
@@ -128,6 +138,8 @@ export default function StackStyle({ onComplete }: Props) {
|
|
|
128
138
|
</GutterLine>
|
|
129
139
|
<GutteredMultiSelect
|
|
130
140
|
options={LANGUAGE_OPTIONS}
|
|
141
|
+
allowCustom
|
|
142
|
+
customPlaceholder="e.g. Elixir, Zig, C++"
|
|
131
143
|
onSubmit={(values) => {
|
|
132
144
|
setSelectedLanguages(values);
|
|
133
145
|
setAdvancedStep("framework");
|
|
@@ -149,6 +161,8 @@ export default function StackStyle({ onComplete }: Props) {
|
|
|
149
161
|
<GutteredMultiSelect
|
|
150
162
|
options={FRAMEWORK_OPTIONS}
|
|
151
163
|
initialSelected={getFrameworkPreselect(selectedLanguages)}
|
|
164
|
+
allowCustom
|
|
165
|
+
customPlaceholder="e.g. Hono, Remix, Astro"
|
|
152
166
|
onSubmit={(values) => {
|
|
153
167
|
setSelectedFrameworks(values);
|
|
154
168
|
setAdvancedStep("codeStyle");
|
|
@@ -168,21 +182,20 @@ export default function StackStyle({ onComplete }: Props) {
|
|
|
168
182
|
<Text dimColor>Frameworks: {selectedFrameworks.join(", ")}</Text>
|
|
169
183
|
</GutterLine>
|
|
170
184
|
<GutterLine>
|
|
171
|
-
<Text bold>Code style conventions
|
|
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
|
-
/>
|
|
185
|
+
<Text bold>Code style conventions:</Text>
|
|
185
186
|
</GutterLine>
|
|
187
|
+
<GutteredMultiSelect
|
|
188
|
+
options={CODE_STYLE_OPTIONS}
|
|
189
|
+
allowCustom
|
|
190
|
+
customPlaceholder="e.g. tabs over spaces, max 80 chars"
|
|
191
|
+
onSubmit={(values) => {
|
|
192
|
+
onComplete({
|
|
193
|
+
language: selectedLanguages.join(", "),
|
|
194
|
+
framework: selectedFrameworks.join(", "),
|
|
195
|
+
codeStyle: values,
|
|
196
|
+
});
|
|
197
|
+
}}
|
|
198
|
+
/>
|
|
186
199
|
</>
|
|
187
200
|
);
|
|
188
201
|
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
usage() {
|
|
5
|
+
echo "Usage: $0 [patch|minor|major]"
|
|
6
|
+
exit 1
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
if [[ "${1:-}" == "" ]]; then
|
|
10
|
+
usage
|
|
11
|
+
fi
|
|
12
|
+
|
|
13
|
+
BUMP="$1"
|
|
14
|
+
if [[ "$BUMP" != "patch" && "$BUMP" != "minor" && "$BUMP" != "major" ]]; then
|
|
15
|
+
usage
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
echo "==> Checking git working tree..."
|
|
19
|
+
if [[ -n "$(git status --porcelain)" ]]; then
|
|
20
|
+
echo "Error: git working tree is not clean. Commit or stash changes first."
|
|
21
|
+
exit 1
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
{{#if (eq packageManager "bun")}}
|
|
25
|
+
echo "==> Running verification..."
|
|
26
|
+
bun run build
|
|
27
|
+
|
|
28
|
+
echo "==> Bumping version ($BUMP)..."
|
|
29
|
+
npm version "$BUMP"
|
|
30
|
+
|
|
31
|
+
CURRENT_BRANCH="$(git branch --show-current)"
|
|
32
|
+
echo "==> Pushing branch and tags to origin/$CURRENT_BRANCH..."
|
|
33
|
+
git push origin "$CURRENT_BRANCH"
|
|
34
|
+
git push origin --tags
|
|
35
|
+
|
|
36
|
+
echo "==> Publishing..."
|
|
37
|
+
bun publish
|
|
38
|
+
{{else if (eq packageManager "pnpm")}}
|
|
39
|
+
echo "==> Checking npm auth..."
|
|
40
|
+
if ! npm whoami >/dev/null 2>&1; then
|
|
41
|
+
echo "Error: npm is not authenticated. Run: npm login"
|
|
42
|
+
exit 1
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
echo "==> Running verification..."
|
|
46
|
+
pnpm run build
|
|
47
|
+
|
|
48
|
+
echo "==> Bumping version ($BUMP)..."
|
|
49
|
+
npm version "$BUMP"
|
|
50
|
+
|
|
51
|
+
CURRENT_BRANCH="$(git branch --show-current)"
|
|
52
|
+
echo "==> Pushing branch and tags to origin/$CURRENT_BRANCH..."
|
|
53
|
+
git push origin "$CURRENT_BRANCH"
|
|
54
|
+
git push origin --tags
|
|
55
|
+
|
|
56
|
+
echo "==> Publishing to npm..."
|
|
57
|
+
pnpm publish --access public
|
|
58
|
+
{{else if (eq packageManager "yarn")}}
|
|
59
|
+
echo "==> Checking npm auth..."
|
|
60
|
+
if ! npm whoami >/dev/null 2>&1; then
|
|
61
|
+
echo "Error: npm is not authenticated. Run: npm login"
|
|
62
|
+
exit 1
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
echo "==> Running verification..."
|
|
66
|
+
yarn build
|
|
67
|
+
|
|
68
|
+
echo "==> Bumping version ($BUMP)..."
|
|
69
|
+
npm version "$BUMP"
|
|
70
|
+
|
|
71
|
+
CURRENT_BRANCH="$(git branch --show-current)"
|
|
72
|
+
echo "==> Pushing branch and tags to origin/$CURRENT_BRANCH..."
|
|
73
|
+
git push origin "$CURRENT_BRANCH"
|
|
74
|
+
git push origin --tags
|
|
75
|
+
|
|
76
|
+
echo "==> Publishing to npm..."
|
|
77
|
+
yarn npm publish --access public
|
|
78
|
+
{{else}}
|
|
79
|
+
echo "==> Checking npm auth..."
|
|
80
|
+
if ! npm whoami >/dev/null 2>&1; then
|
|
81
|
+
echo "Error: npm is not authenticated. Run: npm login"
|
|
82
|
+
exit 1
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
echo "==> Running verification..."
|
|
86
|
+
npm run build
|
|
87
|
+
|
|
88
|
+
echo "==> Bumping version ($BUMP)..."
|
|
89
|
+
npm version "$BUMP"
|
|
90
|
+
|
|
91
|
+
CURRENT_BRANCH="$(git branch --show-current)"
|
|
92
|
+
echo "==> Pushing branch and tags to origin/$CURRENT_BRANCH..."
|
|
93
|
+
git push origin "$CURRENT_BRANCH"
|
|
94
|
+
git push origin --tags
|
|
95
|
+
|
|
96
|
+
echo "==> Publishing to npm..."
|
|
97
|
+
npm publish --access public
|
|
98
|
+
{{/if}}
|
|
99
|
+
|
|
100
|
+
NEW_VERSION="$(node -p "require('./package.json').version")"
|
|
101
|
+
echo "Release complete: {{projectName}}@$NEW_VERSION"
|