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.
@@ -17,7 +17,8 @@
17
17
  "Bash(node -e:*)",
18
18
  "Bash(git add:*)",
19
19
  "Bash(git commit:*)",
20
- "Bash(git push:*)"
20
+ "Bash(git push:*)",
21
+ "Bash(find:*)"
21
22
  ]
22
23
  }
23
24
  }
@@ -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 < options.length - 1 ? prev + 1 : prev);
48
+ setActiveIndex((prev) => prev < allOptions.length - 1 ? prev + 1 : prev);
42
49
  }
43
50
  else if (input === " ") {
44
- const option = options[activeIndex];
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
- onSubmit(Array.from(selected));
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
- return (_jsxs(Box, { flexDirection: "column", children: [options.map((option, index) => {
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
- return (_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "│ " }), isActive ? (_jsx(Text, { color: "cyan", bold: true, children: "❯ " })) : (_jsx(Text, { children: " " })), isSelected ? (_jsx(Text, { color: "green", children: "● " })) : (_jsx(Text, { dimColor: true, children: "○ " })), _jsx(Text, { bold: isActive, children: option.label }), option.description && (_jsx(Text, { dimColor: true, children: " — " + option.description }))] }, option.value));
66
- }), _jsxs(Text, { dimColor: true, children: ["│ ", " ↑↓ move, space toggle, enter confirm"] }), selected.size > 0 && (_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "" }), _jsx(Text, { color: "green", children: "Selected: " }), _jsx(Text, { children: Array.from(selected).join(", ") })] }))] }));
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":"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"}
1
+ {"version":3,"file":"guttered-select.js","sourceRoot":"","sources":["../../src/components/guttered-select.tsx"],"names":[],"mappings":"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"}
package/dist/lib/fs.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export declare function ensureDir(dirPath: string): void;
2
2
  export declare function fileExists(filePath: string): boolean;
3
3
  export declare function resolveTemplatePath(templateName: string): string;
4
+ export declare function homeBinPath(filename: string): string;
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
@@ -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"}
@@ -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;
@@ -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
@@ -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;AAClE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,UAAU,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAGzD,MAAM,UAAU,cAAc,CAC5B,YAAoB,EACpB,IAA6B;IAE7B,MAAM,MAAM,GAAG,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IACnD,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5C,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC;AACxB,CAAC;AAED,MAAM,UAAU,aAAa,CAC3B,YAAoB,EACpB,UAAkB,EAClB,IAA6B;IAE7B,MAAM,QAAQ,GAAG,cAAc,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;IACpD,SAAS,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC;IAC/B,aAAa,CAAC,UAAU,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,YAAY,GAAgD;IAChE,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,EAAE,WAAW,EAAE;IAClD,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,EAAE,WAAW,EAAE;IAClD,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,EAAE,aAAa,EAAE;IACtD,EAAE,QAAQ,EAAE,0BAA0B,EAAE,MAAM,EAAE,sBAAsB,EAAE;IACxE,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,EAAE,cAAc,EAAE;CACzD,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,SAAiB;IAC9C,MAAM,UAAU,GAAG;QACjB,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;QACpC,YAAY;KACb,CAAC;IACF,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,SAAiB,EAAE,IAAmB;IAChE,KAAK,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,YAAY,EAAE,CAAC;QAChD,MAAM,YAAY,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAC;QACnD,MAAM,UAAU,GAAG,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QAC9C,aAAa,CAAC,YAAY,EAAE,UAAU,EAAE,IAA0C,CAAC,CAAC;IACtF,CAAC;IAED,gDAAgD;IAChD,MAAM,YAAY,GAAG,mBAAmB,CAAC,YAAY,CAAC,CAAC;IACvD,MAAM,aAAa,GAAG,OAAO,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;IACvD,MAAM,OAAO,GAAG,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IACpD,SAAS,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC;IAClC,aAAa,CAAC,aAAa,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AACjD,CAAC"}
1
+ {"version":3,"file":"template.js","sourceRoot":"","sources":["../../src/lib/template.ts"],"names":[],"mappings":"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"}
@@ -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
- 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: " " }) }), _jsxs(GutterLine, { children: [_jsx(Text, { children: "Proceed? " }), _jsx(ConfirmInput, { onConfirm: handleConfirm, onCancel: handleCancel })] })] }));
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":"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"}
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(", ")] }) }), _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
- } })] })] }));
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":"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"}
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,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAUvF,MAAM,cAAc,GAAG;IACrB,EAAE,KAAK,EAAE,sBAAsB,EAAE,KAAK,EAAE,iBAAiB,EAAE;CAC5D,CAAC;AAEF,MAAM,gBAAgB,GAAG;IACvB,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,2CAA2C,EAAE;IACtG,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,0BAA0B,EAAE;IACrF,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,2CAA2C,EAAE;IAC9F,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,wCAAwC,EAAE;IACnF,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,iCAAiC,EAAE;IAChF,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,iCAAiC,EAAE;IAChF,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,sCAAsC,EAAE;IACrF,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,iCAAiC,EAAE;IAC9E,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,4BAA4B,EAAE;IAC7E,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,2BAA2B,EAAE;CAC/E,CAAC;AAEF,MAAM,iBAAiB,GAAG;IACxB,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,sBAAsB,EAAE;IAC3E,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,4BAA4B,EAAE;IAC7E,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,4BAA4B,EAAE;IACjF,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,0BAA0B,EAAE;IACvE,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,2BAA2B,EAAE;IAC9E,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,+BAA+B,EAAE;IACpF,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,4BAA4B,EAAE;IACjF,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,+BAA+B,EAAE;IAClF,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,wBAAwB,EAAE;IACzE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,iCAAiC,EAAE;CACnF,CAAC;AAEF,MAAM,kBAAkB,GAAG;IACzB,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,kCAAkC,EAAE;IAC/F,EAAE,KAAK,EAAE,wBAAwB,EAAE,KAAK,EAAE,+CAA+C,EAAE,WAAW,EAAE,uBAAuB,EAAE;IACjI,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,kCAAkC,EAAE,WAAW,EAAE,sBAAsB,EAAE;IACxG,EAAE,KAAK,EAAE,iBAAiB,EAAE,KAAK,EAAE,8CAA8C,EAAE,WAAW,EAAE,iBAAiB,EAAE;IACnH,EAAE,KAAK,EAAE,4BAA4B,EAAE,KAAK,EAAE,4BAA4B,EAAE,WAAW,EAAE,yBAAyB,EAAE;IACpH,EAAE,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,4BAA4B,EAAE,WAAW,EAAE,8BAA8B,EAAE;IAC/G,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,0BAA0B,EAAE,WAAW,EAAE,oBAAoB,EAAE;IAC9F,EAAE,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,uBAAuB,EAAE,WAAW,EAAE,wBAAwB,EAAE;CACnG,CAAC;AAEF,qDAAqD;AACrD,SAAS,qBAAqB,CAAC,SAAmB;IAChD,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IAC/D,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC;QAC3D,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC5B,CAAC;IACD,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1B,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC3B,CAAC;IACD,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QACxB,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC1B,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,OAAO,GAA2C;IACtD,iBAAiB,EAAE;QACjB,QAAQ,EAAE,YAAY;QACtB,SAAS,EAAE,SAAS;QACpB,SAAS,EAAE;YACT,qCAAqC;YACrC,+CAA+C;YAC/C,gDAAgD;YAChD,4BAA4B;SAC7B;QACD,YAAY,EAAE,eAAe;QAC7B,WAAW,EAAE,cAAc;QAC3B,gBAAgB,EAAE,mBAAmB;QACrC,WAAW,EAAE,cAAc;KAC5B;CACF,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,UAAU,CAAC,EAAE,UAAU,EAAS;IACtD,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAO,UAAU,CAAC,CAAC;IACnD,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAe,UAAU,CAAC,CAAC;IAC3E,MAAM,CAAC,iBAAiB,EAAE,oBAAoB,CAAC,GAAG,QAAQ,CAAW,EAAE,CAAC,CAAC;IACzE,MAAM,CAAC,kBAAkB,EAAE,qBAAqB,CAAC,GAAG,QAAQ,CAAW,EAAE,CAAC,CAAC;IAE3E,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;QACxB,OAAO,CACL,8BACE,KAAC,UAAU,cACT,KAAC,IAAI,IAAC,IAAI,kCAAmB,GAClB,EACb,KAAC,cAAc,IACb,OAAO,EAAE;wBACP,EAAE,KAAK,EAAE,2BAA2B,EAAE,KAAK,EAAE,YAAY,EAAE;wBAC3D,EAAE,KAAK,EAAE,0BAA0B,EAAE,KAAK,EAAE,UAAU,EAAE;qBACzD,EACD,QAAQ,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,GAAW,CAAC,GACvC,IACD,CACJ,CAAC;IACJ,CAAC;IAED,IAAI,IAAI,KAAK,YAAY,EAAE,CAAC;QAC1B,OAAO,CACL,8BACE,KAAC,UAAU,cACT,KAAC,IAAI,IAAC,IAAI,6CAA8B,GAC7B,EACb,KAAC,cAAc,IACb,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,CAAC,GAAG,EAAE,EAAE;wBAChB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;wBAC5B,IAAI,MAAM,EAAE,CAAC;4BACX,UAAU,CAAC,MAAM,CAAC,CAAC;wBACrB,CAAC;oBACH,CAAC,GACD,IACD,CACJ,CAAC;IACJ,CAAC;IAED,gBAAgB;IAChB,MAAM,OAAO,GAAG,YAA4B,CAAC;IAE7C,IAAI,OAAO,KAAK,UAAU,EAAE,CAAC;QAC3B,OAAO,CACL,8BACE,KAAC,UAAU,cACT,KAAC,IAAI,IAAC,IAAI,6CAA8B,GAC7B,EACb,KAAC,mBAAmB,IAClB,OAAO,EAAE,gBAAgB,EACzB,WAAW,QACX,iBAAiB,EAAC,uBAAuB,EACzC,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE;wBACnB,oBAAoB,CAAC,MAAM,CAAC,CAAC;wBAC7B,eAAe,CAAC,WAAW,CAAC,CAAC;oBAC/B,CAAC,GACD,IACD,CACJ,CAAC;IACJ,CAAC;IAED,IAAI,OAAO,KAAK,WAAW,EAAE,CAAC;QAC5B,OAAO,CACL,8BACE,KAAC,UAAU,cACT,MAAC,IAAI,IAAC,QAAQ,kCAAa,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAQ,GACpD,EACb,KAAC,UAAU,cACT,KAAC,IAAI,IAAC,IAAI,kCAAmB,GAClB,EACb,KAAC,mBAAmB,IAClB,OAAO,EAAE,iBAAiB,EAC1B,eAAe,EAAE,qBAAqB,CAAC,iBAAiB,CAAC,EACzD,WAAW,QACX,iBAAiB,EAAC,yBAAyB,EAC3C,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE;wBACnB,qBAAqB,CAAC,MAAM,CAAC,CAAC;wBAC9B,eAAe,CAAC,WAAW,CAAC,CAAC;oBAC/B,CAAC,GACD,IACD,CACJ,CAAC;IACJ,CAAC;IAED,iBAAiB;IACjB,OAAO,CACL,8BACE,KAAC,UAAU,cACT,MAAC,IAAI,IAAC,QAAQ,kCAAa,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAQ,GACpD,EACb,KAAC,UAAU,cACT,MAAC,IAAI,IAAC,QAAQ,mCAAc,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAQ,GACtD,EACb,KAAC,UAAU,cACT,KAAC,IAAI,IAAC,IAAI,8CAA+B,GAC9B,EACb,KAAC,mBAAmB,IAClB,OAAO,EAAE,kBAAkB,EAC3B,WAAW,QACX,iBAAiB,EAAC,qCAAqC,EACvD,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE;oBACnB,UAAU,CAAC;wBACT,QAAQ,EAAE,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC;wBACtC,SAAS,EAAE,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC;wBACxC,SAAS,EAAE,MAAM;qBAClB,CAAC,CAAC;gBACL,CAAC,GACD,IACD,CACJ,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "procedure-cli",
3
- "version": "0.1.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({ options, initialSelected, onSubmit }: GutteredMultiSelectProps) {
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 < options.length - 1 ? prev + 1 : prev
101
+ prev < allOptions.length - 1 ? prev + 1 : prev
83
102
  );
84
103
  } else if (input === " ") {
85
- const option = options[activeIndex];
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
- onSubmit(Array.from(selected));
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
- {options.map((option, index) => {
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
+ }
@@ -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
+ }
@@ -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 { scaffoldAll, checkConflicts } from "../lib/template.js";
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
- <GutterLine>
143
- <Text color="green">All project files generated successfully.</Text>
144
- </GutterLine>
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 (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
- />
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"