@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.
- 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/cli-version.js +127 -0
- package/dist/core/diagnostics/probes/config.js +72 -0
- package/dist/core/diagnostics/probes/disk.js +81 -0
- package/dist/core/diagnostics/probes/git.js +65 -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/session.js +74 -0
- package/dist/core/diagnostics/probes/workspace.js +63 -0
- package/dist/core/diagnostics/types.js +70 -0
- package/dist/core/engine/strip-internal-fields.js +124 -0
- package/dist/core/engine/tool-bridge.js +100 -37
- package/dist/core/file-cache.js +113 -1
- package/dist/core/mcp/client.js +66 -6
- package/dist/core/mcp/registry.js +24 -2
- package/dist/core/repl/session.js +34 -0
- package/dist/core/repl/slash-commands.js +9 -0
- package/dist/runtime/cli.js +24 -58
- package/dist/runtime/commands/doctor.js +357 -0
- package/dist/runtime/commands/mcp.js +290 -3
- package/dist/runtime/version.js +1 -1
- package/dist/tools/agent-tool.js +18 -4
- package/dist/tools/ask-user-question.js +213 -0
- package/dist/tools/file-tools.js +57 -14
- package/dist/tools/registry.js +7 -0
- package/dist/tui/ask-user-question-prompt.js +192 -0
- package/dist/tui/conversation-pane.js +68 -7
- package/dist/tui/doctor-table.js +31 -0
- 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
|
-
//
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
|
|
52
|
-
|
|
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.
|
|
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.
|
|
57
|
+
"@pugi/sdk": "0.1.0-beta.18"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
60
|
"@types/node": "^22.0.0",
|