@cueloop/client 0.1.0-alpha.14 → 0.1.0-alpha.15
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 +5 -5
- package/src/App.autoclose.test.tsx +12 -10
- package/src/App.diff.test.tsx +1 -1
- package/src/App.submit-confirm.test.tsx +1 -1
- package/src/App.test.tsx +2 -2
- package/src/components/CompletionOverlay.tsx +7 -8
- package/src/components/__snapshots__/stories.test.tsx.snap +5 -5
- package/src/keymap.test.ts +1 -1
- package/src/keymap.ts +1 -1
- package/src/session-controller.ts +16 -12
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cueloop/client",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.15",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": "./src/index.ts"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@cueloop/daemon": "0.1.0-alpha.
|
|
11
|
-
"@cueloop/extension-api": "0.1.0-alpha.
|
|
12
|
-
"@cueloop/integration-obsidian": "0.1.0-alpha.
|
|
13
|
-
"@cueloop/schema": "0.1.0-alpha.
|
|
10
|
+
"@cueloop/daemon": "0.1.0-alpha.15",
|
|
11
|
+
"@cueloop/extension-api": "0.1.0-alpha.15",
|
|
12
|
+
"@cueloop/integration-obsidian": "0.1.0-alpha.15",
|
|
13
|
+
"@cueloop/schema": "0.1.0-alpha.15",
|
|
14
14
|
"@opentui/core": "^0.5.1",
|
|
15
15
|
"@opentui/keymap": "0.5.1",
|
|
16
16
|
"@opentui/react": "^0.5.1",
|
|
@@ -60,37 +60,39 @@ async function submitApprove(setup: Setup): Promise<void> {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
describe("completion overlay", () => {
|
|
63
|
-
test("submit with auto-close off
|
|
63
|
+
test("submit with auto-close off counts down from 5; ⏎ exits now", async () => {
|
|
64
64
|
let exited = -1;
|
|
65
65
|
const setup = await renderApp((code) => (exited = code));
|
|
66
66
|
await submitApprove(setup);
|
|
67
|
-
const frame = setup
|
|
68
|
-
expect(frame).toContain("
|
|
69
|
-
expect(frame).toContain("
|
|
67
|
+
const frame = await waitForText(setup, "closing in 5s");
|
|
68
|
+
expect(frame).toContain("review approved");
|
|
69
|
+
expect(frame).toContain("close [return]");
|
|
70
|
+
expect(frame).toContain("return to plan [esc]");
|
|
70
71
|
await press(setup, "enter");
|
|
71
72
|
expect(exited).toBe(0);
|
|
72
73
|
});
|
|
73
74
|
|
|
74
|
-
test("a
|
|
75
|
+
test("a remembers the countdown as the persisted default", async () => {
|
|
75
76
|
const setup = await renderApp();
|
|
76
77
|
await submitApprove(setup);
|
|
78
|
+
await waitForText(setup, "closing in 5s");
|
|
77
79
|
await press(setup, "a");
|
|
78
|
-
await waitForText(setup, "closing in 3s");
|
|
79
80
|
expect(existsSync(configPath)).toBe(true);
|
|
80
|
-
expect(readFileSync(configPath, "utf8")).toContain("auto_close =
|
|
81
|
-
//
|
|
81
|
+
expect(readFileSync(configPath, "utf8")).toContain("auto_close = 5");
|
|
82
|
+
// the countdown keeps running; each second is one manual-clock tick
|
|
82
83
|
clock.advance(1000);
|
|
83
|
-
await waitForText(setup, "closing in
|
|
84
|
+
await waitForText(setup, "closing in 4s");
|
|
84
85
|
});
|
|
85
86
|
|
|
86
87
|
test("esc dismisses to the resolved read-only view", async () => {
|
|
87
88
|
const setup = await renderApp();
|
|
88
89
|
await submitApprove(setup);
|
|
90
|
+
await waitForText(setup, "closing in 5s");
|
|
89
91
|
// a bare ESC sits in the input parser's escape-sequence disambiguation
|
|
90
92
|
// window before it is delivered - the frame wait absorbs it
|
|
91
93
|
await press(setup, "escape");
|
|
92
94
|
const frame = await waitForText(setup, "resolved: approve");
|
|
93
|
-
expect(frame).not.toContain("
|
|
95
|
+
expect(frame).not.toContain("closing in");
|
|
94
96
|
});
|
|
95
97
|
|
|
96
98
|
test("configured auto_close counts down and exits without interaction", async () => {
|
package/src/App.diff.test.tsx
CHANGED
|
@@ -72,7 +72,7 @@ describe("diff review", () => {
|
|
|
72
72
|
|
|
73
73
|
await press(setup, "enter"); // submit
|
|
74
74
|
await press(setup, "enter"); // confirm request_changes
|
|
75
|
-
await waitForText(setup, "
|
|
75
|
+
await waitForText(setup, "feedback sent");
|
|
76
76
|
const resolved = server.core.sessionGet(session.id);
|
|
77
77
|
expect(resolved.verdict!.feedback).toContain("new Map()");
|
|
78
78
|
expect(resolved.verdict!.feedback).toContain("Map needs an eviction story.");
|
|
@@ -121,7 +121,7 @@ describe("rail submit confirm", () => {
|
|
|
121
121
|
await setup.mockInput.typeText("Tighten the steps.");
|
|
122
122
|
await press(setup, "enter");
|
|
123
123
|
// the completion flow after submit is unchanged
|
|
124
|
-
await waitForText(setup, "
|
|
124
|
+
await waitForText(setup, "feedback sent");
|
|
125
125
|
const stored = server.core.sessionGet(session.id);
|
|
126
126
|
expect(stored.status).toBe("resolved");
|
|
127
127
|
expect(stored.verdict!.kind).toBe("request_changes");
|
package/src/App.test.tsx
CHANGED
|
@@ -148,13 +148,13 @@ describe("submit", () => {
|
|
|
148
148
|
expect(setup.captureCharFrame()).toContain("[Changes]");
|
|
149
149
|
await type(setup, "Expand the steps.");
|
|
150
150
|
await press(setup, "enter");
|
|
151
|
-
await waitForText(setup, "
|
|
151
|
+
await waitForText(setup, "feedback sent");
|
|
152
152
|
const stored = server.core.sessionGet(session.id);
|
|
153
153
|
expect(stored.status).toBe("resolved");
|
|
154
154
|
expect(stored.verdict!.kind).toBe("request_changes");
|
|
155
155
|
expect(stored.verdict!.feedback).toContain("Needs a phase list.");
|
|
156
156
|
// submit hands the reviewer back to the agent via the completion overlay
|
|
157
|
-
expect(setup.captureCharFrame()).toContain("
|
|
157
|
+
expect(setup.captureCharFrame()).toContain("feedback sent");
|
|
158
158
|
});
|
|
159
159
|
|
|
160
160
|
test("approve via ←/→ verdict cycling", async () => {
|
|
@@ -33,18 +33,17 @@ export function CompletionOverlay({ verdict, completion, status, returnsTo, them
|
|
|
33
33
|
alignItems: "center",
|
|
34
34
|
}}
|
|
35
35
|
>
|
|
36
|
-
<text fg={approved ? tokens.green : tokens.accent}>{approved ? "
|
|
36
|
+
<text fg={approved ? tokens.green : tokens.accent}>{approved ? "review approved" : "feedback sent"}</text>
|
|
37
37
|
<text> </text>
|
|
38
38
|
<text fg={tokens.text}>The agent has your {approved ? "approval" : "feedback"} and is unblocked.</text>
|
|
39
|
+
{returnsTo ? <text fg={tokens.textDim}>returning to {returnsTo} on close</text> : null}
|
|
39
40
|
{status ? <text fg={tokens.textDim}>{status}</text> : null}
|
|
40
41
|
<text> </text>
|
|
41
|
-
{
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
<text fg={tokens.textDim}>⏎ close · a always close after submit (3s) · esc stay</text>
|
|
47
|
-
)}
|
|
42
|
+
<text fg={tokens.textDim}>
|
|
43
|
+
close [return]
|
|
44
|
+
{completion.phase === "counting" ? ` · closing in ${completion.remaining}s` : ""}
|
|
45
|
+
{" · return to plan [esc] · always [a]"}
|
|
46
|
+
</text>
|
|
48
47
|
</box>
|
|
49
48
|
);
|
|
50
49
|
}
|
|
@@ -262,11 +262,11 @@ exports[`stories catalog CompletionOverlay/ApprovedPrompt renders, snapshots, an
|
|
|
262
262
|
|
|
263
263
|
|
|
264
264
|
|
|
265
|
-
|
|
265
|
+
review approved
|
|
266
266
|
|
|
267
267
|
The agent has your approval and is unblocked.
|
|
268
268
|
|
|
269
|
-
|
|
269
|
+
close [return] · return to plan [esc] · always [a]
|
|
270
270
|
|
|
271
271
|
|
|
272
272
|
|
|
@@ -289,13 +289,13 @@ exports[`stories catalog CompletionOverlay/FeedbackCounting renders, snapshots,
|
|
|
289
289
|
|
|
290
290
|
|
|
291
291
|
|
|
292
|
-
|
|
293
|
-
✎ feedback sent
|
|
292
|
+
feedback sent
|
|
294
293
|
|
|
295
294
|
The agent has your feedback and is unblocked.
|
|
295
|
+
returning to agent/worker-3 on close
|
|
296
296
|
exported to vault/cueloop/plan.md
|
|
297
297
|
|
|
298
|
-
|
|
298
|
+
close [return] · closing in 3s · return to plan [esc] · always [a]
|
|
299
299
|
|
|
300
300
|
|
|
301
301
|
|
package/src/keymap.test.ts
CHANGED
|
@@ -76,7 +76,7 @@ describe("completion overlay", () => {
|
|
|
76
76
|
[prompt, "j", []],
|
|
77
77
|
[counting, "return", [{ t: "finishReview" }]],
|
|
78
78
|
[counting, "q", [{ t: "finishReview" }]],
|
|
79
|
-
[counting, "a", []],
|
|
79
|
+
[counting, "a", [{ t: "optInAutoClose" }]],
|
|
80
80
|
[counting, "escape", [{ t: "dismissCompletion" }]],
|
|
81
81
|
];
|
|
82
82
|
for (const [s, name, expected] of table) {
|
package/src/keymap.ts
CHANGED
|
@@ -86,7 +86,7 @@ export function reduceKey(state: KeyState, key: KeyInput, resolvedAction?: strin
|
|
|
86
86
|
}
|
|
87
87
|
if (state.overlay === "completion-prompt" || state.overlay === "completion-counting") {
|
|
88
88
|
if (name === "return" || name === "enter" || name === "q") return [{ t: "finishReview" }];
|
|
89
|
-
if (name === "a"
|
|
89
|
+
if (name === "a") return [{ t: "optInAutoClose" }];
|
|
90
90
|
if (name === "escape") return [{ t: "dismissCompletion" }];
|
|
91
91
|
return [];
|
|
92
92
|
}
|
|
@@ -30,8 +30,8 @@ import { persistAutoClose, type AutoClose, type CueloopConfig } from "./config";
|
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
32
|
* Post-submit lifecycle (a review pane should hand you back to the agent,
|
|
33
|
-
* not linger): idle →
|
|
34
|
-
*
|
|
33
|
+
* not linger): idle → counting (a visible countdown, the default) → exit.
|
|
34
|
+
* esc dismisses to the resolved view; a remembers the countdown as default.
|
|
35
35
|
*/
|
|
36
36
|
export type Completion =
|
|
37
37
|
| { phase: "idle" }
|
|
@@ -39,6 +39,9 @@ export type Completion =
|
|
|
39
39
|
| { phase: "counting"; remaining: number }
|
|
40
40
|
| { phase: "dismissed" };
|
|
41
41
|
|
|
42
|
+
/** Seconds the completion overlay counts down before it hands back. */
|
|
43
|
+
export const DEFAULT_AUTO_CLOSE = 5;
|
|
44
|
+
|
|
42
45
|
export interface ControllerSnapshot {
|
|
43
46
|
session: ReviewSession | null;
|
|
44
47
|
inbox: ReviewSession[] | null;
|
|
@@ -366,7 +369,9 @@ class Controller implements ReviewController {
|
|
|
366
369
|
if (!session) return;
|
|
367
370
|
this.client!.sessionResolve(session.id, verdict, summary)
|
|
368
371
|
.then((resolved) => {
|
|
369
|
-
|
|
372
|
+
// The completion overlay heading already states the verdict, so the
|
|
373
|
+
// status line stays empty here - only export/error messages fill it.
|
|
374
|
+
this.update({ session: resolved, status: "" });
|
|
370
375
|
// notes-vault export: guarded by config, default is manual (no-op)
|
|
371
376
|
const obsidian = this.obsidian;
|
|
372
377
|
const exporter = this.exporters.get("obsidian");
|
|
@@ -375,14 +380,13 @@ class Controller implements ReviewController {
|
|
|
375
380
|
this.setStatus(r.success && r.path ? `exported to ${r.path}` : `export failed: ${r.error ?? "unknown"}`);
|
|
376
381
|
});
|
|
377
382
|
}
|
|
378
|
-
//
|
|
379
|
-
//
|
|
383
|
+
// Hand the reviewer back to the agent. The default is a visible
|
|
384
|
+
// countdown from DEFAULT_AUTO_CLOSE that closes on its own; esc stays,
|
|
385
|
+
// a remembers the choice. A configured delay overrides; 0 closes now.
|
|
380
386
|
const delay = this.autoClose;
|
|
381
|
-
const returns = returnPaneFor(resolved.artifact.meta.herdrPane) !== undefined;
|
|
382
387
|
if (delay === 0) this.finishReview();
|
|
383
388
|
else if (typeof delay === "number") this.startCounting(delay);
|
|
384
|
-
else
|
|
385
|
-
else this.update({ completion: { phase: "prompt" } });
|
|
389
|
+
else this.startCounting(DEFAULT_AUTO_CLOSE);
|
|
386
390
|
})
|
|
387
391
|
.catch((e: unknown) => this.setStatus(String(e instanceof Error ? e.message : e)));
|
|
388
392
|
}
|
|
@@ -407,13 +411,13 @@ class Controller implements ReviewController {
|
|
|
407
411
|
}
|
|
408
412
|
|
|
409
413
|
optInAutoClose(): void {
|
|
410
|
-
//
|
|
414
|
+
// Remember the default countdown for future submits, persisted to the user
|
|
415
|
+
// config. The countdown is already running; this only makes it the default.
|
|
411
416
|
try {
|
|
412
|
-
persistAutoClose(
|
|
417
|
+
persistAutoClose(DEFAULT_AUTO_CLOSE);
|
|
413
418
|
} catch {
|
|
414
419
|
// a read-only config dir must not block closing the review
|
|
415
420
|
}
|
|
416
|
-
this.autoClose =
|
|
417
|
-
this.startCounting(3);
|
|
421
|
+
this.autoClose = DEFAULT_AUTO_CLOSE;
|
|
418
422
|
}
|
|
419
423
|
}
|