@north-light/crouter 0.3.300 → 0.3.301

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.
@@ -2,13 +2,16 @@ import type { NodeRow } from '../../../core/canvas/types.js';
2
2
  interface ProfileFileSearchOptions {
3
3
  cwd: string;
4
4
  nodeDirs: readonly string[];
5
+ nodeIds: readonly string[];
5
6
  profileId: string | null;
6
7
  fdPath: string | null;
7
8
  onNotice: (message: string) => void;
8
9
  }
9
- /** Return the working directories for this node, its complete parent chain,
10
- * and its full descendant subtree. The `parent` hierarchy, rather than the
11
- * subscription graph, is the user-visible child/parent relationship. */
10
+ /** Return the ids of this node, its complete parent chain, and its full
11
+ * descendant subtree. The `parent` hierarchy, rather than the subscription
12
+ * graph, is the user-visible child/parent relationship. */
13
+ export declare function relatedNodeIds(nodes: readonly Pick<NodeRow, 'node_id' | 'parent'>[], nodeId: string): string[];
14
+ /** Return the working directories of the same node family. */
12
15
  export declare function relatedNodeDirs(nodes: readonly Pick<NodeRow, 'node_id' | 'cwd' | 'parent'>[], nodeId: string): string[];
13
16
  export declare function launchProfileFileSearch(opts: ProfileFileSearchOptions): void;
14
17
  export {};
@@ -1,22 +1,24 @@
1
1
  // session/profile-files.ts — fuzzy file search across a node's working family and local profile.
2
2
  //
3
3
  // The popup searches the current workspace, every ancestor and descendant
4
- // node's workspace, every project named by the node's profile manifest, and the
5
- // profile's own memory store. Ancestor `.crouter/memory/` stores are added with
6
- // ignore rules disabled, so project memory remains findable even when a parent
7
- // ignore file hides `.crouter/`. fzf owns matching and selection and nvim owns
8
- // viewing; the attach viewer resolves the related nodes and wires the two popups.
4
+ // node's workspace, the context and reports directories of those same nodes,
5
+ // every project named by the node's profile manifest, and the profile's own
6
+ // memory store. Ancestor `.crouter/memory/` stores are added with ignore rules
7
+ // disabled, so project memory remains findable even when a parent ignore file
8
+ // hides `.crouter/`. fzf owns matching and selection and nvim owns viewing; the
9
+ // attach viewer resolves the related nodes and wires the two popups.
9
10
  import { constants, accessSync, existsSync, mkdtempSync, readFileSync, rmSync, statSync } from 'node:fs';
10
11
  import { tmpdir } from 'node:os';
11
12
  import { delimiter, dirname, join, resolve, sep } from 'node:path';
12
13
  import { execFile, spawnSync } from 'node:child_process';
13
14
  import { loadProfileManifest, profileMemoryDir } from '../../../core/profiles/manifest.js';
15
+ import { contextDir, reportsDir } from '../../../core/canvas/paths.js';
14
16
  import { shellQuote } from '../../../core/runtime/placement-tmux.js';
15
17
  import { surfaceTmuxStyleArgs } from '../../../core/runtime/surface-bg.js';
16
- /** Return the working directories for this node, its complete parent chain,
17
- * and its full descendant subtree. The `parent` hierarchy, rather than the
18
- * subscription graph, is the user-visible child/parent relationship. */
19
- export function relatedNodeDirs(nodes, nodeId) {
18
+ /** Return the ids of this node, its complete parent chain, and its full
19
+ * descendant subtree. The `parent` hierarchy, rather than the subscription
20
+ * graph, is the user-visible child/parent relationship. */
21
+ export function relatedNodeIds(nodes, nodeId) {
20
22
  const byId = new Map(nodes.map((node) => [node.node_id, node]));
21
23
  const included = new Set([nodeId]);
22
24
  for (let current = byId.get(nodeId); current?.parent !== null && current?.parent !== undefined;) {
@@ -36,7 +38,27 @@ export function relatedNodeDirs(nodes, nodeId) {
36
38
  pending.push(child.node_id);
37
39
  }
38
40
  }
39
- return nodes.filter((node) => included.has(node.node_id)).map((node) => node.cwd);
41
+ return nodes.filter((node) => included.has(node.node_id)).map((node) => node.node_id);
42
+ }
43
+ /** Return the working directories of the same node family. */
44
+ export function relatedNodeDirs(nodes, nodeId) {
45
+ const family = new Set(relatedNodeIds(nodes, nodeId));
46
+ return nodes.filter((node) => family.has(node.node_id)).map((node) => node.cwd);
47
+ }
48
+ /** Return the canvas-home directories that hold each family node's shared
49
+ * documents: its context dir and its pushed reports. These live outside every
50
+ * working directory, so they are searched as roots of their own. */
51
+ function familyDocumentDirs(nodeIds) {
52
+ const dirs = [];
53
+ for (const id of nodeIds) {
54
+ try {
55
+ dirs.push(contextDir(id), reportsDir(id));
56
+ }
57
+ catch {
58
+ continue;
59
+ }
60
+ }
61
+ return dedupeExistingDirs(dirs);
40
62
  }
41
63
  function isDirectory(path) {
42
64
  try {
@@ -163,8 +185,15 @@ export function launchProfileFileSearch(opts) {
163
185
  const tempDir = mkdtempSync(join(tmpdir(), 'crtr-profile-files-'));
164
186
  const selected = join(tempDir, 'selection');
165
187
  const candidates = join(tempDir, 'candidates');
188
+ // A context dir can hold a full repo clone a node made for itself, so ignore
189
+ // rules stay on (they are what keeps a clone's dependencies out) and the
190
+ // background-bash log dir directly beneath it is dropped — those are
191
+ // transient command output named by job id, never a document to open.
192
+ const documentDirs = familyDocumentDirs(opts.nodeIds)
193
+ .filter((dir) => !roots.some((root) => dir === root || dir.startsWith(root + sep)));
166
194
  const scans = [
167
195
  ...roots.map((root) => `${shellQuote(fd)} --type f --hidden --exclude .git --exclude ${shellQuote('**/.crouter/memory/**')} . ${shellQuote(root)}`),
196
+ ...documentDirs.map((dir) => `${shellQuote(fd)} --type f --hidden --exclude .git --exclude ${shellQuote('/jobs')} . ${shellQuote(dir)}`),
168
197
  ...memoryDirs.map((memory) => `${shellQuote(fd)} --type f --hidden --exclude .git --no-ignore . ${shellQuote(memory)}`),
169
198
  ];
170
199
  // Every line is the whole path and nothing else, so fzf matches, ranks, and