@diegopetrucci/pi-extensions 0.1.74 → 0.1.78
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/LICENSE +2 -0
- package/README.md +17 -12
- package/extensions/agent-workflow-audit/README.md +1 -1
- package/extensions/agent-workflow-audit/index.ts +1631 -1009
- package/extensions/agent-workflow-audit/package.json +1 -1
- package/extensions/annotate-git-diff/README.md +3 -1
- package/extensions/annotate-git-diff/clipboard.ts +109 -100
- package/extensions/annotate-git-diff/git.ts +846 -768
- package/extensions/annotate-git-diff/glimpseui.d.ts +87 -78
- package/extensions/annotate-git-diff/index.ts +712 -671
- package/extensions/annotate-git-diff/package.json +1 -1
- package/extensions/annotate-git-diff/prompt.ts +45 -45
- package/extensions/annotate-git-diff/quiet-glimpse.ts +161 -155
- package/extensions/annotate-git-diff/review-server.ts +337 -311
- package/extensions/annotate-git-diff/types.ts +132 -132
- package/extensions/annotate-git-diff/ui.ts +27 -27
- package/extensions/annotate-git-diff/watch.ts +74 -74
- package/extensions/annotate-git-diff/web/app.js +1947 -1884
- package/extensions/annotate-git-diff/web/index.html +5 -6
- package/extensions/annotate-last-message/README.md +1 -1
- package/extensions/annotate-last-message/glimpseui.d.ts +87 -78
- package/extensions/annotate-last-message/index.ts +210 -206
- package/extensions/annotate-last-message/package.json +1 -1
- package/extensions/annotate-last-message/prompt.ts +87 -66
- package/extensions/annotate-last-message/quiet-glimpse.ts +161 -155
- package/extensions/annotate-last-message/session.ts +94 -89
- package/extensions/annotate-last-message/theme.ts +199 -193
- package/extensions/annotate-last-message/types.ts +23 -23
- package/extensions/annotate-last-message/ui.ts +27 -25
- package/extensions/annotate-last-message/web/app.js +4 -7
- package/extensions/annotate-last-message/web/index.html +3 -3
- package/extensions/annotate-last-message/web/md-renderer.js +1 -1
- package/extensions/brrr/README.md +1 -1
- package/extensions/brrr/index.ts +213 -191
- package/extensions/brrr/package.json +1 -1
- package/extensions/claude-fast/README.md +5 -79
- package/extensions/claude-fast/index.ts +31 -289
- package/extensions/claude-fast/package.json +2 -3
- package/extensions/code-reviewer/README.md +6 -6
- package/extensions/code-reviewer/index.ts +1825 -1373
- package/extensions/code-reviewer/package.json +1 -1
- package/extensions/confirm-destructive/README.md +1 -1
- package/extensions/confirm-destructive/index.ts +50 -52
- package/extensions/confirm-destructive/package.json +1 -1
- package/extensions/context-cap/README.md +9 -9
- package/extensions/context-cap/index.ts +313 -305
- package/extensions/context-cap/package.json +2 -2
- package/extensions/context-inspector/index.ts +1211 -1064
- package/extensions/context-inspector/package.json +1 -1
- package/extensions/contrarian/README.md +4 -3
- package/extensions/contrarian/index.ts +1863 -1737
- package/extensions/contrarian/package.json +1 -1
- package/extensions/dirty-repo-guard/index.ts +37 -41
- package/extensions/dirty-repo-guard/package.json +1 -1
- package/extensions/dynamic-context-pruning/README.md +11 -43
- package/extensions/dynamic-context-pruning/index.ts +2131 -2105
- package/extensions/dynamic-context-pruning/package.json +1 -1
- package/extensions/fast/README.md +52 -14
- package/extensions/fast/index.ts +513 -317
- package/extensions/fast/package.json +1 -1
- package/extensions/git-footer/README.md +1 -1
- package/extensions/git-footer/index.ts +458 -411
- package/extensions/git-footer/package.json +2 -3
- package/extensions/gnosis/index.ts +180 -130
- package/extensions/gnosis/package.json +1 -1
- package/extensions/inline-bash/index.ts +87 -103
- package/extensions/inline-bash/package.json +1 -1
- package/extensions/librarian/README.md +1 -1
- package/extensions/librarian/index.ts +1877 -1419
- package/extensions/librarian/package.json +1 -1
- package/extensions/minimal-footer/README.md +5 -5
- package/extensions/minimal-footer/index.ts +987 -1012
- package/extensions/minimal-footer/minimal-footer.example.json +1 -1
- package/extensions/minimal-footer/openai-usage.ts +195 -190
- package/extensions/minimal-footer/package.json +1 -1
- package/extensions/notify/index.ts +222 -230
- package/extensions/notify/package.json +1 -1
- package/extensions/openai-fast/README.md +5 -73
- package/extensions/openai-fast/index.ts +32 -272
- package/extensions/openai-fast/package.json +2 -3
- package/extensions/oracle/README.md +4 -3
- package/extensions/oracle/index.ts +1740 -1627
- package/extensions/oracle/package.json +1 -1
- package/extensions/permission-gate/README.md +3 -0
- package/extensions/permission-gate/index.ts +785 -612
- package/extensions/permission-gate/package.json +1 -1
- package/extensions/permission-gate/powershell-safety.ts +840 -793
- package/extensions/project-mcp-json/.pi-fleet-tested-version +1 -0
- package/extensions/project-mcp-json/README.md +199 -0
- package/extensions/project-mcp-json/config.ts +564 -0
- package/extensions/project-mcp-json/index.ts +119 -0
- package/extensions/project-mcp-json/package.json +38 -0
- package/extensions/quiet-tools/.pi-fleet-tested-version +1 -1
- package/extensions/quiet-tools/README.md +30 -19
- package/extensions/quiet-tools/index.ts +376 -291
- package/extensions/quiet-tools/package.json +2 -2
- package/extensions/review/README.md +2 -2
- package/extensions/review/index.ts +1745 -1872
- package/extensions/review/package.json +1 -1
- package/extensions/todo/index.ts +280 -280
- package/extensions/todo/package.json +1 -1
- package/extensions/triage-comments/README.md +6 -4
- package/extensions/triage-comments/index.ts +2620 -1888
- package/extensions/triage-comments/package.json +1 -1
- package/package.json +9 -6
- package/assets/social-preview.png +0 -0
- package/extensions/claude-fast/claude-fast.example.json +0 -4
- package/extensions/openai-fast/openai-fast.example.json +0 -4
|
@@ -7,202 +7,202 @@ export type ReviewFileKind = "text" | "binary" | "image";
|
|
|
7
7
|
export type ReviewCommitKind = "commit" | "working-tree";
|
|
8
8
|
|
|
9
9
|
export interface ReviewFileComparison {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
status: ChangeStatus;
|
|
11
|
+
oldPath: string | null;
|
|
12
|
+
newPath: string | null;
|
|
13
|
+
displayPath: string;
|
|
14
|
+
hasOriginal: boolean;
|
|
15
|
+
hasModified: boolean;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
export interface ReviewFile {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
19
|
+
id: string;
|
|
20
|
+
path: string;
|
|
21
|
+
worktreeStatus: ChangeStatus | null;
|
|
22
|
+
hasWorkingTreeFile: boolean;
|
|
23
|
+
/** True when the file is touched by the current branch diff (merge-base/base ref vs HEAD). */
|
|
24
|
+
inGitDiff: boolean;
|
|
25
|
+
/** Diff comparison to render for the file in the current scope.
|
|
26
|
+
* - branch scope: merge-base/base ref vs HEAD
|
|
27
|
+
* - commits scope: commit vs commit^ (populated when loaded via request-commit) */
|
|
28
|
+
gitDiff: ReviewFileComparison | null;
|
|
29
|
+
kind: ReviewFileKind;
|
|
30
|
+
mimeType: string | null;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
export interface ReviewCommitInfo {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
sha: string;
|
|
35
|
+
shortSha: string;
|
|
36
|
+
subject: string;
|
|
37
|
+
authorName: string;
|
|
38
|
+
authorDate: string;
|
|
39
|
+
kind: ReviewCommitKind;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
export interface ReviewFileContents {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
43
|
+
originalContent: string;
|
|
44
|
+
modifiedContent: string;
|
|
45
|
+
kind: ReviewFileKind;
|
|
46
|
+
mimeType: string | null;
|
|
47
|
+
originalExists: boolean;
|
|
48
|
+
modifiedExists: boolean;
|
|
49
|
+
originalPreviewUrl: string | null;
|
|
50
|
+
modifiedPreviewUrl: string | null;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
export type CommentSide = "original" | "modified" | "file";
|
|
54
54
|
|
|
55
55
|
export interface DiffReviewComment {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
56
|
+
id: string;
|
|
57
|
+
fileId: string;
|
|
58
|
+
scope: ReviewScope;
|
|
59
|
+
/** Commit SHA when scope === "commits". */
|
|
60
|
+
commitSha?: string | null;
|
|
61
|
+
/** Short commit identifier to render in the prompt (e.g. first 7 chars of SHA). */
|
|
62
|
+
commitShort?: string | null;
|
|
63
|
+
commitKind?: ReviewCommitKind | null;
|
|
64
|
+
side: CommentSide;
|
|
65
|
+
startLine: number | null;
|
|
66
|
+
endLine: number | null;
|
|
67
|
+
body: string;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
export interface ReviewSubmitPayload {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
71
|
+
type: "submit";
|
|
72
|
+
overallComment: string;
|
|
73
|
+
comments: DiffReviewComment[];
|
|
74
|
+
/**
|
|
75
|
+
* True means this is a close-recovery draft and must stay editor-only.
|
|
76
|
+
* Only literal false permits an explicit submission to trigger an agent turn;
|
|
77
|
+
* malformed or missing values are normalized to true by the host.
|
|
78
|
+
*/
|
|
79
|
+
draft: boolean;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
export interface ReviewCancelPayload {
|
|
83
|
-
|
|
83
|
+
type: "cancel";
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
export interface ReviewRequestFilePayload {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
87
|
+
type: "request-file";
|
|
88
|
+
requestId: string;
|
|
89
|
+
fileId: string;
|
|
90
|
+
scope: ReviewScope;
|
|
91
|
+
commitSha?: string | null;
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
export interface ReviewRequestCommitPayload {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
type: "request-commit";
|
|
96
|
+
requestId: string;
|
|
97
|
+
sha: string;
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
export interface ReviewRequestReviewDataPayload {
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
type: "request-review-data";
|
|
102
|
+
requestId: string;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
export interface ReviewClipboardReadPayload {
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
type: "clipboard-read";
|
|
107
|
+
requestId: string;
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
export interface ReviewClipboardWritePayload {
|
|
111
|
-
|
|
112
|
-
|
|
111
|
+
type: "clipboard-write";
|
|
112
|
+
text: string;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
export type ReviewWindowMessage =
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
116
|
+
| ReviewSubmitPayload
|
|
117
|
+
| ReviewCancelPayload
|
|
118
|
+
| ReviewRequestFilePayload
|
|
119
|
+
| ReviewRequestCommitPayload
|
|
120
|
+
| ReviewRequestReviewDataPayload
|
|
121
|
+
| ReviewClipboardReadPayload
|
|
122
|
+
| ReviewClipboardWritePayload;
|
|
123
123
|
|
|
124
124
|
export interface ReviewFileDataMessage {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
125
|
+
type: "file-data";
|
|
126
|
+
requestId: string;
|
|
127
|
+
fileId: string;
|
|
128
|
+
scope: ReviewScope;
|
|
129
|
+
commitSha?: string | null;
|
|
130
|
+
originalContent: string;
|
|
131
|
+
modifiedContent: string;
|
|
132
|
+
kind: ReviewFileKind;
|
|
133
|
+
mimeType: string | null;
|
|
134
|
+
originalExists: boolean;
|
|
135
|
+
modifiedExists: boolean;
|
|
136
|
+
originalPreviewUrl: string | null;
|
|
137
|
+
modifiedPreviewUrl: string | null;
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
export interface ReviewFileErrorMessage {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
141
|
+
type: "file-error";
|
|
142
|
+
requestId: string;
|
|
143
|
+
fileId: string;
|
|
144
|
+
scope: ReviewScope;
|
|
145
|
+
commitSha?: string | null;
|
|
146
|
+
message: string;
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
export interface ReviewCommitDataMessage {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
150
|
+
type: "commit-data";
|
|
151
|
+
requestId: string;
|
|
152
|
+
sha: string;
|
|
153
|
+
files: ReviewFile[];
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
export interface ReviewCommitErrorMessage {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
157
|
+
type: "commit-error";
|
|
158
|
+
requestId: string;
|
|
159
|
+
sha: string;
|
|
160
|
+
message: string;
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
export interface ReviewReviewDataMessage {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
164
|
+
type: "review-data";
|
|
165
|
+
requestId: string;
|
|
166
|
+
files: ReviewFile[];
|
|
167
|
+
commits: ReviewCommitInfo[];
|
|
168
|
+
branchBaseRef: string | null;
|
|
169
|
+
branchMergeBaseSha: string | null;
|
|
170
|
+
repositoryHasHead: boolean;
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
export interface ReviewReviewDataErrorMessage {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
174
|
+
type: "review-data-error";
|
|
175
|
+
requestId: string;
|
|
176
|
+
message: string;
|
|
177
177
|
}
|
|
178
178
|
|
|
179
179
|
export interface ReviewClipboardDataMessage {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
180
|
+
type: "clipboard-data";
|
|
181
|
+
requestId: string;
|
|
182
|
+
text: string;
|
|
183
|
+
message?: string;
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
export interface ReviewWorkingTreeChangedMessage {
|
|
187
|
-
|
|
188
|
-
|
|
187
|
+
type: "working-tree-changed";
|
|
188
|
+
changedAt: number;
|
|
189
189
|
}
|
|
190
190
|
|
|
191
191
|
export type ReviewHostMessage =
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
192
|
+
| ReviewFileDataMessage
|
|
193
|
+
| ReviewFileErrorMessage
|
|
194
|
+
| ReviewCommitDataMessage
|
|
195
|
+
| ReviewCommitErrorMessage
|
|
196
|
+
| ReviewReviewDataMessage
|
|
197
|
+
| ReviewReviewDataErrorMessage
|
|
198
|
+
| ReviewClipboardDataMessage
|
|
199
|
+
| ReviewWorkingTreeChangedMessage;
|
|
200
200
|
|
|
201
201
|
export interface ReviewWindowData {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
202
|
+
repoRoot: string;
|
|
203
|
+
files: ReviewFile[];
|
|
204
|
+
commits: ReviewCommitInfo[];
|
|
205
|
+
branchBaseRef: string | null;
|
|
206
|
+
branchMergeBaseSha: string | null;
|
|
207
|
+
repositoryHasHead: boolean;
|
|
208
208
|
}
|
|
@@ -7,41 +7,41 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
7
7
|
const webDir = join(__dirname, "web");
|
|
8
8
|
|
|
9
9
|
function escapeForInlineScript(value: string): string {
|
|
10
|
-
|
|
10
|
+
return value.replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026");
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
function escapeInlineScriptSource(value: string): string {
|
|
14
|
-
|
|
14
|
+
return value.replace(/<\/(script)/gi, "<\\/$1");
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export interface ReviewUiAssets {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
tailwindBrowserJs: string;
|
|
19
|
+
monacoLoaderJs: string;
|
|
20
|
+
monacoVsBaseUrl: string;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export function buildReviewHtml(data: ReviewWindowData, assets: ReviewUiAssets): string {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
24
|
+
const templateHtml = readFileSync(join(webDir, "index.html"), "utf8");
|
|
25
|
+
const appJs = escapeInlineScriptSource(readFileSync(join(webDir, "app.js"), "utf8"));
|
|
26
|
+
const payload = escapeForInlineScript(JSON.stringify(data));
|
|
27
|
+
const assetConfig = escapeForInlineScript(
|
|
28
|
+
JSON.stringify({
|
|
29
|
+
monacoVsBaseUrl: assets.monacoVsBaseUrl,
|
|
30
|
+
bootstrapError: null,
|
|
31
|
+
}),
|
|
32
|
+
);
|
|
33
|
+
const replacements = new Map([
|
|
34
|
+
['"__INLINE_DATA__"', payload],
|
|
35
|
+
["__INLINE_ASSET_CONFIG__", assetConfig],
|
|
36
|
+
["__INLINE_TAILWIND_JS__", escapeInlineScriptSource(assets.tailwindBrowserJs)],
|
|
37
|
+
["__INLINE_MONACO_LOADER_JS__", escapeInlineScriptSource(assets.monacoLoaderJs)],
|
|
38
|
+
["__INLINE_JS__", appJs],
|
|
39
|
+
]);
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
41
|
+
// Replace the original template in one pass so marker-like repository data
|
|
42
|
+
// or runtime source is never scanned as another template placeholder.
|
|
43
|
+
return templateHtml.replace(
|
|
44
|
+
/"__INLINE_DATA__"|__INLINE_ASSET_CONFIG__|__INLINE_TAILWIND_JS__|__INLINE_MONACO_LOADER_JS__|__INLINE_JS__/g,
|
|
45
|
+
(marker) => replacements.get(marker) ?? marker,
|
|
46
|
+
);
|
|
47
47
|
}
|
|
@@ -1,104 +1,104 @@
|
|
|
1
1
|
import { type FSWatcher, watch } from "node:fs";
|
|
2
2
|
|
|
3
3
|
export interface RepoChangeWatcher {
|
|
4
|
-
|
|
4
|
+
dispose(): void;
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
interface RepoChangeWatcherOptions {
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
debounceMs?: number;
|
|
9
|
+
onError?: (error: Error) => void;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
const DEFAULT_DEBOUNCE_MS = 2000;
|
|
13
13
|
|
|
14
14
|
const ignoredPathSegments = new Set([
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
15
|
+
".cache",
|
|
16
|
+
".git",
|
|
17
|
+
".hg",
|
|
18
|
+
".next",
|
|
19
|
+
".nuxt",
|
|
20
|
+
".svn",
|
|
21
|
+
".turbo",
|
|
22
|
+
"build",
|
|
23
|
+
"coverage",
|
|
24
|
+
"dist",
|
|
25
|
+
"node_modules",
|
|
26
|
+
"out",
|
|
27
|
+
"target",
|
|
28
|
+
"tmp",
|
|
29
29
|
]);
|
|
30
30
|
|
|
31
31
|
const ignoredFileNames = new Set([".DS_Store"]);
|
|
32
32
|
|
|
33
33
|
function normalizeWatchPath(path: string): string {
|
|
34
|
-
|
|
34
|
+
return path.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
export function isIgnoredWatchPath(path: string | Buffer | null | undefined): boolean {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
if (path == null) return false;
|
|
39
|
+
const normalized = normalizeWatchPath(path.toString());
|
|
40
|
+
if (normalized.length === 0) return false;
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
const segments = normalized.split("/").filter((segment) => segment.length > 0);
|
|
43
|
+
if (segments.some((segment) => ignoredPathSegments.has(segment))) return true;
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
const fileName = segments.at(-1) ?? normalized;
|
|
46
|
+
if (ignoredFileNames.has(fileName)) return true;
|
|
47
|
+
if (fileName.endsWith("~") || fileName.endsWith(".swp") || fileName.endsWith(".tmp")) return true;
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
return false;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
export function createRepoChangeWatcher(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
repoRoot: string,
|
|
54
|
+
onChange: () => void,
|
|
55
|
+
options: RepoChangeWatcherOptions = {},
|
|
56
56
|
): RepoChangeWatcher {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
57
|
+
const debounceMs = options.debounceMs ?? DEFAULT_DEBOUNCE_MS;
|
|
58
|
+
let disposed = false;
|
|
59
|
+
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
60
|
+
let watcher: FSWatcher | null = null;
|
|
61
|
+
|
|
62
|
+
const clearPending = (): void => {
|
|
63
|
+
if (timer == null) return;
|
|
64
|
+
clearTimeout(timer);
|
|
65
|
+
timer = null;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const scheduleChange = (path: string | Buffer | null): void => {
|
|
69
|
+
if (disposed || isIgnoredWatchPath(path)) return;
|
|
70
|
+
clearPending();
|
|
71
|
+
timer = setTimeout(() => {
|
|
72
|
+
timer = null;
|
|
73
|
+
if (!disposed) onChange();
|
|
74
|
+
}, debounceMs);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
watcher = watch(repoRoot, { recursive: true }, (_eventType, fileName) => {
|
|
79
|
+
scheduleChange(fileName);
|
|
80
|
+
});
|
|
81
|
+
watcher.on("error", (error) => {
|
|
82
|
+
if (!disposed) options.onError?.(error);
|
|
83
|
+
});
|
|
84
|
+
} catch (error) {
|
|
85
|
+
options.onError?.(error instanceof Error ? error : new Error(String(error)));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
dispose() {
|
|
90
|
+
disposed = true;
|
|
91
|
+
clearPending();
|
|
92
|
+
try {
|
|
93
|
+
watcher?.close();
|
|
94
|
+
} catch {
|
|
95
|
+
// Ignore watcher shutdown races.
|
|
96
|
+
}
|
|
97
|
+
watcher = null;
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
export const __testing = {
|
|
103
|
-
|
|
103
|
+
isIgnoredWatchPath,
|
|
104
104
|
};
|