opencode-swarm 6.64.0 → 6.65.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.
@@ -25,6 +25,7 @@ export { pkg_audit } from './pkg-audit';
25
25
  export { type PlaceholderFinding, type PlaceholderScanInput, type PlaceholderScanResult, placeholder_scan, placeholderScan, } from './placeholder-scan';
26
26
  export { type PreCheckBatchInput, type PreCheckBatchResult, pre_check_batch, runPreCheckBatch, type ToolResult, } from './pre-check-batch';
27
27
  export { type QualityBudgetInput, type QualityBudgetResult, quality_budget, qualityBudget, } from './quality-budget';
28
+ export { buildWorkspaceGraph, type GraphEdge, type GraphNode, loadGraph, loadOrCreateGraph, type RepoGraph, resolveModuleSpecifier, saveGraph, updateGraphForFiles, } from './repo-graph';
28
29
  export { repo_map } from './repo-map';
29
30
  export { req_coverage } from './req-coverage';
30
31
  export { retrieve_summary } from './retrieve-summary';
@@ -0,0 +1,226 @@
1
+ /**
2
+ * Repo graph storage module for persisting code dependency graphs.
3
+ * Stores module-level in-memory cache and safe load/save to .swarm/repo-graph.json.
4
+ *
5
+ * Security: All file operations use validateSwarmPath to reject workspace-escaping paths.
6
+ * Uses atomic temp+rename writes to prevent partial writes.
7
+ */
8
+ /**
9
+ * A node in the dependency graph representing a source file.
10
+ */
11
+ export interface GraphNode {
12
+ /** Resolved absolute path to the source file */
13
+ filePath: string;
14
+ /** Normalized module name (relative path from workspace root) */
15
+ moduleName: string;
16
+ /** Exported symbols from this file */
17
+ exports: string[];
18
+ /** Imported module specifiers */
19
+ imports: string[];
20
+ /** Language/extension of the file */
21
+ language: string;
22
+ /** Last modified timestamp */
23
+ mtime: string;
24
+ }
25
+ /**
26
+ * An edge in the dependency graph representing a dependency relationship.
27
+ */
28
+ export interface GraphEdge {
29
+ /** Source file path */
30
+ source: string;
31
+ /** Target file path (resolved) */
32
+ target: string;
33
+ /** Import specifier used */
34
+ importSpecifier: string;
35
+ /** Type of import */
36
+ importType: 'default' | 'named' | 'namespace' | 'require' | 'sideeffect';
37
+ }
38
+ /**
39
+ * The complete dependency graph for a workspace.
40
+ */
41
+ export interface RepoGraph {
42
+ /** Schema version for future compatibility */
43
+ schema_version: string;
44
+ /** Workspace root directory */
45
+ workspaceRoot: string;
46
+ /** Graph nodes keyed by resolved file path */
47
+ nodes: Record<string, GraphNode>;
48
+ /** Graph edges representing dependencies */
49
+ edges: GraphEdge[];
50
+ /** Graph metadata */
51
+ metadata: {
52
+ generatedAt: string;
53
+ generator: string;
54
+ nodeCount: number;
55
+ edgeCount: number;
56
+ };
57
+ }
58
+ /**
59
+ * Validate that a workspace directory is safe to use.
60
+ * Accepts both absolute and relative paths.
61
+ *
62
+ * @param workspace - The workspace directory (path, absolute or relative, e.g. "/home/user/project" or "my-project")
63
+ * @throws Error if the workspace is invalid
64
+ */
65
+ export declare function validateWorkspace(workspace: string): void;
66
+ /**
67
+ * Validate a graph node before adding to the graph.
68
+ * @param node - The node to validate
69
+ * @throws Error if the node is invalid
70
+ */
71
+ export declare function validateGraphNode(node: GraphNode): void;
72
+ /**
73
+ * Validate a graph edge before adding to the graph.
74
+ * @param edge - The edge to validate
75
+ * @throws Error if the edge is invalid
76
+ */
77
+ export declare function validateGraphEdge(edge: GraphEdge): void;
78
+ /**
79
+ * Resolve a module specifier relative to a source file within a workspace.
80
+ *
81
+ * CONTRACT for bare specifiers:
82
+ * - Bare specifiers (e.g., 'lodash', 'zod', '@scope/pkg') return null because
83
+ * they require node_modules traversal to resolve, which is outside the scope
84
+ * of this module's responsibilities.
85
+ * - Callers should treat null as "unresolvable at graph-build time" and may
86
+ * defer resolution to runtime or external tools.
87
+ *
88
+ * CONTRACT for workspace format:
89
+ * - workspaceRoot is normally a relative path (e.g., "my-project") validated by
90
+ * validateWorkspace, but when called by buildWorkspaceGraph it may be an
91
+ * absolute scan root path. Both forms are accepted - the function handles
92
+ * path boundary checks consistently regardless of which form is provided.
93
+ * - sourceFile must be an absolute path
94
+ * - Returns absolute path if resolved, null otherwise
95
+ *
96
+ * @param workspaceRoot - The workspace root directory (relative or absolute path)
97
+ * @param sourceFile - The file containing the import (absolute path)
98
+ * @param specifier - The module specifier from the import statement
99
+ * @returns Resolved absolute path or null if unresolvable
100
+ */
101
+ export declare function resolveModuleSpecifier(workspaceRoot: string, sourceFile: string, specifier: string): string | null;
102
+ /**
103
+ * Create an empty graph for a workspace.
104
+ * @param workspaceRoot - The workspace root directory
105
+ * @returns Empty RepoGraph structure
106
+ */
107
+ export declare function createEmptyGraph(workspaceRoot: string): RepoGraph;
108
+ /**
109
+ * Add or update a node in the graph.
110
+ * @param graph - The graph to modify
111
+ * @param node - The node to add/update
112
+ */
113
+ export declare function upsertNode(graph: RepoGraph, node: GraphNode): void;
114
+ /**
115
+ * Add an edge to the graph.
116
+ * @param graph - The graph to modify
117
+ * @param edge - The edge to add
118
+ */
119
+ export declare function addEdge(graph: RepoGraph, edge: GraphEdge): void;
120
+ /**
121
+ * Get the cached graph for a workspace.
122
+ * @param workspace - The workspace directory (absolute or relative path)
123
+ * @returns The cached graph or undefined if not cached
124
+ */
125
+ export declare function getCachedGraph(workspace: string): RepoGraph | undefined;
126
+ /**
127
+ * Set the cached graph for a workspace.
128
+ * @param workspace - The workspace directory (absolute or relative path)
129
+ * @param graph - The graph to cache
130
+ * @param mtime - Optional file mtime to track for cache invalidation
131
+ */
132
+ export declare function setCachedGraph(workspace: string, graph: RepoGraph, mtime?: number): void;
133
+ /**
134
+ * Mark a workspace's cache as dirty (modified since last save).
135
+ * @param workspace - The workspace directory (absolute or relative path)
136
+ */
137
+ export declare function markDirty(workspace: string): void;
138
+ /**
139
+ * Check if a workspace's cache is dirty.
140
+ * @param workspace - The workspace directory (absolute or relative path)
141
+ * @returns True if the cache has been modified since last save
142
+ */
143
+ export declare function isDirty(workspace: string): boolean;
144
+ /**
145
+ * Clear the cache for a workspace.
146
+ * @param workspace - The workspace directory (absolute or relative path)
147
+ */
148
+ export declare function clearCache(workspace: string): void;
149
+ /**
150
+ * Get the validated path for the repo-graph.json file.
151
+ * Resolves symlinks via realpath before validation to prevent
152
+ * workspace-escaping attacks via symlink manipulation.
153
+ *
154
+ * @param workspace - The workspace directory (absolute or relative path)
155
+ * @returns Absolute path to repo-graph.json
156
+ * @throws Error if path validation fails or resolved path escapes workspace
157
+ */
158
+ export declare function getGraphPath(workspace: string): string;
159
+ /**
160
+ * Load the graph from .swarm/repo-graph.json.
161
+ * Uses the in-memory cache if available, not dirty, and file mtime unchanged.
162
+ *
163
+ * @param workspace - The workspace directory (absolute or relative path)
164
+ * @returns The loaded graph or null if not found
165
+ * @throws Error if file exists but is invalid/corrupted
166
+ */
167
+ export declare function loadGraph(workspace: string): Promise<RepoGraph | null>;
168
+ /**
169
+ * Save the graph to .swarm/repo-graph.json atomically.
170
+ * Uses temp file + rename pattern to prevent partial writes.
171
+ *
172
+ * @param workspace - The workspace directory (absolute or relative path)
173
+ * @param graph - The graph to save
174
+ * @param options.createAtomic - If true, fails if file already exists (for atomic create)
175
+ * @throws Error if validation fails, write fails, or file exists when createAtomic=true
176
+ */
177
+ export declare function saveGraph(workspace: string, graph: RepoGraph, options?: {
178
+ createAtomic?: boolean;
179
+ }): Promise<void>;
180
+ /**
181
+ * Load or create a graph for a workspace atomically.
182
+ * Returns existing graph or creates a new empty one.
183
+ * Handles concurrent creation by treating a create-fail as "graph exists".
184
+ *
185
+ * @param workspace - The workspace directory (absolute or relative path)
186
+ * @returns The existing or new graph
187
+ */
188
+ export declare function loadOrCreateGraph(workspace: string): Promise<RepoGraph>;
189
+ /**
190
+ * Save the cached graph for a workspace if it's dirty.
191
+ *
192
+ * @param workspace - The workspace directory (absolute or relative path)
193
+ * @throws Error if workspace is dirty but cache is missing (inconsistent state)
194
+ * @throws Error if save fails
195
+ */
196
+ export declare function saveIfDirty(workspace: string): Promise<void>;
197
+ /**
198
+ * Build a complete dependency graph for a workspace by scanning all source files.
199
+ *
200
+ * The scan is deterministic: files are processed in sorted order, and edges
201
+ * are added in a stable order based on source file and import specifier.
202
+ *
203
+ * @param workspaceRoot - Workspace root directory (absolute or relative path)
204
+ * @param options - Optional scan configuration
205
+ * @param options.maxFileSizeBytes - Maximum file size to scan (default 1MB)
206
+ * @returns Complete RepoGraph with nodes and edges
207
+ * @throws Error if workspace validation fails
208
+ */
209
+ export declare function buildWorkspaceGraph(workspaceRoot: string, options?: {
210
+ maxFileSizeBytes?: number;
211
+ maxFiles?: number;
212
+ }): RepoGraph;
213
+ /**
214
+ * Incrementally update the graph for a set of changed files.
215
+ * Re-scans only the specified files, updates their nodes and edges,
216
+ * and falls back to a full rebuild if the incremental pass cannot be validated.
217
+ *
218
+ * @param workspaceRoot - Workspace root directory (relative path)
219
+ * @param filePaths - Array of absolute file paths that changed
220
+ * @param options - Optional configuration
221
+ * @param options.forceRebuild - Force a full rebuild instead of incremental
222
+ * @returns Updated RepoGraph
223
+ */
224
+ export declare function updateGraphForFiles(workspaceRoot: string, filePaths: string[], options?: {
225
+ forceRebuild?: boolean;
226
+ }): Promise<RepoGraph>;
@@ -24,3 +24,13 @@ export declare function containsControlChars(str: string): boolean;
24
24
  * @throws Error if directory is invalid
25
25
  */
26
26
  export declare function validateDirectory(directory: string): void;
27
+ /**
28
+ * Validate that a resolved path stays within an allowed root directory.
29
+ * Resolves symlinks via realpathSync for both the target path and the root,
30
+ * then verifies the resolved target is within the resolved root.
31
+ *
32
+ * @param targetPath - The path to validate (absolute)
33
+ * @param rootPath - The root directory boundary (absolute)
34
+ * @throws Error if the resolved target escapes the root boundary
35
+ */
36
+ export declare function validateSymlinkBoundary(targetPath: string, rootPath: string): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-swarm",
3
- "version": "6.64.0",
3
+ "version": "6.65.0",
4
4
  "description": "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",