@pugi/cli 0.1.0-beta.3 → 0.1.0-beta.31
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/THIRD_PARTY_NOTICES.md +40 -0
- package/assets/pugi-mascot.ansi +15 -40
- package/bin/run.js +33 -1
- package/dist/commands/jobs-watch.js +201 -0
- package/dist/commands/jobs.js +15 -0
- package/dist/core/agent-progress/cleanup.js +134 -0
- package/dist/core/agent-progress/schema.js +144 -0
- package/dist/core/agent-progress/writer.js +101 -0
- package/dist/core/artifact-chain/dispatcher.js +148 -0
- package/dist/core/artifact-chain/exporter.js +164 -0
- package/dist/core/artifact-chain/state.js +243 -0
- package/dist/core/artifact-chain/steps.js +169 -0
- package/dist/core/auth/env-provider.js +238 -0
- package/dist/core/auto-update/channels.js +122 -0
- package/dist/core/auto-update/checker.js +241 -0
- package/dist/core/auto-update/state.js +235 -0
- package/dist/core/bare-mode/index.js +107 -0
- package/dist/core/checkpoint/resumer.js +149 -0
- package/dist/core/checkpoint/rewinder.js +291 -0
- package/dist/core/compact/auto-trigger.js +96 -0
- package/dist/core/compact/buffer-rewriter.js +115 -0
- package/dist/core/compact/summarizer.js +208 -0
- package/dist/core/compact/token-counter.js +108 -0
- package/dist/core/consensus/diff-capture.js +73 -0
- package/dist/core/context/index.js +7 -0
- package/dist/core/context/markdown-traverse.js +255 -0
- package/dist/core/cost/rate-card.js +129 -0
- package/dist/core/cost/tracker.js +221 -0
- package/dist/core/denial-tracking/index.js +8 -0
- package/dist/core/denial-tracking/state.js +264 -0
- package/dist/core/diagnostics/probe-runner.js +93 -0
- package/dist/core/diagnostics/probes/api.js +46 -0
- package/dist/core/diagnostics/probes/auth.js +86 -0
- package/dist/core/diagnostics/probes/bare-mode.js +42 -0
- package/dist/core/diagnostics/probes/cli-version.js +127 -0
- package/dist/core/diagnostics/probes/config.js +72 -0
- package/dist/core/diagnostics/probes/denial-tracking.js +57 -0
- package/dist/core/diagnostics/probes/disk.js +81 -0
- package/dist/core/diagnostics/probes/git.js +65 -0
- package/dist/core/diagnostics/probes/mcp.js +75 -0
- package/dist/core/diagnostics/probes/node.js +59 -0
- package/dist/core/diagnostics/probes/pnpm.js +36 -0
- package/dist/core/diagnostics/probes/pugi-md.js +89 -0
- package/dist/core/diagnostics/probes/session.js +74 -0
- package/dist/core/diagnostics/probes/status-snapshot.js +442 -0
- package/dist/core/diagnostics/probes/workspace.js +63 -0
- package/dist/core/diagnostics/types.js +70 -0
- package/dist/core/dispatch/cache-cleanup.js +197 -0
- package/dist/core/dispatch/cache-handoff.js +295 -0
- package/dist/core/edits/dispatch.js +218 -2
- package/dist/core/edits/journal.js +199 -0
- package/dist/core/edits/layer-d-ast.js +557 -14
- package/dist/core/edits/verify-hook.js +273 -0
- package/dist/core/edits/worktree.js +111 -18
- package/dist/core/engine/anvil-client.js +115 -5
- package/dist/core/engine/budgets.js +89 -0
- package/dist/core/engine/context-prefix.js +155 -0
- package/dist/core/engine/intent.js +260 -0
- package/dist/core/engine/native-pugi.js +852 -210
- package/dist/core/engine/prompts.js +89 -6
- package/dist/core/engine/strip-internal-fields.js +124 -0
- package/dist/core/engine/tool-bridge.js +972 -33
- package/dist/core/feedback/queue.js +177 -0
- package/dist/core/feedback/submitter.js +145 -0
- package/dist/core/file-cache.js +113 -1
- package/dist/core/hooks/events.js +44 -0
- package/dist/core/hooks/index.js +15 -0
- package/dist/core/hooks/registry.js +213 -0
- package/dist/core/hooks/runner.js +236 -0
- package/dist/core/init/scaffold.js +195 -0
- package/dist/core/lsp/cache.js +105 -0
- package/dist/core/lsp/client.js +174 -29
- package/dist/core/lsp/language-detect.js +66 -0
- package/dist/core/lsp/post-edit-diagnostics.js +171 -0
- package/dist/core/mcp/client.js +75 -6
- package/dist/core/mcp/http-server.js +553 -0
- package/dist/core/mcp/permission.js +190 -0
- package/dist/core/mcp/registry.js +24 -2
- package/dist/core/mcp/server-tools.js +219 -0
- package/dist/core/mcp/server.js +397 -0
- package/dist/core/memory/dual-write.js +416 -0
- package/dist/core/memory/dual-write.spec.js +297 -0
- package/dist/core/memory/phase1-kinds.js +20 -0
- package/dist/core/memory-sync/queue.js +158 -0
- package/dist/core/memory-sync/queue.spec.js +105 -0
- package/dist/core/onboarding/marker.js +111 -0
- package/dist/core/onboarding/telemetry-state.js +108 -0
- package/dist/core/output-style/presets.js +176 -0
- package/dist/core/output-style/state.js +185 -0
- package/dist/core/permissions/gate.js +187 -0
- package/dist/core/permissions/index.js +18 -0
- package/dist/core/permissions/mode.js +102 -0
- package/dist/core/permissions/state.js +215 -0
- package/dist/core/permissions/tool-class.js +93 -0
- package/dist/core/prd-check/parser.js +215 -0
- package/dist/core/prd-check/reporter.js +127 -0
- package/dist/core/prd-check/session-review.js +557 -0
- package/dist/core/prd-check/verifiers.js +223 -0
- package/dist/core/pugi-md/context-injector.js +76 -0
- package/dist/core/pugi-md/walk-up.js +207 -0
- package/dist/core/release-notes/parser.js +241 -0
- package/dist/core/release-notes/state.js +116 -0
- package/dist/core/repl/codebase-survey.js +308 -0
- package/dist/core/repl/history.js +11 -1
- package/dist/core/repl/init-interview.js +457 -0
- package/dist/core/repl/model-pricing.js +135 -0
- package/dist/core/repl/onboarding-state.js +297 -0
- package/dist/core/repl/session.js +1529 -30
- package/dist/core/repl/slash-commands.js +361 -13
- package/dist/core/repl/store/session-store.js +31 -2
- package/dist/core/repl/workspace-context.js +22 -0
- package/dist/core/repo-map/build.js +125 -0
- package/dist/core/repo-map/cache.js +185 -0
- package/dist/core/repo-map/extractor.js +254 -0
- package/dist/core/repo-map/formatter.js +145 -0
- package/dist/core/repo-map/scanner.js +211 -0
- package/dist/core/retry-budget/budget.js +284 -0
- package/dist/core/retry-budget/index.js +5 -0
- package/dist/core/session.js +44 -0
- package/dist/core/settings.js +80 -0
- package/dist/core/share/formatter.js +271 -0
- package/dist/core/share/redactor.js +221 -0
- package/dist/core/share/uploader.js +267 -0
- package/dist/core/skills/defaults.js +457 -0
- package/dist/core/subagents/dispatcher-real.js +600 -0
- package/dist/core/subagents/dispatcher.js +113 -24
- package/dist/core/subagents/index.js +18 -5
- package/dist/core/subagents/isolation-matrix.js +213 -0
- package/dist/core/subagents/spawn.js +19 -4
- package/dist/core/telemetry/emitter.js +229 -0
- package/dist/core/telemetry/queue.js +251 -0
- package/dist/core/theme/context.js +91 -0
- package/dist/core/theme/presets.js +228 -0
- package/dist/core/theme/state.js +181 -0
- package/dist/core/todos/invariant.js +10 -0
- package/dist/core/todos/state.js +177 -0
- package/dist/core/transport/version-interceptor.js +166 -0
- package/dist/core/vim/keymap.js +288 -0
- package/dist/core/vim/state.js +92 -0
- package/dist/index.js +28 -0
- package/dist/runtime/bootstrap.js +190 -0
- package/dist/runtime/cli.js +2603 -278
- package/dist/runtime/commands/chain.js +489 -0
- package/dist/runtime/commands/compact.js +297 -0
- package/dist/runtime/commands/cost.js +199 -0
- package/dist/runtime/commands/delegate.js +312 -0
- package/dist/runtime/commands/dispatch.js +126 -0
- package/dist/runtime/commands/doctor.js +390 -0
- package/dist/runtime/commands/feedback.js +184 -0
- package/dist/runtime/commands/hooks.js +184 -0
- package/dist/runtime/commands/lsp.js +212 -28
- package/dist/runtime/commands/mcp.js +824 -0
- package/dist/runtime/commands/memory.js +508 -0
- package/dist/runtime/commands/memory.spec.js +174 -0
- package/dist/runtime/commands/model.js +237 -0
- package/dist/runtime/commands/onboarding.js +275 -0
- package/dist/runtime/commands/patch.js +17 -0
- package/dist/runtime/commands/permissions.js +87 -0
- package/dist/runtime/commands/plan.js +143 -0
- package/dist/runtime/commands/prd-check.js +285 -0
- package/dist/runtime/commands/release-notes.js +229 -0
- package/dist/runtime/commands/repo-map.js +95 -0
- package/dist/runtime/commands/report.js +299 -0
- package/dist/runtime/commands/resume.js +118 -0
- package/dist/runtime/commands/review-consensus.js +17 -2
- package/dist/runtime/commands/rewind.js +333 -0
- package/dist/runtime/commands/roster.js +117 -0
- package/dist/runtime/commands/sessions.js +163 -0
- package/dist/runtime/commands/share.js +316 -0
- package/dist/runtime/commands/status.js +178 -0
- package/dist/runtime/commands/stickers.js +82 -0
- package/dist/runtime/commands/style.js +194 -0
- package/dist/runtime/commands/theme.js +196 -0
- package/dist/runtime/commands/update.js +289 -0
- package/dist/runtime/commands/vim.js +140 -0
- package/dist/runtime/commands/worktree.js +50 -6
- package/dist/runtime/headless.js +543 -0
- package/dist/runtime/load-hooks-or-exit.js +71 -0
- package/dist/runtime/plan-decompose.js +531 -0
- package/dist/runtime/version.js +65 -0
- package/dist/tools/agent-tool.js +229 -0
- package/dist/tools/apply-patch.js +281 -39
- package/dist/tools/ask-user-question.js +213 -0
- package/dist/tools/ask-user.js +115 -0
- package/dist/tools/file-tools.js +85 -14
- package/dist/tools/mcp-tool.js +260 -0
- package/dist/tools/multi-edit.js +361 -0
- package/dist/tools/registry.js +30 -2
- package/dist/tools/skill-tool.js +96 -0
- package/dist/tools/tasks.js +208 -0
- package/dist/tools/todo-write.js +184 -0
- package/dist/tools/web-fetch.js +147 -2
- package/dist/tools/web-search.js +458 -0
- package/dist/tui/agent-progress-card.js +111 -0
- package/dist/tui/agent-tree.js +10 -0
- package/dist/tui/ask-modal.js +2 -2
- package/dist/tui/ask-user-question-prompt.js +192 -0
- package/dist/tui/compact-banner.js +81 -0
- package/dist/tui/conversation-pane.js +82 -8
- package/dist/tui/cost-table.js +111 -0
- package/dist/tui/doctor-table.js +46 -0
- package/dist/tui/feedback-prompt.js +156 -0
- package/dist/tui/input-box.js +46 -2
- package/dist/tui/markdown-render.js +4 -4
- package/dist/tui/onboarding-wizard.js +240 -0
- package/dist/tui/repl-render.js +293 -35
- package/dist/tui/repl-splash.js +2 -2
- package/dist/tui/repl.js +45 -13
- package/dist/tui/splash.js +1 -1
- package/dist/tui/status-bar.js +94 -16
- package/dist/tui/status-table.js +7 -0
- package/dist/tui/stickers-art.js +136 -0
- package/dist/tui/style-table.js +28 -0
- package/dist/tui/theme-table.js +29 -0
- package/dist/tui/tool-stream-pane.js +7 -0
- package/dist/tui/update-banner.js +20 -2
- package/dist/tui/vim-input.js +267 -0
- package/docs/examples/codegraph.mcp.json +10 -0
- package/package.json +9 -6
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* `/feedback` interactive prompt — Leak L21 (2026-05-27).
|
|
4
|
+
*
|
|
5
|
+
* Five-step wizard mounted from both the top-level `pugi feedback`
|
|
6
|
+
* shell handler and the in-REPL `/feedback` slash. Steps:
|
|
7
|
+
*
|
|
8
|
+
* 1. category — bug / feature / general / praise (numeric pick 1-4)
|
|
9
|
+
* 2. rating — 1-5 stars (numeric pick 1-5)
|
|
10
|
+
* 3. comment — free-text, multi-line, Ctrl-D submits, Esc cancels
|
|
11
|
+
* 4. context — y/n include last 5 turns (redacted)? default no
|
|
12
|
+
* 5. confirm — y/n submit?
|
|
13
|
+
*
|
|
14
|
+
* Brand voice gate: ASCII glyphs only, no em-dashes, no banned brand
|
|
15
|
+
* words. Copy is intentionally power-word neutral so the future i18n
|
|
16
|
+
* landing localises cleanly.
|
|
17
|
+
*
|
|
18
|
+
* The component is PURE in the Ink sense — props in, one terminal
|
|
19
|
+
* `onResolve` event out. The caller owns the submit + queue logic.
|
|
20
|
+
*
|
|
21
|
+
* The verdict the operator submits is:
|
|
22
|
+
* - `{ cancelled: true }` when the operator presses Esc at any step
|
|
23
|
+
* - `{ cancelled: false, draft }` when the wizard completes
|
|
24
|
+
*
|
|
25
|
+
* `draft` is the assembled `FeedbackDraft` — NOT yet a full envelope
|
|
26
|
+
* (the caller still injects `ts` + `cliVersion` + maybe `tier`).
|
|
27
|
+
*/
|
|
28
|
+
import { useState } from 'react';
|
|
29
|
+
import { Box, Text, render, useApp, useInput } from 'ink';
|
|
30
|
+
const CATEGORIES = [
|
|
31
|
+
{ value: 'bug', label: 'bug', gloss: 'something broke or behaves unexpectedly' },
|
|
32
|
+
{ value: 'feature', label: 'feature', gloss: 'request a new capability' },
|
|
33
|
+
{ value: 'general', label: 'general', gloss: 'observation, idea, comment' },
|
|
34
|
+
{ value: 'praise', label: 'praise', gloss: 'positive note for the team' },
|
|
35
|
+
];
|
|
36
|
+
export function FeedbackPrompt(props) {
|
|
37
|
+
const [step, setStep] = useState('category');
|
|
38
|
+
const [category, setCategory] = useState(null);
|
|
39
|
+
const [rating, setRating] = useState(null);
|
|
40
|
+
const [commentBuffer, setCommentBuffer] = useState('');
|
|
41
|
+
const [includeContext, setIncludeContext] = useState(false);
|
|
42
|
+
useInput((input, key) => {
|
|
43
|
+
if (key.escape) {
|
|
44
|
+
props.onResolve({ cancelled: true });
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (step === 'category') {
|
|
48
|
+
const n = Number.parseInt(input, 10);
|
|
49
|
+
if (!Number.isNaN(n) && n >= 1 && n <= CATEGORIES.length) {
|
|
50
|
+
const picked = CATEGORIES[n - 1];
|
|
51
|
+
if (picked) {
|
|
52
|
+
setCategory(picked.value);
|
|
53
|
+
setStep('rating');
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (step === 'rating') {
|
|
59
|
+
const n = Number.parseInt(input, 10);
|
|
60
|
+
if (!Number.isNaN(n) && n >= 1 && n <= 5) {
|
|
61
|
+
setRating(n);
|
|
62
|
+
setStep('comment');
|
|
63
|
+
}
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (step === 'comment') {
|
|
67
|
+
// Ctrl-D submits the comment (even when empty — rating-only
|
|
68
|
+
// feedback is allowed). Enter inserts a newline so the
|
|
69
|
+
// operator can write multi-line bug repros without the wizard
|
|
70
|
+
// ending the buffer prematurely.
|
|
71
|
+
if (key.ctrl && (input === 'd' || input === '')) {
|
|
72
|
+
setStep('context');
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (key.return) {
|
|
76
|
+
setCommentBuffer((prev) => prev + '\n');
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (key.backspace || key.delete) {
|
|
80
|
+
setCommentBuffer((prev) => prev.slice(0, -1));
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
// Printable characters land in the buffer. We accept anything
|
|
84
|
+
// non-empty + non-control. Ink's useInput sometimes delivers
|
|
85
|
+
// multi-char paste bursts as one `input` — we append the whole
|
|
86
|
+
// burst so paste lands intact.
|
|
87
|
+
if (input && !key.ctrl && !key.meta) {
|
|
88
|
+
setCommentBuffer((prev) => prev + input);
|
|
89
|
+
}
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (step === 'context') {
|
|
93
|
+
if (input === 'y' || input === 'Y') {
|
|
94
|
+
setIncludeContext(true);
|
|
95
|
+
setStep('confirm');
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (input === 'n' || input === 'N' || key.return) {
|
|
99
|
+
// Default no — Enter at the context prompt picks "no" so
|
|
100
|
+
// the operator can blast through with all defaults.
|
|
101
|
+
setIncludeContext(false);
|
|
102
|
+
setStep('confirm');
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (step === 'confirm') {
|
|
108
|
+
if (input === 'y' || input === 'Y' || key.return) {
|
|
109
|
+
if (category != null && rating != null) {
|
|
110
|
+
props.onResolve({
|
|
111
|
+
cancelled: false,
|
|
112
|
+
draft: {
|
|
113
|
+
category,
|
|
114
|
+
rating,
|
|
115
|
+
comment: commentBuffer,
|
|
116
|
+
includeSessionContext: includeContext,
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (input === 'n' || input === 'N') {
|
|
123
|
+
props.onResolve({ cancelled: true });
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
}, { isActive: !props.inert });
|
|
129
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "Pugi /feedback" }), _jsx(Text, { children: " \u2014 share what you saw. Esc cancels at any step." })] }), step === 'category' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "1. Category" }), CATEGORIES.map((c, idx) => (_jsx(Text, { children: ` ${idx + 1}. ${c.label.padEnd(8)} ${c.gloss}` }, c.value))), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Pick 1-4." }) })] })), step === 'rating' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "2. Rating" }), _jsx(Text, { children: ` 1=poor, 5=excellent. Picked category: ${category ?? '?'}` }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Pick 1-5." }) })] })), step === 'comment' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "3. Comment" }), _jsx(Text, { dimColor: true, children: "Multi-line. Enter inserts a newline. Ctrl-D submits." }), _jsx(Box, { marginTop: 1, flexDirection: "column", children: commentBuffer.length === 0 ? (_jsx(Text, { dimColor: true, children: "(empty \u2014 Ctrl-D submits without a comment)" })) : (commentBuffer.split('\n').map((line, idx) => (_jsx(Text, { children: `> ${line}` }, idx)))) })] })), step === 'context' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "4. Include session context?" }), _jsx(Text, { children: ' Last 5 turns, redacted (tokens / *_KEY=* values stripped).' }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "y / n \u2014 default n (Enter)." }) })] })), step === 'confirm' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "5. Submit?" }), _jsx(Text, { children: ` category=${category ?? '?'} rating=${rating ?? '?'} context=${includeContext ? 'yes' : 'no'}` }), _jsx(Text, { children: ` comment=${commentBuffer.length} chars` }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "y / n \u2014 default y (Enter)." }) })] }))] }));
|
|
130
|
+
}
|
|
131
|
+
export async function renderFeedbackPrompt() {
|
|
132
|
+
let resolveOuter;
|
|
133
|
+
const outerPromise = new Promise((resolve) => {
|
|
134
|
+
resolveOuter = resolve;
|
|
135
|
+
});
|
|
136
|
+
function App() {
|
|
137
|
+
const { exit } = useApp();
|
|
138
|
+
return (_jsx(FeedbackPrompt, { onResolve: (verdict) => {
|
|
139
|
+
resolveOuter(verdict);
|
|
140
|
+
// Mirror renderAskCli: tiny delay so Ink flushes unmount
|
|
141
|
+
// before the caller prints the toast line.
|
|
142
|
+
setTimeout(() => exit(), 16);
|
|
143
|
+
} }));
|
|
144
|
+
}
|
|
145
|
+
const instance = render(_jsx(App, {}));
|
|
146
|
+
const verdict = await outerPromise;
|
|
147
|
+
try {
|
|
148
|
+
await instance.waitUntilExit();
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
// Ink can throw when exit() races with a re-render — the verdict
|
|
152
|
+
// is already captured so we ignore.
|
|
153
|
+
}
|
|
154
|
+
return verdict;
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=feedback-prompt.js.map
|
package/dist/tui/input-box.js
CHANGED
|
@@ -33,6 +33,14 @@ import { SlashPalette, completePalette, filterPalette, } from './slash-palette.j
|
|
|
33
33
|
import { EMPTY_KILL_RING, killToLineEnd, killToLineStart, killWordBackward, yankAtCursor, } from '../core/repl/kill-ring.js';
|
|
34
34
|
import { readClipboard } from '../core/repl/clipboard-read.js';
|
|
35
35
|
const CTRL_C_DOUBLE_TAP_MS = 1_000;
|
|
36
|
+
/**
|
|
37
|
+
* Wave 6 BT 8 (Claude Code parity): Esc-Esc walks the conversation back
|
|
38
|
+
* one turn. 500ms is tight enough that an operator clearing the buffer +
|
|
39
|
+
* later changing their mind does NOT accidentally pop a turn, while
|
|
40
|
+
* still feeling like one motion. Matches Claude Code's documented
|
|
41
|
+
* double-Esc window.
|
|
42
|
+
*/
|
|
43
|
+
const ESCAPE_DOUBLE_TAP_MS = 500;
|
|
36
44
|
/** Width subtracted from the terminal width so the border + padding fit. */
|
|
37
45
|
const FRAME_OVERHEAD_COLUMNS = 4;
|
|
38
46
|
/** Fallback width when ink cannot read stdout (e.g. test harness). */
|
|
@@ -75,6 +83,11 @@ export function InputBox(props) {
|
|
|
75
83
|
const [history, setHistory] = useState(seededHistory);
|
|
76
84
|
const [historyIndex, setHistoryIndex] = useState(-1);
|
|
77
85
|
const [lastCtrlCAt, setLastCtrlCAt] = useState(undefined);
|
|
86
|
+
// Wave 6 BT 8: Esc-Esc walkback double-tap window. Tracks the epoch
|
|
87
|
+
// ms of the most recent Esc press so the next Esc within
|
|
88
|
+
// ESCAPE_DOUBLE_TAP_MS triggers the walkback handler instead of
|
|
89
|
+
// re-clearing the buffer.
|
|
90
|
+
const [lastEscapeAt, setLastEscapeAt] = useState(undefined);
|
|
78
91
|
const [cursorVisible, setCursorVisible] = useState(true);
|
|
79
92
|
// Ctrl+R / Ctrl+S reverse-search mode. Undefined when idle, a
|
|
80
93
|
// HistorySearchState while the operator is searching.
|
|
@@ -375,10 +388,41 @@ export function InputBox(props) {
|
|
|
375
388
|
if (key.escape) {
|
|
376
389
|
if (paletteOpen) {
|
|
377
390
|
// Close the palette without clearing the buffer so the operator
|
|
378
|
-
// can still send `/help` as plain text if they want.
|
|
391
|
+
// can still send `/help` as plain text if they want. Palette
|
|
392
|
+
// takes precedence over walkback because the operator's mental
|
|
393
|
+
// model is "Esc closes the visible overlay first".
|
|
379
394
|
setPaletteSuppressed(true);
|
|
395
|
+
setLastEscapeAt(undefined);
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
// Wave 6 BT 8: Esc-Esc walkback. Two presses within
|
|
399
|
+
// ESCAPE_DOUBLE_TAP_MS step the conversation back by one turn.
|
|
400
|
+
// First press still clears the buffer (legacy behaviour for the
|
|
401
|
+
// single-Esc cancel UX); the second press calls the host's
|
|
402
|
+
// walkback handler. Buffer-clear on the first press is what makes
|
|
403
|
+
// the double-tap feel "free" - the operator did not have to
|
|
404
|
+
// memorise a new chord; they just have to keep pressing.
|
|
405
|
+
const tEsc = now();
|
|
406
|
+
const withinEscapeWindow = typeof lastEscapeAt === 'number'
|
|
407
|
+
&& tEsc - lastEscapeAt <= ESCAPE_DOUBLE_TAP_MS;
|
|
408
|
+
if (withinEscapeWindow && props.onWalkback) {
|
|
409
|
+
// Second tap inside the window. Buffer was already cleared on
|
|
410
|
+
// the first press, so the host sees a clean input box AND the
|
|
411
|
+
// walkback result. We clear the window so a third tap restarts
|
|
412
|
+
// the cycle (no run-on walkbacks from a stuck Esc key).
|
|
413
|
+
const verdict = props.onWalkback();
|
|
414
|
+
setLastEscapeAt(undefined);
|
|
415
|
+
if (verdict !== 'walked-back') {
|
|
416
|
+
// Host refused (dispatch in flight, no turns to pop). The
|
|
417
|
+
// host owns the refusal copy via its own writeOutput path;
|
|
418
|
+
// we do not double-message here.
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
380
421
|
return;
|
|
381
422
|
}
|
|
423
|
+
// First Esc (or no walkback wired). Arm the window + clear the
|
|
424
|
+
// buffer per the long-standing single-Esc cancel contract.
|
|
425
|
+
setLastEscapeAt(tEsc);
|
|
382
426
|
setLine('');
|
|
383
427
|
setCursor(0);
|
|
384
428
|
setHistoryIndex(-1);
|
|
@@ -499,7 +543,7 @@ export function InputBox(props) {
|
|
|
499
543
|
: Math.min(paletteIndex, paletteView.rows.length - 1);
|
|
500
544
|
const divider = '─'.repeat(innerWidth);
|
|
501
545
|
const focusedMatch = search ? search.matches[search.focusedIndex] : undefined;
|
|
502
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: "
|
|
546
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: "#3da9fc", dimColor: true, children: divider }), _jsx(Box, { paddingX: 1, flexDirection: "column", children: search ? (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: "#3da9fc", children: '(reverse-i-search) ' }), _jsx(Text, { children: `\`${search.query}\`: ` }), _jsx(Text, { color: "yellow", children: focusedMatch ? focusedMatch.brief : '(no match)' })] }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: `Ctrl+R next · Ctrl+S prev · Enter accept · Esc cancel · ${search.matches.length} match${search.matches.length === 1 ? '' : 'es'}` }) })] })) : (_jsxs(Box, { children: [_jsx(Text, { color: "#3da9fc", children: '› ' }), _jsx(Text, { children: renderLineWithCursor(line, cursor, cursorVisible) })] })) }), _jsx(Text, { color: "#3da9fc", dimColor: true, children: divider }), line.length > innerWidth - 4 ? (_jsxs(Box, { children: [_jsx(Text, { color: "gray", children: '┊ ' }), _jsx(Text, { dimColor: true, children: 'line wraps - Enter still submits' })] })) : null, _jsx(SlashPalette, { rows: paletteView.rows, focusedIndex: clampedPaletteIndex, totalBeforeLimit: paletteView.totalBeforeLimit }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: '↑/↓ history · Ctrl+R search · / commands · Enter brief · Esc cancel · Ctrl+C abort / ×2 exit' }) })] }));
|
|
503
547
|
}
|
|
504
548
|
/**
|
|
505
549
|
* Render the line with the cursor glyph inserted at `cursor`. The cursor
|
|
@@ -107,9 +107,9 @@ function renderBlock(block, key) {
|
|
|
107
107
|
case 'paragraph':
|
|
108
108
|
return (_jsx(Text, { children: renderInline(block.text) }, key));
|
|
109
109
|
case 'bullet':
|
|
110
|
-
return (_jsxs(Box, { children: [_jsx(Text, { color: "
|
|
110
|
+
return (_jsxs(Box, { children: [_jsx(Text, { color: "#3da9fc", children: '• ' }), _jsx(Text, { children: renderInline(block.text) })] }, key));
|
|
111
111
|
case 'ordered':
|
|
112
|
-
return (_jsxs(Box, { children: [_jsx(Text, { color: "
|
|
112
|
+
return (_jsxs(Box, { children: [_jsx(Text, { color: "#3da9fc", children: `${block.index}. ` }), _jsx(Text, { children: renderInline(block.text) })] }, key));
|
|
113
113
|
case 'code':
|
|
114
114
|
return renderCodeBlock(block.lang, block.body, key);
|
|
115
115
|
case 'blank':
|
|
@@ -148,7 +148,7 @@ function renderCodeLine(line, keywords) {
|
|
|
148
148
|
spans.push(_jsx(Text, { color: "green", children: tok }, key));
|
|
149
149
|
}
|
|
150
150
|
else if (keywords.includes(tok)) {
|
|
151
|
-
spans.push(_jsx(Text, { color: "
|
|
151
|
+
spans.push(_jsx(Text, { color: "#3da9fc", bold: true, children: tok }, key));
|
|
152
152
|
}
|
|
153
153
|
else {
|
|
154
154
|
spans.push(_jsx(Text, { children: tok }, key));
|
|
@@ -260,7 +260,7 @@ function renderSpan(span, key) {
|
|
|
260
260
|
case 'code':
|
|
261
261
|
return _jsx(Text, { color: "green", children: span.text }, key);
|
|
262
262
|
case 'link':
|
|
263
|
-
return (_jsxs(Text, { children: [_jsx(Text, { color: "
|
|
263
|
+
return (_jsxs(Text, { children: [_jsx(Text, { color: "#3da9fc", underline: true, children: span.text }), _jsx(Text, { dimColor: true, children: ` (${span.url})` })] }, key));
|
|
264
264
|
}
|
|
265
265
|
}
|
|
266
266
|
//# sourceMappingURL=markdown-render.js.map
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Leak L25 (2026-05-27) — Onboarding Ink wizard.
|
|
4
|
+
*
|
|
5
|
+
* Six-screen interactive walk:
|
|
6
|
+
*
|
|
7
|
+
* 1. Welcome + auth status (single Enter to continue)
|
|
8
|
+
* 2. Default permission mode (4-row picker)
|
|
9
|
+
* 3. Output style preset (5-row picker)
|
|
10
|
+
* 4. MCP server pointer (informational, Enter to continue)
|
|
11
|
+
* 5. Telemetry consent (3-row picker)
|
|
12
|
+
* 6. Recap card (Enter to commit + exit)
|
|
13
|
+
*
|
|
14
|
+
* Driven entirely by Ink's `useInput`. The component does NOT perform
|
|
15
|
+
* any fs writes — it resolves the verdict back to the caller
|
|
16
|
+
* (`runOnboardingCommand`), which translates verdicts into L6 / L18 /
|
|
17
|
+
* telemetry-state mutations. Single source of truth: the runner.
|
|
18
|
+
*
|
|
19
|
+
* Each picker step pre-selects the CURRENT persisted value (from the
|
|
20
|
+
* snapshot passed in props) so pressing Enter on Step 2/3/5 keeps the
|
|
21
|
+
* current value — that is the idempotency contract.
|
|
22
|
+
*
|
|
23
|
+
* Cancellation:
|
|
24
|
+
* - Esc / Ctrl-C at any step → verdict.cancelled = true, the runner
|
|
25
|
+
* skips ALL writes (including the marker touch) so the next bare
|
|
26
|
+
* `pugi` invocation still surfaces the first-run hint.
|
|
27
|
+
*
|
|
28
|
+
* Keystrokes:
|
|
29
|
+
* - ↑/↓ or j/k — move selection in pickers.
|
|
30
|
+
* - Enter — confirm step (keep current = pass through; new pick
|
|
31
|
+
* = update verdict for that tier).
|
|
32
|
+
* - 's' — skip current step explicitly (verdict slot stays null).
|
|
33
|
+
* - Esc / 'q' — cancel the wizard.
|
|
34
|
+
*/
|
|
35
|
+
import { useState } from 'react';
|
|
36
|
+
import { Box, Text, render, useApp, useInput } from 'ink';
|
|
37
|
+
import { PERMISSION_MODES, PERMISSION_MODE_GLOSS, } from '../core/permissions/index.js';
|
|
38
|
+
import { OUTPUT_STYLES, OUTPUT_STYLE_SLUGS, } from '../core/output-style/presets.js';
|
|
39
|
+
import { TELEMETRY_CHOICES, } from '../core/onboarding/telemetry-state.js';
|
|
40
|
+
const EMPTY_DRAFT = {
|
|
41
|
+
permissionMode: null,
|
|
42
|
+
outputStyle: null,
|
|
43
|
+
telemetry: null,
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Lookup the snapshot value's index within the picker rows so the
|
|
47
|
+
* initial cursor sits on the current value. Returns 0 (safe default)
|
|
48
|
+
* when the snapshot value is outside the closed list — should never
|
|
49
|
+
* happen given the type guards in the state modules, but the index
|
|
50
|
+
* fallback keeps Ink from crashing on a malformed config.
|
|
51
|
+
*/
|
|
52
|
+
function indexOf(rows, value) {
|
|
53
|
+
const idx = rows.indexOf(value);
|
|
54
|
+
return idx === -1 ? 0 : idx;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The wizard component. Pure: no fs, no env, no network. Verdicts
|
|
58
|
+
* flow up via `onComplete`; the caller owns the writes.
|
|
59
|
+
*/
|
|
60
|
+
export function OnboardingWizard(props) {
|
|
61
|
+
const { snapshot, onComplete } = props;
|
|
62
|
+
const [step, setStep] = useState(1);
|
|
63
|
+
const [permissionIdx, setPermissionIdx] = useState(indexOf(PERMISSION_MODES, snapshot.permissionMode));
|
|
64
|
+
const [styleIdx, setStyleIdx] = useState(indexOf(OUTPUT_STYLE_SLUGS, snapshot.outputStyle));
|
|
65
|
+
const [telemetryIdx, setTelemetryIdx] = useState(indexOf(TELEMETRY_CHOICES, snapshot.telemetry));
|
|
66
|
+
const [draft, setDraft] = useState(EMPTY_DRAFT);
|
|
67
|
+
const finish = (final, cancelled) => {
|
|
68
|
+
onComplete({
|
|
69
|
+
permissionMode: final.permissionMode,
|
|
70
|
+
outputStyle: final.outputStyle,
|
|
71
|
+
telemetry: final.telemetry,
|
|
72
|
+
cancelled,
|
|
73
|
+
});
|
|
74
|
+
};
|
|
75
|
+
useInput((input, key) => {
|
|
76
|
+
// Universal cancel.
|
|
77
|
+
if (key.escape || (key.ctrl && input === 'c')) {
|
|
78
|
+
finish(EMPTY_DRAFT, true);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
// Universal skip — Enter on a picker means "keep current"; explicit
|
|
82
|
+
// 's' makes the skip intent obvious in the recap.
|
|
83
|
+
const isAdvance = key.return;
|
|
84
|
+
const moveUp = key.upArrow || input === 'k';
|
|
85
|
+
const moveDown = key.downArrow || input === 'j';
|
|
86
|
+
const explicitSkip = input === 's';
|
|
87
|
+
switch (step) {
|
|
88
|
+
case 1: {
|
|
89
|
+
if (isAdvance)
|
|
90
|
+
setStep(2);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
case 2: {
|
|
94
|
+
if (moveUp) {
|
|
95
|
+
setPermissionIdx((i) => (i === 0 ? PERMISSION_MODES.length - 1 : i - 1));
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (moveDown) {
|
|
99
|
+
setPermissionIdx((i) => (i === PERMISSION_MODES.length - 1 ? 0 : i + 1));
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (isAdvance || explicitSkip) {
|
|
103
|
+
const picked = PERMISSION_MODES[permissionIdx];
|
|
104
|
+
const verdict = explicitSkip || picked === snapshot.permissionMode ? null : picked ?? null;
|
|
105
|
+
setDraft((d) => ({ ...d, permissionMode: verdict }));
|
|
106
|
+
setStep(3);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
case 3: {
|
|
112
|
+
if (moveUp) {
|
|
113
|
+
setStyleIdx((i) => (i === 0 ? OUTPUT_STYLE_SLUGS.length - 1 : i - 1));
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (moveDown) {
|
|
117
|
+
setStyleIdx((i) => (i === OUTPUT_STYLE_SLUGS.length - 1 ? 0 : i + 1));
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (isAdvance || explicitSkip) {
|
|
121
|
+
const picked = OUTPUT_STYLE_SLUGS[styleIdx];
|
|
122
|
+
const verdict = explicitSkip || picked === snapshot.outputStyle ? null : picked ?? null;
|
|
123
|
+
setDraft((d) => ({ ...d, outputStyle: verdict }));
|
|
124
|
+
setStep(4);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
case 4: {
|
|
130
|
+
if (isAdvance)
|
|
131
|
+
setStep(5);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
case 5: {
|
|
135
|
+
if (moveUp) {
|
|
136
|
+
setTelemetryIdx((i) => (i === 0 ? TELEMETRY_CHOICES.length - 1 : i - 1));
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (moveDown) {
|
|
140
|
+
setTelemetryIdx((i) => (i === TELEMETRY_CHOICES.length - 1 ? 0 : i + 1));
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
if (isAdvance || explicitSkip) {
|
|
144
|
+
const picked = TELEMETRY_CHOICES[telemetryIdx];
|
|
145
|
+
const verdict = explicitSkip || picked === snapshot.telemetry ? null : picked ?? null;
|
|
146
|
+
setDraft((d) => ({ ...d, telemetry: verdict }));
|
|
147
|
+
setStep(6);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
case 6: {
|
|
153
|
+
if (isAdvance)
|
|
154
|
+
finish(draft, false);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [_jsx(StepHeader, { step: step }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [step === 1 && _jsx(WelcomeStep, { snapshot: snapshot }), step === 2 && (_jsx(ModeStep, { current: snapshot.permissionMode, selectedIdx: permissionIdx })), step === 3 && (_jsx(StyleStep, { current: snapshot.outputStyle, currentSource: snapshot.outputStyleSource, selectedIdx: styleIdx })), step === 4 && _jsx(McpStep, {}), step === 5 && (_jsx(TelemetryStep, { current: snapshot.telemetry, selectedIdx: telemetryIdx })), step === 6 && _jsx(RecapStep, { snapshot: snapshot, draft: draft })] }), _jsx(FooterHints, { step: step })] }));
|
|
160
|
+
}
|
|
161
|
+
function StepHeader({ step }) {
|
|
162
|
+
const titles = {
|
|
163
|
+
1: 'Welcome to Pugi',
|
|
164
|
+
2: 'Step 2 / 5 — Default permission mode',
|
|
165
|
+
3: 'Step 3 / 5 — Output style',
|
|
166
|
+
4: 'Step 4 / 5 — MCP servers',
|
|
167
|
+
5: 'Step 5 / 5 — Telemetry consent',
|
|
168
|
+
6: 'Setup complete',
|
|
169
|
+
};
|
|
170
|
+
return (_jsx(Text, { bold: true, color: "cyan", children: titles[step] }));
|
|
171
|
+
}
|
|
172
|
+
function WelcomeStep({ snapshot }) {
|
|
173
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: "Brief it. It ships." }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: snapshot.authPresent
|
|
174
|
+
? 'You are signed in. The wizard configures local defaults; values persist to ~/.pugi/config.json.'
|
|
175
|
+
: 'You are NOT signed in. The wizard still configures local defaults, but you should run `pugi login` after.' }) })] }));
|
|
176
|
+
}
|
|
177
|
+
function ModeStep({ current, selectedIdx, }) {
|
|
178
|
+
return (_jsx(Box, { flexDirection: "column", children: PERMISSION_MODES.map((mode, idx) => (_jsx(PickerRow, { isSelected: idx === selectedIdx, isCurrent: mode === current, title: mode, gloss: PERMISSION_MODE_GLOSS[mode] }, mode))) }));
|
|
179
|
+
}
|
|
180
|
+
function StyleStep({ current, currentSource, selectedIdx, }) {
|
|
181
|
+
return (_jsxs(Box, { flexDirection: "column", children: [OUTPUT_STYLE_SLUGS.map((slug, idx) => (_jsx(PickerRow, { isSelected: idx === selectedIdx, isCurrent: slug === current, title: slug, gloss: OUTPUT_STYLES[slug].gloss }, slug))), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: currentSource === 'workspace'
|
|
182
|
+
? 'Active style is currently a workspace override. The wizard writes the user-tier default.'
|
|
183
|
+
: `Active source: ${currentSource}.` }) })] }));
|
|
184
|
+
}
|
|
185
|
+
function McpStep() {
|
|
186
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: "MCP servers extend Pugi with extra tools (filesystem, browser, custom APIs)." }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Add one with:" }) }), _jsx(Text, { children: ' pugi mcp add <name> <command>' }), _jsx(Text, { children: ' pugi mcp list' }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "You can skip for now and add servers later." }) })] }));
|
|
187
|
+
}
|
|
188
|
+
function TelemetryStep({ current, selectedIdx, }) {
|
|
189
|
+
const gloss = {
|
|
190
|
+
off: 'No telemetry of any kind.',
|
|
191
|
+
anonymous: 'Counts + error categories only; no payloads.',
|
|
192
|
+
community: 'Anonymous + opt-in usage panels.',
|
|
193
|
+
};
|
|
194
|
+
return (_jsx(Box, { flexDirection: "column", children: TELEMETRY_CHOICES.map((choice, idx) => (_jsx(PickerRow, { isSelected: idx === selectedIdx, isCurrent: choice === current, title: choice, gloss: gloss[choice] }, choice))) }));
|
|
195
|
+
}
|
|
196
|
+
function RecapStep({ snapshot, draft, }) {
|
|
197
|
+
const finalMode = draft.permissionMode ?? snapshot.permissionMode;
|
|
198
|
+
const finalStyle = draft.outputStyle ?? snapshot.outputStyle;
|
|
199
|
+
const finalTelemetry = draft.telemetry ?? snapshot.telemetry;
|
|
200
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: ` Permission mode: ${finalMode}${draft.permissionMode === null ? ' (unchanged)' : ''}` }), _jsx(Text, { children: ` Output style: ${finalStyle}${draft.outputStyle === null ? ' (unchanged)' : ''}` }), _jsx(Text, { children: ` Telemetry: ${finalTelemetry}${draft.telemetry === null ? ' (unchanged)' : ''}` }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Press Enter to write defaults + exit. Esc to cancel without saving." }) })] }));
|
|
201
|
+
}
|
|
202
|
+
function PickerRow({ isSelected, isCurrent, title, gloss, }) {
|
|
203
|
+
const indicator = isSelected ? '▸ ' : ' ';
|
|
204
|
+
const currentTag = isCurrent ? ' [current]' : '';
|
|
205
|
+
return (_jsxs(Text, { children: [_jsxs(Text, { color: isSelected ? 'cyan' : undefined, bold: isSelected, children: [indicator, title.padEnd(18, ' ')] }), _jsx(Text, { dimColor: true, children: `${gloss}${currentTag}` })] }));
|
|
206
|
+
}
|
|
207
|
+
function FooterHints({ step }) {
|
|
208
|
+
const hint = step === 1 || step === 4
|
|
209
|
+
? 'Enter continue Esc cancel'
|
|
210
|
+
: step === 6
|
|
211
|
+
? 'Enter commit + exit Esc cancel'
|
|
212
|
+
: '↑/↓ select Enter confirm s skip Esc cancel';
|
|
213
|
+
return (_jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: hint }) }));
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Mount the wizard, await the operator's verdict, unmount Ink, return
|
|
217
|
+
* the verdict to the runner. Wrapped in a `useApp` consumer so we can
|
|
218
|
+
* call `exit()` and let `waitUntilExit()` resolve cleanly.
|
|
219
|
+
*/
|
|
220
|
+
export async function renderOnboardingWizard(opts) {
|
|
221
|
+
return new Promise((resolvePromise) => {
|
|
222
|
+
let resolved = false;
|
|
223
|
+
const handleComplete = (verdict) => {
|
|
224
|
+
if (resolved)
|
|
225
|
+
return;
|
|
226
|
+
resolved = true;
|
|
227
|
+
app.unmount();
|
|
228
|
+
resolvePromise(verdict);
|
|
229
|
+
};
|
|
230
|
+
const Wrapper = () => {
|
|
231
|
+
const { exit } = useApp();
|
|
232
|
+
return (_jsx(OnboardingWizard, { snapshot: opts.snapshot, onComplete: (verdict) => {
|
|
233
|
+
handleComplete(verdict);
|
|
234
|
+
exit();
|
|
235
|
+
} }));
|
|
236
|
+
};
|
|
237
|
+
const app = render(_jsx(Wrapper, {}));
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
//# sourceMappingURL=onboarding-wizard.js.map
|