@pugi/cli 0.1.0-beta.5 → 0.1.0-beta.50
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 -25
- package/assets/pugi-prozr2-mascot.ansi +9 -0
- package/bin/run.js +33 -1
- package/dist/commands/jobs-watch.js +201 -0
- package/dist/commands/jobs.js +15 -0
- package/dist/commands/smoke.js +133 -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/ensure-authenticated.js +129 -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/bash-classifier.js +400 -4
- package/dist/core/checkpoint/resumer.js +149 -0
- package/dist/core/checkpoint/rewinder.js +291 -0
- package/dist/core/codegraph/decision-store.js +248 -0
- package/dist/core/codegraph/detect-repo.js +459 -0
- package/dist/core/codegraph/install.js +134 -0
- package/dist/core/codegraph/offer-hook.js +220 -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 +112 -3
- 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/hooks.js +118 -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/sandbox.js +40 -0
- package/dist/core/diagnostics/probes/session.js +74 -0
- package/dist/core/diagnostics/probes/status-snapshot.js +488 -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 +322 -0
- package/dist/core/engine/anvil-client.js +115 -5
- package/dist/core/engine/budgets.js +98 -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 +860 -211
- package/dist/core/engine/prompts.js +88 -2
- package/dist/core/engine/strip-internal-fields.js +124 -0
- package/dist/core/engine/tool-bridge.js +1045 -36
- 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/hooks/v2/event-emitter.js +115 -0
- package/dist/core/hooks/v2/executor.js +282 -0
- package/dist/core/hooks/v2/index.js +25 -0
- package/dist/core/hooks/v2/lifecycle.js +104 -0
- package/dist/core/hooks/v2/loader.js +216 -0
- package/dist/core/hooks/v2/matcher.js +125 -0
- package/dist/core/hooks/v2/trust.js +143 -0
- package/dist/core/hooks/v2/types.js +86 -0
- package/dist/core/lsp/cache.js +105 -0
- package/dist/core/lsp/client.js +776 -0
- 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/orchestrator-tools.js +662 -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/phase1-kinds.js +20 -0
- package/dist/core/memory-sync/queue.js +158 -0
- package/dist/core/onboarding/ensure-initialized.js +133 -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/path-security.js +284 -2
- package/dist/core/permissions/auto-classifier.js +124 -0
- package/dist/core/permissions/circuit-breaker.js +83 -0
- package/dist/core/permissions/gate.js +278 -0
- package/dist/core/permissions/index.js +20 -0
- package/dist/core/permissions/mode.js +174 -0
- package/dist/core/permissions/state.js +241 -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/history.js +11 -1
- package/dist/core/repl/model-pricing.js +135 -0
- package/dist/core/repl/session.js +1897 -37
- package/dist/core/repl/slash-commands.js +430 -15
- 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 +92 -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/smoke/headless-driver.js +174 -0
- package/dist/core/smoke/orchestrator.js +194 -0
- package/dist/core/smoke/runner.js +238 -0
- package/dist/core/smoke/scenario-parser.js +316 -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/core/worktree-manager/cleanup.js +123 -0
- package/dist/core/worktree-manager/manager.js +303 -0
- package/dist/index.js +28 -0
- package/dist/runtime/bootstrap.js +190 -0
- package/dist/runtime/cli.js +3241 -343
- package/dist/runtime/commands/cancel.js +231 -0
- package/dist/runtime/commands/chain.js +489 -0
- package/dist/runtime/commands/codegraph-status.js +227 -0
- package/dist/runtime/commands/compact.js +297 -0
- package/dist/runtime/commands/cost.js +199 -0
- package/dist/runtime/commands/delegate.js +242 -11
- package/dist/runtime/commands/dispatch.js +126 -0
- package/dist/runtime/commands/doctor.js +412 -0
- package/dist/runtime/commands/feedback.js +184 -0
- package/dist/runtime/commands/hooks.js +184 -0
- package/dist/runtime/commands/lsp.js +368 -0
- package/dist/runtime/commands/mcp.js +879 -0
- package/dist/runtime/commands/memory.js +508 -0
- package/dist/runtime/commands/model.js +237 -0
- package/dist/runtime/commands/onboarding.js +275 -0
- package/dist/runtime/commands/patch.js +128 -0
- package/dist/runtime/commands/permissions.js +112 -0
- package/dist/runtime/commands/plan.js +143 -0
- package/dist/runtime/commands/prd-check.js +285 -0
- package/dist/runtime/commands/redo-blob-store.js +92 -0
- package/dist/runtime/commands/redo.js +361 -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/sessions.js +163 -0
- package/dist/runtime/commands/share.js +316 -0
- package/dist/runtime/commands/status.js +186 -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/undo.js +32 -0
- package/dist/runtime/commands/update.js +289 -0
- package/dist/runtime/commands/vim.js +140 -0
- package/dist/runtime/commands/worktree.js +177 -0
- package/dist/runtime/commands/worktrees.js +155 -0
- package/dist/runtime/headless-repl.js +195 -0
- 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 +556 -0
- package/dist/tools/ask-user-question.js +213 -0
- package/dist/tools/ask-user.js +115 -0
- package/dist/tools/bash.js +203 -4
- package/dist/tools/file-tools.js +85 -14
- package/dist/tools/lsp-tools.js +189 -0
- package/dist/tools/mcp-tool.js +260 -0
- package/dist/tools/multi-edit.js +361 -0
- package/dist/tools/powershell.js +268 -0
- package/dist/tools/registry.js +51 -0
- 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 +218 -3
- package/dist/tui/markdown-render.js +4 -4
- package/dist/tui/onboarding-wizard.js +240 -0
- package/dist/tui/permissions-picker.js +86 -0
- package/dist/tui/render.js +35 -0
- package/dist/tui/repl-render.js +313 -35
- package/dist/tui/repl-splash-art.js +1 -1
- package/dist/tui/repl-splash-mascot.js +32 -8
- package/dist/tui/repl-splash.js +2 -2
- package/dist/tui/repl.js +85 -5
- 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/thinking-spinner.js +123 -0
- package/dist/tui/tool-stream-pane.js +52 -3
- package/dist/tui/update-banner.js +27 -2
- package/dist/tui/vim-input.js +267 -0
- package/dist/tui/welcome-banner.js +107 -0
- package/dist/tui/welcome-data.js +293 -0
- package/docs/examples/codegraph.mcp.json +10 -0
- package/package.json +12 -6
- package/test/scenarios/codegen-create-file.scenario.txt +13 -0
- package/test/scenarios/compact-force.scenario.txt +11 -0
- package/test/scenarios/identity.scenario.txt +11 -0
- package/test/scenarios/persona-handoff.scenario.txt +11 -0
- package/test/scenarios/walkback.scenario.txt +12 -0
- package/dist/core/engine/compaction-hook.js +0 -154
package/dist/tui/repl-render.js
CHANGED
|
@@ -16,14 +16,20 @@
|
|
|
16
16
|
* a tiny `event:`/`data:`/`id:` parser. This keeps the dependency
|
|
17
17
|
* graph at zero new packages.
|
|
18
18
|
*/
|
|
19
|
+
import { existsSync } from 'node:fs';
|
|
20
|
+
import { resolve } from 'node:path';
|
|
19
21
|
import React from 'react';
|
|
20
22
|
import { render } from 'ink';
|
|
21
23
|
import { Repl } from './repl.js';
|
|
22
24
|
import { printPugMascotPreInk } from './repl-splash-mascot.js';
|
|
25
|
+
import { collectWelcomeData } from './welcome-data.js';
|
|
26
|
+
import { ThemeProvider } from '../core/theme/context.js';
|
|
27
|
+
import { resolveTheme } from '../core/theme/state.js';
|
|
23
28
|
import { ReplSession, } from '../core/repl/session.js';
|
|
24
29
|
import { resolveWorkspaceContext } from '../core/repl/workspace-context.js';
|
|
25
30
|
import { SqliteSessionStore } from '../core/repl/store/index.js';
|
|
26
31
|
import { slugForCwd } from '../core/repl/history.js';
|
|
32
|
+
import { loadSettings } from '../core/settings.js';
|
|
27
33
|
import { WorkingSet, buildRepoSkeleton, loadPugiIgnore, PugiWatcher, } from '../core/context/index.js';
|
|
28
34
|
/**
|
|
29
35
|
* Mount the REPL and resolve when the user exits via Ctrl+C × 2 or
|
|
@@ -31,12 +37,85 @@ import { WorkingSet, buildRepoSkeleton, loadPugiIgnore, PugiWatcher, } from '../
|
|
|
31
37
|
* `pugi resume <sessionId>` once that command exists).
|
|
32
38
|
*/
|
|
33
39
|
export async function renderRepl(options) {
|
|
40
|
+
// beta.9 CEO dogfood 2026-05-26: claim stdin raw mode + alt-screen
|
|
41
|
+
// BEFORE any async bootstrap step so keystrokes typed during the
|
|
42
|
+
// [launch -> Ink mount] window cannot echo into the terminal in
|
|
43
|
+
// cooked mode. Previously openLocalStore (SQLite open) +
|
|
44
|
+
// bootstrapContext (chokidar start) could take hundreds of ms to
|
|
45
|
+
// multiple seconds on a fresh install / large repo; during that
|
|
46
|
+
// window stdin stayed in cooked mode and the terminal echoed
|
|
47
|
+
// every typed character literally onto the screen below the
|
|
48
|
+
// pre-printed mascot/header. The visible result was the operator's
|
|
49
|
+
// "ssssss" landing on the rendered status-bar bottom row (CEO
|
|
50
|
+
// screenshot 2026-05-26: beta.8 REPL bug 2).
|
|
51
|
+
//
|
|
52
|
+
// The claim is idempotent with Ink's own raw-mode enable: Ink
|
|
53
|
+
// ref-counts setRawMode calls, and Node's stdin.setRawMode is
|
|
54
|
+
// safe to call twice with the same value. The pre-Ink claim acts
|
|
55
|
+
// as a "raw-mode floor" - whatever Ink does after mount layers on
|
|
56
|
+
// top, and our finally{} restore drops the floor only after Ink
|
|
57
|
+
// has cleanly torn down (or never mounted on a bootstrap crash).
|
|
58
|
+
const bootstrap = claimTerminalForRepl();
|
|
59
|
+
// beta.13 auto-init wire (CEO dogfood 2026-05-26): scaffold the
|
|
60
|
+
// `.pugi/` workspace silently on REPL boot so launching `pugi` in a
|
|
61
|
+
// fresh cwd no longer demands an explicit `pugi init` round-trip.
|
|
62
|
+
// Idempotent — every helper inside scaffoldPugiWorkspace is a
|
|
63
|
+
// `*_IfMissing` write, so re-running over an existing workspace is
|
|
64
|
+
// a no-op. Fail-safe: any FS / perms error never blocks REPL launch.
|
|
65
|
+
// Operator escape hatch: PUGI_NO_AUTO_INIT=1.
|
|
66
|
+
//
|
|
67
|
+
// Beta.13 P2 fix 2026-05-26: gate the scaffold on project-root markers
|
|
68
|
+
// so launching `pugi` from `$HOME` / `/tmp` / arbitrary dirs does NOT
|
|
69
|
+
// sprinkle `.pugi/` directories all over the filesystem. The gate
|
|
70
|
+
// mirrors `isBoundWorkspace` from workspace-context.ts but also
|
|
71
|
+
// accepts non-JS roots (Cargo / pyproject / go.mod) because the CLI
|
|
72
|
+
// is language-agnostic and an operator working in a Rust repo deserves
|
|
73
|
+
// the same auto-init UX as a Node operator. Already-bound `.pugi/`
|
|
74
|
+
// dirs also opt back in so the scaffold can fill any missing
|
|
75
|
+
// sub-artifacts the operator deleted.
|
|
76
|
+
// Leak L22 (2026-05-27): `--bare` (PUGI_BARE=1) ALSO suppresses the
|
|
77
|
+
// auto-init scaffold. Bare mode is the deterministic "fresh install
|
|
78
|
+
// anywhere" path — no `.pugi/` writes, no PUGI.md scaffold, no
|
|
79
|
+
// settings.json seed. The pre-existing PUGI_NO_AUTO_INIT escape
|
|
80
|
+
// hatch stays — bare mode just unions with it.
|
|
81
|
+
const { isBareMode } = await import('../core/bare-mode/index.js');
|
|
82
|
+
if (process.env.PUGI_NO_AUTO_INIT !== '1' &&
|
|
83
|
+
!isBareMode() &&
|
|
84
|
+
isProjectRoot(process.cwd())) {
|
|
85
|
+
try {
|
|
86
|
+
const { scaffoldPugiWorkspace } = await import('../runtime/cli.js');
|
|
87
|
+
await scaffoldPugiWorkspace({
|
|
88
|
+
cwd: process.cwd(),
|
|
89
|
+
noDefaults: true,
|
|
90
|
+
log: () => {
|
|
91
|
+
/* silent — never leak scaffold progress into the REPL alt-screen */
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
// Fail-safe: read-only FS or perms error never blocks REPL launch.
|
|
97
|
+
// Beta.13 P2 fix 2026-05-26: bare-catch swallowed the diagnostic;
|
|
98
|
+
// surface it on stderr under PUGI_DEBUG=1 so operator-triage on
|
|
99
|
+
// "why isn't .pugi/ being created?" has a starting point without
|
|
100
|
+
// having to re-instrument the bootstrap.
|
|
101
|
+
if (process.env.PUGI_DEBUG === '1') {
|
|
102
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
103
|
+
process.stderr.write(`[pugi-debug] auto-init failed: ${msg}\n`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
34
107
|
const transport = createProductionTransport();
|
|
35
108
|
// Auto-bind the workspace context from process.cwd() so Mira knows
|
|
36
109
|
// which repo the operator launched the CLI in. The resolver is
|
|
37
110
|
// best-effort — any FS error falls back to a basename-only summary,
|
|
38
111
|
// never blocks REPL launch. Wave 4 fix 2026-05-25.
|
|
39
112
|
const workspace = options.workspace ?? resolveWorkspaceContext(process.cwd());
|
|
113
|
+
// Beta.13 P1 fix 2026-05-26: read `ui.cyberZoo` from
|
|
114
|
+
// `.pugi/settings.json` so the operator's splash posture flows to
|
|
115
|
+
// admin-api on session open. Without this, the renderer's `cyberZoo`
|
|
116
|
+
// parameter (added beta.13) was always defaulted to 'on' regardless
|
|
117
|
+
// of the operator's actual setting.
|
|
118
|
+
const cyberZoo = readCyberZooSetting(process.cwd());
|
|
40
119
|
// α6.4: open the local SessionStore for `/resume` persistence. The
|
|
41
120
|
// store lives under `~/.pugi/projects/<slug>/`; failure is fail-safe
|
|
42
121
|
// — we log a one-line warning to stderr and continue with the REPL
|
|
@@ -64,6 +143,7 @@ export async function renderRepl(options) {
|
|
|
64
143
|
cliVersion: options.cliVersion,
|
|
65
144
|
transport,
|
|
66
145
|
workspace,
|
|
146
|
+
cyberZoo,
|
|
67
147
|
store,
|
|
68
148
|
localSessionId: openedSessionId,
|
|
69
149
|
repoSkeleton: skeleton,
|
|
@@ -86,21 +166,14 @@ export async function renderRepl(options) {
|
|
|
86
166
|
// Kick off the connect; the Repl renders the connecting state until
|
|
87
167
|
// the session pushes `connection: 'on_watch'` from the SSE onOpen.
|
|
88
168
|
void session.start();
|
|
89
|
-
//
|
|
90
|
-
//
|
|
91
|
-
//
|
|
92
|
-
//
|
|
93
|
-
//
|
|
94
|
-
//
|
|
95
|
-
//
|
|
96
|
-
|
|
97
|
-
// BEFORE the chafa mascot pre-print. Reversed, the alt-screen clear
|
|
98
|
-
// wiped the freshly-painted pug and the operator saw nothing.
|
|
99
|
-
const supportsAltScreen = process.stdout.isTTY === true;
|
|
100
|
-
if (supportsAltScreen) {
|
|
101
|
-
process.stdout.write('\x1b[?1049h');
|
|
102
|
-
process.stdout.write('\x1b[H');
|
|
103
|
-
}
|
|
169
|
+
// beta.9: drain any keystrokes that landed in stdin between the
|
|
170
|
+
// pre-Ink raw-mode claim and now. Without this, the queued bytes
|
|
171
|
+
// would feed Ink's first useInput tick as a flood of "stale"
|
|
172
|
+
// characters once the InputBox mounts - the operator would see
|
|
173
|
+
// their pre-typed input materialise in the prompt as if they had
|
|
174
|
+
// typed it after the REPL became interactive. Idempotent: no-op
|
|
175
|
+
// when stdin is not a TTY or no bytes were buffered.
|
|
176
|
+
drainBufferedStdin(process.stdin);
|
|
104
177
|
// α6.14.2 wave 5: paint the chafa-baked brand-pug ANSI render to
|
|
105
178
|
// stdout BEFORE Ink mounts (but AFTER alt-screen enter). Ink's
|
|
106
179
|
// layout engine would mis-measure the truecolor escape sequences,
|
|
@@ -111,33 +184,55 @@ export async function renderRepl(options) {
|
|
|
111
184
|
// (operator opted out via --no-splash), we suppress the pre-print
|
|
112
185
|
// too so the boot stays silent.
|
|
113
186
|
const mascotPrePrinted = options.skipSplash === true ? false : printPugMascotPreInk(process.stdout);
|
|
114
|
-
|
|
187
|
+
// Leak L30 (2026-05-27): resolve the active theme ONCE at mount
|
|
188
|
+
// and wrap `<Repl />` in `<ThemeProvider>` so every Ink consumer
|
|
189
|
+
// (`<Header>`, `<DoctorTable>`, `<StyleTable>`, `<ThemeTable>`,
|
|
190
|
+
// …) picks up the same color tokens. The provider is stable for
|
|
191
|
+
// the lifetime of the REPL — operator `/theme <name>` writes to
|
|
192
|
+
// disk + appends a system line, and the next `pugi` launch re-
|
|
193
|
+
// mounts with the new slug. Re-mounting mid-session would race
|
|
194
|
+
// against Ink's raw-mode handler so we deliberately keep the
|
|
195
|
+
// session-lifetime contract instead of polling the config file.
|
|
196
|
+
const resolvedTheme = resolveTheme({
|
|
197
|
+
workspaceRoot: process.cwd(),
|
|
198
|
+
env: process.env,
|
|
199
|
+
});
|
|
200
|
+
// CEO P0 #2 (2026-05-29): collect welcome banner data BEFORE Ink
|
|
201
|
+
// mounts so the banner paints on the first frame instead of swapping
|
|
202
|
+
// in mid-render. The collector swallows every IO error so а missing
|
|
203
|
+
// CHANGELOG / unreadable credential / malformed settings never
|
|
204
|
+
// blocks the boot.
|
|
205
|
+
let welcomeData;
|
|
206
|
+
if (options.skipSplash !== true) {
|
|
207
|
+
try {
|
|
208
|
+
welcomeData = collectWelcomeData({
|
|
209
|
+
cliVersion: options.cliVersion,
|
|
210
|
+
cwd: process.cwd(),
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
welcomeData = undefined;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
const instance = render(React.createElement(ThemeProvider, { slug: resolvedTheme.slug }, React.createElement(Repl, {
|
|
115
218
|
session,
|
|
116
219
|
updateBanner: options.updateBanner ?? null,
|
|
117
220
|
skipSplash: options.skipSplash === true,
|
|
118
221
|
hideToolStream: options.hideToolStream === true,
|
|
119
222
|
mascotPrePrinted,
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
try {
|
|
124
|
-
process.stdout.write('\x1b[?1049l');
|
|
125
|
-
}
|
|
126
|
-
catch {
|
|
127
|
-
/* shutdown race — terminal already detached */
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
};
|
|
223
|
+
welcomeData,
|
|
224
|
+
autoInitStatus: options.autoInitStatus ?? null,
|
|
225
|
+
})));
|
|
131
226
|
// Make sure we leave the alt screen on abrupt exits too. Without
|
|
132
227
|
// this the operator's shell stays "frozen" on the Pugi splash.
|
|
133
|
-
process.once('exit',
|
|
134
|
-
process.once('SIGINT',
|
|
135
|
-
process.once('SIGTERM',
|
|
228
|
+
process.once('exit', bootstrap.restore);
|
|
229
|
+
process.once('SIGINT', bootstrap.restore);
|
|
230
|
+
process.once('SIGTERM', bootstrap.restore);
|
|
136
231
|
try {
|
|
137
232
|
await instance.waitUntilExit();
|
|
138
233
|
}
|
|
139
234
|
finally {
|
|
140
|
-
|
|
235
|
+
bootstrap.restore();
|
|
141
236
|
session.close();
|
|
142
237
|
if (store) {
|
|
143
238
|
try {
|
|
@@ -157,6 +252,138 @@ export async function renderRepl(options) {
|
|
|
157
252
|
}
|
|
158
253
|
}
|
|
159
254
|
}
|
|
255
|
+
export function claimTerminalForRepl(stdin = process.stdin, stdout = process.stdout) {
|
|
256
|
+
const isStdoutTty = stdout.isTTY === true;
|
|
257
|
+
const isStdinTty = stdin.isTTY === true && typeof stdin.setRawMode === 'function';
|
|
258
|
+
let altScreenEntered = false;
|
|
259
|
+
if (isStdoutTty) {
|
|
260
|
+
try {
|
|
261
|
+
stdout.write('\x1b[?1049h');
|
|
262
|
+
stdout.write('\x1b[H');
|
|
263
|
+
altScreenEntered = true;
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
/* terminal already detached */
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
let rawModeClaimed = false;
|
|
270
|
+
if (isStdinTty) {
|
|
271
|
+
try {
|
|
272
|
+
stdin.setEncoding('utf8');
|
|
273
|
+
stdin.setRawMode(true);
|
|
274
|
+
// Resume so the kernel actually delivers bytes to Node's event
|
|
275
|
+
// loop. Without resume, raw mode is set but data does not flow
|
|
276
|
+
// until something else (e.g. Ink) attaches a 'data' listener.
|
|
277
|
+
stdin.resume();
|
|
278
|
+
rawModeClaimed = true;
|
|
279
|
+
}
|
|
280
|
+
catch {
|
|
281
|
+
/* raw mode unsupported - the operator's shell still works */
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
let restored = false;
|
|
285
|
+
const restore = () => {
|
|
286
|
+
if (restored)
|
|
287
|
+
return;
|
|
288
|
+
restored = true;
|
|
289
|
+
if (rawModeClaimed && isStdinTty) {
|
|
290
|
+
try {
|
|
291
|
+
stdin.setRawMode(false);
|
|
292
|
+
}
|
|
293
|
+
catch {
|
|
294
|
+
/* terminal already detached */
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
if (altScreenEntered) {
|
|
298
|
+
try {
|
|
299
|
+
stdout.write('\x1b[?1049l');
|
|
300
|
+
}
|
|
301
|
+
catch {
|
|
302
|
+
/* shutdown race - terminal already detached */
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
return { altScreenEntered, rawModeClaimed, restore };
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Read and discard any bytes buffered in stdin between
|
|
310
|
+
* `claimTerminalForRepl()` and the Ink mount. Returns the number of
|
|
311
|
+
* bytes drained so tests can assert the behaviour without intercepting
|
|
312
|
+
* the side effect.
|
|
313
|
+
*
|
|
314
|
+
* `stdin.read()` is a no-op when no data is buffered, so this is safe
|
|
315
|
+
* to call whether or not the operator actually typed during bootstrap.
|
|
316
|
+
* Wrapped in try/catch because a closed / piped stdin will throw on
|
|
317
|
+
* read in some Node versions.
|
|
318
|
+
*/
|
|
319
|
+
export function drainBufferedStdin(stdin = process.stdin) {
|
|
320
|
+
if (stdin.isTTY !== true)
|
|
321
|
+
return 0;
|
|
322
|
+
try {
|
|
323
|
+
let bytesDrained = 0;
|
|
324
|
+
// Loop until read() returns null - readable streams may chunk
|
|
325
|
+
// buffered bytes across multiple read() calls when the operator
|
|
326
|
+
// typed faster than the kernel could deliver to Node's loop.
|
|
327
|
+
for (;;) {
|
|
328
|
+
const chunk = stdin.read();
|
|
329
|
+
if (chunk === null)
|
|
330
|
+
return bytesDrained;
|
|
331
|
+
bytesDrained += typeof chunk === 'string' ? chunk.length : chunk.byteLength;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
catch {
|
|
335
|
+
return 0;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Project-root probe — beta.13 P2 fix 2026-05-26.
|
|
340
|
+
*
|
|
341
|
+
* Beta.13 auto-init was unconditional and silently created `.pugi/` in
|
|
342
|
+
* every cwd the REPL was launched from, including `$HOME` and `/tmp`.
|
|
343
|
+
* Operators who ran `pugi` to ask a quick question outside of any
|
|
344
|
+
* project ended up with stray `.pugi/` directories polluting their
|
|
345
|
+
* filesystem. The gate looks for any of six project-root markers
|
|
346
|
+
* before scaffolding:
|
|
347
|
+
*
|
|
348
|
+
* - `package.json` — JS / TS workspaces
|
|
349
|
+
* - `.git` — any cloned repo regardless of language
|
|
350
|
+
* - `.pugi` — already-bound Pugi workspace (re-scaffold
|
|
351
|
+
* fills any missing artifacts the operator
|
|
352
|
+
* deleted, idempotent over existing files)
|
|
353
|
+
* - `Cargo.toml` — Rust crates
|
|
354
|
+
* - `pyproject.toml` — Python projects (PEP 518)
|
|
355
|
+
* - `go.mod` — Go modules
|
|
356
|
+
*
|
|
357
|
+
* The probe is six cheap `existsSync` calls; the cost is negligible
|
|
358
|
+
* compared with the alt-screen + Ink mount that follows. Exported so a
|
|
359
|
+
* future unit spec can lock the contract.
|
|
360
|
+
*/
|
|
361
|
+
export function isProjectRoot(cwd) {
|
|
362
|
+
// ESM static imports — `require()` is not defined in a `"type": "module"`
|
|
363
|
+
// bundle and would throw `ReferenceError: require is not defined` the
|
|
364
|
+
// moment the REPL bootstrap calls this gate. Beta.16 P0 fix 2026-05-27.
|
|
365
|
+
return (existsSync(resolve(cwd, 'package.json')) ||
|
|
366
|
+
existsSync(resolve(cwd, '.git')) ||
|
|
367
|
+
existsSync(resolve(cwd, '.pugi')) ||
|
|
368
|
+
existsSync(resolve(cwd, 'Cargo.toml')) ||
|
|
369
|
+
existsSync(resolve(cwd, 'pyproject.toml')) ||
|
|
370
|
+
existsSync(resolve(cwd, 'go.mod')));
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Read the operator's cyber-zoo posture from `.pugi/settings.json`.
|
|
374
|
+
* Best-effort: when the file is missing / malformed, fall through to
|
|
375
|
+
* the historical 'on' default so the REPL never refuses to launch on
|
|
376
|
+
* a settings error. Beta.13 P1 fix 2026-05-26.
|
|
377
|
+
*/
|
|
378
|
+
function readCyberZooSetting(cwd) {
|
|
379
|
+
try {
|
|
380
|
+
const settings = loadSettings(cwd);
|
|
381
|
+
return settings.ui?.cyberZoo ?? 'on';
|
|
382
|
+
}
|
|
383
|
+
catch {
|
|
384
|
+
return 'on';
|
|
385
|
+
}
|
|
386
|
+
}
|
|
160
387
|
/**
|
|
161
388
|
* Open the local SessionStore for the REPL bootstrap. Returns
|
|
162
389
|
* `{ store: null, openedSessionId: undefined }` on any error so the
|
|
@@ -248,13 +475,20 @@ async function bootstrapContext(input) {
|
|
|
248
475
|
/* ------------------------------------------------------------------ */
|
|
249
476
|
/* Production transport */
|
|
250
477
|
/* ------------------------------------------------------------------ */
|
|
251
|
-
function createProductionTransport() {
|
|
478
|
+
export function createProductionTransport() {
|
|
252
479
|
return {
|
|
253
|
-
async createSession({ apiUrl, apiKey, workspace }) {
|
|
480
|
+
async createSession({ apiUrl, apiKey, workspace, cyberZoo }) {
|
|
254
481
|
// Forward the workspace bundle in the POST body so admin-api can
|
|
255
482
|
// surface `<workspace-context>` in Mira's prompt. Older admin-api
|
|
256
483
|
// builds ignore unknown fields, so this stays forward-compatible.
|
|
257
484
|
// Wave 4 fix 2026-05-25.
|
|
485
|
+
//
|
|
486
|
+
// Beta.13 P1 fix 2026-05-26: also forward `cyberZoo` so admin-api
|
|
487
|
+
// can render Mira's `<cyber-zoo>` marker matching the operator's
|
|
488
|
+
// `.pugi/settings.json::ui.cyberZoo` toggle instead of the
|
|
489
|
+
// historical 'on' default. Only included on the wire when set
|
|
490
|
+
// explicitly so a missing setting still survives older admin-api
|
|
491
|
+
// builds that do not declare the DTO field.
|
|
258
492
|
const body = {};
|
|
259
493
|
if (workspace?.workspaceCwd)
|
|
260
494
|
body.workspaceCwd = workspace.workspaceCwd;
|
|
@@ -262,6 +496,8 @@ function createProductionTransport() {
|
|
|
262
496
|
body.workspaceSlug = workspace.workspaceSlug;
|
|
263
497
|
if (workspace?.workspaceSummary)
|
|
264
498
|
body.workspaceSummary = workspace.workspaceSummary;
|
|
499
|
+
if (cyberZoo === 'on' || cyberZoo === 'off')
|
|
500
|
+
body.cyberZoo = cyberZoo;
|
|
265
501
|
const response = await fetch(joinUrl(apiUrl, '/api/pugi/sessions'), {
|
|
266
502
|
method: 'POST',
|
|
267
503
|
headers: jsonHeaders(apiKey),
|
|
@@ -307,6 +543,31 @@ function createProductionTransport() {
|
|
|
307
543
|
if (lastEventId) {
|
|
308
544
|
headers['Last-Event-ID'] = lastEventId;
|
|
309
545
|
}
|
|
546
|
+
// beta.9 CEO dogfood 2026-05-26: hard timeout on the SSE
|
|
547
|
+
// handshake so a CDN/proxy that buffers the response (or an
|
|
548
|
+
// admin-api that accepted the route but never flushed headers)
|
|
549
|
+
// cannot freeze the REPL in `connecting` forever. The 5s budget
|
|
550
|
+
// is generous - admin-api routinely responds in <500ms when
|
|
551
|
+
// healthy - but tight enough that an operator who launched
|
|
552
|
+
// `pugi` and is staring at the screen will see the status flip
|
|
553
|
+
// to `reconnecting` instead of an indefinite hang. The
|
|
554
|
+
// AbortController bound to the fetch aborts the in-flight
|
|
555
|
+
// request when the timer fires, which surfaces as an
|
|
556
|
+
// `AbortError` and routes through the existing onError handler
|
|
557
|
+
// (which calls scheduleReconnect via the session). The timer
|
|
558
|
+
// is cleared the moment onOpen fires so a slow-but-eventually-
|
|
559
|
+
// successful handshake still works.
|
|
560
|
+
const handshakeDeadlineMs = 5_000;
|
|
561
|
+
const handshakeTimer = setTimeout(() => {
|
|
562
|
+
controller.abort();
|
|
563
|
+
// onError is called from the catch block below (the abort
|
|
564
|
+
// synthesises an AbortError that consumeSseStream / fetch
|
|
565
|
+
// will throw). No explicit onError call here - we let the
|
|
566
|
+
// catch path normalise the error message so the operator
|
|
567
|
+
// sees the consistent "SSE handshake timed out (5s)" prose
|
|
568
|
+
// through the same plumbing that surfaces every other
|
|
569
|
+
// transport failure.
|
|
570
|
+
}, handshakeDeadlineMs);
|
|
310
571
|
void (async () => {
|
|
311
572
|
try {
|
|
312
573
|
const response = await fetch(url, {
|
|
@@ -320,6 +581,9 @@ function createProductionTransport() {
|
|
|
320
581
|
if (!response.body) {
|
|
321
582
|
throw new Error('SSE response has no body');
|
|
322
583
|
}
|
|
584
|
+
// Handshake survived; cancel the deadline so a slow
|
|
585
|
+
// first-event stream does not get aborted later.
|
|
586
|
+
clearTimeout(handshakeTimer);
|
|
323
587
|
onOpen();
|
|
324
588
|
await consumeSseStream(response.body, onEvent);
|
|
325
589
|
// Server closed the stream cleanly. Treat as an error so
|
|
@@ -329,13 +593,27 @@ function createProductionTransport() {
|
|
|
329
593
|
onError(new Error('SSE stream ended'));
|
|
330
594
|
}
|
|
331
595
|
catch (error) {
|
|
332
|
-
|
|
596
|
+
clearTimeout(handshakeTimer);
|
|
597
|
+
if (controller.signal.aborted) {
|
|
598
|
+
// Distinguish operator-driven close (session.close())
|
|
599
|
+
// from the handshake-deadline abort. The session sets a
|
|
600
|
+
// `closed` flag before calling controller.abort(); the
|
|
601
|
+
// handshake-deadline abort fires while the session is
|
|
602
|
+
// still expecting onOpen. We cannot read session state
|
|
603
|
+
// from here, so we surface a single error class with a
|
|
604
|
+
// clear message - the session-side onError handler
|
|
605
|
+
// already short-circuits when `closed=true`.
|
|
606
|
+
onError(new Error(`SSE handshake timed out after ${handshakeDeadlineMs}ms`));
|
|
333
607
|
return;
|
|
608
|
+
}
|
|
334
609
|
onError(error instanceof Error ? error : new Error(String(error)));
|
|
335
610
|
}
|
|
336
611
|
})();
|
|
337
612
|
return {
|
|
338
|
-
close: () =>
|
|
613
|
+
close: () => {
|
|
614
|
+
clearTimeout(handshakeTimer);
|
|
615
|
+
controller.abort();
|
|
616
|
+
},
|
|
339
617
|
};
|
|
340
618
|
},
|
|
341
619
|
};
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Hand-crafted at 9 rows × 20 columns to read as a pug at a single
|
|
5
5
|
* glance — references the cyber-zoo hero glyph in
|
|
6
|
-
* `apps/
|
|
6
|
+
* `apps/console-web/public/brand/hero-pug.png`: blocky pug face with
|
|
7
7
|
* angular ear flaps on either side of the head, forehead crease,
|
|
8
8
|
* angular cyan eyes (`◉`), smushed snout, undershot jaw, and a small
|
|
9
9
|
* cyan circuit chip (`▐■▌`) on the lower-right cheek.
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
*
|
|
23
23
|
* Generation (operator-side, one-shot):
|
|
24
24
|
* chafa --size 80x40 --symbols=vhalf --colors=full \
|
|
25
|
-
* apps/
|
|
25
|
+
* apps/console-web/public/brand/hero-pug.png \
|
|
26
26
|
* > apps/pugi-cli/assets/pugi-mascot.ansi
|
|
27
27
|
*
|
|
28
28
|
* The output is committed verbatim to the repo and shipped inside the
|
|
@@ -48,9 +48,21 @@ import { fileURLToPath } from 'node:url';
|
|
|
48
48
|
* — two directory hops up from this file. In a local `pnpm dev`
|
|
49
49
|
* checkout the structure is the same (`src/tui/` ⇒ `../../assets/`)
|
|
50
50
|
* because tsx re-resolves the same relative tree.
|
|
51
|
+
*
|
|
52
|
+
* CEO P0 #2 (2026-05-29) — banner mascot bake. The prozr2 portrait is
|
|
53
|
+
* the canonical brand-pug glyph from `apps/console-web/public/brand/
|
|
54
|
+
* Pugi-prozr2.png`, baked к а 16x8 vhalf truecolor render. We prefer
|
|
55
|
+
* the prozr2 bake when it ships alongside the CLI (small — ~900 bytes,
|
|
56
|
+
* shaped for the compact welcome-banner left column) and fall back к
|
|
57
|
+
* the legacy 40KB `pugi-mascot.ansi` (hero-pug 80x40) when prozr2 is
|
|
58
|
+
* missing — preserves the splash-mascot install surface for any
|
|
59
|
+
* tarball that predates the bake.
|
|
51
60
|
*/
|
|
52
61
|
export function pugMascotAssetPath() {
|
|
53
62
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
63
|
+
const prozr2 = resolvePath(here, '..', '..', 'assets', 'pugi-prozr2-mascot.ansi');
|
|
64
|
+
if (existsSync(prozr2))
|
|
65
|
+
return prozr2;
|
|
54
66
|
return resolvePath(here, '..', '..', 'assets', 'pugi-mascot.ansi');
|
|
55
67
|
}
|
|
56
68
|
/**
|
|
@@ -89,21 +101,33 @@ export function loadPugMascotAnsi() {
|
|
|
89
101
|
// icon, clipboard, hyperlinks, color-palette change). Drop them
|
|
90
102
|
// so a corrupted asset cannot rename the operator's terminal tab
|
|
91
103
|
// or smuggle a hyperlink into the splash region.
|
|
92
|
-
// 2. Drop CSI ? <
|
|
93
|
-
//
|
|
94
|
-
//
|
|
95
|
-
//
|
|
104
|
+
// 2. Drop ALL CSI ? <numbers and semicolons> [lh] (DEC private-mode
|
|
105
|
+
// set / reset). The legitimate chafa output for a splash is
|
|
106
|
+
// truecolor SGR (`CSI 38;2;R;G;B m`) plus cursor-positioning —
|
|
107
|
+
// no private-mode toggle ever appears там legitimately. A
|
|
108
|
+
// permissive deny-all pattern covers every disruptive private
|
|
109
|
+
// mode in one regex:
|
|
110
|
+
// - cursor visibility (25)
|
|
111
|
+
// - alt-screen buffer (47, 1047, 1048, 1049)
|
|
112
|
+
// - mouse tracking (1000, 1001, 1002, 1003, 1004, 1005, 1006, 1015)
|
|
113
|
+
// - bracketed paste (2004)
|
|
114
|
+
// - focus reporting (1004)
|
|
115
|
+
// - multi-mode forms (e.g. `CSI ? 47 ; 1049 h` — legal per
|
|
116
|
+
// xterm ctlseqs but missed by the previous single-mode regex)
|
|
117
|
+
// - any future private mode a corrupt asset might emit
|
|
118
|
+
// Allowlisting the modes the splash needs is impossible because
|
|
119
|
+
// the splash needs ZERO of them — chafa renders glyph-by-glyph,
|
|
120
|
+
// not via private-mode toggles. A pure deny-all is strictly
|
|
121
|
+
// safer than enumerating known-bad modes one-by-one.
|
|
96
122
|
// 3. Drop CSI 6 n (cursor-position report). Would inject a fake
|
|
97
123
|
// CPR into the operator's stdin stream.
|
|
98
124
|
// 4. Drop CSI [23]J / CSI [23]K (full screen / line clear). A
|
|
99
125
|
// chafa render uses cursor-positioning per row, not bulk
|
|
100
126
|
// erases; bulk clears would wipe whatever the operator already
|
|
101
127
|
// had on screen above the splash.
|
|
102
|
-
// The cursor-hide/show wrappers (CSI ? 25 [lh]) are handled by
|
|
103
|
-
// the same CSI-?-mode pattern as the mouse / alt-screen modes.
|
|
104
128
|
const stripped = raw
|
|
105
129
|
.replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g, '')
|
|
106
|
-
.replace(/\x1b\[\?
|
|
130
|
+
.replace(/\x1b\[\?[0-9;]+[lh]/g, '')
|
|
107
131
|
.replace(/\x1b\[6n/g, '')
|
|
108
132
|
.replace(/\x1b\[[23]?[JK]/g, '');
|
|
109
133
|
if (stripped.trim().length === 0)
|
package/dist/tui/repl-splash.js
CHANGED
|
@@ -67,7 +67,7 @@ export function ReplSplash(props) {
|
|
|
67
67
|
// pugs. The header card still renders inline so wordmark + status
|
|
68
68
|
// rows stay attached to the splash flow.
|
|
69
69
|
const showHandCraftedMascot = props.mascotPrePrinted !== true;
|
|
70
|
-
return (_jsxs(Box, { flexDirection: "column", paddingX: 1, paddingY: 1, children: [_jsxs(Box, { flexDirection: "row", children: [showHandCraftedMascot ? _jsx(MascotColumn, {}) : null, _jsxs(Box, { flexDirection: "column", marginLeft: showHandCraftedMascot ? 2 : 0, marginTop: 1, children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Pugi" }), _jsx(Text, { bold: true, color: "
|
|
70
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: 1, paddingY: 1, children: [_jsxs(Box, { flexDirection: "row", children: [showHandCraftedMascot ? _jsx(MascotColumn, {}) : null, _jsxs(Box, { flexDirection: "column", marginLeft: showHandCraftedMascot ? 2 : 0, marginTop: 1, children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Pugi" }), _jsx(Text, { bold: true, color: "#3da9fc", children: ".io" }), _jsx(Text, { dimColor: true, children: ` v${props.cliVersion}` })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(HeaderRow, { label: "Plan", value: props.plan ?? PLACEHOLDER }), _jsx(HeaderRow, { label: "Model", value: props.model ?? PLACEHOLDER }), _jsx(HeaderRow, { label: "Tenant", value: props.tenant ?? PLACEHOLDER }), _jsx(HeaderRow, { label: "Workspace", value: props.workspaceLabel })] })] })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: '─'.repeat(40) }) }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: "Tips for getting started:" }), _jsx(TipRow, { index: 1, text: "Type a brief, the workforce dispatches" }), _jsx(TipRow, { index: 2, text: "/help for slash commands, /web <url> to pull a page" }), _jsx(TipRow, { index: 3, text: "/skills install <name> for Anthropic / OpenClaw skills" })] })] }));
|
|
71
71
|
}
|
|
72
72
|
/**
|
|
73
73
|
* Renders the multi-line ASCII pug. Each row is split into colored
|
|
@@ -105,7 +105,7 @@ function MascotRow({ row, mask, }) {
|
|
|
105
105
|
if (buffer.length > 0) {
|
|
106
106
|
runs.push({ text: buffer, cyan: bufferCyan });
|
|
107
107
|
}
|
|
108
|
-
return (_jsx(Text, { children: runs.map((run, runIndex) => run.cyan ? (_jsx(Text, { color: "
|
|
108
|
+
return (_jsx(Text, { children: runs.map((run, runIndex) => run.cyan ? (_jsx(Text, { color: "#3da9fc", children: run.text }, runIndex)) : (_jsx(Text, { color: "gray", children: run.text }, runIndex))) }));
|
|
109
109
|
}
|
|
110
110
|
function HeaderRow({ label, value }) {
|
|
111
111
|
const padded = `${label}:`.padEnd(11, ' ');
|