@ai-setting/roy-plugin-task-show 2.0.0 → 2.0.2
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/dist/file-tree.d.ts +184 -0
- package/dist/file-tree.d.ts.map +1 -0
- package/dist/file-tree.js +377 -0
- package/dist/file-tree.js.map +1 -0
- package/dist/server.d.ts +13 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +148 -53
- package/dist/server.js.map +1 -1
- package/dist/tool-call-detail.d.ts +123 -0
- package/dist/tool-call-detail.d.ts.map +1 -0
- package/dist/tool-call-detail.js +423 -0
- package/dist/tool-call-detail.js.map +1 -0
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/public/app.js +19 -3
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview File tree data layer (v2.0.0).
|
|
3
|
+
*
|
|
4
|
+
* Pure functions that turn a flat list of git-tracked paths into a
|
|
5
|
+
* hierarchical tree suitable for the per-task detail page sidebar.
|
|
6
|
+
*
|
|
7
|
+
* Design context: see the Feishu design doc (`roy-plugin-task-show
|
|
8
|
+
* v2.0.0 设计方案`) §2.3 文件树组件 and §5.1 关键组件拆分. We keep
|
|
9
|
+
* the data layer framework-free so the SSR side can serialize it as
|
|
10
|
+
* a `data-files` JSON attribute on the sidebar container and the
|
|
11
|
+
* client (`public/file-tree.js`) can hydrate the rendered DOM.
|
|
12
|
+
*
|
|
13
|
+
* Conventions:
|
|
14
|
+
* - Paths use forward slashes (POSIX). Git always emits `/`.
|
|
15
|
+
* - Paths are relative to the repo root (the worktree). No leading
|
|
16
|
+
* `/`. The frontend never sees the absolute filesystem path.
|
|
17
|
+
* - Tree nodes carry enough metadata for the renderer to decide
|
|
18
|
+
* highlight / collapse / scroll behaviour without a second pass.
|
|
19
|
+
*
|
|
20
|
+
* The renderer side (vanilla JS in `public/file-tree.js`) handles:
|
|
21
|
+
* - chevron toggle (▶ / ▼),
|
|
22
|
+
* - default expansion depth = 2,
|
|
23
|
+
* - keyboard navigation (↑ / ↓ / Enter),
|
|
24
|
+
* - scroll-into-view when a mermaid node is clicked.
|
|
25
|
+
*
|
|
26
|
+
* The data side (this file) handles:
|
|
27
|
+
* - nesting,
|
|
28
|
+
* - sorting (directories first, then alphabetical),
|
|
29
|
+
* - dedup,
|
|
30
|
+
* - lookup helpers (find by path, collect all paths).
|
|
31
|
+
*/
|
|
32
|
+
/**
|
|
33
|
+
* A single node in the file tree.
|
|
34
|
+
*
|
|
35
|
+
* `type` distinguishes leaves (files) from interior nodes (directories)
|
|
36
|
+
* so the renderer can pick the right glyph + click handler without
|
|
37
|
+
* inspecting `children`.
|
|
38
|
+
*
|
|
39
|
+
* `depth` is the 0-indexed level relative to the tree root. Roots are
|
|
40
|
+
* always depth 0, even if the input path is `"a/b/c.ts"` (the `a`
|
|
41
|
+
* directory becomes a depth-0 root).
|
|
42
|
+
*/
|
|
43
|
+
export interface TreeNode {
|
|
44
|
+
/** Basename (last path segment). */
|
|
45
|
+
name: string;
|
|
46
|
+
/** Full path with `/` separators, identical to the input. */
|
|
47
|
+
path: string;
|
|
48
|
+
/** `"file"` for leaves, `"directory"` for interior nodes. */
|
|
49
|
+
type: "file" | "directory";
|
|
50
|
+
/** 0-indexed depth from the tree root. */
|
|
51
|
+
depth: number;
|
|
52
|
+
/** Empty for leaves. Always defined (possibly empty) for directories. */
|
|
53
|
+
children: TreeNode[];
|
|
54
|
+
/**
|
|
55
|
+
* v2.0.0 highlight flag, set by the caller (the per-task renderer)
|
|
56
|
+
* to indicate the file is touched by the currently-selected tool
|
|
57
|
+
* call. The data layer never sets it.
|
|
58
|
+
*/
|
|
59
|
+
highlighted?: boolean;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Threshold above which the renderer switches to windowed virtualization.
|
|
63
|
+
*
|
|
64
|
+
* 500 was chosen because it's the inflection point where most browsers
|
|
65
|
+
* start to lag on `scrollIntoView` + addEventListener for the whole
|
|
66
|
+
* tree. We surface the constant so tests can lock it down and the
|
|
67
|
+
* renderer side can import the same value without duplication.
|
|
68
|
+
*/
|
|
69
|
+
export declare const MAX_NODES_BEFORE_VIRTUAL = 500;
|
|
70
|
+
/**
|
|
71
|
+
* Build a nested tree from a flat list of paths.
|
|
72
|
+
*
|
|
73
|
+
* Behaviour:
|
|
74
|
+
* - Empty input → `[]`.
|
|
75
|
+
* - Duplicate paths → silently deduped.
|
|
76
|
+
* - Sorting at every level: directories first, then alphabetical
|
|
77
|
+
* by basename (case-sensitive, identical to git's default sort).
|
|
78
|
+
* - The `path` field on each node is the full slash-joined path.
|
|
79
|
+
*
|
|
80
|
+
* Complexity: O(N * D) where N is the number of paths and D is the
|
|
81
|
+
* average depth. We use a `Map` keyed by path to avoid O(N²) lookups
|
|
82
|
+
* during tree assembly.
|
|
83
|
+
*/
|
|
84
|
+
export declare function buildFileTree(paths: readonly string[]): TreeNode[];
|
|
85
|
+
/**
|
|
86
|
+
* Extract the set of files referenced by an array of tool-call records.
|
|
87
|
+
*
|
|
88
|
+
* Mirrors `extractArgPath()` but returns ALL paths (not just the first
|
|
89
|
+
* match), deduped. Used by the renderer to compute the "highlight"
|
|
90
|
+
* set for the file-tree sidebar.
|
|
91
|
+
*
|
|
92
|
+
* Inputs are typed loosely so the function is callable from both the
|
|
93
|
+
* SSR side (where we have `ToolCallRecord[]`) and tests (where we
|
|
94
|
+
* usually pass `{ args: { file_path: "..." } }` shims).
|
|
95
|
+
*/
|
|
96
|
+
export declare function extractAffectedPaths(toolCalls: ReadonlyArray<{
|
|
97
|
+
args?: Record<string, unknown>;
|
|
98
|
+
}>): string[];
|
|
99
|
+
/**
|
|
100
|
+
* Find a node by its full path. Returns null when the path doesn't
|
|
101
|
+
* appear in the tree. Performs a depth-first walk — acceptable because
|
|
102
|
+
* the renderer only calls this when a mermaid node is clicked (one
|
|
103
|
+
* lookup per click), and the tree depth is bounded by typical repo
|
|
104
|
+
* depth (≤ 10 levels).
|
|
105
|
+
*/
|
|
106
|
+
export declare function findNodeByPath(roots: readonly TreeNode[], target: string): TreeNode | null;
|
|
107
|
+
/**
|
|
108
|
+
* Collect every leaf (file) path from the tree, in DFS pre-order.
|
|
109
|
+
* Used by the keyboard-search index — the renderer keeps a flat array
|
|
110
|
+
* so search-by-prefix is O(N) over a reasonable file count.
|
|
111
|
+
*/
|
|
112
|
+
export declare function collectAllPaths(roots: readonly TreeNode[]): string[];
|
|
113
|
+
/**
|
|
114
|
+
* Subset of `child_process` we use. Inlined here so tests can pass a
|
|
115
|
+
* fake `runner` without importing `node:child_process`.
|
|
116
|
+
*/
|
|
117
|
+
export interface GitRunner {
|
|
118
|
+
(args: readonly string[], opts?: {
|
|
119
|
+
cwd?: string;
|
|
120
|
+
}): {
|
|
121
|
+
stdout: string;
|
|
122
|
+
stderr: string;
|
|
123
|
+
status: number;
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Default runner: shells out to `git ls-files` via the system PATH.
|
|
128
|
+
* Uses `-z` to NUL-separate paths so filenames with embedded newlines
|
|
129
|
+
* survive the round-trip, AND `--full-name` so paths are emitted
|
|
130
|
+
* relative to the worktree / repo root (NOT the current working
|
|
131
|
+
* directory). This keeps the file-tree stable when the plugin is
|
|
132
|
+
* started from a subdirectory — important because `args.file_path`
|
|
133
|
+
* from the host typically uses worktree-root-relative paths.
|
|
134
|
+
*
|
|
135
|
+
* On non-zero exit (e.g. cwd is not a git repo) returns `stdout: ""`
|
|
136
|
+
* so callers can degrade gracefully — the file-tree panel renders
|
|
137
|
+
* empty rather than blowing up the page.
|
|
138
|
+
*/
|
|
139
|
+
export declare const defaultGitLsFilesRunner: GitRunner;
|
|
140
|
+
/**
|
|
141
|
+
* Run `git ls-files` and return the parsed file list.
|
|
142
|
+
*
|
|
143
|
+
* Behaviour:
|
|
144
|
+
* - Splits on NUL (the `-z` separator).
|
|
145
|
+
* - Filters out empty segments.
|
|
146
|
+
* - On non-zero exit (not a git repo, git missing) returns `[]`
|
|
147
|
+
* instead of throwing — the renderer should still render an
|
|
148
|
+
* empty (but valid) tree.
|
|
149
|
+
* - Honors a 30-second in-memory cache keyed by cwd. This keeps
|
|
150
|
+
* SSR renders cheap — without it every page refresh would
|
|
151
|
+
* spawn `git` synchronously. The cache is bounded to 8 entries
|
|
152
|
+
* (FIFO drop). Exported as a separate `fileTreeCacheTestOnly`
|
|
153
|
+
* getter for test injection; production callers should never
|
|
154
|
+
* touch the cache directly.
|
|
155
|
+
*
|
|
156
|
+
* The runner is injected so tests can swap it for a fake; production
|
|
157
|
+
* callers pass `defaultGitLsFilesRunner`.
|
|
158
|
+
*/
|
|
159
|
+
export declare function gitLsFiles(runner?: GitRunner, opts?: {
|
|
160
|
+
cwd?: string;
|
|
161
|
+
}): string[];
|
|
162
|
+
/**
|
|
163
|
+
* In-memory TTL cache used by `gitLsFiles()`. Exported as a const so
|
|
164
|
+
* tests can clear it between cases; production callers should never
|
|
165
|
+
* touch it directly.
|
|
166
|
+
*/
|
|
167
|
+
export declare const fileTreeCache: Map<string, {
|
|
168
|
+
files: string[];
|
|
169
|
+
at: number;
|
|
170
|
+
}>;
|
|
171
|
+
/** v2.0.0 TTL: 30s — same window as the SSE stale cache. */
|
|
172
|
+
export declare const FILE_TREE_CACHE_TTL_MS = 30000;
|
|
173
|
+
/** v2.0.0 max cached cwds (worktree + main + a few tests). */
|
|
174
|
+
export declare const FILE_TREE_CACHE_MAX = 8;
|
|
175
|
+
/**
|
|
176
|
+
* Parse the raw output of `git ls-files -z`. Public so the server-side
|
|
177
|
+
* cache layer (and tests) can re-parse a captured snapshot without
|
|
178
|
+
* re-spawning git.
|
|
179
|
+
*
|
|
180
|
+
* `git ls-files -z` emits paths separated by NUL bytes; the very last
|
|
181
|
+
* segment has no trailing NUL. We split on NUL and drop empties.
|
|
182
|
+
*/
|
|
183
|
+
export declare function parseLsFiles(raw: string): string[];
|
|
184
|
+
//# sourceMappingURL=file-tree.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-tree.d.ts","sourceRoot":"","sources":["../src/file-tree.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH;;;;;;;;;;GAUG;AACH,MAAM,WAAW,QAAQ;IACvB,oCAAoC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,0CAA0C;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,yEAAyE;IACzE,QAAQ,EAAE,QAAQ,EAAE,CAAC;IACrB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAkB5C;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,QAAQ,EAAE,CAmElE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,aAAa,CAAC;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,GAC3D,MAAM,EAAE,CAcV;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,SAAS,QAAQ,EAAE,EAC1B,MAAM,EAAE,MAAM,GACb,QAAQ,GAAG,IAAI,CAOjB;AAWD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,GAAG,MAAM,EAAE,CAIpE;AAQD;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG;QAClD,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,uBAAuB,EAAE,SAiBrC,CAAC;AAEF;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,UAAU,CACxB,MAAM,GAAE,SAAmC,EAC3C,IAAI,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GACtB,MAAM,EAAE,CAgCV;AAED;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAC5D,CAAC;AAEZ,4DAA4D;AAC5D,eAAO,MAAM,sBAAsB,QAAS,CAAC;AAE7C,8DAA8D;AAC9D,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CASlD"}
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview File tree data layer (v2.0.0).
|
|
3
|
+
*
|
|
4
|
+
* Pure functions that turn a flat list of git-tracked paths into a
|
|
5
|
+
* hierarchical tree suitable for the per-task detail page sidebar.
|
|
6
|
+
*
|
|
7
|
+
* Design context: see the Feishu design doc (`roy-plugin-task-show
|
|
8
|
+
* v2.0.0 设计方案`) §2.3 文件树组件 and §5.1 关键组件拆分. We keep
|
|
9
|
+
* the data layer framework-free so the SSR side can serialize it as
|
|
10
|
+
* a `data-files` JSON attribute on the sidebar container and the
|
|
11
|
+
* client (`public/file-tree.js`) can hydrate the rendered DOM.
|
|
12
|
+
*
|
|
13
|
+
* Conventions:
|
|
14
|
+
* - Paths use forward slashes (POSIX). Git always emits `/`.
|
|
15
|
+
* - Paths are relative to the repo root (the worktree). No leading
|
|
16
|
+
* `/`. The frontend never sees the absolute filesystem path.
|
|
17
|
+
* - Tree nodes carry enough metadata for the renderer to decide
|
|
18
|
+
* highlight / collapse / scroll behaviour without a second pass.
|
|
19
|
+
*
|
|
20
|
+
* The renderer side (vanilla JS in `public/file-tree.js`) handles:
|
|
21
|
+
* - chevron toggle (▶ / ▼),
|
|
22
|
+
* - default expansion depth = 2,
|
|
23
|
+
* - keyboard navigation (↑ / ↓ / Enter),
|
|
24
|
+
* - scroll-into-view when a mermaid node is clicked.
|
|
25
|
+
*
|
|
26
|
+
* The data side (this file) handles:
|
|
27
|
+
* - nesting,
|
|
28
|
+
* - sorting (directories first, then alphabetical),
|
|
29
|
+
* - dedup,
|
|
30
|
+
* - lookup helpers (find by path, collect all paths).
|
|
31
|
+
*/
|
|
32
|
+
/**
|
|
33
|
+
* Threshold above which the renderer switches to windowed virtualization.
|
|
34
|
+
*
|
|
35
|
+
* 500 was chosen because it's the inflection point where most browsers
|
|
36
|
+
* start to lag on `scrollIntoView` + addEventListener for the whole
|
|
37
|
+
* tree. We surface the constant so tests can lock it down and the
|
|
38
|
+
* renderer side can import the same value without duplication.
|
|
39
|
+
*/
|
|
40
|
+
export const MAX_NODES_BEFORE_VIRTUAL = 500;
|
|
41
|
+
/**
|
|
42
|
+
* Canonical alias list for "the path-like field in a tool call args".
|
|
43
|
+
*
|
|
44
|
+
* Matches `extractArgPath()` in `src/server.ts:1095` — keep them in sync.
|
|
45
|
+
* If you add a new alias, add it here AND in the server-side helper AND
|
|
46
|
+
* update `test/server-extract-arg-path.test.ts` if we add one.
|
|
47
|
+
*/
|
|
48
|
+
const PATH_KEY_ALIASES = [
|
|
49
|
+
"file_path",
|
|
50
|
+
"filepath",
|
|
51
|
+
"path",
|
|
52
|
+
"target_file",
|
|
53
|
+
"uri",
|
|
54
|
+
"filename",
|
|
55
|
+
];
|
|
56
|
+
/**
|
|
57
|
+
* Build a nested tree from a flat list of paths.
|
|
58
|
+
*
|
|
59
|
+
* Behaviour:
|
|
60
|
+
* - Empty input → `[]`.
|
|
61
|
+
* - Duplicate paths → silently deduped.
|
|
62
|
+
* - Sorting at every level: directories first, then alphabetical
|
|
63
|
+
* by basename (case-sensitive, identical to git's default sort).
|
|
64
|
+
* - The `path` field on each node is the full slash-joined path.
|
|
65
|
+
*
|
|
66
|
+
* Complexity: O(N * D) where N is the number of paths and D is the
|
|
67
|
+
* average depth. We use a `Map` keyed by path to avoid O(N²) lookups
|
|
68
|
+
* during tree assembly.
|
|
69
|
+
*/
|
|
70
|
+
export function buildFileTree(paths) {
|
|
71
|
+
if (paths.length === 0)
|
|
72
|
+
return [];
|
|
73
|
+
// Dedupe (preserve first occurrence's order).
|
|
74
|
+
const seen = new Set();
|
|
75
|
+
const unique = [];
|
|
76
|
+
for (const p of paths) {
|
|
77
|
+
if (typeof p !== "string" || p.length === 0)
|
|
78
|
+
continue;
|
|
79
|
+
if (seen.has(p))
|
|
80
|
+
continue;
|
|
81
|
+
seen.add(p);
|
|
82
|
+
unique.push(p);
|
|
83
|
+
}
|
|
84
|
+
if (unique.length === 0)
|
|
85
|
+
return [];
|
|
86
|
+
// Mutable root map keyed by full path. Each entry is the in-progress
|
|
87
|
+
// TreeNode; we'll freeze the structure at the end.
|
|
88
|
+
const nodeMap = new Map();
|
|
89
|
+
for (const fullPath of unique) {
|
|
90
|
+
const segments = fullPath.split("/");
|
|
91
|
+
let acc = "";
|
|
92
|
+
for (let i = 0; i < segments.length; i++) {
|
|
93
|
+
const seg = segments[i];
|
|
94
|
+
acc = acc.length === 0 ? seg : `${acc}/${seg}`;
|
|
95
|
+
const isLeaf = i === segments.length - 1;
|
|
96
|
+
let existing = nodeMap.get(acc);
|
|
97
|
+
if (!existing) {
|
|
98
|
+
existing = {
|
|
99
|
+
name: seg,
|
|
100
|
+
path: acc,
|
|
101
|
+
type: isLeaf ? "file" : "directory",
|
|
102
|
+
depth: 0, // fixed up below
|
|
103
|
+
children: [],
|
|
104
|
+
};
|
|
105
|
+
nodeMap.set(acc, existing);
|
|
106
|
+
// Attach to parent (unless we're at the root level).
|
|
107
|
+
if (i > 0) {
|
|
108
|
+
const parentPath = segments.slice(0, i).join("/");
|
|
109
|
+
const parent = nodeMap.get(parentPath);
|
|
110
|
+
if (parent)
|
|
111
|
+
parent.children.push(existing);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
else if (isLeaf && existing.type === "directory") {
|
|
115
|
+
// A previous iteration registered this node as a directory
|
|
116
|
+
// (because we saw a child first). Promote it to file? No —
|
|
117
|
+
// git can't track both a file and a directory at the same
|
|
118
|
+
// path. Keep the directory shape and treat the leaf as a
|
|
119
|
+
// no-op duplicate. This matches `git ls-tree` semantics.
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Roots are every node whose path has no parent in the map.
|
|
124
|
+
const roots = [];
|
|
125
|
+
for (const node of nodeMap.values()) {
|
|
126
|
+
const parentPath = parentPathOf(node.path);
|
|
127
|
+
if (parentPath === null || !nodeMap.has(parentPath)) {
|
|
128
|
+
roots.push(node);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// Recursively set depth + sort children.
|
|
132
|
+
for (const root of roots)
|
|
133
|
+
fixDepthAndSort(root, 0);
|
|
134
|
+
// Final pass: sort the root array too (mirrors inner-level rules).
|
|
135
|
+
sortNodes(roots);
|
|
136
|
+
return roots;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Extract the set of files referenced by an array of tool-call records.
|
|
140
|
+
*
|
|
141
|
+
* Mirrors `extractArgPath()` but returns ALL paths (not just the first
|
|
142
|
+
* match), deduped. Used by the renderer to compute the "highlight"
|
|
143
|
+
* set for the file-tree sidebar.
|
|
144
|
+
*
|
|
145
|
+
* Inputs are typed loosely so the function is callable from both the
|
|
146
|
+
* SSR side (where we have `ToolCallRecord[]`) and tests (where we
|
|
147
|
+
* usually pass `{ args: { file_path: "..." } }` shims).
|
|
148
|
+
*/
|
|
149
|
+
export function extractAffectedPaths(toolCalls) {
|
|
150
|
+
const out = [];
|
|
151
|
+
const seen = new Set();
|
|
152
|
+
for (const tc of toolCalls) {
|
|
153
|
+
const args = tc?.args ?? {};
|
|
154
|
+
for (const key of PATH_KEY_ALIASES) {
|
|
155
|
+
const v = args[key];
|
|
156
|
+
if (typeof v === "string" && v.length > 0 && !seen.has(v)) {
|
|
157
|
+
seen.add(v);
|
|
158
|
+
out.push(v);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return out;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Find a node by its full path. Returns null when the path doesn't
|
|
166
|
+
* appear in the tree. Performs a depth-first walk — acceptable because
|
|
167
|
+
* the renderer only calls this when a mermaid node is clicked (one
|
|
168
|
+
* lookup per click), and the tree depth is bounded by typical repo
|
|
169
|
+
* depth (≤ 10 levels).
|
|
170
|
+
*/
|
|
171
|
+
export function findNodeByPath(roots, target) {
|
|
172
|
+
if (!target)
|
|
173
|
+
return null;
|
|
174
|
+
for (const root of roots) {
|
|
175
|
+
const hit = findNodeByPathInSubtree(root, target);
|
|
176
|
+
if (hit)
|
|
177
|
+
return hit;
|
|
178
|
+
}
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
function findNodeByPathInSubtree(node, target) {
|
|
182
|
+
if (node.path === target)
|
|
183
|
+
return node;
|
|
184
|
+
for (const child of node.children) {
|
|
185
|
+
const hit = findNodeByPathInSubtree(child, target);
|
|
186
|
+
if (hit)
|
|
187
|
+
return hit;
|
|
188
|
+
}
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Collect every leaf (file) path from the tree, in DFS pre-order.
|
|
193
|
+
* Used by the keyboard-search index — the renderer keeps a flat array
|
|
194
|
+
* so search-by-prefix is O(N) over a reasonable file count.
|
|
195
|
+
*/
|
|
196
|
+
export function collectAllPaths(roots) {
|
|
197
|
+
const out = [];
|
|
198
|
+
for (const root of roots)
|
|
199
|
+
collectAllPathsInner(root, out);
|
|
200
|
+
return out;
|
|
201
|
+
}
|
|
202
|
+
// ---------------------------------------------------------------------------
|
|
203
|
+
// Git integration
|
|
204
|
+
// ---------------------------------------------------------------------------
|
|
205
|
+
import { spawnSync } from "node:child_process";
|
|
206
|
+
/**
|
|
207
|
+
* Default runner: shells out to `git ls-files` via the system PATH.
|
|
208
|
+
* Uses `-z` to NUL-separate paths so filenames with embedded newlines
|
|
209
|
+
* survive the round-trip, AND `--full-name` so paths are emitted
|
|
210
|
+
* relative to the worktree / repo root (NOT the current working
|
|
211
|
+
* directory). This keeps the file-tree stable when the plugin is
|
|
212
|
+
* started from a subdirectory — important because `args.file_path`
|
|
213
|
+
* from the host typically uses worktree-root-relative paths.
|
|
214
|
+
*
|
|
215
|
+
* On non-zero exit (e.g. cwd is not a git repo) returns `stdout: ""`
|
|
216
|
+
* so callers can degrade gracefully — the file-tree panel renders
|
|
217
|
+
* empty rather than blowing up the page.
|
|
218
|
+
*/
|
|
219
|
+
export const defaultGitLsFilesRunner = (args, opts) => {
|
|
220
|
+
// v2.0.2 fix: replaced lazy require() with top-level import.
|
|
221
|
+
// The previous `require("node:child_process")` invocation failed
|
|
222
|
+
// in Node.js ESM scope (`ReferenceError: require is not defined`).
|
|
223
|
+
// The throw was swallowed by `gitLsFiles()`'s try/catch, so the
|
|
224
|
+
// file-tree panel silently rendered "No git-tracked files" forever.
|
|
225
|
+
// See Task #2682 Bug 1.
|
|
226
|
+
const result = spawnSync("git", ["ls-files", "-z", "--full-name", ...args], {
|
|
227
|
+
cwd: opts?.cwd ?? process.cwd(),
|
|
228
|
+
encoding: "utf8",
|
|
229
|
+
maxBuffer: 32 * 1024 * 1024,
|
|
230
|
+
});
|
|
231
|
+
return {
|
|
232
|
+
stdout: typeof result.stdout === "string" ? result.stdout : "",
|
|
233
|
+
stderr: typeof result.stderr === "string" ? result.stderr : "",
|
|
234
|
+
status: result.status ?? 0,
|
|
235
|
+
};
|
|
236
|
+
};
|
|
237
|
+
/**
|
|
238
|
+
* Run `git ls-files` and return the parsed file list.
|
|
239
|
+
*
|
|
240
|
+
* Behaviour:
|
|
241
|
+
* - Splits on NUL (the `-z` separator).
|
|
242
|
+
* - Filters out empty segments.
|
|
243
|
+
* - On non-zero exit (not a git repo, git missing) returns `[]`
|
|
244
|
+
* instead of throwing — the renderer should still render an
|
|
245
|
+
* empty (but valid) tree.
|
|
246
|
+
* - Honors a 30-second in-memory cache keyed by cwd. This keeps
|
|
247
|
+
* SSR renders cheap — without it every page refresh would
|
|
248
|
+
* spawn `git` synchronously. The cache is bounded to 8 entries
|
|
249
|
+
* (FIFO drop). Exported as a separate `fileTreeCacheTestOnly`
|
|
250
|
+
* getter for test injection; production callers should never
|
|
251
|
+
* touch the cache directly.
|
|
252
|
+
*
|
|
253
|
+
* The runner is injected so tests can swap it for a fake; production
|
|
254
|
+
* callers pass `defaultGitLsFilesRunner`.
|
|
255
|
+
*/
|
|
256
|
+
export function gitLsFiles(runner = defaultGitLsFilesRunner, opts) {
|
|
257
|
+
const cwd = opts?.cwd ?? (typeof process !== "undefined" ? process.cwd() : "");
|
|
258
|
+
const cacheKey = cwd ? `filetree:${cwd}` : "filetree:";
|
|
259
|
+
const now = Date.now();
|
|
260
|
+
const cached = fileTreeCache.get(cacheKey);
|
|
261
|
+
if (cached && now - cached.at < FILE_TREE_CACHE_TTL_MS) {
|
|
262
|
+
return cached.files;
|
|
263
|
+
}
|
|
264
|
+
let stdout;
|
|
265
|
+
let status;
|
|
266
|
+
try {
|
|
267
|
+
const result = runner([], opts);
|
|
268
|
+
stdout = result.stdout;
|
|
269
|
+
status = result.status;
|
|
270
|
+
}
|
|
271
|
+
catch {
|
|
272
|
+
return cached ? cached.files : [];
|
|
273
|
+
}
|
|
274
|
+
// Only cache successful results — failures (status != 0) are
|
|
275
|
+
// transient and we want the next caller to be able to retry. This
|
|
276
|
+
// also prevents the cache from being poisoned by a test that
|
|
277
|
+
// intentionally returns a fake failure.
|
|
278
|
+
if (status !== 0)
|
|
279
|
+
return cached ? cached.files : [];
|
|
280
|
+
const files = parseLsFiles(stdout);
|
|
281
|
+
// FIFO bound — drop oldest entry if we'd exceed the cap.
|
|
282
|
+
if (fileTreeCache.size >= FILE_TREE_CACHE_MAX) {
|
|
283
|
+
const firstKey = fileTreeCache.keys().next().value;
|
|
284
|
+
if (firstKey !== undefined && firstKey !== cacheKey) {
|
|
285
|
+
fileTreeCache.delete(firstKey);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
fileTreeCache.set(cacheKey, { files, at: now });
|
|
289
|
+
return files;
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* In-memory TTL cache used by `gitLsFiles()`. Exported as a const so
|
|
293
|
+
* tests can clear it between cases; production callers should never
|
|
294
|
+
* touch it directly.
|
|
295
|
+
*/
|
|
296
|
+
export const fileTreeCache = new Map();
|
|
297
|
+
/** v2.0.0 TTL: 30s — same window as the SSE stale cache. */
|
|
298
|
+
export const FILE_TREE_CACHE_TTL_MS = 30_000;
|
|
299
|
+
/** v2.0.0 max cached cwds (worktree + main + a few tests). */
|
|
300
|
+
export const FILE_TREE_CACHE_MAX = 8;
|
|
301
|
+
/**
|
|
302
|
+
* Parse the raw output of `git ls-files -z`. Public so the server-side
|
|
303
|
+
* cache layer (and tests) can re-parse a captured snapshot without
|
|
304
|
+
* re-spawning git.
|
|
305
|
+
*
|
|
306
|
+
* `git ls-files -z` emits paths separated by NUL bytes; the very last
|
|
307
|
+
* segment has no trailing NUL. We split on NUL and drop empties.
|
|
308
|
+
*/
|
|
309
|
+
export function parseLsFiles(raw) {
|
|
310
|
+
if (!raw)
|
|
311
|
+
return [];
|
|
312
|
+
const parts = raw.split("\0");
|
|
313
|
+
const out = [];
|
|
314
|
+
for (const p of parts) {
|
|
315
|
+
if (p.length === 0)
|
|
316
|
+
continue;
|
|
317
|
+
out.push(p);
|
|
318
|
+
}
|
|
319
|
+
return out;
|
|
320
|
+
}
|
|
321
|
+
function collectAllPathsInner(node, out) {
|
|
322
|
+
if (node.type === "file")
|
|
323
|
+
out.push(node.path);
|
|
324
|
+
for (const child of node.children)
|
|
325
|
+
collectAllPathsInner(child, out);
|
|
326
|
+
}
|
|
327
|
+
// ---------------------------------------------------------------------------
|
|
328
|
+
// Internal helpers
|
|
329
|
+
// ---------------------------------------------------------------------------
|
|
330
|
+
/**
|
|
331
|
+
* Return the parent path, or `null` if `p` is a top-level segment.
|
|
332
|
+
*
|
|
333
|
+
* parentPathOf("a") → null
|
|
334
|
+
* parentPathOf("a/b") → "a"
|
|
335
|
+
* parentPathOf("a/b/c.ts") → "a/b"
|
|
336
|
+
*/
|
|
337
|
+
function parentPathOf(p) {
|
|
338
|
+
const idx = p.lastIndexOf("/");
|
|
339
|
+
if (idx < 0)
|
|
340
|
+
return null;
|
|
341
|
+
return p.slice(0, idx);
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Recursively assign `depth` and sort children.
|
|
345
|
+
*
|
|
346
|
+
* Sorting rule (per design doc §5.1): directories first, then files,
|
|
347
|
+
* alphabetical by basename within each group. Case-sensitive — git
|
|
348
|
+
* doesn't case-fold paths either.
|
|
349
|
+
*/
|
|
350
|
+
function fixDepthAndSort(node, depth) {
|
|
351
|
+
node.depth = depth;
|
|
352
|
+
sortNodes(node.children);
|
|
353
|
+
for (const child of node.children) {
|
|
354
|
+
if (child.type === "directory") {
|
|
355
|
+
fixDepthAndSort(child, depth + 1);
|
|
356
|
+
}
|
|
357
|
+
else {
|
|
358
|
+
// Leaves: depth = parent.depth + 1.
|
|
359
|
+
child.depth = depth + 1;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
function sortNodes(nodes) {
|
|
364
|
+
nodes.sort((a, b) => {
|
|
365
|
+
// Directories first.
|
|
366
|
+
if (a.type !== b.type) {
|
|
367
|
+
return a.type === "directory" ? -1 : 1;
|
|
368
|
+
}
|
|
369
|
+
// Then alphabetical by name (locale-independent — matches git).
|
|
370
|
+
if (a.name < b.name)
|
|
371
|
+
return -1;
|
|
372
|
+
if (a.name > b.name)
|
|
373
|
+
return 1;
|
|
374
|
+
return 0;
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
//# sourceMappingURL=file-tree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-tree.js","sourceRoot":"","sources":["../src/file-tree.ts"],"names":[],"mappings":"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"}
|
package/dist/server.d.ts
CHANGED
|
@@ -164,6 +164,19 @@ export declare class TaskShowServer {
|
|
|
164
164
|
* `ts.config.fileSandboxPaths`).
|
|
165
165
|
*/
|
|
166
166
|
private handleFileContent;
|
|
167
|
+
/**
|
|
168
|
+
* v2.0.0: GET /api/task/:id/file-tree — returns the git-tracked
|
|
169
|
+
* file list for the project root as `{ files: string[] }`.
|
|
170
|
+
*
|
|
171
|
+
* The task id in the URL is ignored (the file tree is project-wide,
|
|
172
|
+
* not per-task). We still include the segment so the route reads
|
|
173
|
+
* naturally: "give me the file tree for the task's project".
|
|
174
|
+
*
|
|
175
|
+
* Caching: a 30-second in-memory cache keyed by cwd. Beyond that we
|
|
176
|
+
* re-spawn `git ls-files -z`. The cache key is the resolved cwd so a
|
|
177
|
+
* worktree-mode runner gets its own bucket.
|
|
178
|
+
*/
|
|
179
|
+
private handleFileTree;
|
|
167
180
|
private handleSSE;
|
|
168
181
|
/**
|
|
169
182
|
* Render the index page. v0.9.0+ selects the session-scoped forest when
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAGlC,OAAO,KAAK,EACV,WAAW,EACX,cAAc,EACf,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,OAAO,EACL,eAAe,EAChB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,cAAc,EACf,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|