@pugi/cli 0.1.0-beta.17 → 0.1.0-beta.18

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.
Files changed (32) hide show
  1. package/dist/core/diagnostics/probe-runner.js +93 -0
  2. package/dist/core/diagnostics/probes/api.js +46 -0
  3. package/dist/core/diagnostics/probes/auth.js +86 -0
  4. package/dist/core/diagnostics/probes/cli-version.js +127 -0
  5. package/dist/core/diagnostics/probes/config.js +72 -0
  6. package/dist/core/diagnostics/probes/disk.js +81 -0
  7. package/dist/core/diagnostics/probes/git.js +65 -0
  8. package/dist/core/diagnostics/probes/mcp.js +75 -0
  9. package/dist/core/diagnostics/probes/node.js +59 -0
  10. package/dist/core/diagnostics/probes/pnpm.js +36 -0
  11. package/dist/core/diagnostics/probes/session.js +74 -0
  12. package/dist/core/diagnostics/probes/workspace.js +63 -0
  13. package/dist/core/diagnostics/types.js +70 -0
  14. package/dist/core/engine/strip-internal-fields.js +124 -0
  15. package/dist/core/engine/tool-bridge.js +100 -37
  16. package/dist/core/file-cache.js +113 -1
  17. package/dist/core/mcp/client.js +66 -6
  18. package/dist/core/mcp/registry.js +24 -2
  19. package/dist/core/repl/session.js +34 -0
  20. package/dist/core/repl/slash-commands.js +9 -0
  21. package/dist/runtime/cli.js +24 -58
  22. package/dist/runtime/commands/doctor.js +357 -0
  23. package/dist/runtime/commands/mcp.js +290 -3
  24. package/dist/runtime/version.js +1 -1
  25. package/dist/tools/agent-tool.js +18 -4
  26. package/dist/tools/ask-user-question.js +213 -0
  27. package/dist/tools/file-tools.js +57 -14
  28. package/dist/tools/registry.js +7 -0
  29. package/dist/tui/ask-user-question-prompt.js +192 -0
  30. package/dist/tui/conversation-pane.js +68 -7
  31. package/dist/tui/doctor-table.js +31 -0
  32. package/package.json +2 -2
@@ -43,16 +43,77 @@ function ConversationRow({ row, personaNames, }) {
43
43
  const slug = row.personaSlug ?? '';
44
44
  const color = HUE_COLOR_BY_SLUG[slug] ?? 'white';
45
45
  const displayName = personaNames?.get(slug) ?? slug;
46
- // α6.12: persona bodies travel through MarkdownRender so code
47
- // fences, headings, and inline accents land correctly. A row that
48
- // carries no Markdown syntax renders as plain text under the same
49
- // path (the parser falls through to a single paragraph span), so
50
- // there is no regression for the simple "Mira shipped." baseline.
51
- const containsMarkdown = looksLikeMarkdown(row.text);
52
- return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: color, bold: true, children: `▸ ${displayName} ` }), containsMarkdown ? null : _jsx(Text, { children: row.text })] }), containsMarkdown ? (_jsx(Box, { marginLeft: 2, children: _jsx(MarkdownRender, { source: row.text }) })) : null] }));
46
+ // CEO live dogfood 2026-05-27: a body that started with a
47
+ // Cyrillic letter (e.g. "Принял.") rendered as "PugiПринял." on
48
+ // the same line because the persona label sat at zero margin and
49
+ // the body fell flush behind it. The label is bold-coloured chrome,
50
+ // not text the operator is meant to read as a sentence stem.
51
+ // Split body to its own row and indent two columns so the
52
+ // transcript reads:
53
+ //
54
+ // ▸ Pugi
55
+ // Принял.
56
+ //
57
+ // matching the Claude Code / Codex / Gemini baseline visually +
58
+ // preventing the "PugiПринял" glue regardless of the body's
59
+ // leading character.
60
+ //
61
+ // The render also strips a leading identity-intro phrase as a
62
+ // defense-in-depth complement to the backend output gate — when
63
+ // turnIndex > 1, the operator must NOT see "Я Pugi - твой
64
+ // инженерный напарник." opening every reply. This is belt-and-
65
+ // braces; the prompt + output-gate already block it upstream.
66
+ const stripped = stripLeadingIdentityIntro(row.text);
67
+ const containsMarkdown = looksLikeMarkdown(stripped);
68
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { children: _jsx(Text, { color: color, bold: true, children: `▸ ${displayName}` }) }), containsMarkdown ? (_jsx(Box, { marginLeft: 2, children: _jsx(MarkdownRender, { source: stripped }) })) : (_jsx(Box, { marginLeft: 2, children: _jsx(Text, { children: stripped }) }))] }));
53
69
  }
54
70
  }
55
71
  }
72
+ /**
73
+ * Strip a leading identity-intro line ("I'm Pugi — your engineering
74
+ * copilot..." / "Я Pugi — твой инженерный напарник...") from the
75
+ * transcript body as a defense-in-depth complement to the backend
76
+ * output gate. The prompt + gate already block this upstream; the
77
+ * render guards against the rare case where a legacy model release
78
+ * sneaks the intro past both layers.
79
+ *
80
+ * The strip is intentionally conservative — we only remove the
81
+ * canonical phrasings the prompt teaches Mira to emit. Any other
82
+ * prose containing the word "Pugi" passes through unchanged.
83
+ *
84
+ * Exported for spec.
85
+ */
86
+ export function stripLeadingIdentityIntro(text) {
87
+ if (!text || text.length === 0)
88
+ return text;
89
+ // Try each canonical intro shape in turn (RU + EN). We strip only
90
+ // the LEADING occurrence; a mid-body mention is intentional citation
91
+ // (e.g. "the Pugi codename..." in an explainer turn).
92
+ const introPatterns = [
93
+ // RU canonical — both masculine ("напарник/напарника/напарнику") and
94
+ // feminine ("напарница/напарницей/напарнице") declensions land on this
95
+ // strip. The prior pattern `напарни[ккк][аеу]?` was a character-class
96
+ // typo (three `к`s collapse to one) and accepted no feminine form, so
97
+ // a Mira reply opening "Я Pugi — твоя инженерная напарница…" leaked
98
+ // past the strip. P1 reviewer fix PR #540 (2026-05-27).
99
+ /^(?:Я\s+Pugi\s*[—–-]\s*тв(?:ой|ё|оя)\s+инженерн[аыяий][хйеомя]*\s+напарни(?:к[аеу]?|ц(?:ей|а|ы|е|у))[.,!]?\s*)/u,
100
+ /^(?:Я\s+Pugi\s*[—–-]\s*координатор[.,!]?\s*)/u,
101
+ // EN canonical
102
+ /^(?:I'?m\s+Pugi\s*[—–-]\s*your\s+engineering\s+copilot[.,!]?\s*)/iu,
103
+ /^(?:Pugi\s+here[.,!]?\s*)/iu,
104
+ /^(?:This\s+is\s+Pugi[.,!]?\s*)/iu,
105
+ ];
106
+ let out = text;
107
+ for (const re of introPatterns) {
108
+ out = out.replace(re, '');
109
+ }
110
+ // If the body had ONLY the intro phrase, return the original — never
111
+ // hand the operator an empty bubble. The output gate would have
112
+ // logged the verbosity hit; the operator sees the unmodified text.
113
+ if (out.trim().length === 0)
114
+ return text;
115
+ return out;
116
+ }
56
117
  /**
57
118
  * Cheap heuristic for "this transcript row will benefit from Markdown
58
119
  * rendering". We only pay the parser cost when the row plausibly
@@ -0,0 +1,31 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text } from 'ink';
3
+ /** Brand-voice palette for status cells. Mirrors the table chrome
4
+ * used by `<AgentProgressCard>` so the operator's eye trains on a
5
+ * consistent OK/WARN/ERROR/SKIPPED colour grammar. */
6
+ const STATUS_COLOR = {
7
+ ok: 'green',
8
+ warn: 'yellow',
9
+ error: 'red',
10
+ skipped: 'gray',
11
+ };
12
+ const OVERALL_COLOR = {
13
+ healthy: 'green',
14
+ warning: 'yellow',
15
+ error: 'red',
16
+ };
17
+ export function DoctorTable({ envelope }) {
18
+ const nameWidth = Math.max('NAME'.length, ...envelope.probes.map((row) => row.name.length));
19
+ const statusWidth = Math.max('STATUS'.length, ...envelope.probes.map((row) => row.status.length));
20
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, children: "Pugi Doctor" }) }), envelope.probes.map((row) => (_jsx(DoctorRow, { row: row, nameWidth: nameWidth, statusWidth: statusWidth }, row.name))), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { children: [envelope.counts.error, " error(s), ", envelope.counts.warn, " warning(s), ", envelope.counts.ok, " ok,", ' ', envelope.counts.skipped, " skipped. Overall:", ' ', _jsx(Text, { color: OVERALL_COLOR[envelope.overall], bold: true, children: envelope.overall.toUpperCase() })] }) }), _jsx(Box, { children: _jsxs(Text, { dimColor: true, children: ["CLI ", envelope.meta.cliVersion, " Node ", envelope.meta.nodeVersion, " cwd ", envelope.meta.cwd] }) })] }));
21
+ }
22
+ function DoctorRow({ row, nameWidth, statusWidth }) {
23
+ const namePart = row.name.padEnd(nameWidth, ' ');
24
+ const statusPart = row.status.toUpperCase().padEnd(statusWidth, ' ');
25
+ const latencyPart = typeof row.latencyMs === 'number' ? ` (${row.latencyMs}ms)` : '';
26
+ const showRemediation = typeof row.remediation === 'string' &&
27
+ row.remediation.length > 0 &&
28
+ (row.status === 'warn' || row.status === 'error');
29
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsxs(Text, { children: [namePart, " "] }), _jsx(Text, { color: STATUS_COLOR[row.status], bold: true, children: statusPart }), _jsxs(Text, { children: [' ', row.detail, latencyPart] })] }), showRemediation && (_jsx(Box, { marginLeft: nameWidth + statusWidth + 4, children: _jsxs(Text, { dimColor: true, children: ["\u2192 ", row.remediation] }) }))] }));
30
+ }
31
+ //# sourceMappingURL=doctor-table.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pugi/cli",
3
- "version": "0.1.0-beta.17",
3
+ "version": "0.1.0-beta.18",
4
4
  "description": "Pugi CLI - terminal-native software execution system",
5
5
  "homepage": "https://pugi.io",
6
6
  "repository": {
@@ -54,7 +54,7 @@
54
54
  "undici": "^8.3.0",
55
55
  "zod": "^3.23.0",
56
56
  "@pugi/personas": "0.1.2",
57
- "@pugi/sdk": "0.1.0-beta.17"
57
+ "@pugi/sdk": "0.1.0-beta.18"
58
58
  },
59
59
  "devDependencies": {
60
60
  "@types/node": "^22.0.0",