@giovannijecha/jecode 0.7.4 → 0.8.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/README.md +25 -13
- package/dist/batch.js +1 -1
- package/dist/cli-info.js +1 -1
- package/dist/config.js +4 -1
- package/dist/controller.js +34 -11
- package/dist/duration.js +8 -0
- package/dist/openai-account.js +12 -8
- package/dist/providers/anthropic-stream.js +8 -0
- package/dist/providers/anthropic-wire.js +22 -10
- package/dist/providers/ollama-stream.js +7 -0
- package/dist/providers/ollama-wire.js +11 -3
- package/dist/providers/openai-stream.js +57 -2
- package/dist/providers/openai-wire.js +27 -12
- package/dist/sessions/codec.js +13 -5
- package/dist/settings-command.js +0 -5
- package/dist/settings.js +0 -2
- package/dist/steering.js +58 -0
- package/dist/tools/args.js +1 -1
- package/dist/transcript.js +6 -1
- package/dist/tui/activity.js +20 -5
- package/dist/tui/app-input.js +31 -6
- package/dist/tui/app-workflows.js +55 -13
- package/dist/tui/app.js +18 -3
- package/dist/tui/blocks.js +7 -2
- package/dist/tui/components/messages.js +18 -12
- package/dist/tui/components/misc.js +7 -6
- package/dist/tui/components/status.js +12 -1
- package/dist/tui/components/tool.js +145 -48
- package/dist/tui/help.js +1 -1
- package/dist/tui/motion.js +32 -0
- package/dist/tui/session-view.js +3 -2
- package/dist/tui/transcript-grammar.js +25 -0
- package/dist/tui/transcript-view.js +132 -7
- package/dist/tui/turn.js +49 -31
- package/dist/tui/view.js +4 -2
- package/dist/ui/render.js +1 -3
- package/dist/ui/theme.js +19 -18
- package/package.json +1 -1
package/dist/tui/turn.js
CHANGED
|
@@ -10,7 +10,7 @@ import { promptFor } from "./approve.js";
|
|
|
10
10
|
// reasoning and tools keep the detailed work visible in the transcript.
|
|
11
11
|
const WAITING = "Waiting";
|
|
12
12
|
const THINKING = "Thinking";
|
|
13
|
-
const
|
|
13
|
+
const RESPONDING = "Responding";
|
|
14
14
|
const ASKING = "Waiting for you";
|
|
15
15
|
/** Unchanged rows kept either side of a change. */
|
|
16
16
|
const CONTEXT = 2;
|
|
@@ -19,10 +19,7 @@ export function transcribe(stage) {
|
|
|
19
19
|
// one, which is what keeps reasoning and answer from running together.
|
|
20
20
|
let open;
|
|
21
21
|
const tools = new Map();
|
|
22
|
-
|
|
23
|
-
let steps = 1;
|
|
24
|
-
let tool = 1;
|
|
25
|
-
let toolTotal = 1;
|
|
22
|
+
const progress = new Map();
|
|
26
23
|
const close = () => {
|
|
27
24
|
const block = open?.block;
|
|
28
25
|
const changed = block?.kind === "reasoning" && block.live === true;
|
|
@@ -41,19 +38,28 @@ export function transcribe(stage) {
|
|
|
41
38
|
continue;
|
|
42
39
|
block.tone = "fail";
|
|
43
40
|
block.right = reason ?? "failed";
|
|
44
|
-
block
|
|
41
|
+
settleDuration(block);
|
|
45
42
|
stage.render(block);
|
|
46
43
|
}
|
|
47
44
|
},
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
45
|
+
onToolPreparing(call, current, total) {
|
|
46
|
+
progress.set(call.id, { current, total });
|
|
47
|
+
const changed = close();
|
|
48
|
+
if (changed !== undefined)
|
|
49
|
+
stage.render(changed);
|
|
50
|
+
stage.status(toolStatus("Preparing", call, progress));
|
|
52
51
|
stage.render();
|
|
53
52
|
},
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
onToolStart(call, current, total) {
|
|
54
|
+
progress.set(call.id, { current, total });
|
|
55
|
+
stage.status(toolStatus("Running", call, progress));
|
|
56
|
+
const block = tools.get(call.id);
|
|
57
|
+
if (block !== undefined && block.kind === "tool") {
|
|
58
|
+
block.right = "running";
|
|
59
|
+
block.startedAt = Date.now();
|
|
60
|
+
block.durationMs = undefined;
|
|
61
|
+
}
|
|
62
|
+
stage.render(block);
|
|
57
63
|
},
|
|
58
64
|
onUsage(usage) {
|
|
59
65
|
stage.usage?.(usage);
|
|
@@ -66,8 +72,16 @@ export function transcribe(stage) {
|
|
|
66
72
|
stage.render();
|
|
67
73
|
},
|
|
68
74
|
onStream(event) {
|
|
75
|
+
if (event.kind === "tool") {
|
|
76
|
+
const changed = close();
|
|
77
|
+
if (changed !== undefined)
|
|
78
|
+
stage.render(changed);
|
|
79
|
+
stage.status(`Preparing ${event.name ?? "tool"}`);
|
|
80
|
+
stage.render();
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
69
83
|
const kind = event.kind === "thinking" ? "reasoning" : "answer";
|
|
70
|
-
stage.status(kind === "reasoning" ? THINKING :
|
|
84
|
+
stage.status(kind === "reasoning" ? THINKING : RESPONDING);
|
|
71
85
|
if (open === undefined || open.kind !== kind) {
|
|
72
86
|
const changed = close();
|
|
73
87
|
if (changed !== undefined)
|
|
@@ -87,15 +101,13 @@ export function transcribe(stage) {
|
|
|
87
101
|
const changed = close();
|
|
88
102
|
if (changed !== undefined)
|
|
89
103
|
stage.render(changed);
|
|
90
|
-
stage.status(`Running ${call.name}${toolTotal > 1 ? ` · tool ${tool}/${toolTotal}` : ""}${step > 1 ? ` · step ${step}/${steps}` : ""}`);
|
|
91
104
|
const block = {
|
|
92
105
|
kind: "tool",
|
|
93
106
|
name: call.name,
|
|
94
107
|
target: target(call.input),
|
|
95
|
-
right: "
|
|
108
|
+
right: "ready",
|
|
96
109
|
tone: "pending",
|
|
97
110
|
body: preview(call, look),
|
|
98
|
-
startedAt: Date.now(),
|
|
99
111
|
};
|
|
100
112
|
tools.set(call.id, block);
|
|
101
113
|
stage.emit(block);
|
|
@@ -109,7 +121,7 @@ export function transcribe(stage) {
|
|
|
109
121
|
stage.render(block);
|
|
110
122
|
},
|
|
111
123
|
onToolResult(call, result, summary) {
|
|
112
|
-
stage.status(
|
|
124
|
+
stage.status(WAITING);
|
|
113
125
|
const block = tools.get(call.id);
|
|
114
126
|
if (block === undefined || block.kind !== "tool")
|
|
115
127
|
return;
|
|
@@ -122,7 +134,7 @@ export function transcribe(stage) {
|
|
|
122
134
|
}
|
|
123
135
|
block.tone = result.isError ? "fail" : "ok";
|
|
124
136
|
block.right = summary ?? "";
|
|
125
|
-
block
|
|
137
|
+
settleDuration(block);
|
|
126
138
|
// A failure replaces the preview: what the call was going to do stops
|
|
127
139
|
// being the interesting part the moment it did not do it.
|
|
128
140
|
const outcome = result.isError
|
|
@@ -134,14 +146,8 @@ export function transcribe(stage) {
|
|
|
134
146
|
},
|
|
135
147
|
approve(call) {
|
|
136
148
|
const block = tools.get(call.id);
|
|
137
|
-
if (stage.approved(call))
|
|
138
|
-
if (block !== undefined && block.kind === "tool") {
|
|
139
|
-
block.right = "running";
|
|
140
|
-
block.startedAt ??= Date.now();
|
|
141
|
-
stage.render(block);
|
|
142
|
-
}
|
|
149
|
+
if (stage.approved(call))
|
|
143
150
|
return Promise.resolve(true);
|
|
144
|
-
}
|
|
145
151
|
const changed = close();
|
|
146
152
|
if (changed !== undefined)
|
|
147
153
|
stage.render(changed);
|
|
@@ -160,8 +166,8 @@ export function transcribe(stage) {
|
|
|
160
166
|
if (settled !== undefined && settled.kind === "tool") {
|
|
161
167
|
if (approved) {
|
|
162
168
|
settled.tone = "pending";
|
|
163
|
-
settled.right = "
|
|
164
|
-
settled.startedAt =
|
|
169
|
+
settled.right = "ready";
|
|
170
|
+
settled.startedAt = undefined;
|
|
165
171
|
}
|
|
166
172
|
else {
|
|
167
173
|
settled.tone = "deny";
|
|
@@ -169,7 +175,9 @@ export function transcribe(stage) {
|
|
|
169
175
|
settled.startedAt = undefined;
|
|
170
176
|
}
|
|
171
177
|
}
|
|
172
|
-
stage.status(approved
|
|
178
|
+
stage.status(approved
|
|
179
|
+
? toolStatus("Preparing", call, progress)
|
|
180
|
+
: WAITING);
|
|
173
181
|
stage.render(settled);
|
|
174
182
|
resolve(approved);
|
|
175
183
|
});
|
|
@@ -177,14 +185,24 @@ export function transcribe(stage) {
|
|
|
177
185
|
},
|
|
178
186
|
};
|
|
179
187
|
}
|
|
188
|
+
function toolStatus(phase, call, progress) {
|
|
189
|
+
const position = progress.get(call.id);
|
|
190
|
+
const tool = position !== undefined && position.total > 1
|
|
191
|
+
? ` · tool ${position.current}/${position.total}`
|
|
192
|
+
: "";
|
|
193
|
+
return `${phase} ${call.name}${tool}`;
|
|
194
|
+
}
|
|
180
195
|
function pendingApproval(body) {
|
|
181
196
|
const added = body?.filter((detail) => detail.kind === "add").length ?? 0;
|
|
182
197
|
const removed = body?.filter((detail) => detail.kind === "del").length ?? 0;
|
|
183
198
|
const summary = added + removed === 0 ? "" : `+${added} −${removed} · `;
|
|
184
199
|
return `${summary}pending approval`;
|
|
185
200
|
}
|
|
186
|
-
function
|
|
187
|
-
|
|
201
|
+
function settleDuration(block) {
|
|
202
|
+
if (block.startedAt !== undefined) {
|
|
203
|
+
block.durationMs = Math.max(0, Date.now() - block.startedAt);
|
|
204
|
+
}
|
|
205
|
+
block.startedAt = undefined;
|
|
188
206
|
}
|
|
189
207
|
/** The argument worth showing: the thing the call acts on. */
|
|
190
208
|
function target(input) {
|
package/dist/tui/view.js
CHANGED
|
@@ -25,7 +25,7 @@ export function compose(view, size, transcript = transcriptRenderer()) {
|
|
|
25
25
|
// Any spare height belongs above the conversation, so short sessions grow
|
|
26
26
|
// upward from the fixed dock rhythm instead of leaving a changing hole
|
|
27
27
|
// beneath the latest reply.
|
|
28
|
-
const viewport = transcript.viewport(view.blocks, width, transcriptHeight, view.scroll, view.pal, { now: view.now });
|
|
28
|
+
const viewport = transcript.viewport(view.blocks, width, transcriptHeight, view.scroll, view.pal, { now: view.now, reducedMotion: view.reducedMotion });
|
|
29
29
|
const cursor = dock.cursor === undefined
|
|
30
30
|
? undefined
|
|
31
31
|
: { row: transcriptHeight + dock.cursor.row, col: dock.cursor.col };
|
|
@@ -34,6 +34,7 @@ export function compose(view, size, transcript = transcriptRenderer()) {
|
|
|
34
34
|
cursor,
|
|
35
35
|
maxScroll: viewport.maxScroll,
|
|
36
36
|
transcriptPending: viewport.pending,
|
|
37
|
+
transcriptAnimating: viewport.animating,
|
|
37
38
|
};
|
|
38
39
|
}
|
|
39
40
|
function dockRows(view, width, height) {
|
|
@@ -42,6 +43,7 @@ function dockRows(view, width, height) {
|
|
|
42
43
|
// use the footer while that interaction is open instead of repeating a
|
|
43
44
|
// generic "Running /…" label beside it.
|
|
44
45
|
status: view.modal === undefined ? view.status : undefined,
|
|
46
|
+
steering: view.modal === undefined ? view.steering : undefined,
|
|
45
47
|
feedback: view.feedback,
|
|
46
48
|
readiness: view.readiness,
|
|
47
49
|
unseen: view.unseen ?? 0,
|
|
@@ -87,5 +89,5 @@ function tooSmall(height, width, view) {
|
|
|
87
89
|
{ text: elide(`need ${MIN_COLS}×${MIN_ROWS}`, Math.max(1, width)), fg: view.pal.ink.dim },
|
|
88
90
|
]);
|
|
89
91
|
}
|
|
90
|
-
return { rows, maxScroll: 0, transcriptPending: false };
|
|
92
|
+
return { rows, maxScroll: 0, transcriptPending: false, transcriptAnimating: false };
|
|
91
93
|
}
|
package/dist/ui/render.js
CHANGED
|
@@ -125,9 +125,7 @@ export function blank(width, ground) {
|
|
|
125
125
|
/**
|
|
126
126
|
* A full-width divider.
|
|
127
127
|
*
|
|
128
|
-
* Edge to edge
|
|
129
|
-
* behind a user's message: two elements that stop at different columns read as
|
|
130
|
-
* two grids, and the eye finds the discrepancy before it finds the text.
|
|
128
|
+
* Edge to edge so the composer and dock keep one exact terminal boundary.
|
|
131
129
|
*/
|
|
132
130
|
export function rule(width, color) {
|
|
133
131
|
return paint({ text: "─".repeat(Math.max(0, width)), fg: color });
|
package/dist/ui/theme.js
CHANGED
|
@@ -1,26 +1,27 @@
|
|
|
1
1
|
// Jecode's fixed terminal identity. Components depend on semantic tokens, not
|
|
2
|
-
// literal colours, while the product exposes one deliberate dark
|
|
3
|
-
// Jecode's fixed dark
|
|
4
|
-
//
|
|
2
|
+
// literal colours, while the product exposes one deliberate dark Slate look.
|
|
3
|
+
// Jecode's fixed dark Slate baseline. The exported name stays stable while
|
|
4
|
+
// cooler, quieter values leave the transcript bright enough to read and let
|
|
5
|
+
// live state carry the colour.
|
|
5
6
|
// Components depend on these roles rather than embedding presentation values.
|
|
6
7
|
export const STEEL = {
|
|
7
|
-
accent: [
|
|
8
|
-
technical: [
|
|
9
|
-
focus: [
|
|
10
|
-
rule: [
|
|
8
|
+
accent: [124, 164, 222],
|
|
9
|
+
technical: [126, 186, 208],
|
|
10
|
+
focus: [124, 164, 222],
|
|
11
|
+
rule: [44, 60, 78],
|
|
11
12
|
ink: {
|
|
12
|
-
fg: [
|
|
13
|
-
bright: [
|
|
14
|
-
muted: [
|
|
15
|
-
dim: [
|
|
16
|
-
attention: [
|
|
17
|
-
added: [
|
|
18
|
-
removed: [
|
|
13
|
+
fg: [214, 219, 226],
|
|
14
|
+
bright: [241, 244, 248],
|
|
15
|
+
muted: [149, 160, 174],
|
|
16
|
+
dim: [99, 111, 125],
|
|
17
|
+
attention: [226, 188, 112],
|
|
18
|
+
added: [138, 190, 150],
|
|
19
|
+
removed: [223, 120, 120],
|
|
19
20
|
},
|
|
20
21
|
surface: {
|
|
21
|
-
subtle: [
|
|
22
|
-
added: [
|
|
23
|
-
removed: [
|
|
24
|
-
attention: [
|
|
22
|
+
subtle: [23, 29, 37],
|
|
23
|
+
added: [21, 52, 33],
|
|
24
|
+
removed: [58, 24, 27],
|
|
25
|
+
attention: [58, 47, 20],
|
|
25
26
|
},
|
|
26
27
|
};
|