create-claudecraft 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +194 -0
- package/bin/cli.js +2 -0
- package/dist/constants.d.ts +71 -0
- package/dist/constants.js +128 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +229 -0
- package/dist/ink-prompts.d.ts +12 -0
- package/dist/ink-prompts.js +363 -0
- package/dist/prompts.d.ts +16 -0
- package/dist/prompts.js +434 -0
- package/dist/scaffold.d.ts +19 -0
- package/dist/scaffold.js +303 -0
- package/dist/ui.d.ts +27 -0
- package/dist/ui.js +254 -0
- package/package.json +74 -0
- package/templates/app/App.tsx +21 -0
- package/templates/base/CLAUDE.md +332 -0
- package/templates/base/eslint.config.js +28 -0
- package/templates/base/index.html +17 -0
- package/templates/base/package.json +43 -0
- package/templates/base/postcss.config.js +6 -0
- package/templates/base/tailwind.config.js +81 -0
- package/templates/base/tsconfig.json +25 -0
- package/templates/base/vite.config.ts +16 -0
- package/templates/commands/brainstorm.md +6 -0
- package/templates/commands/build.md +41 -0
- package/templates/commands/execute-plan.md +6 -0
- package/templates/commands/lint.md +41 -0
- package/templates/commands/ralph.md +113 -0
- package/templates/commands/typecheck.md +44 -0
- package/templates/commands/write-plan.md +6 -0
- package/templates/components/ErrorBoundary.tsx +49 -0
- package/templates/components/ui/Button.tsx +60 -0
- package/templates/components/ui/CodeBlock.tsx +46 -0
- package/templates/components/ui/CopyCommand.tsx +38 -0
- package/templates/components/ui/FilePreview.tsx +46 -0
- package/templates/components/ui/SkipLink.tsx +7 -0
- package/templates/components/ui/ThemeSelector.tsx +41 -0
- package/templates/components/ui/UICarousel.tsx +309 -0
- package/templates/context/ThemeContext.tsx +61 -0
- package/templates/homepage/HomePage.tsx +534 -0
- package/templates/homepage/NotFoundPage.tsx +17 -0
- package/templates/hooks/README.md +82 -0
- package/templates/hooks/check-branch.js +27 -0
- package/templates/hooks/typecheck-after-edit.js +51 -0
- package/templates/index.css +67 -0
- package/templates/lib/utils.ts +9 -0
- package/templates/main.tsx +16 -0
- package/templates/settings/MCP_SETUP.md +76 -0
- package/templates/settings/settings.json +16 -0
- package/templates/settings/settings.local.json +37 -0
- package/templates/skills/design/a11y-audit/SKILL.md +173 -0
- package/templates/skills/design/design-polish/SKILL.md +75 -0
- package/templates/skills/design/figma-to-code/SKILL.md +157 -0
- package/templates/skills/design/json-ld/SKILL.md +125 -0
- package/templates/skills/design/microcopy/SKILL.md +197 -0
- package/templates/skills/design/og-image/SKILL.md +157 -0
- package/templates/skills/design/ralph-wiggum-loops/SKILL.md +299 -0
- package/templates/skills/design/react-best-practices/SKILL.md +106 -0
- package/templates/skills/design/react-best-practices/references/react-performance-guidelines.md +143 -0
- package/templates/skills/design/seo-review/SKILL.md +96 -0
- package/templates/skills/design/sitemap-generator/SKILL.md +66 -0
- package/templates/skills/design/testing-patterns/SKILL.md +276 -0
- package/templates/skills/design/ui-skills/SKILL.md +85 -0
- package/templates/skills/design/visual-iteration/SKILL.md +88 -0
- package/templates/skills/workflow/brainstorming/SKILL.md +54 -0
- package/templates/skills/workflow/dispatching-parallel-agents/SKILL.md +180 -0
- package/templates/skills/workflow/executing-plans/SKILL.md +76 -0
- package/templates/skills/workflow/finishing-a-development-branch/SKILL.md +200 -0
- package/templates/skills/workflow/receiving-code-review/SKILL.md +213 -0
- package/templates/skills/workflow/requesting-code-review/SKILL.md +105 -0
- package/templates/skills/workflow/requesting-code-review/code-reviewer.md +146 -0
- package/templates/skills/workflow/subagent-driven-development/SKILL.md +240 -0
- package/templates/skills/workflow/subagent-driven-development/code-quality-reviewer-prompt.md +20 -0
- package/templates/skills/workflow/subagent-driven-development/implementer-prompt.md +78 -0
- package/templates/skills/workflow/subagent-driven-development/spec-reviewer-prompt.md +61 -0
- package/templates/skills/workflow/systematic-debugging/CREATION-LOG.md +119 -0
- package/templates/skills/workflow/systematic-debugging/SKILL.md +296 -0
- package/templates/skills/workflow/systematic-debugging/condition-based-waiting-example.ts +158 -0
- package/templates/skills/workflow/systematic-debugging/condition-based-waiting.md +115 -0
- package/templates/skills/workflow/systematic-debugging/defense-in-depth.md +122 -0
- package/templates/skills/workflow/systematic-debugging/find-polluter.sh +63 -0
- package/templates/skills/workflow/systematic-debugging/root-cause-tracing.md +169 -0
- package/templates/skills/workflow/systematic-debugging/test-academic.md +14 -0
- package/templates/skills/workflow/systematic-debugging/test-pressure-1.md +58 -0
- package/templates/skills/workflow/systematic-debugging/test-pressure-2.md +68 -0
- package/templates/skills/workflow/systematic-debugging/test-pressure-3.md +69 -0
- package/templates/skills/workflow/test-driven-development/SKILL.md +371 -0
- package/templates/skills/workflow/test-driven-development/testing-anti-patterns.md +299 -0
- package/templates/skills/workflow/using-git-worktrees/SKILL.md +217 -0
- package/templates/skills/workflow/using-superpowers/SKILL.md +87 -0
- package/templates/skills/workflow/verification-before-completion/SKILL.md +139 -0
- package/templates/skills/workflow/writing-plans/SKILL.md +116 -0
- package/templates/skills/workflow/writing-skills/SKILL.md +655 -0
- package/templates/skills/workflow/writing-skills/anthropic-best-practices.md +1150 -0
- package/templates/skills/workflow/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
- package/templates/skills/workflow/writing-skills/graphviz-conventions.dot +172 -0
- package/templates/skills/workflow/writing-skills/persuasion-principles.md +187 -0
- package/templates/skills/workflow/writing-skills/render-graphs.js +168 -0
- package/templates/skills/workflow/writing-skills/testing-skills-with-subagents.md +384 -0
- package/templates/types/index.ts +17 -0
- package/templates/vite-env.d.ts +1 -0
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import React, { useState, useEffect } from 'react';
|
|
3
|
+
import { render, Box, Text, useInput, useApp } from 'ink';
|
|
4
|
+
import TextInput from 'ink-text-input';
|
|
5
|
+
import SelectInput from 'ink-select-input';
|
|
6
|
+
import figlet from 'figlet';
|
|
7
|
+
import gradient from 'gradient-string';
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import { SKILLS, BUNDLES, VERSION, STACK, ASSETS, DEFAULT_THEME, DEFAULT_PORT, TAGLINE } from './constants.js';
|
|
11
|
+
// Split gradients for two-line logo
|
|
12
|
+
const claudeGradient = gradient(['#00ff9f', '#00b8ff']); // Green → Cyan (fresh)
|
|
13
|
+
const craftGradient = gradient(['#a855f7', '#6d28d9']); // Purple → Violet (deep)
|
|
14
|
+
// Generate ASCII art for each word separately
|
|
15
|
+
const asciiClaude = figlet.textSync('CLAUDE', {
|
|
16
|
+
font: 'ANSI Shadow',
|
|
17
|
+
horizontalLayout: 'fitted',
|
|
18
|
+
});
|
|
19
|
+
const asciiCraft = figlet.textSync('CRAFT', {
|
|
20
|
+
font: 'ANSI Shadow',
|
|
21
|
+
horizontalLayout: 'fitted',
|
|
22
|
+
});
|
|
23
|
+
// Get today's date for the revision block
|
|
24
|
+
const today = new Date().toISOString().split('T')[0].replace(/-/g, '.');
|
|
25
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
26
|
+
// ALIGNMENT: Frame width = 76 chars exactly (must match ui.ts)
|
|
27
|
+
// Normal row: " │ │ " (7) + content (66) + "│ │" (3) = 76
|
|
28
|
+
// CAP HT row: " │ │ " (7) + content (58) + "│◀┼─ CAP HT" (11) = 76
|
|
29
|
+
// BASELINE row: " │ │ " (7) + content (56) + "│◀┼─ BASELINE" (13) = 76
|
|
30
|
+
// ASCII: CLAUDE=49 chars, CRAFT=41 chars
|
|
31
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
32
|
+
const FRAME = 76;
|
|
33
|
+
const INNER = 66; // Normal row content width
|
|
34
|
+
const CAP_HT_WIDTH = 58; // CAP HT row content width (76-7-11)
|
|
35
|
+
const BASELINE_WIDTH = 56; // BASELINE row content width (76-7-13)
|
|
36
|
+
// Helper to pad strings
|
|
37
|
+
const pad = (s, len) => s + ' '.repeat(Math.max(0, len - s.length));
|
|
38
|
+
// Pad to exact width (strips ANSI codes for length calc)
|
|
39
|
+
const padTo = (line, width) => {
|
|
40
|
+
const visible = line.replace(/\x1b\[[0-9;]*m/g, '').length;
|
|
41
|
+
return line + ' '.repeat(Math.max(0, width - visible));
|
|
42
|
+
};
|
|
43
|
+
// Existential dread collections
|
|
44
|
+
const DREAD = {
|
|
45
|
+
help: [
|
|
46
|
+
"You're not being replaced. You're being... augmented. Involuntarily.",
|
|
47
|
+
"The AI can't feel impostor syndrome. That's still your job.",
|
|
48
|
+
"Remember when 'prompt engineering' wasn't a career? Good times.",
|
|
49
|
+
"The robots write code now. You write prompts. This is fine.",
|
|
50
|
+
"At least the AI doesn't have opinions about your font choices. Yet.",
|
|
51
|
+
],
|
|
52
|
+
reassurance: [
|
|
53
|
+
"But hey, someone still needs taste. That's you. Allegedly.",
|
|
54
|
+
"Breathe. The AI still can't center a div without help.",
|
|
55
|
+
"Keep going. The AI believes in you. (It doesn't. It can't.)",
|
|
56
|
+
],
|
|
57
|
+
cancel: [
|
|
58
|
+
"You'll be back. They always come back.",
|
|
59
|
+
"Ctrl+C won't save you from the future.",
|
|
60
|
+
"Quitting is a valid design decision.",
|
|
61
|
+
],
|
|
62
|
+
};
|
|
63
|
+
const random = (arr) => arr[Math.floor(Math.random() * arr.length)];
|
|
64
|
+
// Slash commands
|
|
65
|
+
const COMMANDS = [
|
|
66
|
+
{ name: '/help', hint: 'existential guidance' },
|
|
67
|
+
{ name: '/skills', hint: 'what the AI knows' },
|
|
68
|
+
{ name: '/quit', hint: 'escape while you can' },
|
|
69
|
+
];
|
|
70
|
+
// Specs/Manifest panel component (76 chars wide to match header)
|
|
71
|
+
function SpecsPanel() {
|
|
72
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { dimColor: true, children: [" \u256D\u2500\u2500\u2500 SPECS ", '─'.repeat(60), "\u256E"] }), _jsxs(Text, { dimColor: true, children: [" \u2502 ", TAGLINE.padEnd(70), "\u2502"] }), _jsx(Text, { dimColor: true, children: " \u251C\u2500\u2500\u2500 STACK \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500 ASSETS \u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500 DEFAULTS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524" }), _jsxs(Text, { dimColor: true, children: [" \u2502 ", _jsxs(Text, { children: ["react ", pad(STACK.react, 6)] }), "\u2502 ", _jsxs(Text, { children: ["skills ", pad(String(ASSETS.skills), 4)] }), "\u2502 ", _jsxs(Text, { children: ["theme ", pad(DEFAULT_THEME, 8)] }), "\u2502"] }), _jsxs(Text, { dimColor: true, children: [" \u2502 ", _jsxs(Text, { children: ["typescript ", pad(STACK.typescript, 6)] }), "\u2502 ", _jsxs(Text, { children: ["commands ", pad(String(ASSETS.commands), 4)] }), "\u2502 ", _jsxs(Text, { children: ["port ", pad(String(DEFAULT_PORT), 8)] }), "\u2502"] }), _jsxs(Text, { dimColor: true, children: [" \u2502 ", _jsxs(Text, { children: ["vite ", pad(STACK.vite, 6)] }), "\u2502 ", _jsxs(Text, { children: ["themes ", pad(String(ASSETS.themes), 4)] }), "\u2502 ", _jsx(Text, { children: "tests vitest " }), "\u2502"] }), _jsxs(Text, { dimColor: true, children: [" \u2502 ", _jsxs(Text, { children: ["tailwind ", pad(STACK.tailwind, 6)] }), "\u2502 ", _jsxs(Text, { children: ["hooks ", pad(String(ASSETS.hooks), 4)] }), "\u2502 ", _jsx(Text, { children: "pkg bun " }), "\u2502"] }), _jsxs(Text, { dimColor: true, children: [" \u2502 ", _jsxs(Text, { children: ["daisyui ", pad(STACK.daisyui, 6)] }), "\u2502 ", _jsxs(Text, { children: ["comps ", pad(String(ASSETS.components), 4)] }), "\u2502 ", _jsx(Text, { children: "license MIT " }), "\u2502"] }), _jsx(Text, { dimColor: true, children: " \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524" }), _jsx(Text, { dimColor: true, children: " \u2502 ~48 files \u00B7 0 deps \u00B7 type /help for existential guidance \u2502" }), _jsxs(Text, { dimColor: true, children: [" \u2570", '─'.repeat(72), "\u256F"] })] }));
|
|
73
|
+
}
|
|
74
|
+
// Progress panel - 76 chars wide to match header
|
|
75
|
+
function ProgressPanel({ step, total, labels }) {
|
|
76
|
+
const stepWidth = Math.floor(66 / total);
|
|
77
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { dimColor: true, children: [" \u256D\u2500\u2500\u2500 PROGRESS ", '─'.repeat(57), "\u256E"] }), _jsx(Text, { dimColor: true, children: " \u2502 \u2502" }), _jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: " \u2502 " }), Array.from({ length: total }, (_, i) => {
|
|
78
|
+
const num = i + 1;
|
|
79
|
+
const label = labels[i] || `Step ${num}`;
|
|
80
|
+
const isComplete = num < step;
|
|
81
|
+
const isCurrent = num === step;
|
|
82
|
+
const isPending = num > step;
|
|
83
|
+
return (_jsxs(React.Fragment, { children: [isComplete && _jsxs(Text, { color: "green", children: ["\u25CF ", label.substring(0, stepWidth - 3).padEnd(stepWidth - 1)] }), isCurrent && _jsxs(Text, { color: "cyan", children: ["\u25C9 ", label.substring(0, stepWidth - 3).padEnd(stepWidth - 1)] }), isPending && _jsxs(Text, { dimColor: true, children: ["\u25CB ", label.substring(0, stepWidth - 3).padEnd(stepWidth - 1)] })] }, num));
|
|
84
|
+
}), _jsx(Text, { dimColor: true, children: "\u2502" })] }), _jsx(Text, { dimColor: true, children: " \u2502 \u2502" }), _jsxs(Text, { dimColor: true, children: [" \u2570", '─'.repeat(72), "\u256F"] })] }));
|
|
85
|
+
}
|
|
86
|
+
// Step labels for progress panel
|
|
87
|
+
const STEP_LABELS = ['Name', 'Skills', 'Homepage', 'Git'];
|
|
88
|
+
// Technical Drawing Header with prepress marks (76 chars wide, perfectly aligned)
|
|
89
|
+
function Header({ showSpecs = true, step, totalSteps }) {
|
|
90
|
+
// Apply gradients and filter empty lines
|
|
91
|
+
const claudeLines = claudeGradient(asciiClaude).split('\n').filter(l => l.trim());
|
|
92
|
+
const craftLines = craftGradient(asciiCraft).split('\n').filter(l => l.trim());
|
|
93
|
+
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Text, { children: [_jsx(Text, { color: "cyan", children: " \u2295" }), _jsxs(Text, { dimColor: true, children: ["\u2500\u252C", '─'.repeat(68), "\u252C\u2500"] }), _jsx(Text, { color: "cyan", children: "\u2295" })] }), _jsxs(Text, { dimColor: true, children: [" \u2502 \u25C0", '─'.repeat(26), " 72 COLS ", '─'.repeat(26), "\u25B6 \u2502"] }), _jsxs(Text, { dimColor: true, children: [" \u250C\u2500\u253C", '─'.repeat(68), "\u253C\u2500\u2510"] }), _jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: " \u2502 \u2502 " }), _jsx(Text, { dimColor: true, children: padTo('┆ · · · · ┆ · · · · ┆ · · · · ┆ · · · · ┆ · · · · ┆ · · · · ┆ ·', INNER) }), _jsx(Text, { dimColor: true, children: "\u2502 \u2502" })] }), claudeLines.map((line, i) => (_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: " \u2502 \u2502 " }), i === 0 ? (_jsxs(_Fragment, { children: [_jsx(Text, { children: padTo(line, CAP_HT_WIDTH) }), _jsx(Text, { dimColor: true, children: "\u2502\u25C0\u253C\u2500 CAP HT" })] })) : (_jsxs(_Fragment, { children: [_jsx(Text, { children: padTo(line, INNER) }), _jsx(Text, { dimColor: true, children: "\u2502 \u2502" })] }))] }, `claude-${i}`))), _jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: " \u2502 \u2502 " }), _jsx(Text, { dimColor: true, children: padTo('├' + '─'.repeat(53) + '┤', BASELINE_WIDTH) }), _jsx(Text, { dimColor: true, children: "\u2502\u25C0\u253C\u2500 BASELINE" })] }), craftLines.map((line, i) => (_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: " \u2502 \u2502 " }), _jsx(Text, { children: padTo(line, INNER) }), _jsx(Text, { dimColor: true, children: "\u2502 \u2502" })] }, `craft-${i}`))), _jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: " \u2502 \u2502 " }), _jsx(Text, { dimColor: true, children: padTo('0 10 20 30 40 50 60', INNER) }), _jsx(Text, { dimColor: true, children: "\u2502 \u2502" })] }), _jsxs(Text, { dimColor: true, children: [" \u2514\u2500\u253C", '─'.repeat(68), "\u253C\u2500\u2518"] }), _jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: " \u2502 " }), _jsx(Text, { color: "cyan", children: "\u25A0" }), _jsx(Text, { dimColor: true, children: " C " }), _jsx(Text, { color: "magenta", children: "\u25A0" }), _jsx(Text, { dimColor: true, children: " M " }), _jsx(Text, { color: "yellow", children: "\u25A0" }), _jsx(Text, { dimColor: true, children: " Y " }), _jsx(Text, { color: "white", children: "\u25A0" }), _jsx(Text, { dimColor: true, children: " K " }), _jsxs(Text, { color: "green", children: ["REV ", VERSION] }), _jsxs(Text, { dimColor: true, children: [" \u2502 ", today, " \u2502 "] }), _jsx(Text, { color: "yellow", children: "NOT FOR PROD" }), _jsx(Text, { dimColor: true, children: " \u2502" })] }), _jsxs(Text, { children: [_jsx(Text, { color: "cyan", children: " \u2295" }), _jsxs(Text, { dimColor: true, children: ["\u2500\u2534", '─'.repeat(68), "\u2534\u2500"] }), _jsx(Text, { color: "cyan", children: "\u2295" })] }), showSpecs ? (_jsx(Box, { marginTop: 1, children: _jsx(SpecsPanel, {}) })) : step && totalSteps ? (_jsx(Box, { marginTop: 1, children: _jsx(ProgressPanel, { step: step, total: totalSteps, labels: STEP_LABELS }) })) : null] }));
|
|
94
|
+
}
|
|
95
|
+
// Autocomplete dropdown - matches design language
|
|
96
|
+
function Autocomplete({ query, commands, onSelect }) {
|
|
97
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
98
|
+
const filtered = commands.filter(c => c.name.toLowerCase().startsWith(query.toLowerCase()));
|
|
99
|
+
useInput((input, key) => {
|
|
100
|
+
if (key.downArrow) {
|
|
101
|
+
setSelectedIndex(i => Math.min(i + 1, filtered.length - 1));
|
|
102
|
+
}
|
|
103
|
+
else if (key.upArrow) {
|
|
104
|
+
setSelectedIndex(i => Math.max(i - 1, 0));
|
|
105
|
+
}
|
|
106
|
+
else if (key.return && filtered[selectedIndex]) {
|
|
107
|
+
onSelect(filtered[selectedIndex].name);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
if (filtered.length === 0)
|
|
111
|
+
return null;
|
|
112
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: "\u251C\u2500\u2500\u2500 COMMANDS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524" }), filtered.map((cmd, i) => (_jsxs(Box, { children: [_jsx(Text, { dimColor: true, children: "\u2502 " }), _jsxs(Text, { color: i === selectedIndex ? 'cyan' : 'white', children: [i === selectedIndex ? '› ' : ' ', cmd.name.padEnd(10)] }), _jsx(Text, { dimColor: true, children: cmd.hint })] }, cmd.name))), _jsx(Text, { dimColor: true, children: "\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F" })] }));
|
|
113
|
+
}
|
|
114
|
+
// Help panel - matches specs panel design
|
|
115
|
+
function HelpPanel({ onClose }) {
|
|
116
|
+
useInput((_, key) => {
|
|
117
|
+
if (key.escape || key.return)
|
|
118
|
+
onClose();
|
|
119
|
+
});
|
|
120
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: "\u256D\u2500\u2500\u2500 HELP \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E" }), _jsxs(Text, { dimColor: true, children: ["\u2502 ", _jsx(Text, { children: random(DREAD.help).padEnd(69) }), "\u2502"] }), _jsx(Text, { dimColor: true, children: "\u251C\u2500\u2500\u2500 COMMANDS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524" }), _jsxs(Text, { dimColor: true, children: ["\u2502 ", _jsx(Text, { color: "cyan", children: "/help" }), " ", _jsx(Text, { dimColor: true, children: "you are here (existentially, too)" }), " \u2502"] }), _jsxs(Text, { dimColor: true, children: ["\u2502 ", _jsx(Text, { color: "cyan", children: "/skills" }), " ", _jsx(Text, { dimColor: true, children: "see what the robots learned" }), " \u2502"] }), _jsxs(Text, { dimColor: true, children: ["\u2502 ", _jsx(Text, { color: "cyan", children: "/quit" }), " ", _jsx(Text, { dimColor: true, children: "rage quit" }), " \u2502"] }), _jsx(Text, { dimColor: true, children: "\u251C\u2500\u2500\u2500 NAVIGATION \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524" }), _jsx(Text, { dimColor: true, children: "\u2502 \u2191\u2193 move through options \u2502" }), _jsx(Text, { dimColor: true, children: "\u2502 space toggle selection \u2502" }), _jsx(Text, { dimColor: true, children: "\u2502 enter confirm your life choices \u2502" }), _jsx(Text, { dimColor: true, children: "\u2502 esc cancel (the project, not your career) \u2502" }), _jsx(Text, { dimColor: true, children: "\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524" }), _jsxs(Text, { dimColor: true, children: ["\u2502 ", _jsx(Text, { children: random(DREAD.reassurance).padEnd(69) }), "\u2502"] }), _jsx(Text, { dimColor: true, children: "\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524" }), _jsxs(Text, { dimColor: true, children: ["\u2502 ", _jsx(Text, { dimColor: true, children: "Press Enter or Esc to continue..." }), " \u2502"] }), _jsx(Text, { dimColor: true, children: "\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F" })] }));
|
|
121
|
+
}
|
|
122
|
+
// Skills panel - matches specs panel design
|
|
123
|
+
function SkillsPanel({ onClose }) {
|
|
124
|
+
useInput((_, key) => {
|
|
125
|
+
if (key.escape || key.return)
|
|
126
|
+
onClose();
|
|
127
|
+
});
|
|
128
|
+
const workflow = SKILLS.filter(s => s.group === 'workflow');
|
|
129
|
+
const design = SKILLS.filter(s => s.group === 'design');
|
|
130
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: "\u256D\u2500\u2500\u2500 SKILLS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E" }), _jsx(Text, { dimColor: true, children: "\u2502 Things the AI learned so you don't have to: \u2502" }), _jsxs(Text, { dimColor: true, children: ["\u251C\u2500\u2500\u2500 WORKFLOW (", String(workflow.length).padEnd(2), ") \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524"] }), workflow.map(s => (_jsxs(Text, { dimColor: true, children: ["\u2502 ", s.name.padEnd(24), " ", _jsx(Text, { dimColor: true, children: s.description.substring(0, 40).padEnd(40) }), "\u2502"] }, s.name))), _jsxs(Text, { dimColor: true, children: ["\u251C\u2500\u2500\u2500 DESIGN (", String(design.length).padEnd(2), ") \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524"] }), design.map(s => (_jsxs(Text, { dimColor: true, children: ["\u2502 ", s.name.padEnd(24), " ", _jsx(Text, { dimColor: true, children: s.description.substring(0, 40).padEnd(40) }), "\u2502"] }, s.name))), _jsx(Text, { dimColor: true, children: "\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524" }), _jsx(Text, { dimColor: true, children: "\u2502 Press Enter or Esc to continue... \u2502" }), _jsx(Text, { dimColor: true, children: "\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F" })] }));
|
|
131
|
+
}
|
|
132
|
+
// Frame component - matches specs panel design
|
|
133
|
+
function Frame({ title, children, hint }) {
|
|
134
|
+
// Calculate padding for consistent width
|
|
135
|
+
const titleSection = `─── ${title} `;
|
|
136
|
+
const hintSection = hint ? ` ${hint} ` : ' ';
|
|
137
|
+
const remaining = 71 - titleSection.length - hintSection.length;
|
|
138
|
+
const padding = '─'.repeat(Math.max(0, remaining));
|
|
139
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { dimColor: true, children: ["\u256D\u2500\u2500\u2500", ' ', _jsx(Text, { bold: true, color: "cyan", children: title }), ' ', hint ? _jsx(Text, { dimColor: true, children: hint }) : null, ' ', padding, "\u256E"] }), _jsxs(Box, { flexDirection: "column", paddingLeft: 1, children: [_jsx(Text, { dimColor: true, children: "\u2502" }), children, _jsx(Text, { dimColor: true, children: "\u2502" })] }), _jsx(Text, { dimColor: true, children: "\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F" })] }));
|
|
140
|
+
}
|
|
141
|
+
// Input with slash command support
|
|
142
|
+
function CommandInput({ value, onChange, onSubmit, placeholder, validate, }) {
|
|
143
|
+
const [showAutocomplete, setShowAutocomplete] = useState(false);
|
|
144
|
+
const [error, setError] = useState();
|
|
145
|
+
useEffect(() => {
|
|
146
|
+
setShowAutocomplete(value.startsWith('/') && value.length < 10);
|
|
147
|
+
}, [value]);
|
|
148
|
+
const handleSubmit = (val) => {
|
|
149
|
+
if (val.startsWith('/')) {
|
|
150
|
+
// Handle as command
|
|
151
|
+
onSubmit(val);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
// Validate and submit
|
|
155
|
+
const err = validate?.(val);
|
|
156
|
+
if (err) {
|
|
157
|
+
setError(err);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
setError(undefined);
|
|
161
|
+
onSubmit(val);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: "\u203A " }), _jsx(TextInput, { value: value, onChange: onChange, onSubmit: handleSubmit, placeholder: placeholder })] }), showAutocomplete && (_jsx(Box, { marginTop: 1, children: _jsx(Autocomplete, { query: value, commands: COMMANDS, onSelect: (cmd) => {
|
|
166
|
+
onChange(cmd);
|
|
167
|
+
onSubmit(cmd);
|
|
168
|
+
} }) })), error && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "red", children: ["\u2717 ", error] }) }))] }));
|
|
169
|
+
}
|
|
170
|
+
function WizardApp({ defaultName, onComplete }) {
|
|
171
|
+
const { exit } = useApp();
|
|
172
|
+
const [step, setStep] = useState('name');
|
|
173
|
+
const [panel, setPanel] = useState(null);
|
|
174
|
+
const [input, setInput] = useState(defaultName || '');
|
|
175
|
+
// Collected choices
|
|
176
|
+
const [projectName, setProjectName] = useState('');
|
|
177
|
+
const [bundle, setBundle] = useState('everything');
|
|
178
|
+
const [selectedSkills, setSelectedSkills] = useState([]);
|
|
179
|
+
const [includeHomepage, setIncludeHomepage] = useState(true);
|
|
180
|
+
const [initGit, setInitGit] = useState(true);
|
|
181
|
+
// Handle escape to quit
|
|
182
|
+
useInput((_, key) => {
|
|
183
|
+
if (key.escape && !panel) {
|
|
184
|
+
console.log('\n' + random(DREAD.cancel));
|
|
185
|
+
exit();
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
// Handle slash commands
|
|
189
|
+
const handleCommand = (cmd) => {
|
|
190
|
+
const normalized = cmd.toLowerCase().trim();
|
|
191
|
+
if (normalized === '/help' || normalized === '/h') {
|
|
192
|
+
setPanel('help');
|
|
193
|
+
}
|
|
194
|
+
else if (normalized === '/skills' || normalized === '/list') {
|
|
195
|
+
setPanel('skills');
|
|
196
|
+
}
|
|
197
|
+
else if (normalized === '/quit' || normalized === '/q' || normalized === '/exit') {
|
|
198
|
+
console.log('\n' + random(DREAD.cancel));
|
|
199
|
+
exit();
|
|
200
|
+
}
|
|
201
|
+
setInput('');
|
|
202
|
+
};
|
|
203
|
+
// Handle name submission
|
|
204
|
+
const handleNameSubmit = (value) => {
|
|
205
|
+
if (value.startsWith('/')) {
|
|
206
|
+
handleCommand(value);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
setProjectName(value);
|
|
210
|
+
setStep('bundle');
|
|
211
|
+
};
|
|
212
|
+
// Validate project name
|
|
213
|
+
const validateName = (value) => {
|
|
214
|
+
if (!value)
|
|
215
|
+
return 'Required';
|
|
216
|
+
if (!/^[a-z0-9-]+$/.test(value))
|
|
217
|
+
return 'Lowercase, numbers, dashes only';
|
|
218
|
+
if (value.length > 64)
|
|
219
|
+
return 'Max 64 chars';
|
|
220
|
+
const targetDir = path.resolve(process.cwd(), value);
|
|
221
|
+
if (fs.existsSync(targetDir))
|
|
222
|
+
return `Directory "${value}" already exists`;
|
|
223
|
+
return undefined;
|
|
224
|
+
};
|
|
225
|
+
// Bundle options
|
|
226
|
+
const bundleOptions = [
|
|
227
|
+
{ label: 'Everything', value: 'everything', hint: `All ${SKILLS.length} skills · no restraint` },
|
|
228
|
+
{ label: 'Designer Essentials', value: 'designer', hint: `${BUNDLES.designer.skills.length} skills · UI, a11y, Figma` },
|
|
229
|
+
{ label: 'Workflow Only', value: 'workflow', hint: `${SKILLS.filter(s => s.group === 'workflow').length} skills · process only` },
|
|
230
|
+
{ label: 'Let Me Pick', value: 'custom', hint: 'Control freak? Respect.' },
|
|
231
|
+
];
|
|
232
|
+
const handleBundleSelect = (item) => {
|
|
233
|
+
const b = item.value;
|
|
234
|
+
setBundle(b);
|
|
235
|
+
if (b === 'custom') {
|
|
236
|
+
setStep('skills-workflow');
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
// Set skills based on bundle
|
|
240
|
+
if (b === 'everything') {
|
|
241
|
+
setSelectedSkills(SKILLS.map(s => s.name));
|
|
242
|
+
}
|
|
243
|
+
else if (b === 'designer') {
|
|
244
|
+
setSelectedSkills([...BUNDLES.designer.skills]);
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
setSelectedSkills(SKILLS.filter(s => s.group === 'workflow').map(s => s.name));
|
|
248
|
+
}
|
|
249
|
+
setStep('homepage');
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
// Homepage options
|
|
253
|
+
const homepageOptions = [
|
|
254
|
+
{ label: 'Yes', value: true, hint: '+6 files · delete it later' },
|
|
255
|
+
{ label: 'No', value: false, hint: 'Blank App.tsx · the void awaits' },
|
|
256
|
+
];
|
|
257
|
+
// Git options
|
|
258
|
+
const gitOptions = [
|
|
259
|
+
{ label: 'Yes', value: true, hint: 'Fresh repo · future you says thanks' },
|
|
260
|
+
{ label: 'No', value: false, hint: 'No .git · living dangerously' },
|
|
261
|
+
];
|
|
262
|
+
// Render panels
|
|
263
|
+
if (panel === 'help') {
|
|
264
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Header, { showSpecs: false }), _jsx(HelpPanel, { onClose: () => setPanel(null) })] }));
|
|
265
|
+
}
|
|
266
|
+
if (panel === 'skills') {
|
|
267
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Header, { showSpecs: false }), _jsx(SkillsPanel, { onClose: () => setPanel(null) })] }));
|
|
268
|
+
}
|
|
269
|
+
// Show full header with specs only on first screen
|
|
270
|
+
const isFirstScreen = step === 'name';
|
|
271
|
+
// Map step to number for progress bar
|
|
272
|
+
const stepToNum = {
|
|
273
|
+
'name': 1,
|
|
274
|
+
'bundle': 2,
|
|
275
|
+
'skills-workflow': 2,
|
|
276
|
+
'skills-design': 2,
|
|
277
|
+
'homepage': 3,
|
|
278
|
+
'git': 4,
|
|
279
|
+
'done': 4,
|
|
280
|
+
};
|
|
281
|
+
const currentStep = stepToNum[step];
|
|
282
|
+
const totalSteps = 4;
|
|
283
|
+
// Render steps
|
|
284
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Header, { showSpecs: isFirstScreen, step: isFirstScreen ? undefined : currentStep, totalSteps: isFirstScreen ? undefined : totalSteps }), step === 'name' && (_jsxs(Frame, { title: "PROJECT NAME", hint: "lowercase \u00B7 numbers \u00B7 dashes \u00B7 /help", children: [_jsx(Text, { children: "What are we calling this?" }), _jsx(Box, { marginTop: 1, children: _jsx(CommandInput, { value: input, onChange: setInput, onSubmit: handleNameSubmit, placeholder: "my-app", validate: validateName }) })] })), step === 'bundle' && (_jsxs(Frame, { title: "SKILL BUNDLE", children: [_jsx(Text, { children: "How much help do you want?" }), _jsx(Box, { marginTop: 1, children: _jsx(SelectInput, { items: bundleOptions.map(o => ({
|
|
285
|
+
label: `${o.label} - ${o.hint}`,
|
|
286
|
+
value: o.value
|
|
287
|
+
})), onSelect: handleBundleSelect }) })] })), step === 'homepage' && (_jsxs(Frame, { title: "EXAMPLE HOMEPAGE", children: [_jsx(Text, { children: "Include the example homepage?" }), _jsx(Box, { marginTop: 1, children: _jsx(SelectInput, { items: homepageOptions.map(o => ({
|
|
288
|
+
label: `${o.label} - ${o.hint}`,
|
|
289
|
+
value: o.value
|
|
290
|
+
})), onSelect: (item) => {
|
|
291
|
+
setIncludeHomepage(item.value);
|
|
292
|
+
setStep('git');
|
|
293
|
+
} }) })] })), step === 'git' && (_jsxs(Frame, { title: "GIT INIT", children: [_jsx(Text, { children: "Initialize git?" }), _jsx(Box, { marginTop: 1, children: _jsx(SelectInput, { items: gitOptions.map(o => ({
|
|
294
|
+
label: `${o.label} - ${o.hint}`,
|
|
295
|
+
value: o.value
|
|
296
|
+
})), onSelect: (item) => {
|
|
297
|
+
setInitGit(item.value);
|
|
298
|
+
// Complete!
|
|
299
|
+
onComplete({
|
|
300
|
+
projectName,
|
|
301
|
+
bundle,
|
|
302
|
+
selectedSkills,
|
|
303
|
+
includeHomepage,
|
|
304
|
+
initGit: item.value,
|
|
305
|
+
});
|
|
306
|
+
} }) })] })), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: "\u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E" }), _jsx(Text, { dimColor: true, children: "\u2502 Type / for commands \u00B7 \u2191\u2193 navigate \u00B7 Enter confirm \u00B7 Esc quit \u2502" }), _jsx(Text, { dimColor: true, children: "\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F" })] })] }));
|
|
307
|
+
}
|
|
308
|
+
// Export the prompt runner
|
|
309
|
+
export function runInkPrompts(defaultName) {
|
|
310
|
+
return new Promise((resolve) => {
|
|
311
|
+
const { unmount, waitUntilExit } = render(_jsx(WizardApp, { defaultName: defaultName, onComplete: (choices) => {
|
|
312
|
+
unmount();
|
|
313
|
+
resolve(choices);
|
|
314
|
+
} }));
|
|
315
|
+
waitUntilExit().then(() => {
|
|
316
|
+
resolve(null);
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
// Init mode prompts (simpler)
|
|
321
|
+
function InitApp({ onComplete }) {
|
|
322
|
+
const { exit } = useApp();
|
|
323
|
+
const [bundle, setBundle] = useState('everything');
|
|
324
|
+
useInput((_, key) => {
|
|
325
|
+
if (key.escape) {
|
|
326
|
+
console.log('\n' + random(DREAD.cancel));
|
|
327
|
+
exit();
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
const bundleOptions = [
|
|
331
|
+
{ label: 'Everything', value: 'everything', hint: `All ${SKILLS.length} skills` },
|
|
332
|
+
{ label: 'Designer Essentials', value: 'designer', hint: `${BUNDLES.designer.skills.length} skills` },
|
|
333
|
+
{ label: 'Workflow Only', value: 'workflow', hint: `${SKILLS.filter(s => s.group === 'workflow').length} skills` },
|
|
334
|
+
];
|
|
335
|
+
const handleSelect = (item) => {
|
|
336
|
+
let skills;
|
|
337
|
+
if (item.value === 'everything') {
|
|
338
|
+
skills = SKILLS.map(s => s.name);
|
|
339
|
+
}
|
|
340
|
+
else if (item.value === 'designer') {
|
|
341
|
+
skills = [...BUNDLES.designer.skills];
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
skills = SKILLS.filter(s => s.group === 'workflow').map(s => s.name);
|
|
345
|
+
}
|
|
346
|
+
onComplete({ selectedSkills: skills });
|
|
347
|
+
};
|
|
348
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Header, { showSpecs: true }), _jsxs(Frame, { title: "INIT MODE", hint: "Adding to existing project", children: [_jsx(Text, { children: "Which skills do you want?" }), _jsx(Box, { marginTop: 1, children: _jsx(SelectInput, { items: bundleOptions.map(o => ({
|
|
349
|
+
label: `${o.label} - ${o.hint}`,
|
|
350
|
+
value: o.value
|
|
351
|
+
})), onSelect: handleSelect }) })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: "\u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E" }), _jsx(Text, { dimColor: true, children: "\u2502 \u2191\u2193 navigate \u00B7 Enter confirm \u00B7 Esc quit \u2502" }), _jsx(Text, { dimColor: true, children: "\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F" })] })] }));
|
|
352
|
+
}
|
|
353
|
+
export function runInkInitPrompts() {
|
|
354
|
+
return new Promise((resolve) => {
|
|
355
|
+
const { unmount, waitUntilExit } = render(_jsx(InitApp, { onComplete: (choices) => {
|
|
356
|
+
unmount();
|
|
357
|
+
resolve(choices);
|
|
358
|
+
} }));
|
|
359
|
+
waitUntilExit().then(() => {
|
|
360
|
+
resolve(null);
|
|
361
|
+
});
|
|
362
|
+
});
|
|
363
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface UserChoices {
|
|
2
|
+
projectName: string;
|
|
3
|
+
bundle: 'everything' | 'designer' | 'workflow' | 'custom';
|
|
4
|
+
selectedSkills: string[];
|
|
5
|
+
includeHomepage: boolean;
|
|
6
|
+
initGit: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface InitChoices {
|
|
9
|
+
selectedSkills: string[];
|
|
10
|
+
}
|
|
11
|
+
export declare function runPrompts(defaultName?: string): Promise<UserChoices | null>;
|
|
12
|
+
/**
|
|
13
|
+
* Prompts for --init mode (existing project)
|
|
14
|
+
* Only asks about skills selection
|
|
15
|
+
*/
|
|
16
|
+
export declare function runInitPrompts(): Promise<InitChoices | null>;
|