@pugi/cli 0.1.0-beta.48 → 0.1.0-beta.49

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.
@@ -44,7 +44,7 @@ export function sanitizeSemver(raw) {
44
44
  * during import). When bumping the CLI version BOTH literals must be
45
45
  * updated; the release smoke-test (`pack:smoke`) verifies they agree.
46
46
  */
47
- export const PUGI_CLI_VERSION = sanitizeSemver('0.1.0-beta.48');
47
+ export const PUGI_CLI_VERSION = sanitizeSemver('0.1.0-beta.49');
48
48
  /**
49
49
  * Outbound: the CLI's installed semver. Read at request time by
50
50
  * `version-interceptor.ts` and injected on every `fetch` call.
package/dist/tui/repl.js CHANGED
@@ -108,17 +108,20 @@ export function Repl(props) {
108
108
  setSplashVisible(false);
109
109
  }
110
110
  }, [splashVisible, state.agents.length, state.transcript.length]);
111
- // CEO P0 #2 (2026-05-29): same dismissal contract for the welcome
112
- // banner. The banner is the "boot card" once any agent fires or
113
- // the transcript gains а row, the banner clears так the conversation
114
- // pane owns the vertical real estate.
111
+ // CEO P0 #2 (2026-05-29) v2: welcome banner stays until the operator
112
+ // actively engages the loopfirst agent spawn. Boot-time auto-init
113
+ // emits system rows into `state.transcript` (skip-trust hints, dirty
114
+ // tree warnings) which used к dismiss the banner within ~2s, hiding
115
+ // the brand mascot before the operator could read it. Drop the
116
+ // `transcript.length` trigger; agent spawn (= real dispatch) remains
117
+ // the sole signal that the operator stopped reading the banner.
115
118
  useEffect(() => {
116
119
  if (!welcomeVisible)
117
120
  return;
118
- if (state.agents.length > 0 || state.transcript.length > 0) {
121
+ if (state.agents.length > 0) {
119
122
  setWelcomeVisible(false);
120
123
  }
121
- }, [welcomeVisible, state.agents.length, state.transcript.length]);
124
+ }, [welcomeVisible, state.agents.length]);
122
125
  const personaNames = useMemo(() => buildPersonaNameMap(), []);
123
126
  const { exit } = useApp();
124
127
  const handleSubmit = useCallback((line) => {
@@ -255,7 +258,7 @@ export function Repl(props) {
255
258
  // input, and the input stays the sole focusable surface adjacent
256
259
  // to the cursor row, so all keystrokes route through it.
257
260
  const altScreenRows = process.stdout.rows ?? 24;
258
- return (_jsxs(Box, { flexDirection: "column", paddingX: 1, minHeight: altScreenRows, children: [props.updateBanner ? _jsx(UpdateBanner, { result: props.updateBanner }) : null, welcomeVisible && props.welcomeData ? (_jsx(WelcomeBanner, { data: props.welcomeData, mascotPrePrinted: props.mascotPrePrinted === true, autoInitStatus: props.autoInitStatus ?? null })) : null, splashVisible ? (_jsx(ReplSplash, { cliVersion: state.cliVersion, workspaceLabel: state.workspaceLabel, plan: props.splashPlan, model: props.splashModel, tenant: props.splashTenant, onDismiss: dismissSplash, mascotPrePrinted: props.mascotPrePrinted === true })) : null, _jsx(Header, { state: state }), _jsx(Box, { flexDirection: "column", marginTop: 1, flexGrow: 1, justifyContent: "flex-end", children: overlay === 'help' ? (_jsx(HelpOverlay, {})) : overlay === 'roster' ? (_jsx(RosterOverlay, {})) : overlay === 'farewell' ? (_jsx(FarewellOverlay, {})) : (_jsx(MainArea, { state: state, personaNames: personaNames, nowEpochMs: tickNow, hideToolStream: props.hideToolStream === true, toolStreamCollapsed: toolStreamCollapsed })) }), state.pendingAsk ? (_jsx(Box, { marginTop: 1, children: _jsx(AskModal, { tag: state.pendingAsk, onResolve: handleAskResolve }) })) : null, state.pendingPlanReview ? (_jsx(Box, { marginTop: 1, children: _jsx(PlanReviewModal, { tag: state.pendingPlanReview, onResolve: handlePlanReviewResolve }) })) : null, _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [overlay === 'farewell' || modalActive ? null : (_jsx(InputBox, { onSubmit: handleSubmit, onExit: handleExit, onCancel: handleCancel, onWalkback: handleWalkback, onCyclePermissionMode: handleCyclePermissionMode, now: props.now,
261
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 1, minHeight: altScreenRows, children: [welcomeVisible && props.welcomeData ? (_jsx(WelcomeBanner, { data: props.welcomeData, mascotPrePrinted: props.mascotPrePrinted === true, autoInitStatus: props.autoInitStatus ?? null })) : null, splashVisible ? (_jsx(ReplSplash, { cliVersion: state.cliVersion, workspaceLabel: state.workspaceLabel, plan: props.splashPlan, model: props.splashModel, tenant: props.splashTenant, onDismiss: dismissSplash, mascotPrePrinted: props.mascotPrePrinted === true })) : null, _jsx(Header, { state: state }), _jsx(Box, { flexDirection: "column", marginTop: 1, flexGrow: 1, justifyContent: "flex-end", children: overlay === 'help' ? (_jsx(HelpOverlay, {})) : overlay === 'roster' ? (_jsx(RosterOverlay, {})) : overlay === 'farewell' ? (_jsx(FarewellOverlay, {})) : (_jsx(MainArea, { state: state, personaNames: personaNames, nowEpochMs: tickNow, hideToolStream: props.hideToolStream === true, toolStreamCollapsed: toolStreamCollapsed })) }), state.pendingAsk ? (_jsx(Box, { marginTop: 1, children: _jsx(AskModal, { tag: state.pendingAsk, onResolve: handleAskResolve }) })) : null, state.pendingPlanReview ? (_jsx(Box, { marginTop: 1, children: _jsx(PlanReviewModal, { tag: state.pendingPlanReview, onResolve: handlePlanReviewResolve }) })) : null, _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [overlay === 'farewell' || modalActive ? null : (_jsx(InputBox, { onSubmit: handleSubmit, onExit: handleExit, onCancel: handleCancel, onWalkback: handleWalkback, onCyclePermissionMode: handleCyclePermissionMode, now: props.now,
259
262
  // Slug from process.cwd() (full path) so two workspaces with
260
263
  // the same basename do not share history. state.workspaceLabel
261
264
  // is the basename only. Codex review P2.
@@ -263,7 +266,7 @@ export function Repl(props) {
263
266
  // α7 cost-meter sprint — surface accumulated session totals
264
267
  // + per-turn delta flash on the status bar's top row. The
265
268
  // session module owns accumulation; the bar is a pure render.
266
- sessionTokensIn: state.sessionTokensIn, sessionTokensOut: state.sessionTokensOut, sessionCostUsd: state.sessionCostUsd, sessionStartedAtEpochMs: state.sessionStartedAtEpochMs, lastTurnDelta: state.lastTurnDelta })] })] }));
269
+ sessionTokensIn: state.sessionTokensIn, sessionTokensOut: state.sessionTokensOut, sessionCostUsd: state.sessionCostUsd, sessionStartedAtEpochMs: state.sessionStartedAtEpochMs, lastTurnDelta: state.lastTurnDelta }), props.updateBanner ? _jsx(UpdateBanner, { result: props.updateBanner }) : null] })] }));
267
270
  }
268
271
  function Header({ state }) {
269
272
  // Leak L30 (2026-05-27): the header `.io` brand accent + connection
@@ -13,6 +13,13 @@ export function resolveDisplayedLatest(npmLatest, serverRecommended) {
13
13
  ? serverRecommended
14
14
  : npmLatest;
15
15
  }
16
+ /**
17
+ * Claude-Code-style corner banner: single line, dim orange, right
18
+ * aligned. Renders to the bottom of the REPL frame so it never
19
+ * displaces conversation content. Only mounts when an update is
20
+ * actually available; identical to no-op when the registry poll
21
+ * reports current. Suppress with `PUGI_SKIP_UPDATE_BANNER=1`.
22
+ */
16
23
  export function UpdateBanner({ result }) {
17
24
  const command = upgradeCommand(result.method);
18
25
  // Read the cache lazily inside the render so a server response that
@@ -21,6 +28,6 @@ export function UpdateBanner({ result }) {
21
28
  // do per render.
22
29
  const serverRecommended = getCachedServerRecommendation();
23
30
  const displayedLatest = resolveDisplayedLatest(result.latest, serverRecommended);
24
- return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Box, { children: [_jsx(Text, { dimColor: true, children: '─ ' }), _jsx(Text, { bold: true, color: "#3da9fc", children: 'Pugi ' }), _jsx(Text, { children: result.installed }), _jsx(Text, { dimColor: true, children: ' (installed) ' }), _jsx(Text, { bold: true, children: displayedLatest }), _jsx(Text, { dimColor: true, children: ' (latest)' })] }), _jsxs(Box, { children: [_jsx(Text, { dimColor: true, children: ' Update: ' }), _jsx(Text, { color: "#3da9fc", children: command })] }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: ' Skip with PUGI_SKIP_UPDATE_BANNER=1' }) })] }));
31
+ return (_jsx(Box, { justifyContent: "flex-end", children: _jsxs(Text, { color: "#d97706", children: [_jsx(Text, { dimColor: true, children: 'Update available! ' }), _jsx(Text, { dimColor: true, children: `v${displayedLatest} Run: ` }), _jsx(Text, { bold: true, children: command })] }) }));
25
32
  }
26
33
  //# sourceMappingURL=update-banner.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pugi/cli",
3
- "version": "0.1.0-beta.48",
3
+ "version": "0.1.0-beta.49",
4
4
  "description": "Pugi CLI - terminal-native software execution system",
5
5
  "homepage": "https://pugi.io",
6
6
  "repository": {
@@ -55,7 +55,7 @@
55
55
  "undici": "^8.3.0",
56
56
  "zod": "^3.23.0",
57
57
  "@pugi/personas": "0.1.2",
58
- "@pugi/sdk": "0.1.0-beta.48"
58
+ "@pugi/sdk": "0.1.0-beta.49"
59
59
  },
60
60
  "devDependencies": {
61
61
  "@types/node": "^22.0.0",