@pugi/cli 0.1.0-beta.3 → 0.1.0-beta.5

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.
@@ -1,41 +1,26 @@
1
- [?25l▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
2
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
3
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
4
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
5
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
6
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
7
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
8
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
9
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
10
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
11
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
12
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
13
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
14
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
15
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
16
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
17
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
18
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
19
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
20
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
21
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
22
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
23
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
24
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
25
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
26
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
27
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
28
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
29
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
30
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
31
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
32
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
33
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
34
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
35
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
36
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
37
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
38
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
39
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
40
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
1
+ [?25l 
2
+ 
3
+ 
4
+ 
5
+ 
6
+ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 
7
+ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 
8
+ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 
9
+ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 
10
+ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 
11
+ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 
12
+ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 
13
+ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 
14
+ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄ 
15
+ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 
16
+ ▄▄▄▄▄▄▄▄▄▄▄ ▄ 
17
+ 
18
+ ▄▄▄▄▄ ▄ ▄▄▄▄ ▄ 
19
+ ▄ ▄ ▄ ▄ ▄ ▄▄ ▄ ▄ ▄ 
20
+ ▄ ▄▄▄▄ ▄▄▄▄▄▄ ▄ ▄▄▄▄ 
21
+ ▄▄▄ 
22
+ 
23
+ 
24
+ 
25
+ 
41
26
  [?25h
@@ -22,10 +22,7 @@ import { getJobRegistry, summarizeJobsForPrompt, } from '../jobs/registry.js';
22
22
  const COMMON_LOCAL_FIRST_PREAMBLE = [
23
23
  'You are the Pugi CLI agent running locally inside the operator\'s repository.',
24
24
  'The local filesystem is the source of truth. Every change you make is committed locally; nothing is uploaded by default (ADR-0037 local-first).',
25
- 'You have a tool registry: read, write, edit, grep, glob, bash, apply_patch, lsp_hover, lsp_definition, lsp_references, lsp_diagnostics, worktree_create, worktree_promote, worktree_drop. Call tools to inspect and modify the workspace.',
26
- 'Use lsp_hover / lsp_definition / lsp_references when you need precise symbol intel — they are faster and more reliable than grep for typed languages (TS/JS/Python/Go/Rust). Fall back to grep when the LSP server is unavailable.',
27
- 'Use apply_patch when you have a complete unified diff in hand (e.g. forwarded from an external review). For incremental edits, prefer edit/write over apply_patch — the failure modes are clearer.',
28
- 'Use worktree_create + worktree_promote when a multi-file change needs validation before landing on the operator\'s working tree. Drop the worktree with worktree_drop when done; never leave scratch worktrees around.',
25
+ 'You have a tool registry: read, write, edit, grep, glob, bash. Call tools to inspect and modify the workspace.',
29
26
  'Cite file paths relative to the workspace root. Keep edits minimal and reversible.',
30
27
  'When you are done, return a single final text answer that the operator can read on the CLI.',
31
28
  ].join(' ');
@@ -28,9 +28,11 @@ import { runUndoCommand } from './commands/undo.js';
28
28
  import { runBudgetCommand } from './commands/budget.js';
29
29
  import { runSkillsCommand } from './commands/skills.js';
30
30
  import { runAgentsCommand } from './commands/agents.js';
31
- import { runLspCommand } from './commands/lsp.js';
32
- import { runPatchCommand } from './commands/patch.js';
33
- import { runWorktreeCommand } from './commands/worktree.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.
34
36
  import { resolveWorkspaceLabel } from '../core/repl/workspace-context.js';
35
37
  import { runReviewConsensus } from './commands/review-consensus.js';
36
38
  import { FtsSyntaxError, SqliteSessionStore, resolveProjectStoreDir } from '../core/repl/store/index.js';
@@ -47,7 +49,7 @@ import { dispatchEdit, } from '../core/edits/index.js';
47
49
  * packages/pugi-sdk/package.json); the publish workflow validates the
48
50
  * three are in lockstep.
49
51
  */
50
- const PUGI_CLI_VERSION = "0.1.0-beta.3";
52
+ const PUGI_CLI_VERSION = "0.1.0-beta.5";
51
53
  const handlers = {
52
54
  accounts,
53
55
  agents: dispatchAgents,
@@ -326,14 +328,13 @@ async function dispatchWeb(args, flags, _session) {
326
328
  * dispatch table stays narrow. The runner spawns + tears down the LSP
327
329
  * server per invocation (no daemon yet — that ships in α7.7b).
328
330
  */
329
- async function dispatchLsp(args, flags, _session) {
330
- const result = await runLspCommand(args, { cwd: process.cwd(), json: flags.json });
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.';
331
333
  if (flags.json)
332
- console.log(result.text);
334
+ console.log(JSON.stringify({ ok: false, code: 'deferred', message: msg }));
333
335
  else
334
- console.log(result.text);
335
- if (result.exitCode !== 0)
336
- process.exitCode = result.exitCode;
336
+ console.log(msg);
337
+ process.exitCode = 6;
337
338
  }
338
339
  /**
339
340
  * α7.7: `pugi patch` — apply a unified-diff patch from stdin or a file.
@@ -342,11 +343,13 @@ async function dispatchLsp(args, flags, _session) {
342
343
  * security taxonomy so CI loops can alert on hostile patches without
343
344
  * confusing them with operator typos.
344
345
  */
345
- async function dispatchPatch(args, flags, _session) {
346
- const result = await runPatchCommand(args, { cwd: process.cwd(), json: flags.json });
347
- console.log(result.text);
348
- if (result.exitCode !== 0)
349
- process.exitCode = result.exitCode;
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;
350
353
  }
351
354
  /**
352
355
  * α7.7: `pugi worktree <op>` — manual scratch worktree management.
@@ -354,11 +357,13 @@ async function dispatchPatch(args, flags, _session) {
354
357
  * primitives internally (`createWorktree` / `promoteWorktree`); this
355
358
  * surface is the operator escape hatch for debug + experiment flows.
356
359
  */
357
- async function dispatchWorktree(args, flags, _session) {
358
- const result = await runWorktreeCommand(args, { cwd: process.cwd(), json: flags.json });
359
- console.log(result.text);
360
- if (result.exitCode !== 0)
361
- process.exitCode = result.exitCode;
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;
362
367
  }
363
368
  export async function runCli(argv) {
364
369
  const { command, args, flags, isBareInvocation } = parseArgs(argv);
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Run the delegate command. Parses args, validates the brief, opens a
3
+ * fresh session, and POSTs the delegate brief.
4
+ *
5
+ * Sets `process.exitCode` instead of throwing so the caller (cli.ts
6
+ * handlers table) does not need a try/catch wrapper.
7
+ */
8
+ export async function runDelegateCommand(args, ctx) {
9
+ const slug = args[0];
10
+ const brief = args.slice(1).join(' ').trim();
11
+ if (!slug || !brief) {
12
+ ctx.writeOutput({
13
+ ok: false,
14
+ error: 'Usage: pugi delegate <persona-slug> "<one-sentence brief>"',
15
+ }, 'Usage: pugi delegate <persona-slug> "<one-sentence brief>"');
16
+ process.exitCode = 2;
17
+ return;
18
+ }
19
+ // Brief size cap mirrors the server-side DTO (8000 chars). The local
20
+ // gate lets the operator know about the truncation before we waste a
21
+ // round-trip on a too-large brief.
22
+ if (brief.length > 8000) {
23
+ ctx.writeOutput({
24
+ ok: false,
25
+ error: `brief is ${brief.length} chars; max 8000`,
26
+ }, `pugi delegate: brief is ${brief.length} chars; max 8000.`);
27
+ process.exitCode = 2;
28
+ return;
29
+ }
30
+ const config = ctx.resolveConfig();
31
+ if (!config) {
32
+ ctx.writeOutput({
33
+ ok: false,
34
+ error: 'no Pugi credential configured; run `pugi login` first',
35
+ }, 'pugi delegate: no credential configured. Run `pugi login` first.');
36
+ process.exitCode = 1;
37
+ return;
38
+ }
39
+ const opened = await ctx.openSession(config, ctx.workspaceCwd);
40
+ if ('error' in opened) {
41
+ ctx.writeOutput({ ok: false, error: opened.error }, `pugi delegate: session open failed: ${opened.error}`);
42
+ process.exitCode = 1;
43
+ return;
44
+ }
45
+ const result = await ctx.submitDelegate(config, opened.sessionId, {
46
+ persona: slug,
47
+ brief,
48
+ });
49
+ switch (result.status) {
50
+ case 'ok':
51
+ ctx.writeOutput({
52
+ ok: true,
53
+ sessionId: opened.sessionId,
54
+ dispatchId: result.response.dispatchId,
55
+ personaSlug: result.response.personaSlug,
56
+ }, `dispatched ${result.response.personaSlug} (dispatchId=${result.response.dispatchId}); stream via GET /sessions/${opened.sessionId}/stream.`);
57
+ return;
58
+ case 'unknown_persona':
59
+ ctx.writeOutput({ ok: false, error: result.message, code: result.code }, `pugi delegate: ${result.message}`);
60
+ process.exitCode = 3;
61
+ return;
62
+ case 'quota_exceeded':
63
+ ctx.writeOutput({ ok: false, error: result.message, code: result.code }, `pugi delegate: ${result.message}`);
64
+ process.exitCode = 4;
65
+ return;
66
+ case 'endpoint_missing':
67
+ ctx.writeOutput({
68
+ ok: false,
69
+ error: 'runtime does not expose POST /api/pugi/sessions/:id/delegate (upgrade admin-api to α7.5+)',
70
+ code: result.code,
71
+ }, 'pugi delegate: runtime does not expose the delegate endpoint. Upgrade admin-api to α7.5+.');
72
+ process.exitCode = 1;
73
+ return;
74
+ case 'unauthenticated':
75
+ case 'failed':
76
+ ctx.writeOutput({ ok: false, error: result.message, code: result.code }, `pugi delegate: ${result.message}`);
77
+ process.exitCode = 1;
78
+ return;
79
+ }
80
+ }
81
+ //# sourceMappingURL=delegate.js.map
@@ -0,0 +1,117 @@
1
+ /**
2
+ * `pugi roster` command - α7.5 Tier 1 instantiation Phase 1.
3
+ *
4
+ * Lists the live Tier 1 personas with display name, role, and routing
5
+ * tag. The CLI walks two sources in order:
6
+ *
7
+ * 1. The local @pugi/personas roster (THE_TEN). Always succeeds; the
8
+ * ten brand-canonical personas are baked into the SDK.
9
+ * 2. The remote `GET /api/pugi/sessions/roster` endpoint when the
10
+ * operator has a valid credential. The remote response carries the
11
+ * server-side dispatch role + dispatchTag for each slug so the
12
+ * operator sees the actual routing decision the dispatcher will
13
+ * apply on a `pugi delegate <slug>` call.
14
+ *
15
+ * The command never fails if the network is unreachable - it falls back
16
+ * to local-only output with a one-line warning. This matches the
17
+ * local-first contract (ADR-0037): the operator can still see who is on
18
+ * the team without an API key.
19
+ *
20
+ * Output:
21
+ * - text default: a 3-column table (slug | name | role).
22
+ * - --json: a structured array of { slug, name, role, totem,
23
+ * dispatchTag } records, used by scripted callers.
24
+ */
25
+ import { THE_TEN } from '@pugi/personas';
26
+ import { fetchPersonaRoster, } from '@pugi/sdk';
27
+ /**
28
+ * Fallback role + tag table the CLI uses when the runtime is unreachable
29
+ * (no credentials, network error, older runtime without the
30
+ * /sessions/roster endpoint). Mirrors the server-side
31
+ * persona-dispatch.ts PERSONA_REGISTRY so a CLI that ran without
32
+ * credentials still shows the operator the right routing intent.
33
+ */
34
+ const FALLBACK_ROLE_BY_SLUG = Object.freeze({
35
+ main: { role: 'orchestrator', dispatchTag: 'reason' },
36
+ architect: { role: 'architect', dispatchTag: 'reason' },
37
+ dev: { role: 'coder', dispatchTag: 'codegen' },
38
+ qa: { role: 'verifier', dispatchTag: 'reason' },
39
+ pm: { role: 'release', dispatchTag: 'reason' },
40
+ devops: { role: 'devops', dispatchTag: 'reason' },
41
+ researcher: { role: 'researcher', dispatchTag: 'reason' },
42
+ analyst: { role: 'analyst', dispatchTag: 'summarize' },
43
+ designer: { role: 'design_qa', dispatchTag: 'reason' },
44
+ frontend: { role: 'frontend', dispatchTag: 'codegen' },
45
+ });
46
+ /**
47
+ * Build the roster rows by merging the local @pugi/personas brand
48
+ * roster with the remote dispatch metadata when a credential is
49
+ * available. Pure function so the runtime CLI command can unit-test it
50
+ * without standing up an Anvil endpoint.
51
+ */
52
+ export function mergeRoster(brandRoster, remote) {
53
+ const remoteIndex = new Map((remote ?? []).map((entry) => [entry.slug, entry]));
54
+ return brandRoster.map((persona) => {
55
+ const fromRemote = remoteIndex.get(persona.slug);
56
+ const fallback = FALLBACK_ROLE_BY_SLUG[persona.slug] ?? {
57
+ role: persona.role,
58
+ dispatchTag: 'reason',
59
+ };
60
+ return {
61
+ slug: persona.slug,
62
+ name: persona.name,
63
+ totem: persona.animal,
64
+ role: fromRemote?.role ?? fallback.role,
65
+ dispatchTag: fromRemote?.dispatchTag ?? fallback.dispatchTag,
66
+ oneLiner: persona.oneLiner,
67
+ source: fromRemote ? 'remote' : 'local-fallback',
68
+ };
69
+ });
70
+ }
71
+ /**
72
+ * Render a roster as a plain-text 3-column table the operator reads in
73
+ * the terminal. The column widths grow to fit the longest cell so a
74
+ * future displayName drift does not truncate silently.
75
+ */
76
+ export function renderRosterTable(rows) {
77
+ if (rows.length === 0)
78
+ return 'Roster is empty.';
79
+ const head = { slug: 'slug', name: 'name', totem: 'totem', role: 'role', dispatchTag: 'tag' };
80
+ const widths = {
81
+ slug: Math.max(head.slug.length, ...rows.map((r) => r.slug.length)),
82
+ name: Math.max(head.name.length, ...rows.map((r) => r.name.length)),
83
+ totem: Math.max(head.totem.length, ...rows.map((r) => r.totem.length)),
84
+ role: Math.max(head.role.length, ...rows.map((r) => r.role.length)),
85
+ dispatchTag: Math.max(head.dispatchTag.length, ...rows.map((r) => r.dispatchTag.length)),
86
+ };
87
+ const pad = (s, width) => s + ' '.repeat(Math.max(0, width - s.length));
88
+ const line = (r) => [pad(r.slug, widths.slug), pad(r.name, widths.name), pad(r.totem, widths.totem), pad(r.role, widths.role), pad(r.dispatchTag, widths.dispatchTag)].join(' ');
89
+ const header = line(head);
90
+ const sep = '-'.repeat(header.length);
91
+ return [header, sep, ...rows.map((r) => line(r))].join('\n');
92
+ }
93
+ /**
94
+ * Resolve the roster by walking remote + local sources. The CLI command
95
+ * is a thin wrapper around this function so unit tests can exercise the
96
+ * merge logic without hitting the runtime.
97
+ */
98
+ export async function resolveRoster(config) {
99
+ if (!config) {
100
+ return {
101
+ rows: mergeRoster(THE_TEN, null),
102
+ warning: 'no credential configured; showing local @pugi/personas roster only',
103
+ };
104
+ }
105
+ const result = await fetchPersonaRoster(config);
106
+ if (result.status === 'ok') {
107
+ return { rows: mergeRoster(THE_TEN, result.response.personas), warning: null };
108
+ }
109
+ const reason = result.status === 'endpoint_missing'
110
+ ? 'runtime does not expose /api/pugi/sessions/roster (upgrade admin-api to α7.5+)'
111
+ : result.message;
112
+ return {
113
+ rows: mergeRoster(THE_TEN, null),
114
+ warning: `roster fetch failed (${result.status}): ${reason}; showing local roster only`,
115
+ };
116
+ }
117
+ //# sourceMappingURL=roster.js.map
@@ -1,19 +1,8 @@
1
1
  const registry = [
2
- // α7.7: unified-diff patch apply. Routes through the same security
3
- // gate as Layer A/B/C, so the risk class matches `edit`/`write`
4
- // (medium — writes inside the workspace, never to protected files).
5
- { name: 'apply_patch', permission: 'edit', risk: 'medium', concurrencySafe: false, m1: true },
6
2
  { name: 'bash', permission: 'bash', risk: 'high', concurrencySafe: false, m1: true },
7
3
  { name: 'edit', permission: 'edit', risk: 'medium', concurrencySafe: false, m1: true },
8
4
  { name: 'glob', permission: 'read', risk: 'low', concurrencySafe: true, m1: true },
9
5
  { name: 'grep', permission: 'read', risk: 'low', concurrencySafe: true, m1: true },
10
- // α7.7: LSP read-only surface. Server runs locally, no Anvil
11
- // round-trip. Concurrency-safe because every operation reads
12
- // server state without mutating workspace files.
13
- { name: 'lsp_definition', permission: 'read', risk: 'low', concurrencySafe: true, m1: true },
14
- { name: 'lsp_diagnostics', permission: 'read', risk: 'low', concurrencySafe: true, m1: true },
15
- { name: 'lsp_hover', permission: 'read', risk: 'low', concurrencySafe: true, m1: true },
16
- { name: 'lsp_references', permission: 'read', risk: 'low', concurrencySafe: true, m1: true },
17
6
  { name: 'question', permission: 'none', risk: 'low', concurrencySafe: false, m1: true },
18
7
  { name: 'read', permission: 'read', risk: 'low', concurrencySafe: true, m1: true },
19
8
  { name: 'skill', permission: 'read', risk: 'low', concurrencySafe: true, m1: true },
@@ -22,13 +11,6 @@ const registry = [
22
11
  { name: 'task_list', permission: 'none', risk: 'low', concurrencySafe: true, m1: true },
23
12
  { name: 'task_update', permission: 'none', risk: 'low', concurrencySafe: false, m1: true },
24
13
  { name: 'web_fetch', permission: 'network', risk: 'medium', concurrencySafe: true, m1: true },
25
- // α7.7: scratch worktree management. `worktree_create` writes nothing
26
- // dangerous (a clone under `.pugi/worktrees/`); `worktree_promote`
27
- // applies a diff back to the main tree, so it shares the `edit`
28
- // risk class. `worktree_drop` is the cleanup primitive.
29
- { name: 'worktree_create', permission: 'edit', risk: 'low', concurrencySafe: false, m1: true },
30
- { name: 'worktree_drop', permission: 'edit', risk: 'low', concurrencySafe: false, m1: true },
31
- { name: 'worktree_promote', permission: 'edit', risk: 'medium', concurrencySafe: false, m1: true },
32
14
  { name: 'write', permission: 'edit', risk: 'medium', concurrencySafe: false, m1: true },
33
15
  ];
34
16
  export const toolRegistry = registry.sort((a, b) => a.name.localeCompare(b.name));
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
- const [splashVisible, setSplashVisible] = useState(props.skipSplash !== true);
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,14 +183,15 @@ export function Repl(props) {
178
183
  return undefined;
179
184
  return props.session.cancel();
180
185
  }, [props.session, modalActive]);
181
- // α6.14.4 CEO dogfood 2026-05-25: stretch to full alt-screen height
182
- // (parity with Claude Code which fills the entire viewport). Without
183
- // an explicit height the Box collapses to its natural content size
184
- // and leaves ~2/3 of the alt-screen blank below the input bar.
185
- // process.stdout.rows is the live terminal row count; the REPL
186
- // re-renders on SIGWINCH via Ink's built-in resize listener.
187
- const altScreenHeight = process.stdout.rows ?? 24;
188
- return (_jsxs(Box, { flexDirection: "column", paddingX: 1, height: altScreenHeight, 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, 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,
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.
193
+ const altScreenRows = process.stdout.rows ?? 24;
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,
189
195
  // Slug from process.cwd() (full path) so two workspaces with
190
196
  // the same basename do not share history. state.workspaceLabel
191
197
  // is the basename only. Codex review P2.