@pugi/cli 0.1.0-beta.4 → 0.1.0-beta.6
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/assets/pugi-mascot.ansi +15 -25
- package/dist/core/repl/session.js +14 -13
- package/dist/core/repl/slash-commands.js +0 -30
- package/dist/runtime/cli.js +53 -63
- package/dist/tui/repl-render.js +39 -7
- package/dist/tui/repl.js +14 -10
- package/package.json +2 -2
package/assets/pugi-mascot.ansi
CHANGED
|
@@ -1,26 +1,16 @@
|
|
|
1
|
-
[?25l[0m
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
[
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
[7m[38;2;16;21;29mâ–„[38;2;21;29;40mâ–„[38;2;55;62;74mâ–„[38;2;56;64;75mâ–„[38;2;53;71;89mâ–„[38;2;53;71;90mâ–„[38;2;30;38;51mâ–„[38;2;3;12;24mâ–„[38;2;4;12;25mâ–„[38;2;3;16;31mâ–„[38;2;3;73;115mâ–„[0m [7m[38;2;1;83;138mâ–„[0m [0m
|
|
17
|
-
[0m
|
|
18
|
-
[38;2;4;11;22;48;2;3;9;19mâ–„[0m[7m[38;2;4;11;22mâ–„â–„â–„[0m[38;2;4;10;20;48;2;3;10;20mâ–„[0m [38;2;3;8;17mâ–„[0m [38;2;2;6;12mâ–„[38;2;4;11;21mâ–„â–„â–„[0m [38;2;2;6;11mâ–„[0m [0m
|
|
19
|
-
[38;2;4;11;22;48;2;4;11;22mâ–„[0m [7m[38;2;4;12;22mâ–„[0m [38;2;4;11;21;48;2;4;11;22mâ–„[0m [38;2;3;8;16;48;2;3;8;16mâ–„[0m [38;2;4;10;21;48;2;4;10;21mâ–„[0m [38;2;2;6;13;48;2;2;6;12mâ–„[38;2;3;8;15;48;2;3;8;15mâ–„[0m [38;2;0;78;133;48;2;0;77;133mâ–„[0m [38;2;0;122;210;48;2;0;126;217mâ–„[0m [38;2;0;119;204;48;2;0;119;203mâ–„[0m [0m
|
|
20
|
-
[7m[38;2;4;11;21mâ–„[0m [7m[38;2;4;11;22mâ–„[38;2;3;10;20mâ–„[38;2;4;10;20mâ–„[38;2;5;13;25mâ–„[0m [7m[38;2;3;9;18mâ–„[38;2;4;10;20mâ–„[38;2;3;10;20mâ–„[38;2;4;11;22mâ–„[0m[38;2;2;6;12;48;2;2;6;12mâ–„[0m[7m[38;2;3;7;15mâ–„[0m [7m[38;2;0;78;133mâ–„[0m [7m[38;2;0;121;209mâ–„[38;2;0;100;170mâ–„[38;2;0;100;172mâ–„[38;2;0;108;185mâ–„[0m [0m
|
|
21
|
-
[7m[38;2;4;11;21mâ–„[38;2;4;11;22mâ–„[38;2;4;11;21mâ–„[0m [0m
|
|
22
|
-
[0m
|
|
23
|
-
[0m
|
|
24
|
-
[0m
|
|
25
|
-
[0m
|
|
1
|
+
[?25l[0m [0m
|
|
2
|
+
[0m
|
|
3
|
+
[0m
|
|
4
|
+
[38;2;40;45;51mâ–„[38;2;62;69;79mâ–„[38;2;59;66;77mâ–„[38;2;8;16;28;48;2;43;46;51mâ–„[0m[38;2;61;68;77mâ–„[38;2;86;93;103mâ–„[38;2;92;99;109mâ–„[38;2;66;73;83mâ–„[38;2;3;48;79mâ–„[38;2;36;44;54mâ–„[38;2;8;16;28;48;2;43;46;51mâ–„[0m[38;2;62;69;79mâ–„â–„[38;2;40;45;51mâ–„[0m [0m
|
|
5
|
+
[38;2;57;64;74;48;2;52;59;68mâ–„[38;2;67;74;85;48;2;61;69;80mâ–„[38;2;33;40;51;48;2;70;77;88mâ–„[38;2;34;42;53;48;2;12;20;32mâ–„[38;2;92;99;110;48;2;42;49;61mâ–„[48;2;91;98;108mâ–„[38;2;86;92;102;48;2;86;92;103mâ–„[38;2;60;67;78;48;2;77;84;95mâ–„[38;2;63;69;80;48;2;60;67;77mâ–„[38;2;73;80;91;48;2;3;60;96mâ–„[38;2;10;68;104;48;2;3;22;37mâ–„[38;2;3;52;82;48;2;3;58;90mâ–„[38;2;3;23;39;48;2;12;21;32mâ–„[38;2;33;41;51;48;2;69;77;87mâ–„[38;2;65;74;84;48;2;61;69;80mâ–„[38;2;56;64;74;48;2;52;59;67mâ–„[0m [0m
|
|
6
|
+
[7m[38;2;35;40;47mâ–„[0m[38;2;30;37;47;48;2;62;70;80mâ–„[38;2;42;49;60;48;2;6;14;26mâ–„[38;2;92;99;110;48;2;81;88;98mâ–„[38;2;78;86;96;48;2;92;99;110mâ–„[38;2;58;65;76;48;2;84;91;101mâ–„[38;2;85;92;102;48;2;59;67;77mâ–„[38;2;28;36;47;48;2;65;73;83mâ–„[48;2;68;74;85mâ–„[38;2;82;89;100;48;2;58;65;76mâ–„[38;2;24;45;62;48;2;34;58;78mâ–„[38;2;3;67;104;48;2;3;42;67mâ–„[38;2;3;61;93;48;2;3;45;72mâ–„[38;2;3;33;55;48;2;4;12;24mâ–„[38;2;32;39;49;48;2;63;71;81mâ–„[0m[7m[38;2;35;40;47mâ–„[0m [0m
|
|
7
|
+
[38;2;58;65;76;48;2;58;66;76mâ–„[38;2;53;60;71;48;2;30;38;49mâ–„[38;2;3;57;93;48;2;2;74;118mâ–„[38;2;42;71;93;48;2;3;21;37mâ–„[38;2;56;64;74;48;2;43;50;61mâ–„[38;2;61;68;79;48;2;74;80;90mâ–„[38;2;63;69;80;48;2;73;80;90mâ–„[38;2;59;66;76;48;2;41;48;59mâ–„[38;2;39;70;92;48;2;3;21;37mâ–„[38;2;2;59;95;48;2;2;64;104mâ–„[38;2;3;57;89;48;2;3;56;85mâ–„[38;2;2;45;74;48;2;3;45;73mâ–„[0m [0m
|
|
8
|
+
[38;2;32;37;44;48;2;59;66;77mâ–„[38;2;62;68;79;48;2;78;85;95mâ–„[38;2;30;38;49;48;2;29;37;48mâ–„[38;2;59;66;77;48;2;59;67;77mâ–„[38;2;59;67;78;48;2;48;55;66mâ–„[38;2;53;61;72;48;2;47;54;65mâ–„[38;2;53;60;72;48;2;49;55;66mâ–„[38;2;53;79;102;48;2;46;54;65mâ–„[38;2;38;86;124;48;2;54;61;72mâ–„[38;2;3;74;118;48;2;6;60;94mâ–„[38;2;0;148;246;48;2;4;83;120mâ–„[38;2;3;90;148;48;2;3;83;129mâ–„[0m [0m
|
|
9
|
+
[7m[38;2;45;51;60mâ–„[0m[38;2;11;18;28;48;2;33;40;52mâ–„[38;2;54;62;73;48;2;59;67;78mâ–„[38;2;36;43;55;48;2;54;62;73mâ–„[38;2;53;73;93;48;2;34;42;53mâ–„[38;2;49;87;122;48;2;33;40;52mâ–„[38;2;16;57;86;48;2;49;57;69mâ–„[38;2;3;66;102;48;2;28;42;55mâ–„[38;2;3;35;56;48;2;3;76;118mâ–„[38;2;2;62;98;48;2;4;91;135mâ–„[0m[7m[38;2;3;80;123mâ–„[0m [0m
|
|
10
|
+
[7m[38;2;9;13;19mâ–„[38;2;34;40;49mâ–„[38;2;40;50;63mâ–„[38;2;35;45;59mâ–„[38;2;4;11;21mâ–„[38;2;2;10;19mâ–„[0m [0m
|
|
11
|
+
[38;2;2;7;13mâ–„[7m[38;2;2;6;13mâ–„â–„[0m[38;2;2;6;12mâ–„[0m [38;2;3;8;17mâ–„[38;2;3;7;14mâ–„[0m [0m
|
|
12
|
+
[7m[38;2;3;8;16mâ–„[0m [7m[38;2;2;6;12mâ–„[0m [38;2;2;6;12mâ–„[48;2;2;6;12mâ–„[0m[7m[38;2;2;6;12mâ–„[0m [0m
|
|
13
|
+
[7m[38;2;2;7;13mâ–„[0m [0m
|
|
14
|
+
[0m
|
|
15
|
+
[0m
|
|
26
16
|
[?25h
|
|
@@ -577,18 +577,6 @@ export class ReplSession {
|
|
|
577
577
|
await this.dispatchStop(verdict.persona);
|
|
578
578
|
return verdict;
|
|
579
579
|
}
|
|
580
|
-
case 'delegate': {
|
|
581
|
-
// α7.5 Phase 1: surface the dispatch intent inline. The actual
|
|
582
|
-
// wire shape (POST /api/pugi/sessions/:id/delegate) requires the
|
|
583
|
-
// SDK transport extension that ships alongside this PR; the
|
|
584
|
-
// REPL session module wires the call when the matching transport
|
|
585
|
-
// method lands (paired CLI follow-up). Today we surface the
|
|
586
|
-
// delegation intent in the transcript so the operator sees the
|
|
587
|
-
// verdict echo for muscle-memory before the round-trip lights up.
|
|
588
|
-
this.appendSystemLine(`delegate ${verdict.persona}: ${verdict.brief.length > 80 ? `${verdict.brief.slice(0, 77)}...` : verdict.brief}`);
|
|
589
|
-
this.appendSystemLine('Run `pugi delegate <slug> "<brief>"` from a fresh shell while the REPL transport wiring lands.');
|
|
590
|
-
return verdict;
|
|
591
|
-
}
|
|
592
580
|
case 'dispatch': {
|
|
593
581
|
await this.dispatchBrief(verdict.brief);
|
|
594
582
|
return verdict;
|
|
@@ -1394,8 +1382,21 @@ export class ReplSession {
|
|
|
1394
1382
|
void this.recreateSessionSilently();
|
|
1395
1383
|
return;
|
|
1396
1384
|
}
|
|
1385
|
+
// α6.14.4 CEO dogfood 2026-05-25 (parity with Claude Code):
|
|
1386
|
+
// collapse the repeated "Stream interrupted (fetch failed).
|
|
1387
|
+
// Reconnecting." spam. The status bar already shows
|
|
1388
|
+
// connection='reconnecting' AND the attempt counter; pushing
|
|
1389
|
+
// a fresh transcript row per attempt fills the screen with
|
|
1390
|
+
// noise. Only emit the system line for the FIRST drop of a
|
|
1391
|
+
// run; subsequent reconnects update the status bar silently
|
|
1392
|
+
// until either success (clears the connection state) or the
|
|
1393
|
+
// give-up path in scheduleReconnect prints the final hint.
|
|
1394
|
+
const wasOnline = this.state.connection === 'on_watch'
|
|
1395
|
+
|| this.state.connection === 'connecting';
|
|
1397
1396
|
this.patch({ connection: 'reconnecting' });
|
|
1398
|
-
|
|
1397
|
+
if (wasOnline) {
|
|
1398
|
+
this.appendSystemLine(`Stream interrupted (${this.errorMessage(error)}). Reconnecting...`);
|
|
1399
|
+
}
|
|
1399
1400
|
this.scheduleReconnect();
|
|
1400
1401
|
},
|
|
1401
1402
|
});
|
|
@@ -52,7 +52,6 @@ export const SLASH_COMMAND_HELP = Object.freeze([
|
|
|
52
52
|
// Workforce dispatch
|
|
53
53
|
{ name: 'brief', args: '<text>', gloss: 'Dispatch a brief to the workforce', group: 'Workforce dispatch' },
|
|
54
54
|
{ name: 'agents', args: '', gloss: 'List the on-watch agent roster', group: 'Workforce dispatch' },
|
|
55
|
-
{ name: 'delegate', args: '<slug> <brief>', gloss: 'Dispatch a brief to one Tier 1 specialist (α7.5)', group: 'Workforce dispatch' },
|
|
56
55
|
{ name: 'stop', args: '<persona>', gloss: 'Stop one agent by persona slug', group: 'Workforce dispatch' },
|
|
57
56
|
{ name: 'jobs', args: '', gloss: 'List background jobs', group: 'Workforce dispatch' },
|
|
58
57
|
{ name: 'ask', args: '<question>', gloss: 'Surface a yes/no modal locally (α6.3 forcing question)', group: 'Workforce dispatch' },
|
|
@@ -136,35 +135,6 @@ export function parseSlashCommand(input) {
|
|
|
136
135
|
case 'roster': {
|
|
137
136
|
return { kind: 'roster' };
|
|
138
137
|
}
|
|
139
|
-
case 'delegate': {
|
|
140
|
-
// tail must start with the persona slug followed by the brief.
|
|
141
|
-
// Slug accepts only the closed-set lowercase ASCII pattern the
|
|
142
|
-
// server-side persona registry enforces; anything else surfaces
|
|
143
|
-
// as a usage error so the operator sees the typo before the
|
|
144
|
-
// round-trip.
|
|
145
|
-
const space = tail.indexOf(' ');
|
|
146
|
-
if (space === -1 || space === 0) {
|
|
147
|
-
return {
|
|
148
|
-
kind: 'error',
|
|
149
|
-
message: 'Usage: /delegate <slug> <one-sentence brief>',
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
const persona = tail.slice(0, space).toLowerCase();
|
|
153
|
-
const brief = tail.slice(space + 1).trim();
|
|
154
|
-
if (!/^[a-z_]+$/.test(persona)) {
|
|
155
|
-
return {
|
|
156
|
-
kind: 'error',
|
|
157
|
-
message: `/delegate slug must be lowercase ASCII; got '${persona}'`,
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
if (brief.length === 0) {
|
|
161
|
-
return {
|
|
162
|
-
kind: 'error',
|
|
163
|
-
message: 'Usage: /delegate <slug> <one-sentence brief>',
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
return { kind: 'delegate', persona, brief };
|
|
167
|
-
}
|
|
168
138
|
case 'stop':
|
|
169
139
|
case 'kill': {
|
|
170
140
|
if (tail.length === 0) {
|
package/dist/runtime/cli.js
CHANGED
|
@@ -17,10 +17,8 @@ import { toolRegistry, toolSchemaBundleHashInput } from '../tools/registry.js';
|
|
|
17
17
|
import { webFetchTool } from '../tools/web-fetch.js';
|
|
18
18
|
import { emptyIndex, rebuildIndex, readIndex, upsertArtifact, writeIndex, } from '../core/index-store.js';
|
|
19
19
|
import { signatureForPlanReview } from '../core/repl/ask.js';
|
|
20
|
-
import { buildRuntimeConfig,
|
|
20
|
+
import { buildRuntimeConfig, loadRuntimeConfig, pollDeviceFlow, pugiHandoffBundleSchema, pugiSyncDryRunPlanSchema, pugiSyncPrivacyModeSchema, pugiSyncRequestSchema, pugiSyncUploadPlanSchema, pugiTripleReviewRequestSchema, startDeviceFlow, submitSync, submitTripleReview, } from '@pugi/sdk';
|
|
21
21
|
import { PUGI_TAGLINE } from '@pugi/personas';
|
|
22
|
-
import { resolveRoster, renderRosterTable } from './commands/roster.js';
|
|
23
|
-
import { runDelegateCommand } from './commands/delegate.js';
|
|
24
22
|
import { clearApiKey, DEFAULT_API_URL, listStoredCredentials, maskApiKey, normalizeApiUrl, purgeAllCredentials, readCredentialsFile, resolveActiveCredential, storeApiKey, switchActiveAccount, } from '../core/credentials.js';
|
|
25
23
|
import { runDeployCommand } from '../commands/deploy.js';
|
|
26
24
|
import { runJobsCommand } from '../commands/jobs.js';
|
|
@@ -30,6 +28,11 @@ import { runUndoCommand } from './commands/undo.js';
|
|
|
30
28
|
import { runBudgetCommand } from './commands/budget.js';
|
|
31
29
|
import { runSkillsCommand } from './commands/skills.js';
|
|
32
30
|
import { runAgentsCommand } from './commands/agents.js';
|
|
31
|
+
// α7.7 lsp/patch/worktree command modules ship behind the α7.7
|
|
32
|
+
// implementer PR (in-flight). The dispatchers below print a clean
|
|
33
|
+
// "deferred" message so `pugi --help` still lists them without the
|
|
34
|
+
// REPL crashing at module load. When α7.7 lands, restore the real
|
|
35
|
+
// imports + delete the inline stubs.
|
|
33
36
|
import { resolveWorkspaceLabel } from '../core/repl/workspace-context.js';
|
|
34
37
|
import { runReviewConsensus } from './commands/review-consensus.js';
|
|
35
38
|
import { FtsSyntaxError, SqliteSessionStore, resolveProjectStoreDir } from '../core/repl/store/index.js';
|
|
@@ -46,7 +49,7 @@ import { dispatchEdit, } from '../core/edits/index.js';
|
|
|
46
49
|
* packages/pugi-sdk/package.json); the publish workflow validates the
|
|
47
50
|
* three are in lockstep.
|
|
48
51
|
*/
|
|
49
|
-
const PUGI_CLI_VERSION = "0.1.0-beta.
|
|
52
|
+
const PUGI_CLI_VERSION = "0.1.0-beta.6";
|
|
50
53
|
const handlers = {
|
|
51
54
|
accounts,
|
|
52
55
|
agents: dispatchAgents,
|
|
@@ -55,7 +58,6 @@ const handlers = {
|
|
|
55
58
|
budget: dispatchBudget,
|
|
56
59
|
code: runEngineTask('code'),
|
|
57
60
|
config: dispatchConfig,
|
|
58
|
-
delegate: dispatchDelegate,
|
|
59
61
|
deploy: dispatchDeploy,
|
|
60
62
|
doctor,
|
|
61
63
|
explain: runEngineTask('explain'),
|
|
@@ -67,12 +69,13 @@ const handlers = {
|
|
|
67
69
|
jobs,
|
|
68
70
|
login,
|
|
69
71
|
logout,
|
|
72
|
+
lsp: dispatchLsp,
|
|
73
|
+
patch: dispatchPatch,
|
|
70
74
|
plan: runEngineTask('plan'),
|
|
71
75
|
'plan-review': dispatchPlanReview,
|
|
72
76
|
privacy: dispatchPrivacy,
|
|
73
77
|
review,
|
|
74
78
|
resume,
|
|
75
|
-
roster: dispatchRoster,
|
|
76
79
|
sessions,
|
|
77
80
|
skills: dispatchSkills,
|
|
78
81
|
sync,
|
|
@@ -80,6 +83,7 @@ const handlers = {
|
|
|
80
83
|
version,
|
|
81
84
|
web: dispatchWeb,
|
|
82
85
|
whoami,
|
|
86
|
+
worktree: dispatchWorktree,
|
|
83
87
|
};
|
|
84
88
|
/**
|
|
85
89
|
* α6.3 `pugi ask "<question>"` — surface the office-hours forcing-question
|
|
@@ -250,59 +254,6 @@ async function dispatchPrivacy(args, flags, _session) {
|
|
|
250
254
|
writeOutput: (payload, text) => writeOutput(flags, payload, text),
|
|
251
255
|
});
|
|
252
256
|
}
|
|
253
|
-
/**
|
|
254
|
-
* `pugi roster` - α7.5 Phase 1.
|
|
255
|
-
*
|
|
256
|
-
* List the live Tier 1 personas with display name, role, and routing
|
|
257
|
-
* tag. Walks the remote /api/pugi/sessions/roster endpoint when a
|
|
258
|
-
* credential is available; falls back to the local @pugi/personas
|
|
259
|
-
* roster when offline so the operator can still see who is on the team.
|
|
260
|
-
*/
|
|
261
|
-
async function dispatchRoster(_args, flags, _session) {
|
|
262
|
-
const credential = resolveActiveCredential();
|
|
263
|
-
const config = credential
|
|
264
|
-
? buildRuntimeConfig({ apiUrl: credential.apiUrl, apiKey: credential.apiKey })
|
|
265
|
-
: null;
|
|
266
|
-
const { rows, warning } = await resolveRoster(config);
|
|
267
|
-
const payload = {
|
|
268
|
-
ok: true,
|
|
269
|
-
personas: rows,
|
|
270
|
-
warning,
|
|
271
|
-
};
|
|
272
|
-
const text = (warning ? `# warning: ${warning}\n\n` : '') +
|
|
273
|
-
renderRosterTable(rows);
|
|
274
|
-
writeOutput(flags, payload, text);
|
|
275
|
-
}
|
|
276
|
-
/**
|
|
277
|
-
* `pugi delegate <slug> "<brief>"` - α7.5 Phase 1.
|
|
278
|
-
*
|
|
279
|
-
* Open a fresh REPL session and POST the brief to one Tier 1 persona,
|
|
280
|
-
* bypassing Mira's coordinator pass. Non-interactive: the CLI prints
|
|
281
|
-
* the dispatch id on success and exits; the operator (or a script) can
|
|
282
|
-
* subscribe to the session stream separately if they want the live
|
|
283
|
-
* lifecycle. Interactive operators use `/delegate` from inside the REPL
|
|
284
|
-
* instead so the dispatch lifecycle surfaces inline.
|
|
285
|
-
*/
|
|
286
|
-
async function dispatchDelegate(args, flags, _session) {
|
|
287
|
-
await runDelegateCommand(args, {
|
|
288
|
-
workspaceCwd: process.cwd(),
|
|
289
|
-
writeOutput: (payload, text) => writeOutput(flags, payload, text),
|
|
290
|
-
resolveConfig: () => {
|
|
291
|
-
const credential = resolveActiveCredential();
|
|
292
|
-
if (!credential)
|
|
293
|
-
return null;
|
|
294
|
-
return buildRuntimeConfig({ apiUrl: credential.apiUrl, apiKey: credential.apiKey });
|
|
295
|
-
},
|
|
296
|
-
fetchRoster: fetchPersonaRoster,
|
|
297
|
-
submitDelegate,
|
|
298
|
-
openSession: async (config, workspaceCwd) => {
|
|
299
|
-
const result = await openPugiSession(config, { workspaceCwd });
|
|
300
|
-
if (result.status === 'ok')
|
|
301
|
-
return { sessionId: result.response.sessionId };
|
|
302
|
-
return { error: `${result.status}: ${result.message}` };
|
|
303
|
-
},
|
|
304
|
-
});
|
|
305
|
-
}
|
|
306
257
|
async function dispatchUndo(args, flags, session) {
|
|
307
258
|
await runUndoCommand(args, {
|
|
308
259
|
workspaceRoot: process.cwd(),
|
|
@@ -371,6 +322,49 @@ async function dispatchWeb(args, flags, _session) {
|
|
|
371
322
|
}
|
|
372
323
|
writeOutput(flags, result, `# ${result.title}\n# ${result.url}\n# fetched ${result.fetched_at}\n\n${result.content_md}`);
|
|
373
324
|
}
|
|
325
|
+
/**
|
|
326
|
+
* α7.7: `pugi lsp <op> <file> [args]` — direct LSP queries. Delegated
|
|
327
|
+
* to the standalone runner in `./commands/lsp.ts` so the giant cli.ts
|
|
328
|
+
* dispatch table stays narrow. The runner spawns + tears down the LSP
|
|
329
|
+
* server per invocation (no daemon yet — that ships in α7.7b).
|
|
330
|
+
*/
|
|
331
|
+
async function dispatchLsp(_args, flags, _session) {
|
|
332
|
+
const msg = 'pugi lsp ships in alpha 7.7 (in-flight). Run `pugi --help` for current surface.';
|
|
333
|
+
if (flags.json)
|
|
334
|
+
console.log(JSON.stringify({ ok: false, code: 'deferred', message: msg }));
|
|
335
|
+
else
|
|
336
|
+
console.log(msg);
|
|
337
|
+
process.exitCode = 6;
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* α7.7: `pugi patch` — apply a unified-diff patch from stdin or a file.
|
|
341
|
+
* Routes through the same security gate as the Layer A/B/C applicators
|
|
342
|
+
* (see `src/core/edits/security-gate.ts`). Exit codes mirror the
|
|
343
|
+
* security taxonomy so CI loops can alert on hostile patches without
|
|
344
|
+
* confusing them with operator typos.
|
|
345
|
+
*/
|
|
346
|
+
async function dispatchPatch(_args, flags, _session) {
|
|
347
|
+
const msg = 'pugi patch ships in alpha 7.7 (in-flight). Run `pugi --help` for current surface.';
|
|
348
|
+
if (flags.json)
|
|
349
|
+
console.log(JSON.stringify({ ok: false, code: 'deferred', message: msg }));
|
|
350
|
+
else
|
|
351
|
+
console.log(msg);
|
|
352
|
+
process.exitCode = 6;
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* α7.7: `pugi worktree <op>` — manual scratch worktree management.
|
|
356
|
+
* The `pugi build` and `pugi review --consensus` paths use the same
|
|
357
|
+
* primitives internally (`createWorktree` / `promoteWorktree`); this
|
|
358
|
+
* surface is the operator escape hatch for debug + experiment flows.
|
|
359
|
+
*/
|
|
360
|
+
async function dispatchWorktree(_args, flags, _session) {
|
|
361
|
+
const msg = 'pugi worktree ships in alpha 7.7 (in-flight). Run `pugi --help` for current surface.';
|
|
362
|
+
if (flags.json)
|
|
363
|
+
console.log(JSON.stringify({ ok: false, code: 'deferred', message: msg }));
|
|
364
|
+
else
|
|
365
|
+
console.log(msg);
|
|
366
|
+
process.exitCode = 6;
|
|
367
|
+
}
|
|
374
368
|
export async function runCli(argv) {
|
|
375
369
|
const { command, args, flags, isBareInvocation } = parseArgs(argv);
|
|
376
370
|
// Bare `pugi` on a TTY enters the REPL-by-default agentic session
|
|
@@ -584,10 +578,6 @@ async function help(_args, flags, _session) {
|
|
|
584
578
|
' pugi ask "<question>" Surface a yes/no question modal locally.',
|
|
585
579
|
' pugi plan-review <task> Generate + present a plan-review modal.',
|
|
586
580
|
'',
|
|
587
|
-
'Persona dispatch (α7.5):',
|
|
588
|
-
' pugi roster List the live Tier 1 personas + roles.',
|
|
589
|
-
' pugi delegate <slug> "<brief>" Dispatch a brief to one specialist.',
|
|
590
|
-
'',
|
|
591
581
|
'Deploy:',
|
|
592
582
|
' pugi deploy --target vercel <vercelProject> --project <id>',
|
|
593
583
|
' Trigger a Vercel deployment from the bound Git source.',
|
package/dist/tui/repl-render.js
CHANGED
|
@@ -86,14 +86,30 @@ export async function renderRepl(options) {
|
|
|
86
86
|
// Kick off the connect; the Repl renders the connecting state until
|
|
87
87
|
// the session pushes `connection: 'on_watch'` from the SSE onOpen.
|
|
88
88
|
void session.start();
|
|
89
|
+
// α6.14.4 CEO dogfood 2026-05-25 (parity with Claude Code): enter
|
|
90
|
+
// the terminal's alternate screen buffer so the REPL renders on a
|
|
91
|
+
// fresh "screen" the operator cannot scroll above. On exit, leave
|
|
92
|
+
// restores the previous terminal contents - the conversation does
|
|
93
|
+
// not pollute the operator's shell history. Skipped under --no-tty
|
|
94
|
+
// and when stdout is not a TTY (pipe/CI), where the escapes would
|
|
95
|
+
// appear as literal characters.
|
|
96
|
+
// ORDER MATTERS (beta.2 follow-up): alt-screen enter MUST happen
|
|
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
|
+
}
|
|
89
104
|
// α6.14.2 wave 5: paint the chafa-baked brand-pug ANSI render to
|
|
90
|
-
// stdout BEFORE Ink mounts. Ink's
|
|
91
|
-
// the truecolor escape sequences,
|
|
92
|
-
// The flag is passed into <Repl />
|
|
93
|
-
// skip its own hand-crafted
|
|
94
|
-
//
|
|
95
|
-
//
|
|
96
|
-
//
|
|
105
|
+
// stdout BEFORE Ink mounts (but AFTER alt-screen enter). Ink's
|
|
106
|
+
// layout engine would mis-measure the truecolor escape sequences,
|
|
107
|
+
// so the pug must land verbatim. The flag is passed into <Repl />
|
|
108
|
+
// so the splash component knows to skip its own hand-crafted
|
|
109
|
+
// PUG_MASCOT column - otherwise the operator sees both the chafa
|
|
110
|
+
// pug AND the ASCII fallback stacked. When skipSplash is true
|
|
111
|
+
// (operator opted out via --no-splash), we suppress the pre-print
|
|
112
|
+
// too so the boot stays silent.
|
|
97
113
|
const mascotPrePrinted = options.skipSplash === true ? false : printPugMascotPreInk(process.stdout);
|
|
98
114
|
const instance = render(React.createElement(Repl, {
|
|
99
115
|
session,
|
|
@@ -102,10 +118,26 @@ export async function renderRepl(options) {
|
|
|
102
118
|
hideToolStream: options.hideToolStream === true,
|
|
103
119
|
mascotPrePrinted,
|
|
104
120
|
}));
|
|
121
|
+
const restoreAltScreen = () => {
|
|
122
|
+
if (supportsAltScreen) {
|
|
123
|
+
try {
|
|
124
|
+
process.stdout.write('\x1b[?1049l');
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
/* shutdown race — terminal already detached */
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
// Make sure we leave the alt screen on abrupt exits too. Without
|
|
132
|
+
// this the operator's shell stays "frozen" on the Pugi splash.
|
|
133
|
+
process.once('exit', restoreAltScreen);
|
|
134
|
+
process.once('SIGINT', restoreAltScreen);
|
|
135
|
+
process.once('SIGTERM', restoreAltScreen);
|
|
105
136
|
try {
|
|
106
137
|
await instance.waitUntilExit();
|
|
107
138
|
}
|
|
108
139
|
finally {
|
|
140
|
+
restoreAltScreen();
|
|
109
141
|
session.close();
|
|
110
142
|
if (store) {
|
|
111
143
|
try {
|
package/dist/tui/repl.js
CHANGED
|
@@ -49,7 +49,12 @@ export function Repl(props) {
|
|
|
49
49
|
// α6.14 wave 3: boot splash visible until first input, first
|
|
50
50
|
// `agent.spawned` event, or 10s idle. The host gates the initial
|
|
51
51
|
// visibility on `--no-splash` / PUGI_SKIP_SPLASH via `skipSplash`.
|
|
52
|
-
|
|
52
|
+
// α6.14.6 CEO dogfood 2026-05-25: default splash to HIDDEN at boot
|
|
53
|
+
// (parity with Claude Code's minimal one-line banner). Operator can
|
|
54
|
+
// opt back in via `/splash` slash. The chafa pug pre-print + header
|
|
55
|
+
// line already give the brand cue without the multi-row Plan/Model/
|
|
56
|
+
// Tenant block crowding the top.
|
|
57
|
+
const [splashVisible, setSplashVisible] = useState(false);
|
|
53
58
|
const dismissSplash = useCallback(() => setSplashVisible(false), []);
|
|
54
59
|
// α6.14 wave 3: workspace context snapshot for the status bar. We
|
|
55
60
|
// read once at mount and freeze; a brand-new PUGI.md or skill is
|
|
@@ -178,16 +183,15 @@ export function Repl(props) {
|
|
|
178
183
|
return undefined;
|
|
179
184
|
return props.session.cancel();
|
|
180
185
|
}, [props.session, modalActive]);
|
|
181
|
-
// α6.14.5 CEO dogfood 2026-05-25 (parity with Claude Code):
|
|
182
|
-
//
|
|
183
|
-
//
|
|
184
|
-
//
|
|
185
|
-
//
|
|
186
|
-
//
|
|
187
|
-
//
|
|
188
|
-
// surface adjacent to the cursor row.
|
|
186
|
+
// α6.14.5 CEO dogfood 2026-05-25 (parity with Claude Code): input
|
|
187
|
+
// box pinned to alt-screen BOTTOM, conversation grows above it.
|
|
188
|
+
// Beta.3's height={rows} fix broke keystroke focus - raw echo at
|
|
189
|
+
// viewport bottom. The right pattern is minHeight on the root +
|
|
190
|
+
// flexGrow=1 on the MainArea Box: empty alt-screen lives ABOVE the
|
|
191
|
+
// input, and the input stays the sole focusable surface adjacent
|
|
192
|
+
// to the cursor row, so all keystrokes route through it.
|
|
189
193
|
const altScreenRows = process.stdout.rows ?? 24;
|
|
190
|
-
return (_jsxs(Box, { flexDirection: "column", paddingX: 1, minHeight: altScreenRows, children: [props.updateBanner ? _jsx(UpdateBanner, { result: props.updateBanner }) : 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, 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, now: props.now,
|
|
194
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: 1, minHeight: altScreenRows, children: [props.updateBanner ? _jsx(UpdateBanner, { result: props.updateBanner }) : 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, now: props.now,
|
|
191
195
|
// Slug from process.cwd() (full path) so two workspaces with
|
|
192
196
|
// the same basename do not share history. state.workspaceLabel
|
|
193
197
|
// is the basename only. Codex review P2.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pugi/cli",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.6",
|
|
4
4
|
"description": "Pugi CLI - terminal-native software execution system",
|
|
5
5
|
"homepage": "https://pugi.io",
|
|
6
6
|
"repository": {
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"undici": "^8.3.0",
|
|
53
53
|
"zod": "^3.23.0",
|
|
54
54
|
"@pugi/personas": "0.1.2",
|
|
55
|
-
"@pugi/sdk": "0.1.0-beta.
|
|
55
|
+
"@pugi/sdk": "0.1.0-beta.6"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
58
|
"@types/node": "^22.0.0",
|