@cueloop/client 0.1.0-alpha.37 → 0.1.0-alpha.39

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cueloop/client",
3
- "version": "0.1.0-alpha.37",
3
+ "version": "0.1.0-alpha.39",
4
4
  "description": "The cueloop review UI: projection renderer, keyboard grammar, review rail",
5
5
  "homepage": "https://github.com/mmurakaru/cueloop#readme",
6
6
  "bugs": {
@@ -18,16 +18,17 @@
18
18
  ],
19
19
  "type": "module",
20
20
  "exports": {
21
- ".": "./src/index.ts"
21
+ ".": "./src/index.ts",
22
+ "./config": "./src/config.ts"
22
23
  },
23
24
  "publishConfig": {
24
25
  "access": "public"
25
26
  },
26
27
  "dependencies": {
27
- "@cueloop/daemon": "0.1.0-alpha.37",
28
- "@cueloop/extension-api": "0.1.0-alpha.37",
29
- "@cueloop/integration-obsidian": "0.1.0-alpha.37",
30
- "@cueloop/schema": "0.1.0-alpha.37",
28
+ "@cueloop/daemon": "0.1.0-alpha.39",
29
+ "@cueloop/extension-api": "0.1.0-alpha.39",
30
+ "@cueloop/integration-obsidian": "0.1.0-alpha.39",
31
+ "@cueloop/schema": "0.1.0-alpha.39",
31
32
  "@opentui/core": "^0.5.2",
32
33
  "@opentui/keymap": "0.5.2",
33
34
  "@opentui/react": "^0.5.2",
@@ -9,6 +9,7 @@ import { testRender } from "@opentui/react/test-utils";
9
9
  import { DaemonServer } from "@cueloop/daemon";
10
10
  import type { ReviewSession } from "@cueloop/schema";
11
11
  import { App } from "./App";
12
+ import { DEFAULT_QUICK_ACTIONS, quickActionBody } from "./config";
12
13
  import { DARK } from "./theme";
13
14
  import {
14
15
  isolateUserConfig,
@@ -599,6 +600,6 @@ describe("marker-actions popover", () => {
599
600
  await waitForState(setup, () => server.core.sessionGet(session.id).annotations.length === 1);
600
601
  const stored = server.core.sessionGet(session.id);
601
602
  expect(stored.annotations[0]!.kind).toBe("comment");
602
- expect(stored.annotations[0]!.body).toBe("Restate simplified");
603
+ expect(stored.annotations[0]!.body).toBe(quickActionBody(DEFAULT_QUICK_ACTIONS[1]!));
603
604
  });
604
605
  });
@@ -53,13 +53,14 @@ export async function loadStories(): Promise<LoadedStory[]> {
53
53
  return loaded;
54
54
  }
55
55
 
56
- /** Component files that must carry stories: every .tsx that is not a story/test. */
56
+ /** Component files that must carry stories: every .tsx that is not a story/test/prototype. */
57
57
  export function componentFilesMissingStories(): string[] {
58
58
  const componentGlob = new Bun.Glob("**/*.tsx");
59
59
  const files = [...componentGlob.scanSync({ cwd: import.meta.dir })];
60
60
  const missing: string[] = [];
61
61
  for (const file of files) {
62
62
  if (file.endsWith(".stories.tsx") || file.endsWith(".test.tsx")) continue;
63
+ if (file.endsWith(".prototype.tsx")) continue;
63
64
  if (file === "stories-app.tsx") continue;
64
65
  const storiesSibling = file.replace(/\.tsx$/, ".stories.tsx");
65
66
  if (!files.includes(storiesSibling)) missing.push(file);
@@ -11,6 +11,8 @@ import {
11
11
  persistReviewState,
12
12
  persistReviewWidth,
13
13
  persistTheme,
14
+ quickActionBody,
15
+ resolveQuickAction,
14
16
  } from "./config";
15
17
  import { REVIEW_DEFAULT_WIDTH, REVIEW_MAX_WIDTH } from "./review-panel";
16
18
  import { DARK } from "./theme";
@@ -418,6 +420,33 @@ describe("quick actions ([[actions]])", () => {
418
420
  });
419
421
  });
420
422
 
423
+ describe("quickActionBody", () => {
424
+ test("appends the system prompt below the prompt when set", () => {
425
+ // Assert
426
+ expect(quickActionBody({ prompt: "Prototype this", metadata: "skip tests" })).toBe(
427
+ "Prototype this\n\nskip tests",
428
+ );
429
+ expect(quickActionBody({ prompt: "Out of scope" })).toBe("Out of scope");
430
+ });
431
+ });
432
+
433
+ describe("resolveQuickAction", () => {
434
+ test("resolves by 1-based index and by case-insensitive prompt", () => {
435
+ // Assert
436
+ expect(resolveQuickAction(DEFAULT_QUICK_ACTIONS, "1")).toEqual(DEFAULT_QUICK_ACTIONS[0]!);
437
+ expect(resolveQuickAction(DEFAULT_QUICK_ACTIONS, "out of scope")).toEqual(
438
+ DEFAULT_QUICK_ACTIONS[2]!,
439
+ );
440
+ });
441
+
442
+ test("returns undefined for an out-of-range index or an unknown name", () => {
443
+ // Assert
444
+ expect(resolveQuickAction(DEFAULT_QUICK_ACTIONS, "0")).toBeUndefined();
445
+ expect(resolveQuickAction(DEFAULT_QUICK_ACTIONS, "99")).toBeUndefined();
446
+ expect(resolveQuickAction(DEFAULT_QUICK_ACTIONS, "no such action")).toBeUndefined();
447
+ });
448
+ });
449
+
421
450
  describe("actionFor", () => {
422
451
  test("resolves keys and shifted keys to actions", () => {
423
452
  // Assert
package/src/config.ts CHANGED
@@ -35,15 +35,56 @@ export interface QuickAction {
35
35
 
36
36
  /** The built-in quick-actions for the marker popover when no `[[actions]]` are configured. */
37
37
  export const DEFAULT_QUICK_ACTIONS: QuickAction[] = [
38
- { prompt: "Zoom out, research in depth" },
39
- { prompt: "Restate simplified" },
40
- { prompt: "Out of scope" },
41
- { prompt: "Let's chat about this" },
42
- { prompt: "Prototype this" },
43
- { prompt: "Ensure 0 regressions" },
44
- { prompt: "Consider existing repo patterns" },
38
+ {
39
+ prompt: "Zoom out, research in depth",
40
+ metadata: "Research the broader problem, prior art, and alternatives before changing anything.",
41
+ },
42
+ {
43
+ prompt: "Restate simplified",
44
+ metadata: "Restate this as the simplest thing that works and cut the incidental complexity.",
45
+ },
46
+ {
47
+ prompt: "Out of scope",
48
+ metadata:
49
+ "This is out of scope for the task, so capture it as a follow-up instead of doing it now.",
50
+ },
51
+ {
52
+ prompt: "Let's chat about this",
53
+ metadata:
54
+ "Do not implement yet, and surface the open questions and trade-offs so we decide together.",
55
+ },
56
+ {
57
+ prompt: "Prototype this",
58
+ metadata: "Build a throwaway prototype to answer the question, skipping tests and polish.",
59
+ },
60
+ {
61
+ prompt: "Ensure 0 regressions",
62
+ metadata:
63
+ "Characterize the current behavior with tests first, then keep them green through the change.",
64
+ },
65
+ {
66
+ prompt: "Consider existing repo patterns",
67
+ metadata:
68
+ "Follow the nearest existing pattern in this codebase rather than introducing a new one.",
69
+ },
45
70
  ];
46
71
 
72
+ /** The comment body a quick action expands to: the prompt, then its system prompt when set. */
73
+ export function quickActionBody(action: QuickAction): string {
74
+ return action.metadata ? `${action.prompt}\n\n${action.metadata}` : action.prompt;
75
+ }
76
+
77
+ /** Resolve a quick action by 1-based index or case-insensitive prompt; undefined when no match. */
78
+ export function resolveQuickAction(
79
+ actions: QuickAction[],
80
+ actionRef: string,
81
+ ): QuickAction | undefined {
82
+ const index = Number(actionRef);
83
+ if (Number.isInteger(index) && index >= 1 && index <= actions.length) return actions[index - 1];
84
+ const wanted = actionRef.trim().toLowerCase();
85
+ return actions.find((action) => action.prompt.toLowerCase() === wanted);
86
+ }
87
+
47
88
  export interface CueloopConfig {
48
89
  keys: Record<string, string[]>;
49
90
  theme: Theme;
@@ -13,7 +13,7 @@ import { displayText, spanKey, startSpan, type DisplayBlock, type SpanState } fr
13
13
  import type { DiffRow } from "./view-diff";
14
14
  import type { ReviewController } from "./session-controller";
15
15
  import type { Intent } from "./keymap";
16
- import type { QuickAction } from "./config";
16
+ import { quickActionBody, type QuickAction } from "./config";
17
17
  import type { PlanSheetHandle } from "./components/PlanSheet";
18
18
  import { VERDICTS } from "./components/ConfirmCard";
19
19
  import {
@@ -251,7 +251,7 @@ export function createIntentDispatch(deps: IntentDispatchDeps): (intent: Intent)
251
251
  if (mode.type !== "spanActions") return;
252
252
  const action = quickActions[intent.index ?? mode.index];
253
253
  if (session && action) {
254
- const body = action.metadata ? `${action.prompt}\n\n${action.metadata}` : action.prompt;
254
+ const body = quickActionBody(action);
255
255
  const annotationId = controller.annotate(
256
256
  "comment",
257
257
  mode.span.displayIndex,