pi-harness-runtime 1.1.348 → 1.1.350

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 (121) hide show
  1. package/index.ts +12 -0
  2. package/package.json +1 -1
  3. package/packages/a2a-adapter/package.json +1 -1
  4. package/packages/agent-policy/package.json +1 -1
  5. package/packages/architecture-generator/package.json +1 -1
  6. package/packages/auth/package.json +1 -1
  7. package/packages/autonomous-refactor/package.json +1 -1
  8. package/packages/autonomous-runtime/package.json +1 -1
  9. package/packages/cache-strategy/package.json +1 -1
  10. package/packages/capability-registry/package.json +1 -1
  11. package/packages/checkpoint/package.json +1 -1
  12. package/packages/cli-plugin-sdk/package.json +1 -1
  13. package/packages/clipboard/package.json +1 -1
  14. package/packages/clipboard-plugin/package.json +1 -1
  15. package/packages/code-generation/package.json +1 -1
  16. package/packages/code-review/package.json +1 -1
  17. package/packages/codex-adapter/package.json +1 -1
  18. package/packages/codex-watcher/package.json +1 -1
  19. package/packages/config-capture/package.json +1 -1
  20. package/packages/connection-runtime/package.json +1 -1
  21. package/packages/context-compiler/package.json +1 -1
  22. package/packages/context-discovery/package.json +1 -1
  23. package/packages/context-manager/package.json +1 -1
  24. package/packages/contracts/dist/delegation-assessor.d.ts +27 -0
  25. package/packages/contracts/dist/delegation-assessor.js +210 -0
  26. package/packages/contracts/dist/delegation.d.ts +142 -0
  27. package/packages/contracts/dist/delegation.js +33 -0
  28. package/packages/contracts/dist/index.d.ts +3 -0
  29. package/packages/contracts/dist/index.js +3 -0
  30. package/packages/contracts/dist/scope-discovery.d.ts +65 -0
  31. package/packages/contracts/dist/scope-discovery.js +247 -0
  32. package/packages/contracts/package.json +1 -1
  33. package/packages/contracts/src/delegation-assessor.ts +268 -0
  34. package/packages/contracts/src/delegation.ts +174 -0
  35. package/packages/contracts/src/index.ts +3 -0
  36. package/packages/contracts/src/scope-discovery.ts +352 -0
  37. package/packages/cookie-sanitizer/package.json +1 -1
  38. package/packages/cost-optimizer/package.json +1 -1
  39. package/packages/dependency-analyzer/package.json +1 -1
  40. package/packages/django-plugin/package.json +1 -1
  41. package/packages/doc-generator/package.json +1 -1
  42. package/packages/durable-review/package.json +1 -1
  43. package/packages/e2e-tester/package.json +1 -1
  44. package/packages/evaluation-engine/package.json +1 -1
  45. package/packages/evaluation-runner/package.json +1 -1
  46. package/packages/event-bus/package.json +1 -1
  47. package/packages/event-sink/package.json +1 -1
  48. package/packages/event-store/package.json +1 -1
  49. package/packages/experience-replay/package.json +1 -1
  50. package/packages/feedback-collector/package.json +1 -1
  51. package/packages/file-copy-helper/package.json +1 -1
  52. package/packages/framework-detector/package.json +1 -1
  53. package/packages/framework-plugin-sdk/package.json +1 -1
  54. package/packages/frappe-plugin/package.json +1 -1
  55. package/packages/generic-web-plugin/package.json +1 -1
  56. package/packages/health-monitor/package.json +1 -1
  57. package/packages/intent-analyzer/package.json +1 -1
  58. package/packages/jev-judge/package.json +1 -1
  59. package/packages/knowledge-graph/package.json +1 -1
  60. package/packages/knowledge-retrieval/package.json +1 -1
  61. package/packages/laravel-plugin/package.json +1 -1
  62. package/packages/learning-engine/package.json +1 -1
  63. package/packages/mcp-adapter/package.json +1 -1
  64. package/packages/memory-engine/package.json +1 -1
  65. package/packages/milestone-manager/package.json +1 -1
  66. package/packages/model-registry/package.json +1 -1
  67. package/packages/nextjs-plugin/package.json +1 -1
  68. package/packages/notification/package.json +1 -1
  69. package/packages/observability/package.json +1 -1
  70. package/packages/okf-indexer/package.json +1 -1
  71. package/packages/paperclip-integration/package.json +1 -1
  72. package/packages/performance-optimizer/package.json +1 -1
  73. package/packages/permission-mode/package.json +1 -1
  74. package/packages/privilege-broker/package.json +1 -1
  75. package/packages/project-analyzer/package.json +1 -1
  76. package/packages/project-bootstrap/package.json +1 -1
  77. package/packages/projection-engine/package.json +1 -1
  78. package/packages/prompt-compiler/package.json +1 -1
  79. package/packages/prompt-versioning/package.json +1 -1
  80. package/packages/provider-adapter-sdk/package.json +1 -1
  81. package/packages/provider-router/package.json +1 -1
  82. package/packages/provider-selector/package.json +1 -1
  83. package/packages/providers/package.json +1 -1
  84. package/packages/qdrant-skills/package.json +1 -1
  85. package/packages/quota-manager/package.json +1 -1
  86. package/packages/rate-limiter/package.json +1 -1
  87. package/packages/react-vite-plugin/package.json +1 -1
  88. package/packages/relay/package.json +1 -1
  89. package/packages/release-manager/package.json +1 -1
  90. package/packages/requirement-compiler/package.json +1 -1
  91. package/packages/runtime/package.json +1 -1
  92. package/packages/scheduler/package.json +1 -1
  93. package/packages/scheduler-adapter/package.json +1 -1
  94. package/packages/session/package.json +1 -1
  95. package/packages/session-api/package.json +1 -1
  96. package/packages/session-export/package.json +1 -1
  97. package/packages/shared-context/package.json +1 -1
  98. package/packages/skill-mcp-client/package.json +1 -1
  99. package/packages/skill-registry/package.json +1 -1
  100. package/packages/skills/package.json +1 -1
  101. package/packages/sprint-planner/package.json +1 -1
  102. package/packages/subscription-engine/package.json +1 -1
  103. package/packages/task-compiler/package.json +1 -1
  104. package/packages/telegram/package.json +1 -1
  105. package/packages/tencentdb-memory/package.json +1 -1
  106. package/packages/tencentdb-sync/package.json +1 -1
  107. package/packages/test-data-generator/package.json +1 -1
  108. package/packages/test-generator/package.json +1 -1
  109. package/packages/todo-bd-sync/package.json +1 -1
  110. package/packages/token-estimation/package.json +1 -1
  111. package/packages/token-optimizer/package.json +1 -1
  112. package/packages/trajectory/package.json +1 -1
  113. package/packages/tui/package.json +1 -1
  114. package/packages/types/package.json +1 -1
  115. package/packages/voice-api/package.json +1 -1
  116. package/packages/webmcp-tester/package.json +1 -1
  117. package/packages/wiki/package.json +1 -1
  118. package/packages/workflow-events/package.json +1 -1
  119. package/packages/workspace-scanner/package.json +1 -1
  120. package/packages/worktree/package.json +1 -1
  121. package/packages/write-review/package.json +1 -1
@@ -0,0 +1,174 @@
1
+ /**
2
+ * Delegation Assessment Types
3
+ *
4
+ * Types for consistent delegation assessment for large refactors.
5
+ */
6
+
7
+ /**
8
+ * Scope estimation for a refactoring task
9
+ */
10
+ export interface ScopeFacts {
11
+ /** Estimated number of relevant files affected */
12
+ estimatedFiles: number;
13
+ /** Actual observed number of files (after scanning) */
14
+ observedFiles: number;
15
+ /** Estimated number of call sites/changes */
16
+ estimatedCallSites: number;
17
+ /** Actual observed call sites */
18
+ observedCallSites: number;
19
+ /** Number of independent components/groups */
20
+ independentComponents: number;
21
+ /** Whether changes are tightly coupled (shared APIs) */
22
+ tightlyCoupled: boolean;
23
+ /** Whether transformation is mechanical (safe for scripts) */
24
+ isMechanical: boolean;
25
+ /** Whether transformation is complex (needs reasoning) */
26
+ isComplex: boolean;
27
+ /** File type breakdown */
28
+ fileTypes: Record<string, number>;
29
+ /** Scope fingerprint for change detection */
30
+ fingerprint: string;
31
+ }
32
+
33
+ /**
34
+ * Delegation strategy decision
35
+ */
36
+ export type DelegationStrategy =
37
+ | "delegate" // Delegate independent work groups
38
+ | "script_then_delegate" // Script simple parts, delegate complex
39
+ | "script_only" // Script approach is best (mechanical + reliable)
40
+ | "single_agent" // Single agent handles all (small scope)
41
+ | "unavailable"; // Extension not available, use fallback
42
+
43
+ /**
44
+ * Work group for delegation
45
+ */
46
+ export interface WorkGroup {
47
+ /** Unique group ID */
48
+ groupId: string;
49
+ /** Human-readable description */
50
+ description: string;
51
+ /** Files owned by this group */
52
+ ownedPaths: string[];
53
+ /** Whether this group is mechanical (script-friendly) */
54
+ isMechanical: boolean;
55
+ /** Assigned child agent ID (if delegated) */
56
+ childId?: string;
57
+ /** Status of this group */
58
+ status: "pending" | "dispatched" | "running" | "completed" | "failed" | "cancelled";
59
+ /** Validation evidence */
60
+ validation?: {
61
+ passed: boolean;
62
+ details: string;
63
+ };
64
+ }
65
+
66
+ /**
67
+ * Delegation decision from Jev or fallback
68
+ */
69
+ export interface DelegationDecision {
70
+ /** Strategy selected */
71
+ strategy: DelegationStrategy;
72
+ /** Reason for the decision */
73
+ reason: string;
74
+ /** Proposed work groups (for delegate strategies) */
75
+ workGroups: WorkGroup[];
76
+ /** Validation expectations */
77
+ validationExpectations: string;
78
+ /** Confidence level */
79
+ confidence: "high" | "medium" | "low";
80
+ /** Whether Jev was used */
81
+ jevUsed: boolean;
82
+ /** Reassessment triggers */
83
+ reassessmentTriggers: string[];
84
+ }
85
+
86
+ /**
87
+ * Delegation receipt for persistence
88
+ */
89
+ export interface DelegationReceipt {
90
+ /** Version for schema compatibility */
91
+ version: number;
92
+ /** Workspace canonical path */
93
+ workspacePath: string;
94
+ /** Session ID */
95
+ sessionId: string;
96
+ /** Parent task ID */
97
+ parentTaskId: string;
98
+ /** Scope fingerprint */
99
+ scopeFingerprint: string;
100
+ /** Decision made */
101
+ decision: DelegationDecision;
102
+ /** Created timestamp */
103
+ createdAt: number;
104
+ /** Last updated timestamp */
105
+ updatedAt: number;
106
+ /** Status */
107
+ status: "assessing" | "in_progress" | "completed" | "failed" | "cancelled";
108
+ /** Active child IDs */
109
+ childIds: string[];
110
+ /** Outstanding validation items */
111
+ pendingValidation: string[];
112
+ /** User constraints (e.g., no delegation) */
113
+ userConstraints: string[];
114
+ }
115
+
116
+ /**
117
+ * Delegation assessment trigger config
118
+ */
119
+ export interface DelegationTriggerConfig {
120
+ /** Minimum files to trigger assessment */
121
+ minFiles: number;
122
+ /** Minimum call sites to trigger assessment */
123
+ minCallSites: number;
124
+ /** Minimum independent components to suggest delegation */
125
+ minIndependentComponents: number;
126
+ /** Whether to assess on scope discovery vs mutation */
127
+ assessOnDiscovery: boolean;
128
+ }
129
+
130
+ /**
131
+ * Default trigger config
132
+ */
133
+ export const DEFAULT_DELEGATION_TRIGGERS: DelegationTriggerConfig = {
134
+ minFiles: 10,
135
+ minCallSites: 50,
136
+ minIndependentComponents: 2,
137
+ assessOnDiscovery: true,
138
+ };
139
+
140
+ /**
141
+ * Extension capability for delegation
142
+ */
143
+ export interface ExtensionCapability {
144
+ /** Extension name */
145
+ name: string;
146
+ /** Whether it supports sub-agents */
147
+ supportsSubagents: boolean;
148
+ /** Whether it supports work isolation */
149
+ supportsWorkIsolation: boolean;
150
+ /** Whether it supports cancellation */
151
+ supportsCancellation: boolean;
152
+ /** Supported tool names */
153
+ availableTools: string[];
154
+ /** Verified working */
155
+ verified: boolean;
156
+ }
157
+
158
+ /**
159
+ * Check if delegation should be triggered
160
+ */
161
+ export function shouldTriggerAssessment(facts: ScopeFacts, config: DelegationTriggerConfig = DEFAULT_DELEGATION_TRIGGERS): boolean {
162
+ // Check file count
163
+ if (facts.observedFiles >= config.minFiles) return true;
164
+ if (facts.estimatedFiles >= config.minFiles) return true;
165
+
166
+ // Check call sites
167
+ if (facts.observedCallSites >= config.minCallSites) return true;
168
+ if (facts.estimatedCallSites >= config.minCallSites) return true;
169
+
170
+ // Check independent components
171
+ if (facts.independentComponents >= config.minIndependentComponents) return true;
172
+
173
+ return false;
174
+ }
@@ -19,3 +19,6 @@ export * from './sessions.js';
19
19
  export * from './projects.js';
20
20
  export * from './notifications.js';
21
21
  export * from './approvals.js';
22
+ export * from './delegation.js';
23
+ export * from './delegation-assessor.js';
24
+ export * from './scope-discovery.js';
@@ -0,0 +1,352 @@
1
+ /**
2
+ * Scope Discovery
3
+ *
4
+ * Discovers and estimates the scope of refactoring work.
5
+ */
6
+
7
+ import { readFileSync, readdirSync } from "fs";
8
+ import { join, extname, relative } from "path";
9
+ import { execSync } from "child_process";
10
+
11
+ /**
12
+ * File pattern matcher
13
+ */
14
+ export interface FilePattern {
15
+ /** Glob pattern or exact extension */
16
+ pattern: string;
17
+ /** Whether to recurse into directories */
18
+ recursive?: boolean;
19
+ }
20
+
21
+ /**
22
+ * Scope discovery configuration
23
+ */
24
+ export interface ScopeDiscoveryConfig {
25
+ /** File extensions to consider relevant */
26
+ relevantExtensions: string[];
27
+ /** Directories to exclude (vendored, generated, etc.) */
28
+ excludeDirs: string[];
29
+ /** Files to exclude by name */
30
+ excludeFiles: string[];
31
+ /** Whether to scan for call sites */
32
+ scanCallSites: boolean;
33
+ /** Call site patterns to count */
34
+ callSitePatterns: string[];
35
+ }
36
+
37
+ /**
38
+ * Default scope discovery config
39
+ */
40
+ export const DEFAULT_SCOPE_CONFIG: ScopeDiscoveryConfig = {
41
+ relevantExtensions: [".ts", ".tsx", ".js", ".jsx", ".py", ".go", ".rs"],
42
+ excludeDirs: [
43
+ "node_modules",
44
+ ".git",
45
+ "dist",
46
+ "build",
47
+ ".next",
48
+ "__pycache__",
49
+ "vendor",
50
+ "generated",
51
+ ".venv",
52
+ "coverage",
53
+ ],
54
+ excludeFiles: [
55
+ "package-lock.json",
56
+ "yarn.lock",
57
+ "pnpm-lock.yaml",
58
+ "bun.lockb",
59
+ "*.min.js",
60
+ "*.bundle.js",
61
+ ],
62
+ scanCallSites: true,
63
+ callSitePatterns: [
64
+ "TODO:",
65
+ "FIXME:",
66
+ "console.log",
67
+ "throw new",
68
+ "await this",
69
+ "// @ts-ignore",
70
+ ],
71
+ };
72
+
73
+ /**
74
+ * Discovered scope result
75
+ */
76
+ export interface DiscoveredScope {
77
+ /** Total relevant files found */
78
+ totalFiles: number;
79
+ /** Files matching the refactoring pattern */
80
+ matchingFiles: number;
81
+ /** Estimated call sites */
82
+ callSites: number;
83
+ /** Independent components found */
84
+ components: string[];
85
+ /** File breakdown by type */
86
+ fileTypes: Record<string, number>;
87
+ /** Whether shared APIs were detected */
88
+ hasSharedApis: boolean;
89
+ /** Whether transformation appears mechanical */
90
+ isMechanical: boolean;
91
+ /** Scope fingerprint for change detection */
92
+ fingerprint: string;
93
+ }
94
+
95
+ /**
96
+ * Discover scope by scanning files
97
+ */
98
+ export function discoverScope(
99
+ workspacePath: string,
100
+ searchPattern: string,
101
+ config: Partial<ScopeDiscoveryConfig> = {}
102
+ ): DiscoveredScope {
103
+ const cfg = { ...DEFAULT_SCOPE_CONFIG, ...config };
104
+ const files: string[] = [];
105
+ const fileTypes: Record<string, number> = {};
106
+ const callSites: string[] = [];
107
+ const components: Set<string> = new Set();
108
+
109
+ // Scan directory recursively
110
+ scanDirectory(workspacePath, files, fileTypes, cfg, 0);
111
+
112
+ // Filter to relevant files
113
+ const relevantFiles = files.filter((f) =>
114
+ cfg.relevantExtensions.includes(extname(f))
115
+ );
116
+
117
+ // Count matching files and call sites
118
+ let matchingCount = 0;
119
+ for (const file of relevantFiles) {
120
+ const content = readFileSync(file, "utf8").slice(0, 10000); // First 10KB
121
+
122
+ // Check if file matches search pattern
123
+ if (content.toLowerCase().includes(searchPattern.toLowerCase())) {
124
+ matchingCount++;
125
+
126
+ // Count call sites
127
+ if (cfg.scanCallSites) {
128
+ for (const pattern of cfg.callSitePatterns) {
129
+ const matches = content.match(new RegExp(pattern, "gi"));
130
+ if (matches) {
131
+ callSites.push(...matches.map((m) => `${file}:${m}`));
132
+ }
133
+ }
134
+ }
135
+
136
+ // Identify component (directory)
137
+ const relPath = relative(workspacePath, file);
138
+ const parts = relPath.split("/");
139
+ if (parts.length > 1) {
140
+ components.add(parts.slice(0, -1).join("/"));
141
+ }
142
+ }
143
+ }
144
+
145
+ // Detect shared APIs (files that are imported by multiple components)
146
+ const hasSharedApis = detectSharedApis(relevantFiles);
147
+
148
+ // Detect if transformation is mechanical (simple replacements)
149
+ const isMechanical = detectMechanicalPattern(relevantFiles, searchPattern);
150
+
151
+ // Generate fingerprint
152
+ const fingerprint = generateFingerprint(matchingCount, callSites.length, components.size);
153
+
154
+ return {
155
+ totalFiles: relevantFiles.length,
156
+ matchingFiles: matchingCount,
157
+ callSites: callSites.slice(0, 100).length,
158
+ components: Array.from(components),
159
+ fileTypes,
160
+ hasSharedApis,
161
+ isMechanical,
162
+ fingerprint,
163
+ };
164
+ }
165
+
166
+ /**
167
+ * Recursively scan directory
168
+ */
169
+ function scanDirectory(
170
+ dir: string,
171
+ files: string[],
172
+ fileTypes: Record<string, number>,
173
+ config: ScopeDiscoveryConfig,
174
+ depth: number
175
+ ): void {
176
+ if (depth > 10) return; // Max depth guard
177
+
178
+ try {
179
+ const entries = readdirSync(dir, { withFileTypes: true });
180
+
181
+ for (const entry of entries) {
182
+ const fullPath = join(dir, entry.name);
183
+
184
+ // Skip excluded
185
+ if (config.excludeDirs.includes(entry.name)) continue;
186
+ if (config.excludeFiles.some((f) => entry.name.includes(f))) continue;
187
+
188
+ if (entry.isDirectory()) {
189
+ scanDirectory(fullPath, files, fileTypes, config, depth + 1);
190
+ } else if (entry.isFile()) {
191
+ files.push(fullPath);
192
+ const ext = extname(entry.name);
193
+ fileTypes[ext] = (fileTypes[ext] || 0) + 1;
194
+ }
195
+ }
196
+ } catch {
197
+ // Skip inaccessible directories
198
+ }
199
+ }
200
+
201
+ /**
202
+ * Detect if files have shared API patterns
203
+ */
204
+ function detectSharedApis(files: string[]): boolean {
205
+ // Look for common export patterns
206
+ const exportCounts = new Map<string, number>();
207
+
208
+ for (const file of files.slice(0, 50)) {
209
+ // Sample first 50 files
210
+ try {
211
+ const content = readFileSync(file, "utf8").slice(0, 5000);
212
+
213
+ // Count exports
214
+ const exportMatches = content.match(/export\s+(const|function|class|type|interface)/g);
215
+ if (exportMatches) {
216
+ exportCounts.set(file, (exportCounts.get(file) || 0) + exportMatches.length);
217
+ }
218
+ } catch {
219
+ // Skip unreadable files
220
+ }
221
+ }
222
+
223
+ // Multiple files with exports suggests shared APIs
224
+ let filesWithExports = 0;
225
+ for (const count of exportCounts.values()) {
226
+ if (count > 0) filesWithExports++;
227
+ }
228
+
229
+ return filesWithExports > 3;
230
+ }
231
+
232
+ /**
233
+ * Detect if transformation is mechanical (simple replacements)
234
+ */
235
+ function detectMechanicalPattern(files: string[], searchPattern: string): boolean {
236
+ // Mechanical = simple string replacement, no complex logic
237
+ // Look for consistent patterns
238
+
239
+ let mechanicalCount = 0;
240
+ let totalMatches = 0;
241
+
242
+ for (const file of files.slice(0, 20)) {
243
+ try {
244
+ const content = readFileSync(file, "utf8");
245
+ const matches = content.match(
246
+ new RegExp(searchPattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "gi")
247
+ );
248
+
249
+ if (matches) {
250
+ totalMatches += matches.length;
251
+
252
+ // Check if each match is a simple replacement (same context)
253
+ // Mechanical if: same indentation, same surrounding code pattern
254
+ const uniqueContexts = new Set<string>();
255
+
256
+ for (const match of matches.slice(0, 5)) {
257
+ const idx = content.indexOf(match);
258
+ const before = content.slice(Math.max(0, idx - 20), idx);
259
+ const after = content.slice(idx + match.length, idx + match.length + 20);
260
+ uniqueContexts.add(before.trim() + "|" + after.trim());
261
+ }
262
+
263
+ // Single context = mechanical
264
+ if (uniqueContexts.size === 1) {
265
+ mechanicalCount++;
266
+ }
267
+ }
268
+ } catch {
269
+ // Skip unreadable files
270
+ }
271
+ }
272
+
273
+ // More than half mechanical = likely mechanical
274
+ return totalMatches > 0 && mechanicalCount / totalMatches > 0.5;
275
+ }
276
+
277
+ /**
278
+ * Generate scope fingerprint
279
+ */
280
+ function generateFingerprint(
281
+ fileCount: number,
282
+ callSiteCount: number,
283
+ componentCount: number
284
+ ): string {
285
+ // Simple fingerprint based on counts
286
+ const data = `${fileCount}:${callSiteCount}:${componentCount}`;
287
+ let hash = 0;
288
+ for (let i = 0; i < data.length; i++) {
289
+ const char = data.charCodeAt(i);
290
+ hash = (hash << 5) - hash + char;
291
+ hash = hash & hash; // Convert to 32bit integer
292
+ }
293
+ return Math.abs(hash).toString(36);
294
+ }
295
+
296
+ /**
297
+ * Quick scope estimate using git grep
298
+ */
299
+ export function quickScopeEstimate(
300
+ workspacePath: string,
301
+ pattern: string
302
+ ): { fileCount: number; lineCount: number } {
303
+ // Validate inputs to prevent command injection
304
+ const safePath = validatePath(workspacePath);
305
+ if (!safePath) return { fileCount: 0, lineCount: 0 };
306
+
307
+ const safePattern = escapeForGrep(pattern);
308
+ if (!safePattern) return { fileCount: 0, lineCount: 0 };
309
+
310
+ try {
311
+ const result = execSync(
312
+ `grep -r "${safePattern}" "${safePath}" --include="*.ts" --include="*.tsx" --include="*.js" --include="*.jsx" -l 2>/dev/null | wc -l`,
313
+ { encoding: "utf8", timeout: 10000 }
314
+ );
315
+
316
+ const fileCount = parseInt(result.trim(), 10) || 0;
317
+
318
+ const lineResult = execSync(
319
+ `grep -r "${safePattern}" "${safePath}" --include="*.ts" --include="*.tsx" --include="*.js" --include="*.jsx" 2>/dev/null | wc -l`,
320
+ { encoding: "utf8", timeout: 10000 }
321
+ );
322
+
323
+ const lineCount = parseInt(lineResult.trim(), 10) || 0;
324
+
325
+ return { fileCount, lineCount };
326
+ } catch {
327
+ return { fileCount: 0, lineCount: 0 };
328
+ }
329
+ }
330
+
331
+ /**
332
+ * Validate and sanitize a file path
333
+ */
334
+ function validatePath(path: string): string | null {
335
+ // Only allow alphanumeric, hyphen, underscore, dot, and forward slash
336
+ if (!/^[\w./-]+$/.test(path)) {
337
+ return null;
338
+ }
339
+ // Prevent path traversal
340
+ if (path.includes("..")) {
341
+ return null;
342
+ }
343
+ return path;
344
+ }
345
+
346
+ /**
347
+ * Escape a string for safe use in grep pattern
348
+ */
349
+ function escapeForGrep(pattern: string): string {
350
+ // Remove any shell metacharacters
351
+ return pattern.replace(/[|;&$`\\"'<>\(\)\[\]\{\}]/g, "");
352
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/cookie-sanitizer",
3
- "version": "1.1.348",
3
+ "version": "1.1.350",
4
4
  "description": "pi-harness-runtime — cookie sanitizer (provider-quota cookie file normaliser)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/cost-optimizer",
3
- "version": "1.1.348",
3
+ "version": "1.1.350",
4
4
  "description": "Cost Optimizer for pi-harness-runtime — budget tracking, forecasting, and model selection optimization",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/dependency-analyzer",
3
- "version": "1.1.348",
3
+ "version": "1.1.350",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/django-plugin",
3
- "version": "1.1.348",
3
+ "version": "1.1.350",
4
4
  "description": "Django framework analysis — models, management commands, settings",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/doc-generator",
3
- "version": "1.1.348",
3
+ "version": "1.1.350",
4
4
  "description": "Auto-Doc Generator — generate documentation from source code (RFC-0014)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moocoding/durable-review",
3
- "version": "1.1.348",
3
+ "version": "1.1.350",
4
4
  "description": "SQLite-backed durable reviewer using @earendil-works/pi-durable",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/e2e-tester",
3
- "version": "1.1.348",
3
+ "version": "1.1.350",
4
4
  "description": "WebMCP-powered E2E testing for pi-harness - natural language test scenarios",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/evaluation-engine",
3
- "version": "1.1.348",
3
+ "version": "1.1.350",
4
4
  "description": "Evaluation engine for pi-harness runtime (RFC-0057)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/evaluation-runner",
3
- "version": "1.1.348",
3
+ "version": "1.1.350",
4
4
  "type": "module",
5
5
  "description": "Framework-agnostic evaluation framework for AI outputs (RFC-0020)",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/event-bus",
3
- "version": "1.1.348",
3
+ "version": "1.1.350",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/event-sink",
3
- "version": "1.1.348",
3
+ "version": "1.1.350",
4
4
  "type": "module",
5
5
  "description": "Atomic event sourcing with SQLite transactions for pi-harness",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/event-store",
3
- "version": "1.1.348",
3
+ "version": "1.1.350",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/experience-replay",
3
- "version": "1.1.348",
3
+ "version": "1.1.350",
4
4
  "description": "Experience replay for debugging and reproducibility (RFC-0059)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/feedback-collector",
3
- "version": "1.1.348",
3
+ "version": "1.1.350",
4
4
  "type": "module",
5
5
  "description": "Collect and aggregate human/automated feedback (RFC-0021)",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moocoding/file-copy-helper",
3
- "version": "1.1.348",
3
+ "version": "1.1.350",
4
4
  "description": "Injects cp/sudo cp rule when user asks to mimic/copy files",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/framework-detector",
3
- "version": "1.1.348",
3
+ "version": "1.1.350",
4
4
  "description": "Framework detection system for pi-harness-runtime",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/framework-plugin-sdk",
3
- "version": "1.1.348",
3
+ "version": "1.1.350",
4
4
  "description": "Framework plugin SDK for pi-harness-runtime",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",