@pugi/cli 0.1.0-alpha.6 → 0.1.0-alpha.8
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/dist/core/auto-open-browser.js +128 -0
- package/dist/core/clipboard.js +70 -0
- package/dist/core/repl/clipboard-read.js +174 -0
- package/dist/core/repl/history-search.js +175 -0
- package/dist/core/repl/history.js +172 -0
- package/dist/core/repl/kill-ring.js +138 -0
- package/dist/core/repl/session.js +194 -1
- package/dist/core/repl/slash-commands.js +133 -22
- package/dist/core/settings.js +13 -0
- package/dist/runtime/cli.js +392 -66
- package/dist/runtime/update-check.js +294 -0
- package/dist/tools/registry.js +1 -0
- package/dist/tools/web-fetch.js +535 -0
- package/dist/tui/device-flow.js +142 -0
- package/dist/tui/input-box.js +410 -27
- package/dist/tui/render.js +57 -0
- package/dist/tui/repl-render.js +1 -1
- package/dist/tui/repl.js +39 -3
- package/dist/tui/slash-palette.js +69 -0
- package/dist/tui/update-banner.js +8 -0
- package/package.json +7 -2
|
@@ -1,31 +1,99 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* REPL slash command registry
|
|
2
|
+
* REPL slash command registry — Sprint α5.7, expanded α6.14 wave 2.
|
|
3
3
|
*
|
|
4
|
-
* The REPL input box surfaces a
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* overlay
|
|
8
|
-
* complementary surfaces (`/sync`, `/handoff`, `/budget`) ship as proper
|
|
9
|
-
* subcommands in α5.8+ and stay reachable from a non-REPL `pugi
|
|
10
|
-
* <command>` invocation.
|
|
4
|
+
* The REPL input box surfaces a palette of slash commands the operator
|
|
5
|
+
* can run from inside a persistent session. The wave-2 expansion (CEO
|
|
6
|
+
* 2026-05-25) grows the surface from 6 to 20 commands so the `/help`
|
|
7
|
+
* overlay matches the breadth Claude Code / Codex CLI operators expect.
|
|
11
8
|
*
|
|
12
|
-
* The registry is pure: each
|
|
13
|
-
* describing what the REPL session should do next.
|
|
14
|
-
* owns the side effects (network calls, dispatcher
|
|
15
|
-
* Keeping the surface pure lets
|
|
16
|
-
* without standing up an Ink runtime
|
|
9
|
+
* The registry is pure: each `parseSlashCommand` call returns a
|
|
10
|
+
* `SlashCommandResult` describing what the REPL session should do next.
|
|
11
|
+
* The session module owns the side effects (network calls, dispatcher
|
|
12
|
+
* invocation, exit, transcript clear). Keeping the surface pure lets
|
|
13
|
+
* the unit test exercise every shape without standing up an Ink runtime
|
|
14
|
+
* or an Anvil endpoint.
|
|
15
|
+
*
|
|
16
|
+
* Tiering (per CEO wave-2 spec):
|
|
17
|
+
*
|
|
18
|
+
* Tier 1 — wired against real state (3 + existing 6 = 9 wired):
|
|
19
|
+
* brief, agents, stop, help, quit, web, clear, version, jobs.
|
|
20
|
+
*
|
|
21
|
+
* Tier 2 — best-effort wiring against existing surfaces (3):
|
|
22
|
+
* diff, cost, status.
|
|
23
|
+
*
|
|
24
|
+
* Tier 3 — deterministic stubs ("coming in αX.Y") (8):
|
|
25
|
+
* compact, resume, memory, config, privacy, budget, mcp, undo.
|
|
17
26
|
*
|
|
18
27
|
* Brand voice (brandbook §08): power words `brief / dispatch / stop /
|
|
19
|
-
* agents / quit`. Tagline `Brief it. It ships.` reserved for
|
|
20
|
-
* confirmation and `/help` footer
|
|
28
|
+
* agents / quit / shipped`. Tagline `Brief it. It ships.` reserved for
|
|
29
|
+
* `/quit` confirmation and `/help` footer — never inline.
|
|
21
30
|
*/
|
|
22
31
|
import { listRoles } from '../agents/registry.js';
|
|
32
|
+
/**
|
|
33
|
+
* Deterministic stub copy returned by the Tier 3 commands. Spec'd
|
|
34
|
+
* inline so the unit test can pin the exact text without poking at
|
|
35
|
+
* the help overlay. The version tag at the end maps to the sprint we
|
|
36
|
+
* intend to land the real wiring in.
|
|
37
|
+
*/
|
|
38
|
+
export const SLASH_STUB_MESSAGES = Object.freeze({
|
|
39
|
+
brief: '',
|
|
40
|
+
agents: '',
|
|
41
|
+
stop: '',
|
|
42
|
+
help: '',
|
|
43
|
+
quit: '',
|
|
44
|
+
web: '',
|
|
45
|
+
clear: '',
|
|
46
|
+
version: '',
|
|
47
|
+
jobs: '',
|
|
48
|
+
diff: '',
|
|
49
|
+
cost: '',
|
|
50
|
+
status: '',
|
|
51
|
+
compact: 'Manual context compaction lands in α6.5.',
|
|
52
|
+
resume: 'Resume last session lands in α6.4 once SQLite session.db lands.',
|
|
53
|
+
memory: 'Session memory editor lands in α6.5.',
|
|
54
|
+
config: 'Run `pugi config list` from a fresh shell for the full surface; in-REPL editor lands in α6.5.',
|
|
55
|
+
privacy: 'Run `pugi privacy show` from a fresh shell; in-REPL toggle lands in α6.5.',
|
|
56
|
+
budget: 'Run `pugi budget` from a fresh shell; in-REPL summary lands in α6.5.',
|
|
57
|
+
mcp: 'Run `pugi config mcp list` from a fresh shell; in-REPL palette lands in α6.5.',
|
|
58
|
+
undo: 'Run `pugi undo` from a fresh shell; in-REPL undo lands in α6.5.',
|
|
59
|
+
});
|
|
23
60
|
export const SLASH_COMMAND_HELP = Object.freeze([
|
|
24
|
-
|
|
25
|
-
{ name: '
|
|
26
|
-
{ name: '
|
|
27
|
-
{ name: '
|
|
28
|
-
{ name: '
|
|
61
|
+
// Workforce dispatch
|
|
62
|
+
{ name: 'brief', args: '<text>', gloss: 'Dispatch a brief to the workforce', group: 'Workforce dispatch' },
|
|
63
|
+
{ name: 'agents', args: '', gloss: 'List the on-watch agent roster', group: 'Workforce dispatch' },
|
|
64
|
+
{ name: 'stop', args: '<persona>', gloss: 'Stop one agent by persona slug', group: 'Workforce dispatch' },
|
|
65
|
+
{ name: 'jobs', args: '', gloss: 'List background jobs', group: 'Workforce dispatch' },
|
|
66
|
+
// Session
|
|
67
|
+
{ name: 'clear', args: '', gloss: 'Clear conversation pane', group: 'Session' },
|
|
68
|
+
{ name: 'resume', args: '', gloss: 'Resume last session (α6.4)', group: 'Session', stub: true },
|
|
69
|
+
{ name: 'compact', args: '', gloss: 'Manual context compaction (α6.5)', group: 'Session', stub: true },
|
|
70
|
+
{ name: 'memory', args: '', gloss: 'Session memory editor (α6.5)', group: 'Session', stub: true },
|
|
71
|
+
// Pugi tools
|
|
72
|
+
{ name: 'web', args: '<url>', gloss: 'Fetch a URL into context', group: 'Pugi tools' },
|
|
73
|
+
{ name: 'diff', args: '', gloss: 'Show pending diff', group: 'Pugi tools' },
|
|
74
|
+
{ name: 'cost', args: '', gloss: 'Token usage + budget', group: 'Pugi tools' },
|
|
75
|
+
{ name: 'status', args: '', gloss: 'Backend + tenant status', group: 'Pugi tools' },
|
|
76
|
+
// Settings
|
|
77
|
+
{ name: 'config', args: '', gloss: 'Show config', group: 'Settings', stub: true },
|
|
78
|
+
{ name: 'privacy', args: '', gloss: 'Show privacy mode', group: 'Settings', stub: true },
|
|
79
|
+
{ name: 'budget', args: '', gloss: 'Show usage budget', group: 'Settings', stub: true },
|
|
80
|
+
{ name: 'mcp', args: '', gloss: 'List MCP servers', group: 'Settings', stub: true },
|
|
81
|
+
{ name: 'undo', args: '', gloss: 'Undo last write', group: 'Settings', stub: true },
|
|
82
|
+
// Meta
|
|
83
|
+
{ name: 'help', args: '', gloss: 'Show this help overlay', group: 'Meta' },
|
|
84
|
+
{ name: 'version', args: '', gloss: 'Show CLI version', group: 'Meta' },
|
|
85
|
+
{ name: 'quit', args: '', gloss: 'Exit the REPL', group: 'Meta' },
|
|
86
|
+
]);
|
|
87
|
+
/**
|
|
88
|
+
* Ordered list of groups. Drives the `/help` overlay sectioning so the
|
|
89
|
+
* operator reads commands by intent (dispatch first, meta last).
|
|
90
|
+
*/
|
|
91
|
+
export const SLASH_COMMAND_GROUPS = Object.freeze([
|
|
92
|
+
'Workforce dispatch',
|
|
93
|
+
'Session',
|
|
94
|
+
'Pugi tools',
|
|
95
|
+
'Settings',
|
|
96
|
+
'Meta',
|
|
29
97
|
]);
|
|
30
98
|
/**
|
|
31
99
|
* Parse one line of input from the REPL. The contract:
|
|
@@ -33,13 +101,15 @@ export const SLASH_COMMAND_HELP = Object.freeze([
|
|
|
33
101
|
* - Empty / whitespace-only input returns `noop` with the original
|
|
34
102
|
* text so the REPL can ignore it without printing anything.
|
|
35
103
|
* - Input that does not start with `/` is treated as an implicit
|
|
36
|
-
* `/brief <text>`
|
|
104
|
+
* `/brief <text>` — the most-common operator action.
|
|
37
105
|
* - `/<name> [args]` resolves the name against the registry; unknown
|
|
38
106
|
* names return `error` so the REPL can render a one-line tip
|
|
39
107
|
* instead of silently dropping the input.
|
|
108
|
+
* - Tier 3 stubs return `{ kind: 'stub', name, message }` so the REPL
|
|
109
|
+
* can render the deterministic "coming in αX.Y" copy uniformly.
|
|
40
110
|
*
|
|
41
111
|
* The function never throws. Bad input maps to a structured result the
|
|
42
|
-
* REPL can render
|
|
112
|
+
* REPL can render — the alternative (throwing from a keystroke handler)
|
|
43
113
|
* would unmount Ink mid-frame.
|
|
44
114
|
*/
|
|
45
115
|
export function parseSlashCommand(input) {
|
|
@@ -90,6 +160,47 @@ export function parseSlashCommand(input) {
|
|
|
90
160
|
case 'q': {
|
|
91
161
|
return { kind: 'quit' };
|
|
92
162
|
}
|
|
163
|
+
case 'web':
|
|
164
|
+
case 'fetch': {
|
|
165
|
+
if (tail.length === 0) {
|
|
166
|
+
return { kind: 'error', message: 'Usage: /web <url>' };
|
|
167
|
+
}
|
|
168
|
+
return { kind: 'web', url: tail };
|
|
169
|
+
}
|
|
170
|
+
case 'clear':
|
|
171
|
+
case 'cls': {
|
|
172
|
+
return { kind: 'clear' };
|
|
173
|
+
}
|
|
174
|
+
case 'version':
|
|
175
|
+
case 'v': {
|
|
176
|
+
return { kind: 'version' };
|
|
177
|
+
}
|
|
178
|
+
case 'jobs': {
|
|
179
|
+
return { kind: 'jobs' };
|
|
180
|
+
}
|
|
181
|
+
case 'diff': {
|
|
182
|
+
return { kind: 'diff' };
|
|
183
|
+
}
|
|
184
|
+
case 'cost': {
|
|
185
|
+
return { kind: 'cost' };
|
|
186
|
+
}
|
|
187
|
+
case 'status': {
|
|
188
|
+
return { kind: 'status' };
|
|
189
|
+
}
|
|
190
|
+
case 'compact':
|
|
191
|
+
case 'resume':
|
|
192
|
+
case 'memory':
|
|
193
|
+
case 'config':
|
|
194
|
+
case 'privacy':
|
|
195
|
+
case 'budget':
|
|
196
|
+
case 'mcp':
|
|
197
|
+
case 'undo': {
|
|
198
|
+
return {
|
|
199
|
+
kind: 'stub',
|
|
200
|
+
name: name,
|
|
201
|
+
message: SLASH_STUB_MESSAGES[name],
|
|
202
|
+
};
|
|
203
|
+
}
|
|
93
204
|
default: {
|
|
94
205
|
return {
|
|
95
206
|
kind: 'error',
|
package/dist/core/settings.js
CHANGED
|
@@ -34,6 +34,19 @@ const pugiSettingsSchema = z.object({
|
|
|
34
34
|
promoteExplicitly: z.boolean().default(true),
|
|
35
35
|
})
|
|
36
36
|
.default({}),
|
|
37
|
+
// `web.fetch.enabled` gates the `pugi web` / `/web` SSRF-guarded
|
|
38
|
+
// fetcher. Default-off matches the spec posture; the schema must
|
|
39
|
+
// declare it explicitly because Zod's strict-pass strips unknown
|
|
40
|
+
// keys and would silently swallow the operator's intent.
|
|
41
|
+
web: z
|
|
42
|
+
.object({
|
|
43
|
+
fetch: z
|
|
44
|
+
.object({
|
|
45
|
+
enabled: z.boolean().optional(),
|
|
46
|
+
})
|
|
47
|
+
.optional(),
|
|
48
|
+
})
|
|
49
|
+
.optional(),
|
|
37
50
|
});
|
|
38
51
|
export function loadSettings(root) {
|
|
39
52
|
const settingsPath = resolve(root, '.pugi/settings.json');
|