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