agent-sh 0.14.8 → 0.14.10
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/agent/agent-loop.d.ts +0 -4
- package/dist/agent/agent-loop.js +8 -166
- package/dist/agent/entry-format.d.ts +5 -0
- package/dist/agent/entry-format.js +9 -0
- package/dist/agent/extensions/rolling-history/constants.d.ts +1 -0
- package/dist/agent/extensions/rolling-history/constants.js +1 -0
- package/dist/agent/extensions/rolling-history/index.d.ts +4 -0
- package/dist/agent/extensions/rolling-history/index.js +203 -0
- package/dist/agent/extensions/rolling-history/recall.d.ts +4 -0
- package/dist/agent/extensions/rolling-history/recall.js +122 -0
- package/dist/agent/extensions/rolling-history/strategy.d.ts +70 -0
- package/dist/agent/extensions/rolling-history/strategy.js +336 -0
- package/dist/agent/host-types.d.ts +0 -3
- package/dist/agent/index.js +50 -5
- package/dist/agent/live-view.d.ts +57 -0
- package/dist/agent/live-view.js +238 -0
- package/dist/agent/llm-client.d.ts +1 -0
- package/dist/agent/llm-client.js +1 -1
- package/dist/agent/providers/ollama.d.ts +11 -0
- package/dist/agent/providers/ollama.js +72 -0
- package/dist/agent/providers/opencode.d.ts +10 -0
- package/dist/agent/providers/opencode.js +112 -0
- package/dist/agent/providers/zai-coding-plan.d.ts +5 -0
- package/dist/agent/providers/zai-coding-plan.js +26 -0
- package/dist/agent/session-store.d.ts +90 -0
- package/dist/agent/session-store.js +288 -0
- package/dist/agent/store.d.ts +74 -0
- package/dist/agent/store.js +284 -0
- package/dist/agent/subagent.js +2 -2
- package/dist/agent/tool-protocol.d.ts +11 -11
- package/dist/cli/args.js +2 -2
- package/dist/cli/index.js +4 -2
- package/dist/core/index.d.ts +0 -1
- package/dist/core/index.js +0 -1
- package/dist/core/settings.d.ts +5 -1
- package/dist/core/settings.js +62 -1
- package/dist/extensions/index.d.ts +1 -0
- package/dist/shell/events.d.ts +1 -0
- package/dist/shell/input-handler.js +4 -0
- package/dist/shell/tui-renderer.js +5 -2
- package/dist/utils/diff-renderer.js +9 -7
- package/examples/extensions/ads/index.ts +695 -0
- package/examples/extensions/ash-acp-bridge/src/index.ts +1 -2
- package/examples/extensions/ash-scheme/index.ts +77 -3
- package/examples/extensions/ashi/package.json +2 -2
- package/examples/extensions/ashi/src/capture.ts +1 -1
- package/examples/extensions/ashi/src/cli.ts +5 -6
- package/examples/extensions/ashi/src/compaction.ts +6 -2
- package/examples/extensions/ashi/src/frontend.ts +13 -13
- package/examples/extensions/ashi/src/multi-session-store.ts +35 -12
- package/examples/extensions/ashi/src/session-commands.ts +1 -1
- package/examples/extensions/ashi/src/user-shell-intents.ts +17 -0
- package/package.json +13 -1
- package/dist/agent/conversation-state.d.ts +0 -142
- package/dist/agent/conversation-state.js +0 -788
- package/dist/agent/history-file.d.ts +0 -81
- package/dist/agent/history-file.js +0 -271
- package/examples/extensions/ashi/src/session-store.ts +0 -363
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import type { ChatCompletionTool } from "./llm-client.js";
|
|
13
13
|
import { type ToolDefinition } from "./types.js";
|
|
14
|
-
import type {
|
|
14
|
+
import type { LiveView } from "./live-view.js";
|
|
15
15
|
export interface PendingToolCall {
|
|
16
16
|
id: string;
|
|
17
17
|
name: string;
|
|
@@ -39,9 +39,9 @@ export interface ToolProtocol {
|
|
|
39
39
|
/** Rewrite a tool call before execution (e.g., unwrap meta-tool). */
|
|
40
40
|
rewriteToolCall(tc: PendingToolCall): PendingToolCall;
|
|
41
41
|
/** Record the assistant turn in conversation state. */
|
|
42
|
-
recordAssistant(conv:
|
|
42
|
+
recordAssistant(conv: LiveView, text: string, toolCalls: PendingToolCall[], extras?: Record<string, unknown>): void;
|
|
43
43
|
/** Record all tool results for a batch as conversation messages. */
|
|
44
|
-
recordResults(conv:
|
|
44
|
+
recordResults(conv: LiveView, results: ToolResult[]): void;
|
|
45
45
|
/** Create a stream filter for stripping tool calls from display. null = pass-through. */
|
|
46
46
|
createStreamFilter(toolNames: string[]): StreamFilter | null;
|
|
47
47
|
/**
|
|
@@ -57,8 +57,8 @@ export declare class ApiToolProtocol implements ToolProtocol {
|
|
|
57
57
|
getToolPrompt(): string;
|
|
58
58
|
extractToolCalls(_text: string, streamedCalls: PendingToolCall[]): PendingToolCall[];
|
|
59
59
|
rewriteToolCall(tc: PendingToolCall): PendingToolCall;
|
|
60
|
-
recordAssistant(conv:
|
|
61
|
-
recordResults(conv:
|
|
60
|
+
recordAssistant(conv: LiveView, text: string, toolCalls: PendingToolCall[], extras?: Record<string, unknown>): void;
|
|
61
|
+
recordResults(conv: LiveView, results: ToolResult[]): void;
|
|
62
62
|
createStreamFilter(): null;
|
|
63
63
|
}
|
|
64
64
|
export declare class InlineToolProtocol implements ToolProtocol {
|
|
@@ -68,8 +68,8 @@ export declare class InlineToolProtocol implements ToolProtocol {
|
|
|
68
68
|
getToolPrompt(tools: ToolDefinition[]): string;
|
|
69
69
|
rewriteToolCall(tc: PendingToolCall): PendingToolCall;
|
|
70
70
|
extractToolCalls(text: string, _streamedCalls: PendingToolCall[]): PendingToolCall[];
|
|
71
|
-
recordAssistant(conv:
|
|
72
|
-
recordResults(conv:
|
|
71
|
+
recordAssistant(conv: LiveView, text: string, _toolCalls: PendingToolCall[], extras?: Record<string, unknown>): void;
|
|
72
|
+
recordResults(conv: LiveView, results: ToolResult[]): void;
|
|
73
73
|
createStreamFilter(_toolNames: string[]): StreamFilter;
|
|
74
74
|
}
|
|
75
75
|
export declare class DeferredToolProtocol implements ToolProtocol {
|
|
@@ -82,8 +82,8 @@ export declare class DeferredToolProtocol implements ToolProtocol {
|
|
|
82
82
|
getToolPrompt(): string;
|
|
83
83
|
extractToolCalls(_text: string, streamedCalls: PendingToolCall[]): PendingToolCall[];
|
|
84
84
|
rewriteToolCall(tc: PendingToolCall): PendingToolCall;
|
|
85
|
-
recordAssistant(conv:
|
|
86
|
-
recordResults(conv:
|
|
85
|
+
recordAssistant(conv: LiveView, text: string, toolCalls: PendingToolCall[], extras?: Record<string, unknown>): void;
|
|
86
|
+
recordResults(conv: LiveView, results: ToolResult[]): void;
|
|
87
87
|
createStreamFilter(): null;
|
|
88
88
|
}
|
|
89
89
|
export declare class DeferredLookupProtocol implements ToolProtocol {
|
|
@@ -97,8 +97,8 @@ export declare class DeferredLookupProtocol implements ToolProtocol {
|
|
|
97
97
|
getToolPrompt(): string;
|
|
98
98
|
extractToolCalls(_text: string, streamedCalls: PendingToolCall[]): PendingToolCall[];
|
|
99
99
|
rewriteToolCall(tc: PendingToolCall): PendingToolCall;
|
|
100
|
-
recordAssistant(conv:
|
|
101
|
-
recordResults(conv:
|
|
100
|
+
recordAssistant(conv: LiveView, text: string, toolCalls: PendingToolCall[], extras?: Record<string, unknown>): void;
|
|
101
|
+
recordResults(conv: LiveView, results: ToolResult[]): void;
|
|
102
102
|
createStreamFilter(): null;
|
|
103
103
|
getProtocolTools(): ToolDefinition[];
|
|
104
104
|
}
|
package/dist/cli/args.js
CHANGED
|
@@ -53,8 +53,8 @@ export function parseArgs(argv, env = process.env) {
|
|
|
53
53
|
let provider;
|
|
54
54
|
let backend;
|
|
55
55
|
let shell = env.SHELL || "/bin/bash";
|
|
56
|
-
let apiKey
|
|
57
|
-
let baseURL
|
|
56
|
+
let apiKey;
|
|
57
|
+
let baseURL;
|
|
58
58
|
for (let i = 0; i < argv.length; i++) {
|
|
59
59
|
const arg = argv[i];
|
|
60
60
|
if (arg === "--model" && argv[i + 1]) {
|
package/dist/cli/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import { activateAgent } from "../agent/index.js";
|
|
|
4
4
|
import { createCore } from "../core/index.js";
|
|
5
5
|
import { palette as p } from "../utils/palette.js";
|
|
6
6
|
import { loadBuiltinExtensions } from "../extensions/index.js";
|
|
7
|
+
import activateRollingHistory from "../agent/extensions/rolling-history/index.js";
|
|
7
8
|
import { loadExtensions } from "../core/extension-loader.js";
|
|
8
9
|
import { getSettings } from "../core/settings.js";
|
|
9
10
|
import { dispatchSubcommand } from "./subcommands.js";
|
|
@@ -95,7 +96,9 @@ async function main() {
|
|
|
95
96
|
registerShellHandlers(extCtx);
|
|
96
97
|
activateAgent(extCtx);
|
|
97
98
|
// Load before spawning the shell so PS1 lands below the banner.
|
|
98
|
-
|
|
99
|
+
const settings = getSettings();
|
|
100
|
+
await loadBuiltinExtensions(extCtx, settings.disabledBuiltins);
|
|
101
|
+
activateRollingHistory(extCtx);
|
|
99
102
|
const loadExtensionsTimeoutMs = 10000;
|
|
100
103
|
let loadedExtensions = [];
|
|
101
104
|
await Promise.race([
|
|
@@ -124,7 +127,6 @@ async function main() {
|
|
|
124
127
|
hint);
|
|
125
128
|
process.exit(1);
|
|
126
129
|
}
|
|
127
|
-
const settings = getSettings();
|
|
128
130
|
if (settings.startupBanner !== false) {
|
|
129
131
|
const termW = process.stdout.columns || 80;
|
|
130
132
|
const bannerW = Math.min(termW, 60);
|
package/dist/core/index.d.ts
CHANGED
|
@@ -20,7 +20,6 @@ export type { ColorPalette } from "../utils/palette.js";
|
|
|
20
20
|
export type { AgentBackend, ToolDefinition, ImageContent } from "../agent/types.js";
|
|
21
21
|
export { runSubagent, type SubagentOptions } from "../agent/subagent.js";
|
|
22
22
|
export { LlmClient } from "../agent/llm-client.js";
|
|
23
|
-
export { HistoryFile, InMemoryHistory, NoopHistory, type HistoryAdapter } from "../agent/history-file.js";
|
|
24
23
|
export type { NuclearEntry } from "../agent/nuclear-form.js";
|
|
25
24
|
export { compileSearchRegex, matchEntry, formatNuclearLine } from "../agent/nuclear-form.js";
|
|
26
25
|
export interface AgentShellCore {
|
package/dist/core/index.js
CHANGED
|
@@ -19,7 +19,6 @@ export { EventBus } from "./event-bus.js";
|
|
|
19
19
|
export { palette, setPalette, resetPalette } from "../utils/palette.js";
|
|
20
20
|
export { runSubagent } from "../agent/subagent.js";
|
|
21
21
|
export { LlmClient } from "../agent/llm-client.js";
|
|
22
|
-
export { HistoryFile, InMemoryHistory, NoopHistory } from "../agent/history-file.js";
|
|
23
22
|
export { compileSearchRegex, matchEntry, formatNuclearLine } from "../agent/nuclear-form.js";
|
|
24
23
|
export function createCore(config) {
|
|
25
24
|
const bus = new EventBus();
|
package/dist/core/settings.d.ts
CHANGED
|
@@ -65,7 +65,7 @@ export interface Settings {
|
|
|
65
65
|
* that boot agent-sh as a library against a specific working tree).
|
|
66
66
|
*/
|
|
67
67
|
historyFilePath?: string;
|
|
68
|
-
|
|
68
|
+
autoCompact?: boolean;
|
|
69
69
|
autoCompactThreshold?: number;
|
|
70
70
|
/** Max command output lines shown inline in TUI. */
|
|
71
71
|
maxCommandOutputLines?: number;
|
|
@@ -116,8 +116,12 @@ export interface Settings {
|
|
|
116
116
|
disabledExtensions?: string[];
|
|
117
117
|
}
|
|
118
118
|
declare const DEFAULTS: Required<Settings>;
|
|
119
|
+
export type SettingSource = "session" | "env" | "file" | "default";
|
|
119
120
|
/** Load settings from disk (cached after first call). */
|
|
120
121
|
export declare function getSettings(): Settings & typeof DEFAULTS;
|
|
122
|
+
export declare function setSessionOverlay(patch: Partial<Settings>): void;
|
|
123
|
+
export declare function clearSessionOverlay(...keys: (keyof Settings)[]): void;
|
|
124
|
+
export declare function getSettingSource(key: keyof Settings): SettingSource;
|
|
121
125
|
/**
|
|
122
126
|
* Get settings for an extension, namespaced under its key in settings.json.
|
|
123
127
|
*
|
package/dist/core/settings.js
CHANGED
|
@@ -28,6 +28,7 @@ const DEFAULTS = {
|
|
|
28
28
|
historyMaxBytes: 104857600, // 100MB — history is only accessed via search/expand, never loaded wholesale
|
|
29
29
|
historyStartupEntries: 100,
|
|
30
30
|
historyFilePath: undefined,
|
|
31
|
+
autoCompact: true,
|
|
31
32
|
autoCompactThreshold: 0.5,
|
|
32
33
|
maxCommandOutputLines: 3,
|
|
33
34
|
readOutputMaxLines: 10,
|
|
@@ -41,6 +42,42 @@ const DEFAULTS = {
|
|
|
41
42
|
disabledExtensions: [],
|
|
42
43
|
};
|
|
43
44
|
let cached = null;
|
|
45
|
+
let envOverrides = null;
|
|
46
|
+
let sessionOverlay = {};
|
|
47
|
+
function parseBoolEnv(raw, key) {
|
|
48
|
+
if (raw === undefined)
|
|
49
|
+
return undefined;
|
|
50
|
+
const v = raw.trim().toLowerCase();
|
|
51
|
+
if (v === "on" || v === "true" || v === "1")
|
|
52
|
+
return true;
|
|
53
|
+
if (v === "off" || v === "false" || v === "0")
|
|
54
|
+
return false;
|
|
55
|
+
console.error(`[agent-sh] Warning: ${key}="${raw}" is not a boolean (off|on|true|false|0|1); ignoring.`);
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
function parseUnitFloatEnv(raw, key) {
|
|
59
|
+
if (raw === undefined)
|
|
60
|
+
return undefined;
|
|
61
|
+
const n = Number(raw);
|
|
62
|
+
if (!Number.isFinite(n) || n < 0 || n > 1) {
|
|
63
|
+
console.error(`[agent-sh] Warning: ${key}="${raw}" is not a number in [0, 1]; ignoring.`);
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
return n;
|
|
67
|
+
}
|
|
68
|
+
function loadEnvOverrides() {
|
|
69
|
+
if (envOverrides)
|
|
70
|
+
return envOverrides;
|
|
71
|
+
const out = {};
|
|
72
|
+
const ac = parseBoolEnv(process.env.AGENT_SH_AUTO_COMPACT, "AGENT_SH_AUTO_COMPACT");
|
|
73
|
+
if (ac !== undefined)
|
|
74
|
+
out.autoCompact = ac;
|
|
75
|
+
const th = parseUnitFloatEnv(process.env.AGENT_SH_AUTO_COMPACT_THRESHOLD, "AGENT_SH_AUTO_COMPACT_THRESHOLD");
|
|
76
|
+
if (th !== undefined)
|
|
77
|
+
out.autoCompactThreshold = th;
|
|
78
|
+
envOverrides = out;
|
|
79
|
+
return envOverrides;
|
|
80
|
+
}
|
|
44
81
|
/** Load settings from disk (cached after first call). */
|
|
45
82
|
export function getSettings() {
|
|
46
83
|
if (!cached) {
|
|
@@ -55,7 +92,29 @@ export function getSettings() {
|
|
|
55
92
|
cached = {};
|
|
56
93
|
}
|
|
57
94
|
}
|
|
58
|
-
return { ...DEFAULTS, ...cached };
|
|
95
|
+
return { ...DEFAULTS, ...cached, ...loadEnvOverrides(), ...sessionOverlay };
|
|
96
|
+
}
|
|
97
|
+
export function setSessionOverlay(patch) {
|
|
98
|
+
sessionOverlay = { ...sessionOverlay, ...patch };
|
|
99
|
+
}
|
|
100
|
+
export function clearSessionOverlay(...keys) {
|
|
101
|
+
if (keys.length === 0) {
|
|
102
|
+
sessionOverlay = {};
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const next = { ...sessionOverlay };
|
|
106
|
+
for (const k of keys)
|
|
107
|
+
delete next[k];
|
|
108
|
+
sessionOverlay = next;
|
|
109
|
+
}
|
|
110
|
+
export function getSettingSource(key) {
|
|
111
|
+
if (key in sessionOverlay)
|
|
112
|
+
return "session";
|
|
113
|
+
if (key in loadEnvOverrides())
|
|
114
|
+
return "env";
|
|
115
|
+
if (cached && key in cached)
|
|
116
|
+
return "file";
|
|
117
|
+
return "default";
|
|
59
118
|
}
|
|
60
119
|
/**
|
|
61
120
|
* Get settings for an extension, namespaced under its key in settings.json.
|
|
@@ -78,6 +137,8 @@ export function getExtensionSettings(namespace, defaults) {
|
|
|
78
137
|
/** Reset cached settings (for testing or after external edit). */
|
|
79
138
|
export function reloadSettings() {
|
|
80
139
|
cached = null;
|
|
140
|
+
envOverrides = null;
|
|
141
|
+
sessionOverlay = {};
|
|
81
142
|
}
|
|
82
143
|
/**
|
|
83
144
|
* Deep-merge a patch into ~/.agent-sh/settings.json on disk.
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Module-owned built-ins activate inline:
|
|
4
4
|
* shell-context, tui-renderer → registerShellHandlers (src/shell/)
|
|
5
5
|
* ash (a specific backend) → activateAgent (src/agent/)
|
|
6
|
+
* rolling-history → activateRollingHistory (src/cli/)
|
|
6
7
|
* backend registry → createCore (src/core/)
|
|
7
8
|
*/
|
|
8
9
|
import type { ExtensionContext } from "../shell/host-types.js";
|
package/dist/shell/events.d.ts
CHANGED
|
@@ -35,6 +35,10 @@ export class InputHandler {
|
|
|
35
35
|
if (this.activeMode)
|
|
36
36
|
this.drawPrompt();
|
|
37
37
|
});
|
|
38
|
+
this.bus.on("input:redraw", () => {
|
|
39
|
+
if (this.activeMode)
|
|
40
|
+
this.renderModeInput();
|
|
41
|
+
});
|
|
38
42
|
this.bus.on("input-mode:register", (config) => {
|
|
39
43
|
this.registerMode(config);
|
|
40
44
|
});
|
|
@@ -408,11 +408,14 @@ export default function activate(ctx) {
|
|
|
408
408
|
bus.on("ui:info", (e) => {
|
|
409
409
|
stopCurrentSpinner();
|
|
410
410
|
showInfo(e.message);
|
|
411
|
-
|
|
411
|
+
bus.emit("input:redraw", {});
|
|
412
412
|
if (s.renderer)
|
|
413
413
|
startThinkingSpinner();
|
|
414
414
|
});
|
|
415
|
-
bus.on("ui:error", (e) =>
|
|
415
|
+
bus.on("ui:error", (e) => {
|
|
416
|
+
showError(e.message);
|
|
417
|
+
bus.emit("input:redraw", {});
|
|
418
|
+
});
|
|
416
419
|
bus.on("ui:suggestion", (e) => {
|
|
417
420
|
compositor.surface("status").writeLine(`${p.dim}💡 ${e.text}${p.reset}`);
|
|
418
421
|
});
|
|
@@ -249,13 +249,15 @@ function renderUnifiedHunk(hunk, layout) {
|
|
|
249
249
|
const out = [];
|
|
250
250
|
const pairs = findChangePairs(hunk);
|
|
251
251
|
const renderedAsPartOfPair = new Set();
|
|
252
|
+
const bgWidth = Math.max(1, textWidth - noW - 3);
|
|
253
|
+
const gutter = (n) => `${p.dim}${n} │${p.reset} `;
|
|
252
254
|
for (let i = 0; i < hunk.lines.length; i++) {
|
|
253
255
|
const line = hunk.lines[i];
|
|
254
256
|
const no = String(line.type === "removed" ? (line.oldNo ?? "") : (line.newNo ?? line.oldNo ?? "")).padStart(noW);
|
|
255
257
|
if (line.type === "context") {
|
|
256
258
|
const raw = truncateText(line.text, lineTextW);
|
|
257
259
|
const text = lang ? highlightLine(raw, lang) : raw;
|
|
258
|
-
out.push(
|
|
260
|
+
out.push(`${gutter(no)} ${p.dim}${text}${p.reset}`);
|
|
259
261
|
continue;
|
|
260
262
|
}
|
|
261
263
|
if (line.type === "removed") {
|
|
@@ -275,17 +277,17 @@ function renderUnifiedHunk(hunk, layout) {
|
|
|
275
277
|
removedText = lang ? highlightLine(raw, lang) : raw;
|
|
276
278
|
}
|
|
277
279
|
if (useTrueColor) {
|
|
278
|
-
out.push(padToWidth(`${p.errorBg}${p.error}- ${
|
|
280
|
+
out.push(gutter(no) + padToWidth(`${p.errorBg}${p.error}- ${preserveBg(removedText, p.errorBg)}`, bgWidth) + p.reset);
|
|
279
281
|
}
|
|
280
282
|
else {
|
|
281
|
-
out.push(`${p.error}- ${
|
|
283
|
+
out.push(`${gutter(no)}${p.error}- ${removedText}${p.reset}`);
|
|
282
284
|
}
|
|
283
285
|
if (addedText !== null && addedNo !== null) {
|
|
284
286
|
if (useTrueColor) {
|
|
285
|
-
out.push(padToWidth(`${p.successBg}${p.success}+ ${
|
|
287
|
+
out.push(gutter(addedNo) + padToWidth(`${p.successBg}${p.success}+ ${preserveBg(addedText, p.successBg)}`, bgWidth) + p.reset);
|
|
286
288
|
}
|
|
287
289
|
else {
|
|
288
|
-
out.push(`${p.success}+ ${
|
|
290
|
+
out.push(`${gutter(addedNo)}${p.success}+ ${addedText}${p.reset}`);
|
|
289
291
|
}
|
|
290
292
|
}
|
|
291
293
|
continue;
|
|
@@ -296,10 +298,10 @@ function renderUnifiedHunk(hunk, layout) {
|
|
|
296
298
|
const raw = truncateText(line.text, lineTextW);
|
|
297
299
|
const text = lang ? highlightLine(raw, lang) : raw;
|
|
298
300
|
if (useTrueColor) {
|
|
299
|
-
out.push(padToWidth(`${p.successBg}${p.success}+ ${
|
|
301
|
+
out.push(gutter(no) + padToWidth(`${p.successBg}${p.success}+ ${preserveBg(text, p.successBg)}`, bgWidth) + p.reset);
|
|
300
302
|
}
|
|
301
303
|
else {
|
|
302
|
-
out.push(`${p.success}+ ${
|
|
304
|
+
out.push(`${gutter(no)}${p.success}+ ${text}${p.reset}`);
|
|
303
305
|
}
|
|
304
306
|
}
|
|
305
307
|
}
|