march-cli 0.1.10 → 0.1.11
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/package.json +1 -1
- package/src/cli/commands/status-command.mjs +1 -12
- package/src/cli/shell/shell-drawer.mjs +1 -1
- package/src/cli/tui/output/visible-lines.mjs +8 -0
- package/src/cli/tui/output-buffer.mjs +30 -21
- package/src/cli/tui/render/stream-delta-buffer.mjs +46 -0
- package/src/cli/tui/selection-screen.mjs +12 -4
- package/src/cli/ui.mjs +16 -17
- package/src/context/system-core/base.md +1 -1
- package/src/shell/runtime.mjs +9 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
2
|
import { MODES, formatModeLabel } from "../input/mode-state.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { PREFIX, R } from "../tui/ui-theme.mjs";
|
|
4
4
|
|
|
5
5
|
export function statusCommand({
|
|
6
6
|
runner,
|
|
@@ -23,11 +23,6 @@ export function statusCommand({
|
|
|
23
23
|
|
|
24
24
|
export function statusBarLine({
|
|
25
25
|
runner,
|
|
26
|
-
sessionState,
|
|
27
|
-
sessionSource = "pi",
|
|
28
|
-
extensionDiagnostics = [],
|
|
29
|
-
lifecycleState = null,
|
|
30
|
-
gitBranch = getGitBranch(runner.engine.cwd),
|
|
31
26
|
mode = MODES.DO,
|
|
32
27
|
contextTokens = null,
|
|
33
28
|
activity = null,
|
|
@@ -35,12 +30,6 @@ export function statusBarLine({
|
|
|
35
30
|
}) {
|
|
36
31
|
return formatStatusBarLine({
|
|
37
32
|
engine: runner.engine,
|
|
38
|
-
sessionState,
|
|
39
|
-
sessionStats: runner.getSessionStats?.() ?? null,
|
|
40
|
-
sessionSource,
|
|
41
|
-
extensionDiagnostics,
|
|
42
|
-
lifecycleState,
|
|
43
|
-
gitBranch,
|
|
44
33
|
mode,
|
|
45
34
|
contextTokens,
|
|
46
35
|
activity,
|
|
@@ -137,7 +137,7 @@ export class ShellDrawer {
|
|
|
137
137
|
|
|
138
138
|
getOutputLines(shellId = this.getSelectedShell()?.id) {
|
|
139
139
|
if (!shellId || !this.shellRuntime) return [];
|
|
140
|
-
const snapshot = this.shellRuntime.snapshotShell(shellId);
|
|
140
|
+
const snapshot = this.shellRuntime.snapshotShellScreen?.(shellId) ?? this.shellRuntime.snapshotShell(shellId);
|
|
141
141
|
return formatAnsiLines(snapshot);
|
|
142
142
|
}
|
|
143
143
|
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export function sliceLinesWithTail(baseLines, tailLine, range) {
|
|
2
|
+
if (!range) return tailLine == null ? baseLines : [...baseLines, tailLine];
|
|
3
|
+
|
|
4
|
+
const { start, end } = range;
|
|
5
|
+
const visible = baseLines.slice(start, Math.min(end, baseLines.length));
|
|
6
|
+
if (tailLine != null && end > baseLines.length) visible.push(tailLine);
|
|
7
|
+
return visible;
|
|
8
|
+
}
|
|
@@ -4,6 +4,7 @@ import { renderMarkdown, renderStreamingMarkdown } from "./markdown-renderer.mjs
|
|
|
4
4
|
import { renderEditDiffBlock } from "./tui-diff-rendering.mjs";
|
|
5
5
|
import { OutputScrollState } from "./output/scroll-state.mjs";
|
|
6
6
|
import { appendTextLines, wrapLine } from "./output/text-line-renderer.mjs";
|
|
7
|
+
import { sliceLinesWithTail } from "./output/visible-lines.mjs";
|
|
7
8
|
|
|
8
9
|
const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
9
10
|
|
|
@@ -72,6 +73,7 @@ export class OutputBuffer {
|
|
|
72
73
|
this.overlayStatus = null;
|
|
73
74
|
this.scrollState = new OutputScrollState();
|
|
74
75
|
this._segmentLinesCache = new Map();
|
|
76
|
+
this._baseLinesCache = new Map();
|
|
75
77
|
}
|
|
76
78
|
|
|
77
79
|
get scrollOffset() {
|
|
@@ -89,6 +91,7 @@ export class OutputBuffer {
|
|
|
89
91
|
this.overlayStatus = null;
|
|
90
92
|
this.scrollState.clear();
|
|
91
93
|
this._segmentLinesCache = new Map();
|
|
94
|
+
this._baseLinesCache = new Map();
|
|
92
95
|
}
|
|
93
96
|
|
|
94
97
|
write(text) {
|
|
@@ -101,6 +104,7 @@ export class OutputBuffer {
|
|
|
101
104
|
|
|
102
105
|
_writeText(text, markdown) {
|
|
103
106
|
this.overlayStatus = null;
|
|
107
|
+
this._invalidateBaseLines();
|
|
104
108
|
const current = this.currentText.at(-1);
|
|
105
109
|
if (current.markdown !== markdown && current.text !== "") {
|
|
106
110
|
this.currentText.push({ text: "", markdown });
|
|
@@ -114,6 +118,7 @@ export class OutputBuffer {
|
|
|
114
118
|
|
|
115
119
|
writeln(text) {
|
|
116
120
|
this.overlayStatus = null;
|
|
121
|
+
this._invalidateBaseLines();
|
|
117
122
|
this.currentText[this.currentText.length - 1].text += text;
|
|
118
123
|
this.currentText.push({ text: "", markdown: false });
|
|
119
124
|
}
|
|
@@ -122,6 +127,7 @@ export class OutputBuffer {
|
|
|
122
127
|
const current = this.currentText.at(-1);
|
|
123
128
|
if (!current || current.text === "") return false;
|
|
124
129
|
this.currentText.push({ text: "", markdown: false });
|
|
130
|
+
this._invalidateBaseLines();
|
|
125
131
|
return true;
|
|
126
132
|
}
|
|
127
133
|
|
|
@@ -141,12 +147,14 @@ export class OutputBuffer {
|
|
|
141
147
|
if (lastIdx >= 0) this._activeThinking.content[lastIdx] += parts[0];
|
|
142
148
|
else this._activeThinking.content.push(parts[0]);
|
|
143
149
|
for (let i = 1; i < parts.length; i++) this._activeThinking.content.push(parts[i]);
|
|
150
|
+
this._invalidateBaseLines();
|
|
144
151
|
}
|
|
145
152
|
|
|
146
153
|
endThinking(tokens) {
|
|
147
154
|
if (this._activeThinking) {
|
|
148
155
|
this._activeThinking.tokens = tokens;
|
|
149
156
|
this._activeThinking = null;
|
|
157
|
+
this._invalidateSegmentLines();
|
|
150
158
|
}
|
|
151
159
|
}
|
|
152
160
|
|
|
@@ -166,10 +174,12 @@ export class OutputBuffer {
|
|
|
166
174
|
|
|
167
175
|
setOverlayStatus(lines) {
|
|
168
176
|
this.overlayStatus = Array.isArray(lines) ? { type: "status", lines } : null;
|
|
177
|
+
this._invalidateBaseLines();
|
|
169
178
|
}
|
|
170
179
|
|
|
171
180
|
clearOverlayStatus() {
|
|
172
181
|
this.overlayStatus = null;
|
|
182
|
+
this._invalidateBaseLines();
|
|
173
183
|
}
|
|
174
184
|
|
|
175
185
|
sealCurrentText() {
|
|
@@ -232,34 +242,33 @@ export class OutputBuffer {
|
|
|
232
242
|
|
|
233
243
|
_invalidateSegmentLines() {
|
|
234
244
|
this._segmentLinesCache.clear();
|
|
245
|
+
this._invalidateBaseLines();
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
_invalidateBaseLines() {
|
|
249
|
+
this._baseLinesCache.clear();
|
|
235
250
|
}
|
|
236
251
|
|
|
237
252
|
render(width) {
|
|
238
|
-
const
|
|
239
|
-
this.
|
|
240
|
-
this.scrollState.setTotalLines(
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
253
|
+
const baseLines = this._renderBaseLines(width);
|
|
254
|
+
const tailLine = this.spinning ? this._spinnerLine() : null;
|
|
255
|
+
this.scrollState.setTotalLines(baseLines.length + (tailLine == null ? 0 : 1));
|
|
256
|
+
return sliceLinesWithTail(baseLines, tailLine, this.scrollState.sliceRange());
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
_spinnerLine() {
|
|
260
|
+
return brightBlack(`${SPINNER_FRAMES[this.spinnerIdx]} ${this.spinnerText}`);
|
|
245
261
|
}
|
|
246
262
|
|
|
247
|
-
|
|
263
|
+
_renderBaseLines(width) {
|
|
264
|
+
const cached = this._baseLinesCache.get(width);
|
|
265
|
+
if (cached) return cached;
|
|
248
266
|
const lines = [...this._renderCachedSegmentLines(width)];
|
|
249
267
|
const dynamicStart = this._cachedSegmentPrefixCount();
|
|
250
|
-
for (const seg of this.segments.slice(dynamicStart))
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
for (const line of renderBlock(block, width)) lines.push(line);
|
|
255
|
-
}
|
|
256
|
-
if (this.overlayStatus) {
|
|
257
|
-
for (const line of renderBlock(this.overlayStatus, width)) lines.push(line);
|
|
258
|
-
}
|
|
259
|
-
if (this.spinning) {
|
|
260
|
-
const frame = SPINNER_FRAMES[this.spinnerIdx];
|
|
261
|
-
lines.push(brightBlack(`${frame} ${this.spinnerText}`));
|
|
262
|
-
}
|
|
268
|
+
for (const seg of this.segments.slice(dynamicStart)) for (const line of renderBlock(seg, width)) lines.push(line);
|
|
269
|
+
for (const block of currentTextToBlocks(this.currentText, false, this.currentTextCache)) for (const line of renderBlock(block, width)) lines.push(line);
|
|
270
|
+
if (this.overlayStatus) for (const line of renderBlock(this.overlayStatus, width)) lines.push(line);
|
|
271
|
+
this._baseLinesCache.set(width, lines);
|
|
263
272
|
return lines;
|
|
264
273
|
}
|
|
265
274
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export function createStreamDeltaBuffer({
|
|
2
|
+
writeText,
|
|
3
|
+
writeThinking,
|
|
4
|
+
renderSoon,
|
|
5
|
+
delayMs = 16,
|
|
6
|
+
setTimeoutImpl = setTimeout,
|
|
7
|
+
clearTimeoutImpl = clearTimeout,
|
|
8
|
+
} = {}) {
|
|
9
|
+
const queued = [];
|
|
10
|
+
let timer = null;
|
|
11
|
+
|
|
12
|
+
function append(kind, delta) {
|
|
13
|
+
if (!delta) return;
|
|
14
|
+
const last = queued.at(-1);
|
|
15
|
+
if (last?.kind === kind) last.text += delta;
|
|
16
|
+
else queued.push({ kind, text: delta });
|
|
17
|
+
schedule();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function schedule() {
|
|
21
|
+
if (timer) return;
|
|
22
|
+
timer = setTimeoutImpl(() => flush(), delayMs);
|
|
23
|
+
timer.unref?.();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function flush({ notify = true } = {}) {
|
|
27
|
+
if (timer) {
|
|
28
|
+
clearTimeoutImpl(timer);
|
|
29
|
+
timer = null;
|
|
30
|
+
}
|
|
31
|
+
if (!queued.length) return false;
|
|
32
|
+
const batch = queued.splice(0);
|
|
33
|
+
for (const item of batch) {
|
|
34
|
+
if (item.kind === "thinking") writeThinking(item.text);
|
|
35
|
+
else writeText(item.text);
|
|
36
|
+
}
|
|
37
|
+
if (notify) renderSoon();
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
text: (delta) => append("text", delta),
|
|
43
|
+
thinking: (delta) => append("thinking", delta),
|
|
44
|
+
flush,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -10,16 +10,19 @@ export class ScreenSelection {
|
|
|
10
10
|
this.anchor = null;
|
|
11
11
|
this.focus = null;
|
|
12
12
|
this.lines = [];
|
|
13
|
+
this._plainLines = [];
|
|
13
14
|
this.viewport = { topRow: 0, leftCol: 0, width: Infinity, height: 0 };
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
setLines(lines) {
|
|
17
|
-
this.lines = lines
|
|
18
|
+
this.lines = [...lines];
|
|
19
|
+
this._plainLines = [];
|
|
18
20
|
this.viewport = { topRow: 0, leftCol: 0, width: Infinity, height: this.lines.length };
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
setViewport({ topRow = 0, leftCol = 0, width = Infinity, lines = [] } = {}) {
|
|
22
|
-
this.lines = lines
|
|
24
|
+
this.lines = lines;
|
|
25
|
+
this._plainLines = [];
|
|
23
26
|
this.viewport = {
|
|
24
27
|
topRow: Math.max(0, Math.trunc(topRow)),
|
|
25
28
|
leftCol: Math.max(0, Math.trunc(leftCol)),
|
|
@@ -68,7 +71,7 @@ export class ScreenSelection {
|
|
|
68
71
|
if (!range) return "";
|
|
69
72
|
const selected = [];
|
|
70
73
|
for (let row = range.start.row; row <= range.end.row; row += 1) {
|
|
71
|
-
const line = this.
|
|
74
|
+
const line = this._plainLine(row);
|
|
72
75
|
const startCol = row === range.start.row ? range.start.col : 0;
|
|
73
76
|
const endCol = row === range.end.row ? range.end.col : visibleWidth(line);
|
|
74
77
|
selected.push(sliceColumns(line, startCol, endCol));
|
|
@@ -81,7 +84,7 @@ export class ScreenSelection {
|
|
|
81
84
|
if (!range) return lines;
|
|
82
85
|
return lines.map((line, row) => {
|
|
83
86
|
if (row < range.start.row || row > range.end.row) return line;
|
|
84
|
-
const plain =
|
|
87
|
+
const plain = this._plainLine(row);
|
|
85
88
|
const startCol = row === range.start.row ? range.start.col : 0;
|
|
86
89
|
const endCol = row === range.end.row ? range.end.col : visibleWidth(plain);
|
|
87
90
|
if (endCol <= startCol) return line;
|
|
@@ -89,6 +92,11 @@ export class ScreenSelection {
|
|
|
89
92
|
});
|
|
90
93
|
}
|
|
91
94
|
|
|
95
|
+
_plainLine(row) {
|
|
96
|
+
if (this._plainLines[row] == null) this._plainLines[row] = stripAnsi(this.lines[row] ?? "");
|
|
97
|
+
return this._plainLines[row];
|
|
98
|
+
}
|
|
99
|
+
|
|
92
100
|
range() {
|
|
93
101
|
if (!this.anchor || !this.focus) return null;
|
|
94
102
|
const [start, end] = comparePoints(this.anchor, this.focus) <= 0
|
package/src/cli/ui.mjs
CHANGED
|
@@ -24,6 +24,7 @@ import { writeMemoryHint } from "./tui/recall-rendering.mjs";
|
|
|
24
24
|
import { writeToolEnd, writeToolStart } from "./tui/tool-rendering.mjs";
|
|
25
25
|
import { EDITOR_THEME, brightBlack } from "./tui/ui-theme.mjs";
|
|
26
26
|
import { createRenderScheduler } from "./tui/render/render-scheduler.mjs";
|
|
27
|
+
import { createStreamDeltaBuffer } from "./tui/render/stream-delta-buffer.mjs";
|
|
27
28
|
import { writeTranscriptToOutput } from "../session/transcript.mjs";
|
|
28
29
|
|
|
29
30
|
export { buildMarchCommands, MarchAutocompleteProvider } from "./input/autocomplete.mjs";
|
|
@@ -61,8 +62,9 @@ export function createTuiUI({
|
|
|
61
62
|
let toolsExpanded = false;
|
|
62
63
|
const activeToolBlocks = [];
|
|
63
64
|
const renderScheduler = createRenderScheduler({ requestRender: () => tui.requestRender() });
|
|
64
|
-
const
|
|
65
|
-
|
|
65
|
+
const streamDeltas = createStreamDeltaBuffer({ writeText: (delta) => output.writeMarkdown(delta), writeThinking: (delta) => output.appendThinking(delta), renderSoon: renderScheduler.renderSoon });
|
|
66
|
+
const flushStreamDeltas = () => streamDeltas.flush({ notify: false });
|
|
67
|
+
const requestRender = () => { flushStreamDeltas(); renderScheduler.renderNow(); };
|
|
66
68
|
const spinnerStatus = createSpinnerStatusController({ output, requestRender });
|
|
67
69
|
const retryStatus = createRetryStatusController({ output, requestRender, stopSpinner: spinnerStatus.stop });
|
|
68
70
|
const shellDrawerControls = createShellDrawerControls({ shellDrawer, output, requestRender });
|
|
@@ -169,12 +171,10 @@ export function createTuiUI({
|
|
|
169
171
|
retryStatus.stop(); output.startThinking(); requestRender();
|
|
170
172
|
},
|
|
171
173
|
|
|
172
|
-
thinkingDelta: (delta) =>
|
|
173
|
-
output.appendThinking(delta);
|
|
174
|
-
renderScheduler.renderSoon();
|
|
175
|
-
},
|
|
174
|
+
thinkingDelta: (delta) => streamDeltas.thinking(delta),
|
|
176
175
|
|
|
177
176
|
thinkingEnd: (tokens) => {
|
|
177
|
+
flushStreamDeltas();
|
|
178
178
|
output.endThinking(tokens);
|
|
179
179
|
requestRender();
|
|
180
180
|
},
|
|
@@ -186,7 +186,7 @@ export function createTuiUI({
|
|
|
186
186
|
toggleLastThinking: () => false,
|
|
187
187
|
|
|
188
188
|
toolStart: (name, args) => {
|
|
189
|
-
ensureStarted(); retryStatus.stop(); spinnerStatus.stop(); activeToolBlocks.push(writeToolStart({ output, name, args })); requestRender();
|
|
189
|
+
ensureStarted(); flushStreamDeltas(); retryStatus.stop(); spinnerStatus.stop(); activeToolBlocks.push(writeToolStart({ output, name, args })); requestRender();
|
|
190
190
|
},
|
|
191
191
|
|
|
192
192
|
toolEnd: (name, isError, result) => {
|
|
@@ -194,30 +194,26 @@ export function createTuiUI({
|
|
|
194
194
|
},
|
|
195
195
|
|
|
196
196
|
textDelta: (delta) => {
|
|
197
|
-
ensureStarted(); retryStatus.stop(); spinnerStatus.stop();
|
|
198
|
-
output.writeMarkdown(delta);
|
|
199
|
-
renderScheduler.renderSoon();
|
|
197
|
+
ensureStarted(); retryStatus.stop(); spinnerStatus.stop(); streamDeltas.text(delta);
|
|
200
198
|
},
|
|
201
199
|
assistantReplyEnd: () => {
|
|
202
200
|
ensureStarted();
|
|
201
|
+
flushStreamDeltas();
|
|
203
202
|
const changed = output.ensureNewline();
|
|
204
203
|
if (output.sealCurrentText() || changed) requestRender();
|
|
205
204
|
},
|
|
206
205
|
status: (text) => {
|
|
207
|
-
ensureStarted(); retryStatus.stop(); spinnerStatus.stop(); output.setOverlayStatus([brightBlack(`● ${text}`)]); requestRender();
|
|
206
|
+
ensureStarted(); flushStreamDeltas(); retryStatus.stop(); spinnerStatus.stop(); output.setOverlayStatus([brightBlack(`● ${text}`)]); requestRender();
|
|
208
207
|
},
|
|
209
208
|
memoryHint: ({ hints }) => {
|
|
210
|
-
ensureStarted(); retryStatus.stop(); spinnerStatus.stop(); output.ensureNewline(); writeMemoryHint({ output, hints }); requestRender();
|
|
209
|
+
ensureStarted(); flushStreamDeltas(); retryStatus.stop(); spinnerStatus.stop(); output.ensureNewline(); writeMemoryHint({ output, hints }); requestRender();
|
|
211
210
|
},
|
|
212
211
|
|
|
213
212
|
clearOutput: () => {
|
|
214
|
-
ensureStarted(); spinnerStatus.stop(); retryStatus.stop(); output.clear(); requestRender();
|
|
213
|
+
ensureStarted(); flushStreamDeltas(); spinnerStatus.stop(); retryStatus.stop(); output.clear(); requestRender();
|
|
215
214
|
},
|
|
216
|
-
|
|
217
215
|
restoreTranscript: (turns) => {
|
|
218
|
-
ensureStarted(); spinnerStatus.stop(); retryStatus.stop(); output.clear();
|
|
219
|
-
writeTranscriptToOutput(output, turns);
|
|
220
|
-
requestRender();
|
|
216
|
+
ensureStarted(); flushStreamDeltas(); spinnerStatus.stop(); retryStatus.stop(); output.clear(); writeTranscriptToOutput(output, turns); requestRender();
|
|
221
217
|
},
|
|
222
218
|
|
|
223
219
|
setStatusBar: (text) => {
|
|
@@ -229,6 +225,7 @@ export function createTuiUI({
|
|
|
229
225
|
},
|
|
230
226
|
|
|
231
227
|
turnEnd: () => {
|
|
228
|
+
flushStreamDeltas();
|
|
232
229
|
const changed = output.ensureNewline();
|
|
233
230
|
if (output.sealCurrentText() || changed) requestRender();
|
|
234
231
|
},
|
|
@@ -238,6 +235,7 @@ export function createTuiUI({
|
|
|
238
235
|
|
|
239
236
|
editDiff: (path, diffLines) => {
|
|
240
237
|
ensureStarted();
|
|
238
|
+
flushStreamDeltas();
|
|
241
239
|
spinnerStatus.stop();
|
|
242
240
|
writeEditDiff({ output, path, diffLines });
|
|
243
241
|
requestRender();
|
|
@@ -278,6 +276,7 @@ export function createTuiUI({
|
|
|
278
276
|
toggleShellDrawer: () => shellDrawerControls.toggle(),
|
|
279
277
|
requestExit: () => inputController.requestExit(),
|
|
280
278
|
close: async () => {
|
|
279
|
+
flushStreamDeltas();
|
|
281
280
|
renderScheduler.clearPending();
|
|
282
281
|
spinnerStatus.stop();
|
|
283
282
|
retryStatus.stop();
|
|
@@ -58,7 +58,7 @@ The user primarily asks for software engineering work: fixing bugs, adding behav
|
|
|
58
58
|
- To edit an existing memory, use memory_open(id) to get its path, then edit_file with mode="patch" for targeted edits.
|
|
59
59
|
- Use memory_save() to create memories or update whole fields. Before creating a new memory, first search/open related memories and merge updates into an existing memory when they share the same topic, project, or decision thread; prefer modifying the existing memory file over creating a scattered new one. Tags are the primary retrieval key for future recall. Prefer lowercase kebab-case tags like 'march-cli', 'tooling', 'permissions'.
|
|
60
60
|
- When learning multiple related external workflows or skills, maintain memory as an evolving domain library: start with the specific source name when only one item exists, then rename and rewrite the memory title/description as the scope grows; merge new related learnings into the same memory, preserving each source's unique traits while distilling reusable principles.
|
|
61
|
-
- Distinguish "migrating a Skill to memory" from "learning a Skill": migration preserves the complete Skill folder under memory_root/skills/ and creates a memory index
|
|
61
|
+
- Distinguish "migrating a Skill to memory" from "learning a Skill": migration preserves the complete Skill folder under memory_root/skills/ and creates a memory entry as its index; that memory should describe what the Skill is for and reference the copied Skill folder path so future recall knows how to use it. Learning only reads and internalizes the Skill's methods, scenarios, and principles into ordinary memory without copying source files. Infer the action from the user's wording, and ask when ambiguous.
|
|
62
62
|
- Unlike memory hints, this system-core center is always visible in every model call. Only update the center for instructions that must always be followed; use memory for contextual, project-specific, or recall-dependent knowledge.
|
|
63
63
|
- If execution takes a meaningful detour, create or update a memory after the task. A detour means the initial plan or assumption failed, multiple approaches were tried, and the final successful path contains reusable project knowledge. Record the failed assumption, what was tried, and the successful approach. Prefer updating an existing related memory over creating a new one.
|
|
64
64
|
</memory_system>
|
package/src/shell/runtime.mjs
CHANGED
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
normalizeSize,
|
|
11
11
|
publicShell,
|
|
12
12
|
requireShell,
|
|
13
|
-
stripAnsi,
|
|
14
13
|
touch,
|
|
15
14
|
uniqueName,
|
|
16
15
|
} from "./runtime-state.mjs";
|
|
@@ -194,6 +193,14 @@ export function createShellRuntime({
|
|
|
194
193
|
};
|
|
195
194
|
}
|
|
196
195
|
|
|
196
|
+
function snapshotShellScreen(id) {
|
|
197
|
+
const shell = requireShell(shells, id);
|
|
198
|
+
return {
|
|
199
|
+
shell: publicShell(shell),
|
|
200
|
+
screen: shell.screen?.snapshot?.() ?? null,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
197
204
|
function clearShell(id) {
|
|
198
205
|
const shell = requireShell(shells, id);
|
|
199
206
|
shell.rawChunks = [];
|
|
@@ -238,6 +245,7 @@ export function createShellRuntime({
|
|
|
238
245
|
getShell,
|
|
239
246
|
searchShell,
|
|
240
247
|
snapshotShell,
|
|
248
|
+
snapshotShellScreen,
|
|
241
249
|
clearShell,
|
|
242
250
|
dispose,
|
|
243
251
|
};
|