@webpresso/agent-kit 0.21.4 → 0.21.5

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.
Files changed (94) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/README.md +105 -41
  3. package/catalog/agent/skills/plan-refine/SKILL.md +5 -4
  4. package/catalog/base-kit/commitlint.config.ts.tmpl +1 -3
  5. package/catalog/base-kit/e2e/fixtures/smoke.html.tmpl +13 -0
  6. package/catalog/base-kit/e2e/smoke.spec.ts.tmpl +13 -0
  7. package/catalog/base-kit/oxlint.config.ts.tmpl +26 -0
  8. package/catalog/base-kit/playwright.config.ts.tmpl +10 -0
  9. package/catalog/base-kit/src/quality-sample.test.ts.tmpl +19 -0
  10. package/catalog/base-kit/src/quality-sample.ts.tmpl +11 -0
  11. package/catalog/base-kit/stryker.config.ts.tmpl +14 -0
  12. package/catalog/base-kit/tsconfig.json.tmpl +9 -0
  13. package/catalog/base-kit/vitest.config.ts.tmpl +10 -0
  14. package/catalog/docs/templates/adr.md +1 -1
  15. package/catalog/docs/templates/blueprint.md +1 -0
  16. package/catalog/docs/templates/blueprint.yaml +6 -3
  17. package/catalog/docs/templates/guide.md +1 -1
  18. package/catalog/docs/templates/postmortem.md +1 -1
  19. package/catalog/docs/templates/research.md +1 -1
  20. package/catalog/docs/templates/runbook.md +1 -1
  21. package/catalog/docs/templates/system.md +12 -3
  22. package/catalog/docs/templates/tech-debt.md +1 -0
  23. package/commands/blueprint.md +37 -4
  24. package/dist/esm/audit/resolve-audit-script.d.ts +24 -0
  25. package/dist/esm/audit/resolve-audit-script.js +27 -0
  26. package/dist/esm/blueprint/index.d.ts +0 -1
  27. package/dist/esm/blueprint/index.js +0 -2
  28. package/dist/esm/blueprint/local.d.ts +0 -3
  29. package/dist/esm/blueprint/local.js +0 -2
  30. package/dist/esm/blueprint/service/BlueprintCreationService.js +5 -2
  31. package/dist/esm/blueprint/utils/package-assets.d.ts +11 -0
  32. package/dist/esm/blueprint/utils/package-assets.js +33 -4
  33. package/dist/esm/build/sync-catalog-doc-templates.d.ts +23 -0
  34. package/dist/esm/build/sync-catalog-doc-templates.js +93 -0
  35. package/dist/esm/cli/commands/audit.js +2 -7
  36. package/dist/esm/cli/commands/blueprint/router.js +5 -2
  37. package/dist/esm/cli/commands/blueprint/template-resolver.js +8 -4
  38. package/dist/esm/cli/commands/init/host-visibility.js +4 -2
  39. package/dist/esm/cli/commands/init/index.js +46 -7
  40. package/dist/esm/cli/commands/init/scaffold-base-kit.d.ts +12 -0
  41. package/dist/esm/cli/commands/init/scaffold-base-kit.js +141 -6
  42. package/dist/esm/cli/commands/typecheck.js +10 -4
  43. package/dist/esm/e2e/command-builder.js +26 -7
  44. package/dist/esm/e2e/execution.js +4 -0
  45. package/dist/esm/e2e/run-planner.js +1 -0
  46. package/dist/esm/e2e/types.d.ts +1 -0
  47. package/dist/esm/format/index.js +7 -1
  48. package/dist/esm/lint/index.js +3 -1
  49. package/dist/esm/mcp/blueprint-server.js +361 -66
  50. package/dist/esm/mcp/tools/audit.js +2 -8
  51. package/dist/esm/mcp/tools/e2e.d.ts +1 -1
  52. package/dist/esm/package.json +3 -0
  53. package/dist/esm/test/command-builder.d.ts +1 -0
  54. package/dist/esm/test/command-builder.js +8 -2
  55. package/dist/esm/test-helpers/hermetic-env.d.ts +25 -0
  56. package/dist/esm/test-helpers/hermetic-env.js +31 -0
  57. package/dist/esm/tool-runtime/index.d.ts +5 -0
  58. package/dist/esm/tool-runtime/index.js +23 -0
  59. package/dist/esm/tool-runtime/resolve-runner.d.ts +13 -0
  60. package/dist/esm/tool-runtime/resolve-runner.js +40 -0
  61. package/package.json +12 -19
  62. package/skills/plan-refine/SKILL.md +5 -4
  63. package/dist/esm/blueprint/dag/cycle-detector.d.ts +0 -12
  64. package/dist/esm/blueprint/dag/cycle-detector.js +0 -46
  65. package/dist/esm/blueprint/dag/executor.d.ts +0 -140
  66. package/dist/esm/blueprint/dag/executor.js +0 -292
  67. package/dist/esm/blueprint/dag/index.d.ts +0 -20
  68. package/dist/esm/blueprint/dag/index.js +0 -17
  69. package/dist/esm/blueprint/dag/interfaces.d.ts +0 -56
  70. package/dist/esm/blueprint/dag/interfaces.js +0 -13
  71. package/dist/esm/blueprint/dag/local/independence.d.ts +0 -107
  72. package/dist/esm/blueprint/dag/local/independence.js +0 -231
  73. package/dist/esm/blueprint/dag/local/index.d.ts +0 -14
  74. package/dist/esm/blueprint/dag/local/index.js +0 -14
  75. package/dist/esm/blueprint/dag/local/package-graph.d.ts +0 -66
  76. package/dist/esm/blueprint/dag/local/package-graph.js +0 -148
  77. package/dist/esm/blueprint/dag/plan-parser.d.ts +0 -54
  78. package/dist/esm/blueprint/dag/plan-parser.js +0 -236
  79. package/dist/esm/blueprint/dag/task-graph-algorithms.d.ts +0 -13
  80. package/dist/esm/blueprint/dag/task-graph-algorithms.js +0 -236
  81. package/dist/esm/blueprint/dag/task-graph.d.ts +0 -171
  82. package/dist/esm/blueprint/dag/task-graph.js +0 -370
  83. package/dist/esm/blueprint/dag/types.d.ts +0 -17
  84. package/dist/esm/blueprint/dag/types.js +0 -2
  85. package/dist/esm/blueprint/graph/index.d.ts +0 -5
  86. package/dist/esm/blueprint/graph/index.js +0 -5
  87. package/dist/esm/blueprint/graph/mermaid-parser.d.ts +0 -3
  88. package/dist/esm/blueprint/graph/mermaid-parser.js +0 -93
  89. package/dist/esm/blueprint/graph/mermaid-serializer.d.ts +0 -3
  90. package/dist/esm/blueprint/graph/mermaid-serializer.js +0 -20
  91. package/dist/esm/blueprint/graph/schema.d.ts +0 -89
  92. package/dist/esm/blueprint/graph/schema.js +0 -104
  93. package/dist/esm/blueprint/graph/task-graph-adapter.d.ts +0 -6
  94. package/dist/esm/blueprint/graph/task-graph-adapter.js +0 -30
@@ -1,17 +0,0 @@
1
- /**
2
- * webpresso blueprint/dag
3
- *
4
- * Workers-safe DAG (Directed Acyclic Graph) analysis utilities.
5
- * Zero Node.js dependencies — safe for Cloudflare Workers, Deno, Bun, and Node.js.
6
- *
7
- * For Node-only utilities (PackageGraph, IndependenceDetector),
8
- * use the `dag/local` subpath instead.
9
- *
10
- * @packageDocumentation
11
- */
12
- export { createExecutor, createExecutorFromTasks, ParallelExecutor } from './executor.js';
13
- export { realClock } from './interfaces.js';
14
- export { parsePlan, planTasksToGraphTasks } from './plan-parser.js';
15
- // Task Graph
16
- export { CycleDetector, TaskGraph } from './task-graph.js';
17
- //# sourceMappingURL=index.js.map
@@ -1,56 +0,0 @@
1
- /**
2
- * Interfaces for dependency injection and testability.
3
- *
4
- * These interfaces allow mocking of external dependencies (filesystem, time)
5
- * for deterministic testing.
6
- */
7
- /**
8
- * Filesystem abstraction for package discovery.
9
- * Allows mocking filesystem access in tests.
10
- */
11
- export interface IFileSystem {
12
- existsSync(path: string): boolean;
13
- readFileSync(path: string, encoding: 'utf-8'): string;
14
- }
15
- /**
16
- * Clock abstraction for time-dependent operations.
17
- * Allows deterministic testing of duration calculations.
18
- */
19
- export interface IClock {
20
- now(): number;
21
- }
22
- /**
23
- * Default clock using Date.now()
24
- */
25
- export declare const realClock: IClock;
26
- /**
27
- * Package graph interface for dependency analysis.
28
- * Allows mocking package structure in tests.
29
- */
30
- export interface IPackageGraph {
31
- findPackageRoot(filePath: string): string | null;
32
- getPackageName(packageRoot: string): string | null;
33
- hasCrossPackageDependency(pkgA: string, pkgB: string): boolean;
34
- areInSamePackage(filePathA: string, filePathB: string): boolean;
35
- }
36
- /**
37
- * Validation result for graph analysis.
38
- */
39
- export interface ValidationResult {
40
- valid: boolean;
41
- errors: string[];
42
- warnings: string[];
43
- }
44
- /**
45
- * Graph statistics for analysis.
46
- */
47
- export interface GraphStats {
48
- nodeCount: number;
49
- edgeCount: number;
50
- maxDepth: number;
51
- maxWidth: number;
52
- waveCount: number;
53
- hasCycles: boolean;
54
- isolatedNodes: string[];
55
- }
56
- //# sourceMappingURL=interfaces.d.ts.map
@@ -1,13 +0,0 @@
1
- /**
2
- * Interfaces for dependency injection and testability.
3
- *
4
- * These interfaces allow mocking of external dependencies (filesystem, time)
5
- * for deterministic testing.
6
- */
7
- /**
8
- * Default clock using Date.now()
9
- */
10
- export const realClock = {
11
- now: () => Date.now(),
12
- };
13
- //# sourceMappingURL=interfaces.js.map
@@ -1,107 +0,0 @@
1
- import type { IPackageGraph } from '#dag/interfaces';
2
- /**
3
- * Represents a task with its file access information.
4
- */
5
- export interface TaskFiles {
6
- id: string;
7
- files: string[];
8
- readOnly: boolean;
9
- }
10
- /**
11
- * Result of parallelization analysis.
12
- */
13
- export interface ParallelizeResult {
14
- canParallelize: boolean;
15
- reason: string;
16
- /** Files that would conflict if parallelized */
17
- conflictingFiles?: string[];
18
- }
19
- /**
20
- * A false dependency that can be removed.
21
- */
22
- export interface FalseDependency {
23
- from: string;
24
- to: string;
25
- reason: string;
26
- }
27
- /**
28
- * Analysis result for a pair of tasks.
29
- */
30
- export interface TaskPairAnalysis {
31
- taskA: string;
32
- taskB: string;
33
- canParallelize: boolean;
34
- reason: string;
35
- packagesA: string[];
36
- packagesB: string[];
37
- overlappingFiles: string[];
38
- hasCrossPackageDependency: boolean;
39
- }
40
- /**
41
- * IndependenceDetector analyzes tasks to find false dependencies.
42
- *
43
- * Uses file and package analysis to determine if tasks can run in parallel,
44
- * even when the plan declares a dependency between them.
45
- *
46
- * Accepts IPackageGraph interface for testability - package structure can be mocked.
47
- */
48
- export declare class IndependenceDetector {
49
- private readonly packageGraph;
50
- /**
51
- * Create a new IndependenceDetector.
52
- * @param rootOrPackageGraph - Either a root path (creates real PackageGraph) or an IPackageGraph for testing
53
- */
54
- constructor(rootOrPackageGraph: string | IPackageGraph);
55
- /**
56
- * Determine if two tasks can run in parallel based on their file access.
57
- *
58
- * Algorithm:
59
- * 1. Get package for each file (walk up to package.json)
60
- * 2. IF different packages AND no cross-package deps → PARALLEL
61
- * 3. IF same package but no file overlap → PARALLEL
62
- * 4. IF file overlap but both read-only → PARALLEL
63
- * 5. ELSE → SERIAL
64
- */
65
- canParallelize(taskA: TaskFiles, taskB: TaskFiles): ParallelizeResult;
66
- private analyzePackageLocation;
67
- private analyzeFilesOutsidePackages;
68
- private analyzeDifferentPackages;
69
- private analyzeSamePackage;
70
- /**
71
- * Analyze a pair of tasks in detail.
72
- * Useful for debugging why tasks can or cannot be parallelized.
73
- */
74
- analyzeTaskPair(taskA: TaskFiles, taskB: TaskFiles): TaskPairAnalysis;
75
- /**
76
- * Find all false dependencies in a task graph.
77
- *
78
- * @param tasks - List of tasks with their file information
79
- * @param edges - List of declared dependencies (from → to)
80
- * @returns List of edges that can be safely removed
81
- */
82
- findFalseDependencies(tasks: TaskFiles[], edges: Array<{
83
- from: string;
84
- to: string;
85
- }>): FalseDependency[];
86
- /**
87
- * Analyze all task pairs and return detailed analysis.
88
- * Useful for understanding the parallelization potential of a task set.
89
- */
90
- analyzeAllPairs(tasks: TaskFiles[]): {
91
- totalPairs: number;
92
- parallelizablePairs: number;
93
- analyses: TaskPairAnalysis[];
94
- };
95
- private getPackagesForFiles;
96
- private hasOverlap;
97
- private hasCrossPackageDep;
98
- private anyDependsOn;
99
- private getOverlappingFiles;
100
- }
101
- /**
102
- * Create a mock package graph for testing.
103
- * @param packages - Map of file paths to package roots
104
- * @param dependencies - Map of package roots to their dependencies
105
- */
106
- export declare function createMockPackageGraph(packages: Map<string, string | null>, dependencies?: Map<string, string[]>): IPackageGraph;
107
- //# sourceMappingURL=independence.d.ts.map
@@ -1,231 +0,0 @@
1
- import { PackageGraph } from './package-graph.js';
2
- /**
3
- * IndependenceDetector analyzes tasks to find false dependencies.
4
- *
5
- * Uses file and package analysis to determine if tasks can run in parallel,
6
- * even when the plan declares a dependency between them.
7
- *
8
- * Accepts IPackageGraph interface for testability - package structure can be mocked.
9
- */
10
- export class IndependenceDetector {
11
- packageGraph;
12
- /**
13
- * Create a new IndependenceDetector.
14
- * @param rootOrPackageGraph - Either a root path (creates real PackageGraph) or an IPackageGraph for testing
15
- */
16
- constructor(rootOrPackageGraph) {
17
- if (typeof rootOrPackageGraph === 'string') {
18
- this.packageGraph = new PackageGraph(rootOrPackageGraph);
19
- }
20
- else {
21
- this.packageGraph = rootOrPackageGraph;
22
- }
23
- }
24
- /**
25
- * Determine if two tasks can run in parallel based on their file access.
26
- *
27
- * Algorithm:
28
- * 1. Get package for each file (walk up to package.json)
29
- * 2. IF different packages AND no cross-package deps → PARALLEL
30
- * 3. IF same package but no file overlap → PARALLEL
31
- * 4. IF file overlap but both read-only → PARALLEL
32
- * 5. ELSE → SERIAL
33
- */
34
- canParallelize(taskA, taskB) {
35
- if (!taskA.files.length || !taskB.files.length) {
36
- return { canParallelize: true, reason: 'one or both tasks have no files' };
37
- }
38
- const packagesA = this.getPackagesForFiles(taskA.files);
39
- const packagesB = this.getPackagesForFiles(taskB.files);
40
- return this.analyzePackageLocation(taskA, taskB, packagesA, packagesB);
41
- }
42
- analyzePackageLocation(taskA, taskB, packagesA, packagesB) {
43
- // If one has packages and the other doesn't, they're in different locations
44
- if (packagesA.size === 0 && packagesB.size > 0) {
45
- return { canParallelize: true, reason: 'different locations (packaged vs unpackaged)' };
46
- }
47
- if (packagesB.size === 0 && packagesA.size > 0) {
48
- return { canParallelize: true, reason: 'different locations (packaged vs unpackaged)' };
49
- }
50
- // If both are outside packages, check file overlap
51
- if (packagesA.size === 0 && packagesB.size === 0) {
52
- return this.analyzeFilesOutsidePackages(taskA, taskB);
53
- }
54
- if (!this.hasOverlap(packagesA, packagesB)) {
55
- return this.analyzeDifferentPackages(packagesA, packagesB);
56
- }
57
- return this.analyzeSamePackage(taskA, taskB);
58
- }
59
- analyzeFilesOutsidePackages(taskA, taskB) {
60
- const overlappingFiles = this.getOverlappingFiles(taskA.files, taskB.files);
61
- if (!overlappingFiles.length) {
62
- return { canParallelize: true, reason: 'files outside packages, no overlap' };
63
- }
64
- if (taskA.readOnly && taskB.readOnly) {
65
- return { canParallelize: true, reason: 'files outside packages, overlap but read-only' };
66
- }
67
- return {
68
- canParallelize: false,
69
- reason: 'files outside packages with write conflict',
70
- conflictingFiles: overlappingFiles,
71
- };
72
- }
73
- analyzeDifferentPackages(packagesA, packagesB) {
74
- // If one is the root package and the other is not, they can parallelize
75
- // Root package is a meta/dev package and its dependencies don't create real conflicts
76
- const hasRoot = packagesA.has('') || packagesB.has('');
77
- const hasNonRoot = Array.from(packagesA).some((p) => p !== '') || Array.from(packagesB).some((p) => p !== '');
78
- if (hasRoot && hasNonRoot) {
79
- return { canParallelize: true, reason: 'root package vs workspace package, no conflict' };
80
- }
81
- const hasCrossDep = this.hasCrossPackageDep(packagesA, packagesB);
82
- if (!hasCrossDep) {
83
- return { canParallelize: true, reason: 'different packages, no cross-dep' };
84
- }
85
- return { canParallelize: false, reason: 'cross-package dependency exists' };
86
- }
87
- analyzeSamePackage(taskA, taskB) {
88
- const overlappingFiles = this.getOverlappingFiles(taskA.files, taskB.files);
89
- if (!overlappingFiles.length) {
90
- return { canParallelize: true, reason: 'same package, no file overlap' };
91
- }
92
- if (taskA.readOnly && taskB.readOnly) {
93
- return { canParallelize: true, reason: 'file overlap but both read-only' };
94
- }
95
- return {
96
- canParallelize: false,
97
- reason: `file overlap with write conflict: ${overlappingFiles.join(', ')}`,
98
- conflictingFiles: overlappingFiles,
99
- };
100
- }
101
- /**
102
- * Analyze a pair of tasks in detail.
103
- * Useful for debugging why tasks can or cannot be parallelized.
104
- */
105
- analyzeTaskPair(taskA, taskB) {
106
- const packagesA = this.getPackagesForFiles(taskA.files);
107
- const packagesB = this.getPackagesForFiles(taskB.files);
108
- const overlappingFiles = this.getOverlappingFiles(taskA.files, taskB.files);
109
- const hasCrossDep = this.hasCrossPackageDep(packagesA, packagesB);
110
- const result = this.canParallelize(taskA, taskB);
111
- return {
112
- taskA: taskA.id,
113
- taskB: taskB.id,
114
- canParallelize: result.canParallelize,
115
- reason: result.reason,
116
- packagesA: Array.from(packagesA),
117
- packagesB: Array.from(packagesB),
118
- overlappingFiles,
119
- hasCrossPackageDependency: hasCrossDep,
120
- };
121
- }
122
- /**
123
- * Find all false dependencies in a task graph.
124
- *
125
- * @param tasks - List of tasks with their file information
126
- * @param edges - List of declared dependencies (from → to)
127
- * @returns List of edges that can be safely removed
128
- */
129
- findFalseDependencies(tasks, edges) {
130
- const taskMap = new Map(tasks.map((t) => [t.id, t]));
131
- const falseDeps = [];
132
- for (const edge of edges) {
133
- const taskA = taskMap.get(edge.from);
134
- const taskB = taskMap.get(edge.to);
135
- if (!taskA || !taskB)
136
- continue;
137
- const result = this.canParallelize(taskA, taskB);
138
- if (result.canParallelize) {
139
- falseDeps.push({
140
- from: edge.from,
141
- to: edge.to,
142
- reason: result.reason,
143
- });
144
- }
145
- }
146
- return falseDeps;
147
- }
148
- /**
149
- * Analyze all task pairs and return detailed analysis.
150
- * Useful for understanding the parallelization potential of a task set.
151
- */
152
- analyzeAllPairs(tasks) {
153
- const analyses = [];
154
- for (let i = 0; i < tasks.length; i++) {
155
- for (let j = i + 1; j < tasks.length; j++) {
156
- // Safe: loop conditions ensure i and j are within bounds
157
- const taskA = tasks[i];
158
- const taskB = tasks[j];
159
- if (!taskA || !taskB)
160
- continue;
161
- analyses.push(this.analyzeTaskPair(taskA, taskB));
162
- }
163
- }
164
- return {
165
- totalPairs: analyses.length,
166
- parallelizablePairs: analyses.filter((a) => a.canParallelize).length,
167
- analyses,
168
- };
169
- }
170
- getPackagesForFiles(files) {
171
- const packages = new Set();
172
- for (const file of files) {
173
- const pkg = this.packageGraph.findPackageRoot(file);
174
- if (pkg !== null) {
175
- packages.add(pkg);
176
- }
177
- }
178
- return packages;
179
- }
180
- hasOverlap(setA, setB) {
181
- for (const item of setA) {
182
- if (setB.has(item))
183
- return true;
184
- }
185
- return false;
186
- }
187
- hasCrossPackageDep(packagesA, packagesB) {
188
- // Check if any package in A depends on any package in B (or vice versa)
189
- for (const pkgA of packagesA) {
190
- if (this.anyDependsOn(pkgA, packagesB))
191
- return true;
192
- }
193
- for (const pkgB of packagesB) {
194
- if (this.anyDependsOn(pkgB, packagesA))
195
- return true;
196
- }
197
- return false;
198
- }
199
- anyDependsOn(pkg, targets) {
200
- for (const target of targets) {
201
- if (this.packageGraph.hasCrossPackageDependency(pkg, target))
202
- return true;
203
- }
204
- return false;
205
- }
206
- getOverlappingFiles(filesA, filesB) {
207
- const setB = new Set(filesB);
208
- return filesA.filter((f) => setB.has(f));
209
- }
210
- }
211
- /**
212
- * Create a mock package graph for testing.
213
- * @param packages - Map of file paths to package roots
214
- * @param dependencies - Map of package roots to their dependencies
215
- */
216
- export function createMockPackageGraph(packages, dependencies = new Map()) {
217
- return {
218
- findPackageRoot: (filePath) => packages.get(filePath) ?? null,
219
- getPackageName: (packageRoot) => packageRoot,
220
- hasCrossPackageDependency: (pkgA, pkgB) => {
221
- const deps = dependencies.get(pkgA) ?? [];
222
- return deps.includes(pkgB);
223
- },
224
- areInSamePackage: (filePathA, filePathB) => {
225
- const pkgA = packages.get(filePathA);
226
- const pkgB = packages.get(filePathB);
227
- return pkgA !== null && pkgA === pkgB;
228
- },
229
- };
230
- }
231
- //# sourceMappingURL=independence.js.map
@@ -1,14 +0,0 @@
1
- /**
2
- * webpresso blueprint/dag/local
3
- *
4
- * Node.js-only DAG utilities for local CLI execution.
5
- * These modules use `node:fs` and `node:path` — DO NOT import in Workers bundles.
6
- *
7
- * For workers-safe DAG utilities, use the parent `dag` subpath instead.
8
- *
9
- * @packageDocumentation
10
- */
11
- export type { FalseDependency, ParallelizeResult, TaskFiles, TaskPairAnalysis, } from './independence.js';
12
- export { createMockPackageGraph, IndependenceDetector } from './independence.js';
13
- export { createMockFileSystem, PackageGraph, realFileSystem } from './package-graph.js';
14
- //# sourceMappingURL=index.d.ts.map
@@ -1,14 +0,0 @@
1
- /**
2
- * webpresso blueprint/dag/local
3
- *
4
- * Node.js-only DAG utilities for local CLI execution.
5
- * These modules use `node:fs` and `node:path` — DO NOT import in Workers bundles.
6
- *
7
- * For workers-safe DAG utilities, use the parent `dag` subpath instead.
8
- *
9
- * @packageDocumentation
10
- */
11
- export { createMockPackageGraph, IndependenceDetector } from './independence.js';
12
- // Package Graph
13
- export { createMockFileSystem, PackageGraph, realFileSystem } from './package-graph.js';
14
- //# sourceMappingURL=index.js.map
@@ -1,66 +0,0 @@
1
- import type { IFileSystem, IPackageGraph } from '#dag/interfaces';
2
- /**
3
- * Default filesystem implementation using Node.js fs module.
4
- */
5
- export declare const realFileSystem: IFileSystem;
6
- /**
7
- * PackageGraph analyzes monorepo package structure for dependency detection.
8
- *
9
- * Used by the false dependency detector to determine if tasks can run in parallel
10
- * based on package boundaries and cross-package dependencies.
11
- *
12
- * Implements IPackageGraph interface for testability - filesystem can be mocked.
13
- */
14
- export declare class PackageGraph implements IPackageGraph {
15
- private readonly root;
16
- private readonly fs;
17
- private readonly packageCache;
18
- private readonly packageJsonCache;
19
- constructor(root: string, fs?: IFileSystem);
20
- /**
21
- * Find the package root (directory containing package.json) for a file path.
22
- * Walks up the directory tree until package.json is found.
23
- *
24
- * @param filePath - Relative path from monorepo root
25
- * @returns Relative path to package root, or null if not found
26
- */
27
- findPackageRoot(filePath: string): string | null;
28
- /**
29
- * Get the package name from package.json.
30
- *
31
- * @param packageRoot - Relative path to package root
32
- * @returns Package name or null if not found
33
- */
34
- getPackageName(packageRoot: string): string | null;
35
- /**
36
- * Check if package A has a dependency on package B.
37
- *
38
- * @param packageRootA - Relative path to first package
39
- * @param packageRootB - Relative path to second package
40
- * @returns True if A depends on B (directly)
41
- */
42
- hasCrossPackageDependency(packageRootA: string, packageRootB: string): boolean;
43
- /**
44
- * Check if two file paths belong to the same package.
45
- *
46
- * @param filePathA - First file path (relative)
47
- * @param filePathB - Second file path (relative)
48
- * @returns True if both files are in the same package
49
- */
50
- areInSamePackage(filePathA: string, filePathB: string): boolean;
51
- /**
52
- * Clear all caches. Useful for testing or when package structure changes.
53
- */
54
- clearCache(): void;
55
- /**
56
- * Get all cached package roots.
57
- */
58
- getCachedPackageRoots(): string[];
59
- private loadPackageJson;
60
- }
61
- /**
62
- * Create a mock filesystem for testing.
63
- * @param files - Map of file paths to contents (or null for non-existent)
64
- */
65
- export declare function createMockFileSystem(files: Map<string, string | null>): IFileSystem;
66
- //# sourceMappingURL=package-graph.d.ts.map
@@ -1,148 +0,0 @@
1
- import { existsSync, readFileSync } from 'node:fs';
2
- import { dirname, join, relative } from 'node:path';
3
- /**
4
- * Default filesystem implementation using Node.js fs module.
5
- */
6
- export const realFileSystem = {
7
- existsSync,
8
- readFileSync: (path, encoding) => readFileSync(path, encoding),
9
- };
10
- /**
11
- * PackageGraph analyzes monorepo package structure for dependency detection.
12
- *
13
- * Used by the false dependency detector to determine if tasks can run in parallel
14
- * based on package boundaries and cross-package dependencies.
15
- *
16
- * Implements IPackageGraph interface for testability - filesystem can be mocked.
17
- */
18
- export class PackageGraph {
19
- root;
20
- fs;
21
- packageCache = new Map();
22
- packageJsonCache = new Map();
23
- constructor(root, fs = realFileSystem) {
24
- this.root = root;
25
- this.fs = fs;
26
- }
27
- /**
28
- * Find the package root (directory containing package.json) for a file path.
29
- * Walks up the directory tree until package.json is found.
30
- *
31
- * @param filePath - Relative path from monorepo root
32
- * @returns Relative path to package root, or null if not found
33
- */
34
- findPackageRoot(filePath) {
35
- if (this.packageCache.has(filePath)) {
36
- return this.packageCache.get(filePath) ?? null;
37
- }
38
- let currentDir = dirname(join(this.root, filePath));
39
- const rootAbs = this.root;
40
- while (currentDir.length >= rootAbs.length) {
41
- const pkgJsonPath = join(currentDir, 'package.json');
42
- if (this.fs.existsSync(pkgJsonPath)) {
43
- const result = relative(this.root, currentDir);
44
- this.packageCache.set(filePath, result);
45
- return result;
46
- }
47
- const parentDir = dirname(currentDir);
48
- if (parentDir === currentDir)
49
- break;
50
- currentDir = parentDir;
51
- }
52
- this.packageCache.set(filePath, null);
53
- return null;
54
- }
55
- /**
56
- * Get the package name from package.json.
57
- *
58
- * @param packageRoot - Relative path to package root
59
- * @returns Package name or null if not found
60
- */
61
- getPackageName(packageRoot) {
62
- const pkgJson = this.loadPackageJson(packageRoot);
63
- return pkgJson?.name ?? null;
64
- }
65
- /**
66
- * Check if package A has a dependency on package B.
67
- *
68
- * @param packageRootA - Relative path to first package
69
- * @param packageRootB - Relative path to second package
70
- * @returns True if A depends on B (directly)
71
- */
72
- hasCrossPackageDependency(packageRootA, packageRootB) {
73
- const pkgA = this.loadPackageJson(packageRootA);
74
- const pkgB = this.loadPackageJson(packageRootB);
75
- if (!pkgA || !pkgB)
76
- return false;
77
- const bName = pkgB.name;
78
- const aDeps = {
79
- ...pkgA.dependencies,
80
- ...pkgA.devDependencies,
81
- };
82
- return bName in aDeps;
83
- }
84
- /**
85
- * Check if two file paths belong to the same package.
86
- *
87
- * @param filePathA - First file path (relative)
88
- * @param filePathB - Second file path (relative)
89
- * @returns True if both files are in the same package
90
- */
91
- areInSamePackage(filePathA, filePathB) {
92
- const pkgA = this.findPackageRoot(filePathA);
93
- const pkgB = this.findPackageRoot(filePathB);
94
- if (pkgA === null || pkgB === null)
95
- return false;
96
- return pkgA === pkgB;
97
- }
98
- /**
99
- * Clear all caches. Useful for testing or when package structure changes.
100
- */
101
- clearCache() {
102
- this.packageCache.clear();
103
- this.packageJsonCache.clear();
104
- }
105
- /**
106
- * Get all cached package roots.
107
- */
108
- getCachedPackageRoots() {
109
- return Array.from(this.packageCache.values()).filter((v) => v !== null);
110
- }
111
- loadPackageJson(packageRoot) {
112
- if (this.packageJsonCache.has(packageRoot)) {
113
- return this.packageJsonCache.get(packageRoot) ?? null;
114
- }
115
- const pkgJsonPath = join(this.root, packageRoot, 'package.json');
116
- if (!this.fs.existsSync(pkgJsonPath)) {
117
- this.packageJsonCache.set(packageRoot, null);
118
- return null;
119
- }
120
- try {
121
- const content = this.fs.readFileSync(pkgJsonPath, 'utf-8');
122
- const parsed = JSON.parse(content);
123
- this.packageJsonCache.set(packageRoot, parsed);
124
- return parsed;
125
- }
126
- catch {
127
- this.packageJsonCache.set(packageRoot, null);
128
- return null;
129
- }
130
- }
131
- }
132
- /**
133
- * Create a mock filesystem for testing.
134
- * @param files - Map of file paths to contents (or null for non-existent)
135
- */
136
- export function createMockFileSystem(files) {
137
- return {
138
- existsSync: (path) => files.has(path) && files.get(path) !== null,
139
- readFileSync: (path, _encoding) => {
140
- const content = files.get(path);
141
- if (content === null || content === undefined) {
142
- throw new Error(`ENOENT: no such file or directory, open '${path}'`);
143
- }
144
- return content;
145
- },
146
- };
147
- }
148
- //# sourceMappingURL=package-graph.js.map