@stigmer/ink 3.34.0 → 3.36.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.
Files changed (61) hide show
  1. package/app/SessionApp.d.ts.map +1 -1
  2. package/app/SessionApp.js.map +1 -1
  3. package/app/SessionView.d.ts.map +1 -1
  4. package/app/SessionView.js.map +1 -1
  5. package/cli/stigmer-ink.d.ts.map +1 -1
  6. package/cli/stigmer-ink.js.map +1 -1
  7. package/components/AgentPicker.d.ts.map +1 -1
  8. package/components/AgentPicker.js.map +1 -1
  9. package/components/ApprovalPrompt.d.ts.map +1 -1
  10. package/components/ApprovalPrompt.js.map +1 -1
  11. package/components/ContextGauge.d.ts.map +1 -1
  12. package/components/ContextGauge.js.map +1 -1
  13. package/components/ExecutionProgress.d.ts.map +1 -1
  14. package/components/ExecutionProgress.js.map +1 -1
  15. package/components/FileChangeProgressBar.d.ts.map +1 -1
  16. package/components/FileChangeProgressBar.js.map +1 -1
  17. package/components/FileDiffBody.d.ts.map +1 -1
  18. package/components/FileDiffBody.js.map +1 -1
  19. package/components/FileReviewAtoms.d.ts.map +1 -1
  20. package/components/FileReviewAtoms.js.map +1 -1
  21. package/components/FileReviewPrompt.d.ts.map +1 -1
  22. package/components/FileReviewPrompt.js.map +1 -1
  23. package/components/FileReviewRecord.d.ts.map +1 -1
  24. package/components/FileReviewRecord.js.map +1 -1
  25. package/components/FollowUpInput.d.ts.map +1 -1
  26. package/components/FollowUpInput.js.map +1 -1
  27. package/components/MessageEntry.d.ts.map +1 -1
  28. package/components/MessageEntry.js.map +1 -1
  29. package/components/MessageThread.d.ts.map +1 -1
  30. package/components/MessageThread.js.map +1 -1
  31. package/components/ResourcePicker.d.ts.map +1 -1
  32. package/components/ResourcePicker.js.map +1 -1
  33. package/components/SessionPicker.d.ts.map +1 -1
  34. package/components/SessionPicker.js.map +1 -1
  35. package/components/SubAgentBlock.d.ts.map +1 -1
  36. package/components/SubAgentBlock.js.map +1 -1
  37. package/components/TodoList.d.ts.map +1 -1
  38. package/components/TodoList.js.map +1 -1
  39. package/components/ToolCallGroup.d.ts.map +1 -1
  40. package/components/ToolCallGroup.js.map +1 -1
  41. package/components/ToolCallItem.d.ts.map +1 -1
  42. package/components/ToolCallItem.js.map +1 -1
  43. package/components/UsageWidget.d.ts.map +1 -1
  44. package/components/UsageWidget.js.map +1 -1
  45. package/file-review.d.ts.map +1 -1
  46. package/file-review.js.map +1 -1
  47. package/index.d.ts.map +1 -1
  48. package/index.js.map +1 -1
  49. package/markdown.d.ts.map +1 -1
  50. package/markdown.js.map +1 -1
  51. package/package.json +4 -4
  52. package/provider.d.ts.map +1 -1
  53. package/provider.js.map +1 -1
  54. package/src/__tests__/components.test.tsx +0 -378
  55. package/src/__tests__/context-gauge.test.tsx +0 -176
  56. package/src/__tests__/data-pickers.test.tsx +0 -147
  57. package/src/__tests__/file-change-progress.test.tsx +0 -295
  58. package/src/__tests__/file-review.test.tsx +0 -811
  59. package/src/__tests__/markdown.test.ts +0 -46
  60. package/src/__tests__/resource-picker.test.tsx +0 -176
  61. package/src/__tests__/test-support.tsx +0 -31
@@ -1,147 +0,0 @@
1
- import React from "react";
2
- import { describe, it, expect, vi } from "vitest";
3
- import { render } from "ink-testing-library";
4
- import { create } from "@bufbuild/protobuf";
5
- import { SearchResultSchema } from "@stigmer/protos/ai/stigmer/search/v1/io_pb";
6
- import { SessionSchema } from "@stigmer/protos/ai/stigmer/agentic/session/v1/api_pb";
7
- import { InkStigmerProvider } from "../provider.js";
8
- import { AgentPicker } from "../components/AgentPicker.js";
9
- import { SessionPicker } from "../components/SessionPicker.js";
10
- import { fakeClient } from "./test-support.js";
11
-
12
- const KEY = { enter: "\r" } as const;
13
-
14
- // Allow the hooks' async fetch + a re-render to settle.
15
- function settle(): Promise<void> {
16
- return new Promise((resolve) => setTimeout(resolve, 60));
17
- }
18
-
19
- function agentResult(id: string, slug: string, description: string) {
20
- return create(SearchResultSchema, { id, qualifiedSlug: slug, description });
21
- }
22
-
23
- function session(id: string, subject: string, agentInstanceId: string) {
24
- return create(SessionSchema, {
25
- metadata: { id },
26
- spec: { subject, agentInstanceId },
27
- });
28
- }
29
-
30
- describe("AgentPicker", () => {
31
- it("renders search results and returns the chosen agent on Enter", async () => {
32
- const results = [
33
- agentResult("agt_1", "acme/alpha", "first"),
34
- agentResult("agt_2", "acme/beta", "second"),
35
- ];
36
- const client = fakeClient({
37
- agent: { list: vi.fn(async () => ({ entries: results, totalPages: 1, totalCount: 2 })) },
38
- });
39
- const onSelect = vi.fn();
40
-
41
- const { lastFrame, stdin } = render(
42
- <InkStigmerProvider client={client}>
43
- <AgentPicker org="acme" onSelect={onSelect} onCancel={vi.fn()} />
44
- </InkStigmerProvider>,
45
- );
46
-
47
- await settle();
48
- expect(lastFrame() ?? "").toContain("acme/alpha");
49
- expect(lastFrame() ?? "").toContain("first");
50
-
51
- stdin.write(KEY.enter);
52
- await settle();
53
- expect(onSelect).toHaveBeenCalledTimes(1);
54
- expect(onSelect.mock.calls[0][0].id).toBe("agt_1");
55
- });
56
-
57
- it("offers the built-in assistant first when asked, and picks it without an agent", async () => {
58
- const results = [agentResult("agt_1", "acme/alpha", "first")];
59
- const client = fakeClient({
60
- agent: { list: vi.fn(async () => ({ entries: results, totalPages: 1, totalCount: 1 })) },
61
- });
62
- const onSelect = vi.fn();
63
- const onSelectBuiltInAssistant = vi.fn();
64
-
65
- const { lastFrame, stdin } = render(
66
- <InkStigmerProvider client={client}>
67
- <AgentPicker
68
- org="acme"
69
- onSelect={onSelect}
70
- onSelectBuiltInAssistant={onSelectBuiltInAssistant}
71
- onCancel={vi.fn()}
72
- />
73
- </InkStigmerProvider>,
74
- );
75
-
76
- await settle();
77
- const frame = lastFrame() ?? "";
78
- expect(frame).toContain("Assistant (built-in)");
79
- // First row: above every agent the search found.
80
- expect(frame.indexOf("Assistant (built-in)")).toBeLessThan(frame.indexOf("acme/alpha"));
81
-
82
- stdin.write(KEY.enter);
83
- await settle();
84
- expect(onSelectBuiltInAssistant).toHaveBeenCalledTimes(1);
85
- expect(onSelect).not.toHaveBeenCalled();
86
- });
87
-
88
- it("offers agents only when no built-in handler is given", async () => {
89
- const client = fakeClient({
90
- agent: { list: vi.fn(async () => ({ entries: [], totalPages: 0, totalCount: 0 })) },
91
- });
92
- const { lastFrame } = render(
93
- <InkStigmerProvider client={client}>
94
- <AgentPicker org="acme" onSelect={vi.fn()} onCancel={vi.fn()} />
95
- </InkStigmerProvider>,
96
- );
97
- await settle();
98
- expect(lastFrame() ?? "").not.toContain("Assistant (built-in)");
99
- });
100
-
101
- it("seeds the search box from initialQuery", async () => {
102
- const list = vi.fn(async () => ({ entries: [], totalPages: 0, totalCount: 0 }));
103
- const client = fakeClient({ agent: { list } });
104
-
105
- const { lastFrame } = render(
106
- <InkStigmerProvider client={client}>
107
- <AgentPicker org="acme" initialQuery="deploy" onSelect={vi.fn()} onCancel={vi.fn()} />
108
- </InkStigmerProvider>,
109
- );
110
-
111
- await settle();
112
- // The picker pre-fills the input with the unresolved query the user
113
- // already typed; useAgentSearch then re-searches it (debounced).
114
- expect(lastFrame() ?? "").toContain("deploy");
115
- });
116
- });
117
-
118
- describe("SessionPicker", () => {
119
- it("client-side filters the session list and returns the chosen session", async () => {
120
- const sessions = [
121
- session("ses_1", "deploy staging", "ai_1"),
122
- session("ses_2", "review PR", "ai_2"),
123
- session("ses_3", "deploy prod", "ai_3"),
124
- ];
125
- const client = fakeClient({
126
- session: { list: vi.fn(async () => ({ entries: sessions })) },
127
- });
128
- const onSelect = vi.fn();
129
-
130
- const { lastFrame, stdin } = render(
131
- <InkStigmerProvider client={client}>
132
- <SessionPicker initialQuery="deploy" onSelect={onSelect} onCancel={vi.fn()} />
133
- </InkStigmerProvider>,
134
- );
135
-
136
- await settle();
137
- const frame = lastFrame() ?? "";
138
- expect(frame).toContain("deploy staging");
139
- expect(frame).toContain("deploy prod");
140
- expect(frame).not.toContain("review PR");
141
-
142
- stdin.write(KEY.enter);
143
- await settle();
144
- expect(onSelect).toHaveBeenCalledTimes(1);
145
- expect(onSelect.mock.calls[0][0].metadata.id).toBe("ses_1");
146
- });
147
- });
@@ -1,295 +0,0 @@
1
- import React from "react";
2
- import { describe, it, expect } from "vitest";
3
- import { render } from "ink-testing-library";
4
- import { create } from "@bufbuild/protobuf";
5
- import {
6
- FileChangeProgressSchema,
7
- FileChangeProgressEntrySchema,
8
- CapturedFileChangeSchema,
9
- } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/filereview_pb";
10
- import type {
11
- FileChangeProgress,
12
- FileChangeProgressEntry,
13
- } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/filereview_pb";
14
- import { FileChangeKind } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
15
- import { FileChangeProgressBar } from "../components/FileChangeProgressBar.js";
16
- import { changeDisplayPath, progressEntryDisplayPath } from "../file-review.js";
17
-
18
- // --- fixtures -------------------------------------------------------------
19
-
20
- interface EntryOpts {
21
- readonly pathBefore?: string;
22
- readonly pathAfter?: string;
23
- readonly kind?: FileChangeKind;
24
- readonly linesAdded?: number;
25
- readonly linesRemoved?: number;
26
- }
27
-
28
- function makeEntry(opts: EntryOpts): FileChangeProgressEntry {
29
- return create(FileChangeProgressEntrySchema, {
30
- pathBefore: opts.pathBefore ?? "",
31
- pathAfter: opts.pathAfter ?? "",
32
- kind: opts.kind ?? FileChangeKind.MODIFY,
33
- linesAdded: opts.linesAdded ?? 0,
34
- linesRemoved: opts.linesRemoved ?? 0,
35
- });
36
- }
37
-
38
- interface ProgressOpts {
39
- readonly filesChanged?: number;
40
- readonly linesAdded?: number;
41
- readonly linesRemoved?: number;
42
- readonly entries?: FileChangeProgressEntry[];
43
- }
44
-
45
- function makeProgress(opts: ProgressOpts): FileChangeProgress {
46
- const entries = opts.entries ?? [];
47
- return create(FileChangeProgressSchema, {
48
- changeSetId: "cs-1",
49
- filesChanged: opts.filesChanged ?? entries.length,
50
- linesAdded: opts.linesAdded ?? 0,
51
- linesRemoved: opts.linesRemoved ?? 0,
52
- entries,
53
- capturedAt: "2026-07-05T00:00:00Z",
54
- });
55
- }
56
-
57
- // --- FileChangeProgressBar: visibility ------------------------------------
58
-
59
- describe("FileChangeProgressBar — visibility", () => {
60
- it("renders nothing when progress is undefined", () => {
61
- const { lastFrame } = render(<FileChangeProgressBar progress={undefined} />);
62
- expect((lastFrame() ?? "").trim()).toBe("");
63
- });
64
-
65
- it("renders nothing when no files have changed yet", () => {
66
- const { lastFrame } = render(
67
- <FileChangeProgressBar progress={makeProgress({ filesChanged: 0 })} />,
68
- );
69
- expect((lastFrame() ?? "").trim()).toBe("");
70
- });
71
- });
72
-
73
- // --- FileChangeProgressBar: compact summary -------------------------------
74
-
75
- describe("FileChangeProgressBar — compact summary", () => {
76
- it("uses the singular noun for a single file", () => {
77
- const { lastFrame } = render(
78
- <FileChangeProgressBar
79
- progress={makeProgress({
80
- filesChanged: 1,
81
- entries: [makeEntry({ pathAfter: "one.ts" })],
82
- })}
83
- />,
84
- );
85
- expect(lastFrame() ?? "").toContain("1 file changing");
86
- });
87
-
88
- it("uses the plural noun for multiple files", () => {
89
- const { lastFrame } = render(
90
- <FileChangeProgressBar
91
- progress={makeProgress({
92
- filesChanged: 3,
93
- entries: [makeEntry({ pathAfter: "one.ts" })],
94
- })}
95
- />,
96
- );
97
- expect(lastFrame() ?? "").toContain("3 files changing");
98
- });
99
-
100
- it("shows the aggregate +N -M when counts exist", () => {
101
- const { lastFrame } = render(
102
- <FileChangeProgressBar
103
- progress={makeProgress({
104
- filesChanged: 2,
105
- linesAdded: 12,
106
- linesRemoved: 4,
107
- entries: [makeEntry({ pathAfter: "one.ts" })],
108
- })}
109
- />,
110
- );
111
- const out = lastFrame() ?? "";
112
- expect(out).toContain("+12");
113
- expect(out).toContain("-4");
114
- });
115
-
116
- it("hides the aggregate stat when both counts are zero (never +0 -0)", () => {
117
- const { lastFrame } = render(
118
- <FileChangeProgressBar
119
- progress={makeProgress({
120
- filesChanged: 1,
121
- linesAdded: 0,
122
- linesRemoved: 0,
123
- entries: [makeEntry({ pathAfter: "one.ts" })],
124
- })}
125
- />,
126
- );
127
- // No "+" anywhere means no stat rendered; the count copy carries none.
128
- expect(lastFrame() ?? "").not.toContain("+");
129
- });
130
- });
131
-
132
- // --- FileChangeProgressBar: expansion -------------------------------------
133
-
134
- describe("FileChangeProgressBar — expansion", () => {
135
- const progress = makeProgress({
136
- filesChanged: 2,
137
- linesAdded: 8,
138
- linesRemoved: 1,
139
- entries: [
140
- makeEntry({ pathAfter: "src/app.ts", linesAdded: 5, linesRemoved: 1 }),
141
- makeEntry({ pathAfter: "src/util.ts", linesAdded: 3, linesRemoved: 0 }),
142
- ],
143
- });
144
-
145
- it("hides the per-file list when collapsed (default)", () => {
146
- const { lastFrame } = render(<FileChangeProgressBar progress={progress} />);
147
- const out = lastFrame() ?? "";
148
- expect(out).toContain("2 files changing");
149
- expect(out).not.toContain("src/app.ts");
150
- expect(out).not.toContain("src/util.ts");
151
- });
152
-
153
- it("shows the per-file list with kind letter, path, and stats when expanded", () => {
154
- const { lastFrame } = render(
155
- <FileChangeProgressBar progress={progress} expanded />,
156
- );
157
- const out = lastFrame() ?? "";
158
- expect(out).toContain("src/app.ts");
159
- expect(out).toContain("src/util.ts");
160
- expect(out).toContain("M"); // MODIFY kind letter
161
- expect(out).toContain("+5");
162
- expect(out).toContain("+3");
163
- });
164
-
165
- it("shows '… and K more' when the entry list is capped below the true total", () => {
166
- const capped = makeProgress({
167
- filesChanged: 5,
168
- entries: [
169
- makeEntry({ pathAfter: "a.ts" }),
170
- makeEntry({ pathAfter: "b.ts" }),
171
- ],
172
- });
173
- const { lastFrame } = render(
174
- <FileChangeProgressBar progress={capped} expanded />,
175
- );
176
- expect(lastFrame() ?? "").toContain("and 3 more");
177
- });
178
-
179
- it("renders a rename entry as 'before → after'", () => {
180
- const renamed = makeProgress({
181
- filesChanged: 1,
182
- entries: [
183
- makeEntry({
184
- pathBefore: "old/name.ts",
185
- pathAfter: "new/name.ts",
186
- kind: FileChangeKind.RENAME,
187
- }),
188
- ],
189
- });
190
- const { lastFrame } = render(
191
- <FileChangeProgressBar progress={renamed} expanded />,
192
- );
193
- expect(lastFrame() ?? "").toContain("old/name.ts → new/name.ts");
194
- });
195
-
196
- it("hides per-file stats for a withheld entry (zero counts)", () => {
197
- const secretish = makeProgress({
198
- filesChanged: 1,
199
- entries: [makeEntry({ pathAfter: ".env", linesAdded: 0, linesRemoved: 0 })],
200
- });
201
- const { lastFrame } = render(
202
- <FileChangeProgressBar progress={secretish} expanded />,
203
- );
204
- const out = lastFrame() ?? "";
205
- expect(out).toContain(".env");
206
- expect(out).not.toContain("+");
207
- });
208
- });
209
-
210
- // --- path helpers (direct unit coverage) ----------------------------------
211
-
212
- interface ChangeOpts {
213
- readonly pathBefore?: string;
214
- readonly pathAfter?: string;
215
- readonly kind: FileChangeKind;
216
- }
217
-
218
- function makeChange(opts: ChangeOpts) {
219
- return create(CapturedFileChangeSchema, {
220
- id: "c-1",
221
- pathBefore: opts.pathBefore ?? "",
222
- pathAfter: opts.pathAfter ?? "",
223
- kind: opts.kind,
224
- });
225
- }
226
-
227
- describe("changeDisplayPath", () => {
228
- it("shows the after-path for an add", () => {
229
- expect(
230
- changeDisplayPath(makeChange({ pathAfter: "new.ts", kind: FileChangeKind.ADD })),
231
- ).toBe("new.ts");
232
- });
233
-
234
- it("shows the after-path for a modify", () => {
235
- expect(
236
- changeDisplayPath(
237
- makeChange({ pathAfter: "app.ts", kind: FileChangeKind.MODIFY }),
238
- ),
239
- ).toBe("app.ts");
240
- });
241
-
242
- it("falls back to the before-path for a delete", () => {
243
- expect(
244
- changeDisplayPath(
245
- makeChange({ pathBefore: "gone.ts", kind: FileChangeKind.DELETE }),
246
- ),
247
- ).toBe("gone.ts");
248
- });
249
-
250
- it("shows 'before → after' for a rename", () => {
251
- expect(
252
- changeDisplayPath(
253
- makeChange({
254
- pathBefore: "old.ts",
255
- pathAfter: "new.ts",
256
- kind: FileChangeKind.RENAME,
257
- }),
258
- ),
259
- ).toBe("old.ts → new.ts");
260
- });
261
- });
262
-
263
- describe("progressEntryDisplayPath", () => {
264
- it("shows the after-path for an add", () => {
265
- expect(
266
- progressEntryDisplayPath(makeEntry({ pathAfter: "new.ts", kind: FileChangeKind.ADD })),
267
- ).toBe("new.ts");
268
- });
269
-
270
- it("shows the after-path for a modify", () => {
271
- expect(
272
- progressEntryDisplayPath(makeEntry({ pathAfter: "app.ts" })),
273
- ).toBe("app.ts");
274
- });
275
-
276
- it("falls back to the before-path for a delete", () => {
277
- expect(
278
- progressEntryDisplayPath(
279
- makeEntry({ pathBefore: "gone.ts", kind: FileChangeKind.DELETE }),
280
- ),
281
- ).toBe("gone.ts");
282
- });
283
-
284
- it("shows 'before → after' for a rename — identical to changeDisplayPath", () => {
285
- expect(
286
- progressEntryDisplayPath(
287
- makeEntry({
288
- pathBefore: "old.ts",
289
- pathAfter: "new.ts",
290
- kind: FileChangeKind.RENAME,
291
- }),
292
- ),
293
- ).toBe("old.ts → new.ts");
294
- });
295
- });