@stigmer/ink 3.1.1 → 3.1.3
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/app/SessionView.d.ts.map +1 -1
- package/app/SessionView.js +12 -1
- package/app/SessionView.js.map +1 -1
- package/components/FileChangeProgressBar.d.ts +46 -0
- package/components/FileChangeProgressBar.d.ts.map +1 -0
- package/components/FileChangeProgressBar.js +55 -0
- package/components/FileChangeProgressBar.js.map +1 -0
- package/components/FileReviewPrompt.d.ts +43 -0
- package/components/FileReviewPrompt.d.ts.map +1 -0
- package/components/FileReviewPrompt.js +172 -0
- package/components/FileReviewPrompt.js.map +1 -0
- package/components/FileReviewRecord.d.ts +18 -0
- package/components/FileReviewRecord.d.ts.map +1 -0
- package/components/FileReviewRecord.js +32 -0
- package/components/FileReviewRecord.js.map +1 -0
- package/components/MessageThread.d.ts +19 -7
- package/components/MessageThread.d.ts.map +1 -1
- package/components/MessageThread.js +145 -77
- package/components/MessageThread.js.map +1 -1
- package/components/ToolCallGroup.d.ts.map +1 -1
- package/components/ToolCallGroup.js +42 -2
- package/components/ToolCallGroup.js.map +1 -1
- package/components/ToolCallItem.d.ts.map +1 -1
- package/components/ToolCallItem.js +16 -1
- package/components/ToolCallItem.js.map +1 -1
- package/file-review.d.ts +48 -0
- package/file-review.d.ts.map +1 -0
- package/file-review.js +113 -0
- package/file-review.js.map +1 -0
- package/index.d.ts +4 -0
- package/index.d.ts.map +1 -1
- package/index.js +5 -0
- package/index.js.map +1 -1
- package/package.json +4 -4
- package/src/__tests__/file-change-progress.test.tsx +295 -0
- package/src/__tests__/file-review.test.tsx +496 -0
- package/src/app/SessionView.tsx +51 -3
- package/src/components/FileChangeProgressBar.tsx +122 -0
- package/src/components/FileReviewPrompt.tsx +328 -0
- package/src/components/FileReviewRecord.tsx +63 -0
- package/src/components/MessageThread.tsx +199 -96
- package/src/components/ToolCallGroup.tsx +50 -1
- package/src/components/ToolCallItem.tsx +25 -0
- package/src/file-review.ts +135 -0
- package/src/index.ts +17 -0
package/file-review.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { FileChangeKind, FileDecisionAction, } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
2
|
+
import { deriveEffectiveVerdicts, fileReviewability } from "@stigmer/react";
|
|
3
|
+
/**
|
|
4
|
+
* Single-letter badge for a file change's kind, matching the web review card:
|
|
5
|
+
* `A` added, `M` modified, `D` deleted, `R` renamed. A binary change reads as
|
|
6
|
+
* `M` — "binary" is a review-completeness property (from `FileContent.is_binary`
|
|
7
|
+
* via `fileReviewability`), not a distinct edit kind.
|
|
8
|
+
*/
|
|
9
|
+
export function kindLetter(kind) {
|
|
10
|
+
switch (kind) {
|
|
11
|
+
case FileChangeKind.ADD:
|
|
12
|
+
return "A";
|
|
13
|
+
case FileChangeKind.DELETE:
|
|
14
|
+
return "D";
|
|
15
|
+
case FileChangeKind.RENAME:
|
|
16
|
+
return "R";
|
|
17
|
+
default:
|
|
18
|
+
return "M";
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The one rename-aware path-display rule for the terminal, over the raw
|
|
23
|
+
* before/after/kind triple shared by every file-change shape (reviewable
|
|
24
|
+
* `CapturedFileChange` and slim `FileChangeProgressEntry` alike). Kept private
|
|
25
|
+
* and fed by the typed adapters below so the two surfaces can never grow
|
|
26
|
+
* divergent rename formatting.
|
|
27
|
+
*/
|
|
28
|
+
function displayChangePath(pathBefore, pathAfter, kind) {
|
|
29
|
+
if (kind === FileChangeKind.RENAME && pathBefore && pathAfter) {
|
|
30
|
+
return `${pathBefore} → ${pathAfter}`;
|
|
31
|
+
}
|
|
32
|
+
return pathAfter || pathBefore;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The path to show for a reviewable change: the post-edit path, or the pre-edit
|
|
36
|
+
* path for a deletion. A rename shows `before → after` so both sides are visible.
|
|
37
|
+
*/
|
|
38
|
+
export function changeDisplayPath(change) {
|
|
39
|
+
return displayChangePath(change.pathBefore, change.pathAfter, change.kind);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The path to show for a mid-run progress entry (DD-32), using the same
|
|
43
|
+
* rename-aware rule as {@link changeDisplayPath}. The slim entry carries no
|
|
44
|
+
* bodies or digests, only the same before/after/kind triple, so the terminal
|
|
45
|
+
* renders its path identically to the reviewable surfaces.
|
|
46
|
+
*/
|
|
47
|
+
export function progressEntryDisplayPath(entry) {
|
|
48
|
+
return displayChangePath(entry.pathBefore, entry.pathAfter, entry.kind);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The honest, one-line reason a change cannot be kept as-is, or `null` when it
|
|
52
|
+
* is fully reviewable. Mirrors the web card's per-file block-reason copy so the
|
|
53
|
+
* terminal reviewer sees the same explanation.
|
|
54
|
+
*/
|
|
55
|
+
export function blockReasonNote(change) {
|
|
56
|
+
const reviewability = fileReviewability(change);
|
|
57
|
+
switch (reviewability.kind) {
|
|
58
|
+
case "reviewable":
|
|
59
|
+
return null;
|
|
60
|
+
case "binary":
|
|
61
|
+
return "binary — no text diff; keep as-is or discard";
|
|
62
|
+
case "unavailable":
|
|
63
|
+
switch (reviewability.reason) {
|
|
64
|
+
case "secret":
|
|
65
|
+
return "contents withheld (looks like a secret) — discard only";
|
|
66
|
+
case "size":
|
|
67
|
+
return "diff too large to display — discard only";
|
|
68
|
+
default:
|
|
69
|
+
return "diff unavailable — discard only";
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Folds a settled change set's decisions into kept / discarded / not-reviewed
|
|
75
|
+
* counts, using the same `deriveEffectiveVerdicts` fold the web uses so a
|
|
76
|
+
* "Keep all" set correctly reads every file as kept.
|
|
77
|
+
*/
|
|
78
|
+
export function settledCounts(set) {
|
|
79
|
+
const verdicts = deriveEffectiveVerdicts(set);
|
|
80
|
+
let kept = 0;
|
|
81
|
+
let discarded = 0;
|
|
82
|
+
for (const change of set.changes) {
|
|
83
|
+
const verdict = verdicts.get(change.id);
|
|
84
|
+
if (verdict === FileDecisionAction.APPROVE)
|
|
85
|
+
kept++;
|
|
86
|
+
else if (verdict === FileDecisionAction.REJECT)
|
|
87
|
+
discarded++;
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
kept,
|
|
91
|
+
discarded,
|
|
92
|
+
notReviewed: set.changes.length - kept - discarded,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* A one-line summary of a settled change set's outcome, e.g. `2 kept · 1
|
|
97
|
+
* discarded`, or `3 files changed` when nothing was individually decided.
|
|
98
|
+
*/
|
|
99
|
+
export function settledSummary(set) {
|
|
100
|
+
const { kept, discarded, notReviewed } = settledCounts(set);
|
|
101
|
+
const parts = [];
|
|
102
|
+
if (kept)
|
|
103
|
+
parts.push(`${kept} kept`);
|
|
104
|
+
if (discarded)
|
|
105
|
+
parts.push(`${discarded} discarded`);
|
|
106
|
+
if (notReviewed)
|
|
107
|
+
parts.push(`${notReviewed} not reviewed`);
|
|
108
|
+
if (parts.length > 0)
|
|
109
|
+
return parts.join(" · ");
|
|
110
|
+
const n = set.changes.length;
|
|
111
|
+
return `${n} file${n === 1 ? "" : "s"} changed`;
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=file-review.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-review.js","sourceRoot":"","sources":["../src/file-review.ts"],"names":[],"mappings":"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"}
|
package/index.d.ts
CHANGED
|
@@ -8,6 +8,10 @@ export { ToolCallGroup, type ToolCallGroupProps } from "./components/ToolCallGro
|
|
|
8
8
|
export { SubAgentBlock, type SubAgentBlockProps } from "./components/SubAgentBlock.js";
|
|
9
9
|
export { TodoList, type TodoListProps } from "./components/TodoList.js";
|
|
10
10
|
export { ApprovalPrompt, type ApprovalPromptProps } from "./components/ApprovalPrompt.js";
|
|
11
|
+
export { FileReviewPrompt, type FileReviewPromptProps } from "./components/FileReviewPrompt.js";
|
|
12
|
+
export { FileReviewRecord, type FileReviewRecordProps } from "./components/FileReviewRecord.js";
|
|
13
|
+
export { FileChangeProgressBar, type FileChangeProgressBarProps, } from "./components/FileChangeProgressBar.js";
|
|
14
|
+
export { kindLetter, changeDisplayPath, progressEntryDisplayPath, blockReasonNote, settledCounts, settledSummary, type SettledCounts, } from "./file-review.js";
|
|
11
15
|
export { ExecutionProgress, type ExecutionProgressProps } from "./components/ExecutionProgress.js";
|
|
12
16
|
export { FollowUpInput, type FollowUpInputProps } from "./components/FollowUpInput.js";
|
|
13
17
|
export { UsageWidget, type UsageWidgetProps } from "./components/UsageWidget.js";
|
package/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,KAAK,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAGjF,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EACnB,KAAK,gBAAgB,GACtB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAG/C,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACpF,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACvF,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACpF,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACvF,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACvF,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAC1F,OAAO,EAAE,iBAAiB,EAAE,KAAK,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AACnG,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACvF,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAGpF,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,KAAK,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAC3G,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AACjF,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAGvF,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAChG,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,KAAK,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAGjF,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EACnB,KAAK,gBAAgB,GACtB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAG/C,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACpF,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACvF,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACpF,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACvF,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACvF,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAC1F,OAAO,EAAE,gBAAgB,EAAE,KAAK,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AAChG,OAAO,EAAE,gBAAgB,EAAE,KAAK,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AAChG,OAAO,EACL,qBAAqB,EACrB,KAAK,0BAA0B,GAChC,MAAM,uCAAuC,CAAC;AAG/C,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,wBAAwB,EACxB,eAAe,EACf,aAAa,EACb,cAAc,EACd,KAAK,aAAa,GACnB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,iBAAiB,EAAE,KAAK,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AACnG,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACvF,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAGpF,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,KAAK,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAC3G,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AACjF,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAGvF,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAChG,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC"}
|
package/index.js
CHANGED
|
@@ -12,6 +12,11 @@ export { ToolCallGroup } from "./components/ToolCallGroup.js";
|
|
|
12
12
|
export { SubAgentBlock } from "./components/SubAgentBlock.js";
|
|
13
13
|
export { TodoList } from "./components/TodoList.js";
|
|
14
14
|
export { ApprovalPrompt } from "./components/ApprovalPrompt.js";
|
|
15
|
+
export { FileReviewPrompt } from "./components/FileReviewPrompt.js";
|
|
16
|
+
export { FileReviewRecord } from "./components/FileReviewRecord.js";
|
|
17
|
+
export { FileChangeProgressBar, } from "./components/FileChangeProgressBar.js";
|
|
18
|
+
// File-review pure helpers (kind letters, paths, block reasons, settled counts)
|
|
19
|
+
export { kindLetter, changeDisplayPath, progressEntryDisplayPath, blockReasonNote, settledCounts, settledSummary, } from "./file-review.js";
|
|
15
20
|
export { ExecutionProgress } from "./components/ExecutionProgress.js";
|
|
16
21
|
export { FollowUpInput } from "./components/FollowUpInput.js";
|
|
17
22
|
export { UsageWidget } from "./components/UsageWidget.js";
|
package/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,WAAW;AACX,OAAO,EAAE,kBAAkB,EAAgC,MAAM,eAAe,CAAC;AAEjF,iDAAiD;AACjD,OAAO,EACL,gBAAgB,EAChB,mBAAmB,GAEpB,MAAM,mBAAmB,CAAC;AAE3B,WAAW;AACX,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,aAAa;AACb,OAAO,EAAE,YAAY,EAA0B,MAAM,8BAA8B,CAAC;AACpF,OAAO,EAAE,aAAa,EAA2B,MAAM,+BAA+B,CAAC;AACvF,OAAO,EAAE,YAAY,EAA0B,MAAM,8BAA8B,CAAC;AACpF,OAAO,EAAE,aAAa,EAA2B,MAAM,+BAA+B,CAAC;AACvF,OAAO,EAAE,aAAa,EAA2B,MAAM,+BAA+B,CAAC;AACvF,OAAO,EAAE,QAAQ,EAAsB,MAAM,0BAA0B,CAAC;AACxE,OAAO,EAAE,cAAc,EAA4B,MAAM,gCAAgC,CAAC;AAC1F,OAAO,EAAE,iBAAiB,EAA+B,MAAM,mCAAmC,CAAC;AACnG,OAAO,EAAE,aAAa,EAA2B,MAAM,+BAA+B,CAAC;AACvF,OAAO,EAAE,WAAW,EAAyB,MAAM,6BAA6B,CAAC;AACjF,OAAO,EAAE,YAAY,EAA0B,MAAM,8BAA8B,CAAC;AAEpF,0CAA0C;AAC1C,OAAO,EAAE,cAAc,EAA6C,MAAM,gCAAgC,CAAC;AAC3G,OAAO,EAAE,WAAW,EAAyB,MAAM,6BAA6B,CAAC;AACjF,OAAO,EAAE,aAAa,EAA2B,MAAM,+BAA+B,CAAC;AAEvF,iBAAiB;AACjB,OAAO,EAAE,WAAW,EAA+C,MAAM,sBAAsB,CAAC;AAChG,OAAO,EAAE,UAAU,EAAwB,MAAM,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,WAAW;AACX,OAAO,EAAE,kBAAkB,EAAgC,MAAM,eAAe,CAAC;AAEjF,iDAAiD;AACjD,OAAO,EACL,gBAAgB,EAChB,mBAAmB,GAEpB,MAAM,mBAAmB,CAAC;AAE3B,WAAW;AACX,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,aAAa;AACb,OAAO,EAAE,YAAY,EAA0B,MAAM,8BAA8B,CAAC;AACpF,OAAO,EAAE,aAAa,EAA2B,MAAM,+BAA+B,CAAC;AACvF,OAAO,EAAE,YAAY,EAA0B,MAAM,8BAA8B,CAAC;AACpF,OAAO,EAAE,aAAa,EAA2B,MAAM,+BAA+B,CAAC;AACvF,OAAO,EAAE,aAAa,EAA2B,MAAM,+BAA+B,CAAC;AACvF,OAAO,EAAE,QAAQ,EAAsB,MAAM,0BAA0B,CAAC;AACxE,OAAO,EAAE,cAAc,EAA4B,MAAM,gCAAgC,CAAC;AAC1F,OAAO,EAAE,gBAAgB,EAA8B,MAAM,kCAAkC,CAAC;AAChG,OAAO,EAAE,gBAAgB,EAA8B,MAAM,kCAAkC,CAAC;AAChG,OAAO,EACL,qBAAqB,GAEtB,MAAM,uCAAuC,CAAC;AAE/C,gFAAgF;AAChF,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,wBAAwB,EACxB,eAAe,EACf,aAAa,EACb,cAAc,GAEf,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,iBAAiB,EAA+B,MAAM,mCAAmC,CAAC;AACnG,OAAO,EAAE,aAAa,EAA2B,MAAM,+BAA+B,CAAC;AACvF,OAAO,EAAE,WAAW,EAAyB,MAAM,6BAA6B,CAAC;AACjF,OAAO,EAAE,YAAY,EAA0B,MAAM,8BAA8B,CAAC;AAEpF,0CAA0C;AAC1C,OAAO,EAAE,cAAc,EAA6C,MAAM,gCAAgC,CAAC;AAC3G,OAAO,EAAE,WAAW,EAAyB,MAAM,6BAA6B,CAAC;AACjF,OAAO,EAAE,aAAa,EAA2B,MAAM,+BAA+B,CAAC;AAEvF,iBAAiB;AACjB,OAAO,EAAE,WAAW,EAA+C,MAAM,sBAAsB,CAAC;AAChG,OAAO,EAAE,UAAU,EAAwB,MAAM,qBAAqB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stigmer/ink",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.3",
|
|
4
4
|
"description": "Ink (React for terminals) components for rendering Stigmer agent sessions in the terminal",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@connectrpc/connect-node": "^2.1.1",
|
|
38
|
-
"@stigmer/react": "3.1.
|
|
38
|
+
"@stigmer/react": "3.1.3",
|
|
39
39
|
"ink-spinner": "^5.0.0",
|
|
40
40
|
"ink-text-input": "^6.0.0",
|
|
41
41
|
"marked": "^15.0.7",
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
45
|
"@bufbuild/protobuf": "^2.0.0",
|
|
46
|
-
"@stigmer/protos": "3.1.
|
|
47
|
-
"@stigmer/sdk": "3.1.
|
|
46
|
+
"@stigmer/protos": "3.1.3",
|
|
47
|
+
"@stigmer/sdk": "3.1.3",
|
|
48
48
|
"ink": "^7.0.0",
|
|
49
49
|
"react": ">=19.0.0"
|
|
50
50
|
}
|
|
@@ -0,0 +1,295 @@
|
|
|
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
|
+
});
|