@selesai/code 0.10.1 → 0.11.0
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/CHANGELOG.md +23 -0
- package/dist/config.d.ts +5 -3
- package/dist/config.js +6 -4
- package/dist/core/skills.d.ts +2 -0
- package/dist/core/skills.js +1 -0
- package/dist/defaults/settings.json +0 -3
- package/dist/extensions/auto-session-name.test.ts +157 -0
- package/dist/extensions/auto-session-name.ts +96 -0
- package/dist/extensions/cost-reconcile.test.ts +76 -0
- package/dist/extensions/cost-reconcile.ts +278 -0
- package/dist/extensions/enable-readonly-tools.test.ts +11 -0
- package/dist/extensions/enable-readonly-tools.ts +7 -3
- package/dist/extensions/package.json +3 -0
- package/dist/extensions/pi-tool-display/CHANGELOG.md +329 -0
- package/dist/extensions/pi-tool-display/LICENSE +21 -0
- package/dist/extensions/pi-tool-display/README.md +417 -0
- package/dist/extensions/pi-tool-display/config/config.example.json +40 -0
- package/dist/extensions/pi-tool-display/index.ts +3 -0
- package/dist/extensions/pi-tool-display/package-lock.json +2555 -0
- package/dist/extensions/pi-tool-display/package.json +86 -0
- package/dist/extensions/pi-tool-display/src/agent-dir.ts +35 -0
- package/dist/extensions/pi-tool-display/src/ansi-utils.ts +116 -0
- package/dist/extensions/pi-tool-display/src/bash-display.ts +213 -0
- package/dist/extensions/pi-tool-display/src/capabilities.ts +88 -0
- package/dist/extensions/pi-tool-display/src/config-modal.ts +541 -0
- package/dist/extensions/pi-tool-display/src/config-store.ts +304 -0
- package/dist/extensions/pi-tool-display/src/debug-logger.ts +150 -0
- package/dist/extensions/pi-tool-display/src/diff-presentation.ts +72 -0
- package/dist/extensions/pi-tool-display/src/diff-renderer.ts +2517 -0
- package/dist/extensions/pi-tool-display/src/disposable.ts +34 -0
- package/dist/extensions/pi-tool-display/src/extension-lifecycle.ts +14 -0
- package/dist/extensions/pi-tool-display/src/index.ts +109 -0
- package/dist/extensions/pi-tool-display/src/line-width-safety.ts +93 -0
- package/dist/extensions/pi-tool-display/src/modal-icons.ts +57 -0
- package/dist/extensions/pi-tool-display/src/pending-diff-preview.ts +370 -0
- package/dist/extensions/pi-tool-display/src/presets.ts +117 -0
- package/dist/extensions/pi-tool-display/src/render-utils.ts +186 -0
- package/dist/extensions/pi-tool-display/src/settings-inspector-modal.ts +475 -0
- package/dist/extensions/pi-tool-display/src/thinking-label.ts +343 -0
- package/dist/extensions/pi-tool-display/src/tool-call-box.ts +181 -0
- package/dist/extensions/pi-tool-display/src/tool-metadata.ts +139 -0
- package/dist/extensions/pi-tool-display/src/tool-overrides.ts +2168 -0
- package/dist/extensions/pi-tool-display/src/types.ts +109 -0
- package/dist/extensions/pi-tool-display/src/user-message-box-markdown.ts +64 -0
- package/dist/extensions/pi-tool-display/src/user-message-box-native.ts +66 -0
- package/dist/extensions/pi-tool-display/src/user-message-box-patch.ts +72 -0
- package/dist/extensions/pi-tool-display/src/user-message-box-renderer.ts +419 -0
- package/dist/extensions/pi-tool-display/src/user-message-box-utils.ts +106 -0
- package/dist/extensions/pi-tool-display/src/write-display-utils.ts +31 -0
- package/dist/extensions/pi-tool-display/src/zellij-modal.ts +1027 -0
- package/dist/extensions/pi-tool-display/tests/active-backlog-red.test.ts +65 -0
- package/dist/extensions/pi-tool-display/tests/ansi-utils.test.ts +345 -0
- package/dist/extensions/pi-tool-display/tests/bash-display.test.ts +442 -0
- package/dist/extensions/pi-tool-display/tests/capabilities-edge.test.ts +360 -0
- package/dist/extensions/pi-tool-display/tests/capabilities.test.ts +54 -0
- package/dist/extensions/pi-tool-display/tests/config-modal.test.ts +360 -0
- package/dist/extensions/pi-tool-display/tests/config-store.test.ts +87 -0
- package/dist/extensions/pi-tool-display/tests/custom-tool-overrides.test.ts +415 -0
- package/dist/extensions/pi-tool-display/tests/debug-logger-edge.test.ts +630 -0
- package/dist/extensions/pi-tool-display/tests/debug-logger.test.ts +62 -0
- package/dist/extensions/pi-tool-display/tests/diff-renderer-ansi.test.ts +55 -0
- package/dist/extensions/pi-tool-display/tests/diff-renderer-edge.test.ts +886 -0
- package/dist/extensions/pi-tool-display/tests/diff-renderer-numbering.test.ts +135 -0
- package/dist/extensions/pi-tool-display/tests/diff-renderer-width.test.ts +454 -0
- package/dist/extensions/pi-tool-display/tests/diff-renderer-wrap.test.ts +169 -0
- package/dist/extensions/pi-tool-display/tests/index-integration.test.ts +354 -0
- package/dist/extensions/pi-tool-display/tests/pending-diff-preview.test.ts +79 -0
- package/dist/extensions/pi-tool-display/tests/presets-edge.test.ts +229 -0
- package/dist/extensions/pi-tool-display/tests/presets.test.ts +26 -0
- package/dist/extensions/pi-tool-display/tests/reload-behavior.test.ts +1241 -0
- package/dist/extensions/pi-tool-display/tests/render-utils.test.ts +710 -0
- package/dist/extensions/pi-tool-display/tests/settings-inspector.test.ts +450 -0
- package/dist/extensions/pi-tool-display/tests/thinking-label-edge.test.ts +904 -0
- package/dist/extensions/pi-tool-display/tests/thinking-label.test.ts +72 -0
- package/dist/extensions/pi-tool-display/tests/tool-call-box.test.ts +105 -0
- package/dist/extensions/pi-tool-display/tests/tool-overrides-config.test.ts +613 -0
- package/dist/extensions/pi-tool-display/tests/tool-overrides-mcp.test.ts +499 -0
- package/dist/extensions/pi-tool-display/tests/tool-overrides-registration.test.ts +315 -0
- package/dist/extensions/pi-tool-display/tests/tool-overrides-write-meta.test.ts +68 -0
- package/dist/extensions/pi-tool-display/tests/tool-ui-utils.test.ts +413 -0
- package/dist/extensions/pi-tool-display/tests/user-message-box-performance-red.test.ts +176 -0
- package/dist/extensions/pi-tool-display/tests/user-message-box.test.ts +908 -0
- package/dist/extensions/pi-tool-display/tests/zellij-modal.test.ts +848 -0
- package/dist/extensions/pi-tool-display/tool-display-api-consumer.d.ts +30 -0
- package/dist/extensions/pi-tool-display/tool-display-api-consumer.js +40 -0
- package/dist/extensions/pi-zentui/extensions/zentui/format.ts +46 -1
- package/dist/extensions/pi-zentui/extensions/zentui/index.ts +10 -0
- package/dist/extensions/pi-zentui/extensions/zentui/user-message-styles.ts +1 -1
- package/dist/extensions/pi-zentui/test/extension-compliance.test.ts +3 -3
- package/dist/extensions/pi-zentui/test/format.test.ts +50 -2
- package/dist/modes/interactive/components/startup-box.d.ts +38 -0
- package/dist/modes/interactive/components/startup-box.js +90 -0
- package/dist/modes/interactive/interactive-mode.d.ts +3 -0
- package/dist/modes/interactive/interactive-mode.js +85 -122
- package/dist/skills/agent-browser/SKILL.md +1 -0
- package/dist/skills/batch-grill-me/SKILL.md +1 -0
- package/dist/skills/brandkit/SKILL.md +1 -0
- package/dist/skills/design-references/SKILL.md +1 -0
- package/dist/skills/design-taste-frontend/SKILL.md +1 -0
- package/dist/skills/design-taste-frontend-v1/SKILL.md +1 -0
- package/dist/skills/full-output-enforcement/SKILL.md +1 -0
- package/dist/skills/gpt-taste/SKILL.md +1 -0
- package/dist/skills/grill-me/SKILL.md +1 -0
- package/dist/skills/handoff/SKILL.md +1 -0
- package/dist/skills/handoff-text/SKILL.md +1 -0
- package/dist/skills/high-end-visual-design/SKILL.md +1 -0
- package/dist/skills/image-to-code/SKILL.md +1 -0
- package/dist/skills/imagegen-frontend-mobile/SKILL.md +1 -0
- package/dist/skills/imagegen-frontend-web/SKILL.md +1 -0
- package/dist/skills/implanger/SKILL.md +1 -0
- package/dist/skills/improve-codebase/SKILL.md +1 -0
- package/dist/skills/industrial-brutalist-ui/SKILL.md +1 -0
- package/dist/skills/minimalist-ui/SKILL.md +1 -0
- package/dist/skills/pi-subagents/SKILL.md +1 -0
- package/dist/skills/planger/SKILL.md +1 -0
- package/dist/skills/ponytail/SKILL.md +1 -0
- package/dist/skills/ponytail-audit/SKILL.md +1 -0
- package/dist/skills/ponytail-debt/SKILL.md +1 -0
- package/dist/skills/ponytail-gain/SKILL.md +1 -0
- package/dist/skills/ponytail-help/SKILL.md +1 -0
- package/dist/skills/ponytail-review/SKILL.md +1 -0
- package/dist/skills/redesign-existing-projects/SKILL.md +1 -0
- package/dist/skills/selesai-handoff/SKILL.md +1 -0
- package/dist/skills/stitch-design-taste/SKILL.md +1 -0
- package/dist/skills/unlazy/SKILL.md +1 -0
- package/dist/skills/web-design-guidelines/SKILL.md +1 -0
- package/dist/skills/workflow/SKILL.md +1 -0
- package/package.json +3 -3
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { cloneCustomToolOverrides } from "./config-store.js";
|
|
2
|
+
import { DEFAULT_TOOL_DISPLAY_CONFIG, type CustomToolOverrideConfig, type ToolDisplayConfig } from "./types.js";
|
|
3
|
+
|
|
4
|
+
export const TOOL_DISPLAY_PRESETS = ["opencode", "balanced", "verbose", "boxed"] as const;
|
|
5
|
+
export type ToolDisplayPreset = (typeof TOOL_DISPLAY_PRESETS)[number];
|
|
6
|
+
|
|
7
|
+
const TOOL_DISPLAY_PRESET_CONFIGS: Record<ToolDisplayPreset, ToolDisplayConfig> = {
|
|
8
|
+
opencode: {
|
|
9
|
+
...DEFAULT_TOOL_DISPLAY_CONFIG,
|
|
10
|
+
registerToolOverrides: { ...DEFAULT_TOOL_DISPLAY_CONFIG.registerToolOverrides },
|
|
11
|
+
boxedToolCalls: false,
|
|
12
|
+
},
|
|
13
|
+
balanced: {
|
|
14
|
+
...DEFAULT_TOOL_DISPLAY_CONFIG,
|
|
15
|
+
registerToolOverrides: { ...DEFAULT_TOOL_DISPLAY_CONFIG.registerToolOverrides },
|
|
16
|
+
boxedToolCalls: false,
|
|
17
|
+
readOutputMode: "summary",
|
|
18
|
+
searchOutputMode: "count",
|
|
19
|
+
mcpOutputMode: "summary",
|
|
20
|
+
bashOutputMode: "summary",
|
|
21
|
+
},
|
|
22
|
+
verbose: {
|
|
23
|
+
...DEFAULT_TOOL_DISPLAY_CONFIG,
|
|
24
|
+
registerToolOverrides: { ...DEFAULT_TOOL_DISPLAY_CONFIG.registerToolOverrides },
|
|
25
|
+
boxedToolCalls: false,
|
|
26
|
+
readOutputMode: "preview",
|
|
27
|
+
searchOutputMode: "preview",
|
|
28
|
+
mcpOutputMode: "preview",
|
|
29
|
+
bashOutputMode: "preview",
|
|
30
|
+
previewLines: 12,
|
|
31
|
+
bashCollapsedLines: 20,
|
|
32
|
+
},
|
|
33
|
+
boxed: {
|
|
34
|
+
...DEFAULT_TOOL_DISPLAY_CONFIG,
|
|
35
|
+
registerToolOverrides: { ...DEFAULT_TOOL_DISPLAY_CONFIG.registerToolOverrides },
|
|
36
|
+
boxedToolCalls: true,
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
function toolOverrideOwnershipEqual(a: ToolDisplayConfig, b: ToolDisplayConfig): boolean {
|
|
41
|
+
return (
|
|
42
|
+
a.registerToolOverrides.read === b.registerToolOverrides.read &&
|
|
43
|
+
a.registerToolOverrides.grep === b.registerToolOverrides.grep &&
|
|
44
|
+
a.registerToolOverrides.find === b.registerToolOverrides.find &&
|
|
45
|
+
a.registerToolOverrides.ls === b.registerToolOverrides.ls &&
|
|
46
|
+
a.registerToolOverrides.bash === b.registerToolOverrides.bash &&
|
|
47
|
+
a.registerToolOverrides.edit === b.registerToolOverrides.edit &&
|
|
48
|
+
a.registerToolOverrides.write === b.registerToolOverrides.write
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function customToolOverridesEqual(a: ToolDisplayConfig, b: ToolDisplayConfig): boolean {
|
|
53
|
+
const aEntries = Object.entries(a.customToolOverrides).sort(([left], [right]) => left.localeCompare(right));
|
|
54
|
+
const bEntries = Object.entries(b.customToolOverrides).sort(([left], [right]) => left.localeCompare(right));
|
|
55
|
+
if (aEntries.length !== bEntries.length) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return aEntries.every(([toolName, override], index) => {
|
|
60
|
+
const [otherToolName, otherOverride] = bEntries[index];
|
|
61
|
+
return (
|
|
62
|
+
toolName === otherToolName &&
|
|
63
|
+
override.enabled === otherOverride.enabled &&
|
|
64
|
+
override.kind === otherOverride.kind &&
|
|
65
|
+
override.outputMode === otherOverride.outputMode
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function configsEqual(a: ToolDisplayConfig, b: ToolDisplayConfig): boolean {
|
|
71
|
+
return (
|
|
72
|
+
toolOverrideOwnershipEqual(a, b) &&
|
|
73
|
+
customToolOverridesEqual(a, b) &&
|
|
74
|
+
a.enableNativeUserMessageBox === b.enableNativeUserMessageBox &&
|
|
75
|
+
a.readOutputMode === b.readOutputMode &&
|
|
76
|
+
a.searchOutputMode === b.searchOutputMode &&
|
|
77
|
+
a.mcpOutputMode === b.mcpOutputMode &&
|
|
78
|
+
a.previewLines === b.previewLines &&
|
|
79
|
+
a.expandedPreviewMaxLines === b.expandedPreviewMaxLines &&
|
|
80
|
+
a.bashOutputMode === b.bashOutputMode &&
|
|
81
|
+
a.bashCollapsedLines === b.bashCollapsedLines &&
|
|
82
|
+
a.diffViewMode === b.diffViewMode &&
|
|
83
|
+
a.diffIndicatorMode === b.diffIndicatorMode &&
|
|
84
|
+
a.diffSplitMinWidth === b.diffSplitMinWidth &&
|
|
85
|
+
a.diffCollapsedLines === b.diffCollapsedLines &&
|
|
86
|
+
a.diffWordWrap === b.diffWordWrap &&
|
|
87
|
+
a.showTruncationHints === b.showTruncationHints &&
|
|
88
|
+
a.showRtkCompactionHints === b.showRtkCompactionHints &&
|
|
89
|
+
a.boxedToolCalls === b.boxedToolCalls
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function getToolDisplayPresetConfig(preset: ToolDisplayPreset): ToolDisplayConfig {
|
|
94
|
+
const config = TOOL_DISPLAY_PRESET_CONFIGS[preset];
|
|
95
|
+
return {
|
|
96
|
+
...config,
|
|
97
|
+
registerToolOverrides: { ...config.registerToolOverrides },
|
|
98
|
+
customToolOverrides: cloneCustomToolOverrides(config.customToolOverrides),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function detectToolDisplayPreset(config: ToolDisplayConfig): ToolDisplayPreset | "custom" {
|
|
103
|
+
for (const preset of TOOL_DISPLAY_PRESETS) {
|
|
104
|
+
if (configsEqual(config, TOOL_DISPLAY_PRESET_CONFIGS[preset])) {
|
|
105
|
+
return preset;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return "custom";
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function parseToolDisplayPreset(raw: string): ToolDisplayPreset | undefined {
|
|
112
|
+
const normalized = raw.trim().toLowerCase();
|
|
113
|
+
if (!normalized) {
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
return TOOL_DISPLAY_PRESETS.find((preset) => preset === normalized);
|
|
117
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { sanitizeAnsiForThemedOutput } from "./ansi-utils.js";
|
|
3
|
+
|
|
4
|
+
export { sanitizeAnsiForThemedOutput };
|
|
5
|
+
|
|
6
|
+
interface TextLikeContent {
|
|
7
|
+
type: string;
|
|
8
|
+
text?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface ToolResultLike {
|
|
12
|
+
content?: unknown;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const QUIET_COMMAND_PREFIXES = [
|
|
16
|
+
"cd",
|
|
17
|
+
"mkdir",
|
|
18
|
+
"rmdir",
|
|
19
|
+
"rm",
|
|
20
|
+
"mv",
|
|
21
|
+
"cp",
|
|
22
|
+
"touch",
|
|
23
|
+
"chmod",
|
|
24
|
+
"chown",
|
|
25
|
+
"git add",
|
|
26
|
+
"git checkout",
|
|
27
|
+
"git switch",
|
|
28
|
+
"git restore",
|
|
29
|
+
"git reset",
|
|
30
|
+
"git clean",
|
|
31
|
+
"npm install",
|
|
32
|
+
"pnpm install",
|
|
33
|
+
"yarn install",
|
|
34
|
+
"bun install",
|
|
35
|
+
"pip install",
|
|
36
|
+
"poetry install",
|
|
37
|
+
"cargo fetch",
|
|
38
|
+
"go mod tidy",
|
|
39
|
+
"Set-Location",
|
|
40
|
+
"New-Item",
|
|
41
|
+
"Remove-Item",
|
|
42
|
+
"Move-Item",
|
|
43
|
+
"Copy-Item",
|
|
44
|
+
] as const;
|
|
45
|
+
|
|
46
|
+
interface CompactOutputOptions {
|
|
47
|
+
expanded: boolean;
|
|
48
|
+
maxCollapsedConsecutiveEmptyLines?: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function trimTrailingEmptyLines(lines: string[]): string[] {
|
|
52
|
+
const next = [...lines];
|
|
53
|
+
while (next.length > 0 && next[next.length - 1]?.trim().length === 0) {
|
|
54
|
+
next.pop();
|
|
55
|
+
}
|
|
56
|
+
return next;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function collapseConsecutiveEmptyLines(
|
|
60
|
+
lines: string[],
|
|
61
|
+
maxConsecutiveEmptyLines: number,
|
|
62
|
+
): string[] {
|
|
63
|
+
const maxAllowed = Math.max(0, maxConsecutiveEmptyLines);
|
|
64
|
+
if (maxAllowed === 0) {
|
|
65
|
+
return lines.filter((line) => line.trim().length > 0);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const compacted: string[] = [];
|
|
69
|
+
let consecutiveEmpty = 0;
|
|
70
|
+
|
|
71
|
+
for (const line of lines) {
|
|
72
|
+
if (line.trim().length === 0) {
|
|
73
|
+
consecutiveEmpty++;
|
|
74
|
+
if (consecutiveEmpty > maxAllowed) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
} else {
|
|
78
|
+
consecutiveEmpty = 0;
|
|
79
|
+
}
|
|
80
|
+
compacted.push(line);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return compacted;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function shortenPath(inputPath: string | undefined): string {
|
|
87
|
+
if (!inputPath) {
|
|
88
|
+
return "";
|
|
89
|
+
}
|
|
90
|
+
const home = homedir();
|
|
91
|
+
return inputPath.startsWith(home)
|
|
92
|
+
? `~${inputPath.slice(home.length)}`
|
|
93
|
+
: inputPath;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function extractTextOutput(result: ToolResultLike): string {
|
|
97
|
+
const rawBlocks = Array.isArray(result.content) ? result.content : [];
|
|
98
|
+
const blocks = rawBlocks.filter(
|
|
99
|
+
(block): block is TextLikeContent =>
|
|
100
|
+
typeof block === "object" &&
|
|
101
|
+
block !== null &&
|
|
102
|
+
"type" in block &&
|
|
103
|
+
(block as TextLikeContent).type === "text" &&
|
|
104
|
+
typeof (block as TextLikeContent).text === "string",
|
|
105
|
+
);
|
|
106
|
+
return blocks.map((block) => block.text ?? "").join("\n");
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function splitLines(text: string): string[] {
|
|
110
|
+
if (!text) {
|
|
111
|
+
return [];
|
|
112
|
+
}
|
|
113
|
+
return text
|
|
114
|
+
.replace(/\r/g, "")
|
|
115
|
+
.split("\n")
|
|
116
|
+
.map((line) => line.replace(/\t/g, " "));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function compactOutputLines(
|
|
120
|
+
lines: string[],
|
|
121
|
+
options: CompactOutputOptions,
|
|
122
|
+
): string[] {
|
|
123
|
+
const trimmed = trimTrailingEmptyLines(lines);
|
|
124
|
+
if (options.expanded) {
|
|
125
|
+
return trimmed;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return collapseConsecutiveEmptyLines(
|
|
129
|
+
trimmed,
|
|
130
|
+
options.maxCollapsedConsecutiveEmptyLines ?? 1,
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function isLikelyQuietCommand(command: string | undefined): boolean {
|
|
135
|
+
if (!command) {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const normalized = command.trim().toLowerCase();
|
|
140
|
+
if (!normalized) {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const primarySegment = normalized
|
|
145
|
+
.split(/&&|\|\||;/)
|
|
146
|
+
.map((segment) => segment.trim())
|
|
147
|
+
.find((segment) => segment.length > 0);
|
|
148
|
+
|
|
149
|
+
if (!primarySegment) {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
for (const prefix of QUIET_COMMAND_PREFIXES) {
|
|
154
|
+
const normalizedPrefix = prefix.toLowerCase();
|
|
155
|
+
if (
|
|
156
|
+
primarySegment === normalizedPrefix ||
|
|
157
|
+
primarySegment.startsWith(`${normalizedPrefix} `)
|
|
158
|
+
) {
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function countNonEmptyLines(lines: string[]): number {
|
|
167
|
+
return lines.filter((line) => line.trim().length > 0).length;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function pluralize(
|
|
171
|
+
count: number,
|
|
172
|
+
singular: string,
|
|
173
|
+
plural = `${singular}s`,
|
|
174
|
+
): string {
|
|
175
|
+
return count === 1 ? singular : plural;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function previewLines(
|
|
179
|
+
lines: string[],
|
|
180
|
+
maxLines: number,
|
|
181
|
+
): { shown: string[]; remaining: number } {
|
|
182
|
+
const limit = Math.max(0, maxLines);
|
|
183
|
+
const shown = lines.slice(0, limit);
|
|
184
|
+
const remaining = Math.max(0, lines.length - shown.length);
|
|
185
|
+
return { shown, remaining };
|
|
186
|
+
}
|