@evref-bl/dev-nexus 0.1.0-alpha.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.
- package/README.md +677 -0
- package/dist/browserOpener.d.ts +9 -0
- package/dist/browserOpener.js +47 -0
- package/dist/cli.d.ts +18 -0
- package/dist/cli.js +2374 -0
- package/dist/gitWorktreeService.d.ts +57 -0
- package/dist/gitWorktreeService.js +157 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.js +47 -0
- package/dist/nexusAgentMcpConfig.d.ts +30 -0
- package/dist/nexusAgentMcpConfig.js +228 -0
- package/dist/nexusAutomation.d.ts +103 -0
- package/dist/nexusAutomation.js +390 -0
- package/dist/nexusAutomationAgentLaunch.d.ts +148 -0
- package/dist/nexusAutomationAgentLaunch.js +855 -0
- package/dist/nexusAutomationAgentProfile.d.ts +39 -0
- package/dist/nexusAutomationAgentProfile.js +103 -0
- package/dist/nexusAutomationAgentSurface.d.ts +62 -0
- package/dist/nexusAutomationAgentSurface.js +90 -0
- package/dist/nexusAutomationCommandExecutor.d.ts +29 -0
- package/dist/nexusAutomationCommandExecutor.js +251 -0
- package/dist/nexusAutomationConfig.d.ts +114 -0
- package/dist/nexusAutomationConfig.js +547 -0
- package/dist/nexusAutomationEnqueue.d.ts +37 -0
- package/dist/nexusAutomationEnqueue.js +128 -0
- package/dist/nexusAutomationRunOnce.d.ts +91 -0
- package/dist/nexusAutomationRunOnce.js +586 -0
- package/dist/nexusAutomationScheduler.d.ts +50 -0
- package/dist/nexusAutomationScheduler.js +196 -0
- package/dist/nexusAutomationStatus.d.ts +55 -0
- package/dist/nexusAutomationStatus.js +462 -0
- package/dist/nexusAutomationTarget.d.ts +19 -0
- package/dist/nexusAutomationTarget.js +33 -0
- package/dist/nexusAutomationTargetCycle.d.ts +90 -0
- package/dist/nexusAutomationTargetCycle.js +282 -0
- package/dist/nexusAutomationTargetReport.d.ts +136 -0
- package/dist/nexusAutomationTargetReport.js +504 -0
- package/dist/nexusAutomationWorktreeSetup.d.ts +89 -0
- package/dist/nexusAutomationWorktreeSetup.js +661 -0
- package/dist/nexusCoordination.d.ts +198 -0
- package/dist/nexusCoordination.js +1018 -0
- package/dist/nexusExtension.d.ts +31 -0
- package/dist/nexusExtension.js +1 -0
- package/dist/nexusHomeConfig.d.ts +38 -0
- package/dist/nexusHomeConfig.js +133 -0
- package/dist/nexusMcpServer.d.ts +31 -0
- package/dist/nexusMcpServer.js +1036 -0
- package/dist/nexusPluginCapabilities.d.ts +197 -0
- package/dist/nexusPluginCapabilities.js +201 -0
- package/dist/nexusProjectConfig.d.ts +95 -0
- package/dist/nexusProjectConfig.js +880 -0
- package/dist/nexusProjectHomeService.d.ts +121 -0
- package/dist/nexusProjectHomeService.js +171 -0
- package/dist/nexusProjectLifecycle.d.ts +62 -0
- package/dist/nexusProjectLifecycle.js +205 -0
- package/dist/nexusProjectOperations.d.ts +101 -0
- package/dist/nexusProjectOperations.js +296 -0
- package/dist/nexusProjectRegistry.d.ts +42 -0
- package/dist/nexusProjectRegistry.js +91 -0
- package/dist/nexusProjectScaffold.d.ts +25 -0
- package/dist/nexusProjectScaffold.js +61 -0
- package/dist/nexusProjectTemplate.d.ts +34 -0
- package/dist/nexusProjectTemplate.js +354 -0
- package/dist/nexusSkills.d.ts +134 -0
- package/dist/nexusSkills.js +647 -0
- package/dist/nexusWorkerContextBundle.d.ts +142 -0
- package/dist/nexusWorkerContextBundle.js +375 -0
- package/dist/processSupervisor.d.ts +89 -0
- package/dist/processSupervisor.js +440 -0
- package/dist/vibeKanbanApi.d.ts +11 -0
- package/dist/vibeKanbanApi.js +14 -0
- package/dist/vibeKanbanAuth.d.ts +25 -0
- package/dist/vibeKanbanAuth.js +101 -0
- package/dist/vibeKanbanBoardAdapter.d.ts +36 -0
- package/dist/vibeKanbanBoardAdapter.js +196 -0
- package/dist/vibeKanbanMcpConfig.d.ts +36 -0
- package/dist/vibeKanbanMcpConfig.js +191 -0
- package/dist/vibeKanbanProjectAdapter.d.ts +39 -0
- package/dist/vibeKanbanProjectAdapter.js +113 -0
- package/dist/vibeKanbanWorkspaceSetup.d.ts +1 -0
- package/dist/vibeKanbanWorkspaceSetup.js +96 -0
- package/dist/workItemService.d.ts +60 -0
- package/dist/workItemService.js +163 -0
- package/dist/workTrackingGitHubProvider.d.ts +71 -0
- package/dist/workTrackingGitHubProvider.js +663 -0
- package/dist/workTrackingGitLabProvider.d.ts +62 -0
- package/dist/workTrackingGitLabProvider.js +523 -0
- package/dist/workTrackingJiraProvider.d.ts +67 -0
- package/dist/workTrackingJiraProvider.js +652 -0
- package/dist/workTrackingLocalProvider.d.ts +49 -0
- package/dist/workTrackingLocalProvider.js +463 -0
- package/dist/workTrackingProviderService.d.ts +21 -0
- package/dist/workTrackingProviderService.js +117 -0
- package/dist/workTrackingTypes.d.ts +202 -0
- package/dist/workTrackingTypes.js +1 -0
- package/dist/workTrackingVibeProvider.d.ts +35 -0
- package/dist/workTrackingVibeProvider.js +119 -0
- package/dist/worktreeExecutionMetadata.d.ts +76 -0
- package/dist/worktreeExecutionMetadata.js +239 -0
- package/package.json +37 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import type { NexusSkillSourceControl } from "./nexusSkills.js";
|
|
2
|
+
import type { WorkStatus, WorkTrackingProviderName } from "./workTrackingTypes.js";
|
|
3
|
+
import type { NexusPluginWorkerFragmentsProjection } from "./nexusPluginCapabilities.js";
|
|
4
|
+
export type NexusWorkerContextFileId = "agents" | "context" | "plan" | "target-state";
|
|
5
|
+
export interface NexusWorkerContextFileReference {
|
|
6
|
+
id: NexusWorkerContextFileId;
|
|
7
|
+
path: string;
|
|
8
|
+
access: "read_only";
|
|
9
|
+
}
|
|
10
|
+
export interface NexusWorkerContextProject {
|
|
11
|
+
id: string | null;
|
|
12
|
+
name: string | null;
|
|
13
|
+
root: string;
|
|
14
|
+
}
|
|
15
|
+
export interface NexusWorkerContextBoundarySet {
|
|
16
|
+
roots: string[];
|
|
17
|
+
files?: NexusWorkerContextFileReference[];
|
|
18
|
+
notes: string[];
|
|
19
|
+
}
|
|
20
|
+
export interface NexusWorkerContextBoundaries {
|
|
21
|
+
commandWorkingDirectory: string;
|
|
22
|
+
gitWorkingDirectory: string;
|
|
23
|
+
write: NexusWorkerContextBoundarySet;
|
|
24
|
+
read: NexusWorkerContextBoundarySet;
|
|
25
|
+
}
|
|
26
|
+
export interface NexusWorkerProjectContextReferences {
|
|
27
|
+
agentsPath: string;
|
|
28
|
+
contextPath: string;
|
|
29
|
+
planPath: string;
|
|
30
|
+
targetStatePath: string;
|
|
31
|
+
files: NexusWorkerContextFileReference[];
|
|
32
|
+
}
|
|
33
|
+
export interface NexusWorkerContextSkillReference {
|
|
34
|
+
id: string;
|
|
35
|
+
sourceSkillRoot: string;
|
|
36
|
+
projectedSkillRoot: string;
|
|
37
|
+
skillPath: string | null;
|
|
38
|
+
}
|
|
39
|
+
export interface NexusWorkerContextAgentSkillProjection {
|
|
40
|
+
agent: string;
|
|
41
|
+
skillsDirectory: string;
|
|
42
|
+
sourceControl: NexusSkillSourceControl;
|
|
43
|
+
skills: NexusWorkerContextSkillReference[];
|
|
44
|
+
}
|
|
45
|
+
export interface NexusWorkerContextSkills {
|
|
46
|
+
projectManagedRoot: string;
|
|
47
|
+
agentNativeProjections: NexusWorkerContextAgentSkillProjection[];
|
|
48
|
+
}
|
|
49
|
+
export type NexusWorkerContextDependencyProjectionSourceControl = "support" | "source";
|
|
50
|
+
export type NexusWorkerContextDependencyProjectionStatus = "linked" | "present" | "skipped";
|
|
51
|
+
export interface NexusWorkerContextDependencyProjectionSourceMetadata {
|
|
52
|
+
pluginId: string;
|
|
53
|
+
pluginName: string | null;
|
|
54
|
+
version: string | null;
|
|
55
|
+
capabilityId: string;
|
|
56
|
+
}
|
|
57
|
+
export interface NexusWorkerContextDependencyProjection {
|
|
58
|
+
id: string;
|
|
59
|
+
source: string;
|
|
60
|
+
target: string;
|
|
61
|
+
sourcePath: string;
|
|
62
|
+
targetPath: string;
|
|
63
|
+
required: boolean;
|
|
64
|
+
sourceControl: NexusWorkerContextDependencyProjectionSourceControl;
|
|
65
|
+
reason: string | null;
|
|
66
|
+
status: NexusWorkerContextDependencyProjectionStatus;
|
|
67
|
+
message: string;
|
|
68
|
+
sourceMetadata: NexusWorkerContextDependencyProjectionSourceMetadata;
|
|
69
|
+
}
|
|
70
|
+
export interface NexusWorkerContextDependencySupport {
|
|
71
|
+
pluginDependencyProjections: NexusWorkerContextDependencyProjection[];
|
|
72
|
+
}
|
|
73
|
+
export interface NexusWorkerContextBundleWorkItem {
|
|
74
|
+
id: string;
|
|
75
|
+
title: string | null;
|
|
76
|
+
status?: WorkStatus;
|
|
77
|
+
provider?: WorkTrackingProviderName | string;
|
|
78
|
+
labels?: string[];
|
|
79
|
+
assignees?: string[];
|
|
80
|
+
}
|
|
81
|
+
export interface NexusWorkerContextBundleWorktree {
|
|
82
|
+
componentId: string;
|
|
83
|
+
sourceRoot: string;
|
|
84
|
+
worktreesRoot: string;
|
|
85
|
+
worktreePath: string;
|
|
86
|
+
branchName: string;
|
|
87
|
+
baseRef: string | null;
|
|
88
|
+
workItem: NexusWorkerContextBundleWorkItem | null;
|
|
89
|
+
}
|
|
90
|
+
export interface NexusWorkerContextBundle {
|
|
91
|
+
version: 1;
|
|
92
|
+
project: NexusWorkerContextProject;
|
|
93
|
+
projectRoot: string;
|
|
94
|
+
component: {
|
|
95
|
+
id: string;
|
|
96
|
+
sourceRoot: string;
|
|
97
|
+
};
|
|
98
|
+
skills: NexusWorkerContextSkills;
|
|
99
|
+
dependencySupport: NexusWorkerContextDependencySupport;
|
|
100
|
+
ownership: NexusWorkerContextBundleWorktree;
|
|
101
|
+
worktree: NexusWorkerContextBundleWorktree;
|
|
102
|
+
projectContext: NexusWorkerProjectContextReferences;
|
|
103
|
+
pluginFragments: NexusPluginWorkerFragmentsProjection;
|
|
104
|
+
boundaries: NexusWorkerContextBoundaries;
|
|
105
|
+
}
|
|
106
|
+
export interface NexusWorkerContextBundleOptions {
|
|
107
|
+
projectRoot: string;
|
|
108
|
+
projectId?: string | null;
|
|
109
|
+
projectName?: string | null;
|
|
110
|
+
componentId: string;
|
|
111
|
+
sourceRoot: string;
|
|
112
|
+
worktreesRoot: string;
|
|
113
|
+
worktreePath: string;
|
|
114
|
+
branchName: string;
|
|
115
|
+
baseRef: string | null;
|
|
116
|
+
workItem: NexusWorkerContextBundleWorkItem | null;
|
|
117
|
+
targetStatePath?: string | null;
|
|
118
|
+
skills?: NexusWorkerContextSkills;
|
|
119
|
+
dependencyProjections?: NexusWorkerContextDependencyProjection[];
|
|
120
|
+
pluginFragments?: NexusPluginWorkerFragmentsProjection;
|
|
121
|
+
}
|
|
122
|
+
export interface MaterializeNexusWorkerContextBundleResult {
|
|
123
|
+
contextDirectoryPath: string;
|
|
124
|
+
contextJsonPath: string;
|
|
125
|
+
briefingPath: string;
|
|
126
|
+
context: NexusWorkerContextBundle;
|
|
127
|
+
briefingMarkdown: string;
|
|
128
|
+
}
|
|
129
|
+
export declare const nexusWorkerContextDirectoryName = "context";
|
|
130
|
+
export declare const nexusWorkerContextJsonFileName = "context.json";
|
|
131
|
+
export declare const nexusWorkerBriefingFileName = "briefing.md";
|
|
132
|
+
export declare const defaultNexusWorkerTargetStatePath = ".dev-nexus/automation/target-state.md";
|
|
133
|
+
export declare const defaultNexusWorkerProjectManagedSkillsPath = ".dev-nexus/skills";
|
|
134
|
+
export declare class NexusWorkerContextBundleError extends Error {
|
|
135
|
+
constructor(message: string);
|
|
136
|
+
}
|
|
137
|
+
export declare function nexusWorkerContextDirectoryPath(worktreePath: string): string;
|
|
138
|
+
export declare function nexusWorkerContextJsonPath(worktreePath: string): string;
|
|
139
|
+
export declare function nexusWorkerBriefingPath(worktreePath: string): string;
|
|
140
|
+
export declare function buildNexusWorkerContextBundle(options: NexusWorkerContextBundleOptions): NexusWorkerContextBundle;
|
|
141
|
+
export declare function renderNexusWorkerBriefing(context: NexusWorkerContextBundle): string;
|
|
142
|
+
export declare function materializeNexusWorkerContextBundle(options: NexusWorkerContextBundleOptions): MaterializeNexusWorkerContextBundleResult;
|
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
export const nexusWorkerContextDirectoryName = "context";
|
|
4
|
+
export const nexusWorkerContextJsonFileName = "context.json";
|
|
5
|
+
export const nexusWorkerBriefingFileName = "briefing.md";
|
|
6
|
+
export const defaultNexusWorkerTargetStatePath = ".dev-nexus/automation/target-state.md";
|
|
7
|
+
export const defaultNexusWorkerProjectManagedSkillsPath = ".dev-nexus/skills";
|
|
8
|
+
export class NexusWorkerContextBundleError extends Error {
|
|
9
|
+
constructor(message) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = "NexusWorkerContextBundleError";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function nexusWorkerContextDirectoryPath(worktreePath) {
|
|
15
|
+
return path.join(normalizedAbsolutePath(worktreePath, "worktreePath"), ".dev-nexus", nexusWorkerContextDirectoryName);
|
|
16
|
+
}
|
|
17
|
+
export function nexusWorkerContextJsonPath(worktreePath) {
|
|
18
|
+
return path.join(nexusWorkerContextDirectoryPath(worktreePath), nexusWorkerContextJsonFileName);
|
|
19
|
+
}
|
|
20
|
+
export function nexusWorkerBriefingPath(worktreePath) {
|
|
21
|
+
return path.join(nexusWorkerContextDirectoryPath(worktreePath), nexusWorkerBriefingFileName);
|
|
22
|
+
}
|
|
23
|
+
export function buildNexusWorkerContextBundle(options) {
|
|
24
|
+
const projectRoot = normalizedAbsolutePath(options.projectRoot, "projectRoot");
|
|
25
|
+
const projectId = optionalNullableString(options.projectId, "projectId") ?? null;
|
|
26
|
+
const projectName = optionalNullableString(options.projectName, "projectName") ?? null;
|
|
27
|
+
const componentId = requiredNonEmptyString(options.componentId, "componentId");
|
|
28
|
+
const sourceRoot = normalizedAbsolutePath(options.sourceRoot, "sourceRoot");
|
|
29
|
+
const worktreesRoot = normalizedAbsolutePath(options.worktreesRoot, "worktreesRoot");
|
|
30
|
+
const worktreePath = normalizedAbsolutePath(options.worktreePath, "worktreePath");
|
|
31
|
+
assertPathInsideRoot(worktreesRoot, worktreePath, "worktreePath", "worktreesRoot");
|
|
32
|
+
const branchName = requiredNonEmptyString(options.branchName, "branchName");
|
|
33
|
+
const baseRef = optionalNullableString(options.baseRef, "baseRef") ?? null;
|
|
34
|
+
const workItem = normalizeWorkerContextWorkItem(options.workItem);
|
|
35
|
+
const projectContext = projectContextReferences({
|
|
36
|
+
projectRoot,
|
|
37
|
+
targetStatePath: options.targetStatePath,
|
|
38
|
+
});
|
|
39
|
+
const skills = normalizeWorkerContextSkills(projectRoot, options.skills);
|
|
40
|
+
const dependencySupport = normalizeWorkerDependencySupport(options.dependencyProjections);
|
|
41
|
+
const pluginFragments = normalizeWorkerPluginFragments(options.pluginFragments);
|
|
42
|
+
const worktree = {
|
|
43
|
+
componentId,
|
|
44
|
+
sourceRoot,
|
|
45
|
+
worktreesRoot,
|
|
46
|
+
worktreePath,
|
|
47
|
+
branchName,
|
|
48
|
+
baseRef,
|
|
49
|
+
workItem,
|
|
50
|
+
};
|
|
51
|
+
return {
|
|
52
|
+
version: 1,
|
|
53
|
+
project: {
|
|
54
|
+
id: projectId,
|
|
55
|
+
name: projectName,
|
|
56
|
+
root: projectRoot,
|
|
57
|
+
},
|
|
58
|
+
projectRoot,
|
|
59
|
+
component: {
|
|
60
|
+
id: componentId,
|
|
61
|
+
sourceRoot,
|
|
62
|
+
},
|
|
63
|
+
skills,
|
|
64
|
+
dependencySupport,
|
|
65
|
+
ownership: worktree,
|
|
66
|
+
worktree,
|
|
67
|
+
projectContext,
|
|
68
|
+
pluginFragments,
|
|
69
|
+
boundaries: {
|
|
70
|
+
commandWorkingDirectory: worktreePath,
|
|
71
|
+
gitWorkingDirectory: worktreePath,
|
|
72
|
+
write: {
|
|
73
|
+
roots: [worktreePath],
|
|
74
|
+
notes: [
|
|
75
|
+
"Write source changes only inside the component worktree unless the coordinator explicitly assigns another boundary.",
|
|
76
|
+
"Run source and git commands from the component worktree.",
|
|
77
|
+
],
|
|
78
|
+
},
|
|
79
|
+
read: {
|
|
80
|
+
roots: uniquePaths([sourceRoot, projectRoot]),
|
|
81
|
+
files: projectContext.files,
|
|
82
|
+
notes: [
|
|
83
|
+
"Use the original component source root and project files as read-only context.",
|
|
84
|
+
"Treat root project files as read-only unless the coordinator owns project state for this work.",
|
|
85
|
+
],
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
export function renderNexusWorkerBriefing(context) {
|
|
91
|
+
const workItemLine = context.worktree.workItem
|
|
92
|
+
? `${context.worktree.workItem.id}: ${context.worktree.workItem.title}`
|
|
93
|
+
: "none";
|
|
94
|
+
const baseRefLine = context.worktree.baseRef ?? "none";
|
|
95
|
+
return [
|
|
96
|
+
"# DevNexus Worker Context",
|
|
97
|
+
"",
|
|
98
|
+
`Component: ${context.component.id}`,
|
|
99
|
+
`Work item: ${workItemLine}`,
|
|
100
|
+
`Branch: ${context.worktree.branchName}`,
|
|
101
|
+
`Base ref: ${baseRefLine}`,
|
|
102
|
+
"",
|
|
103
|
+
`Run source and git commands in: ${context.worktree.worktreePath}`,
|
|
104
|
+
"Source and Git commands run from the component checkout root shown above.",
|
|
105
|
+
"Write source changes only inside this component worktree unless the coordinator assigns another boundary.",
|
|
106
|
+
"Treat project context files as read-only unless the coordinator explicitly assigns project-state ownership.",
|
|
107
|
+
"",
|
|
108
|
+
"Project context:",
|
|
109
|
+
`- AGENTS.md: ${context.projectContext.agentsPath}`,
|
|
110
|
+
`- CONTEXT.md: ${context.projectContext.contextPath}`,
|
|
111
|
+
`- PLAN.md: ${context.projectContext.planPath}`,
|
|
112
|
+
`- target-state: ${context.projectContext.targetStatePath}`,
|
|
113
|
+
"",
|
|
114
|
+
"Skills:",
|
|
115
|
+
`Project-managed skills: ${context.skills.projectManagedRoot}`,
|
|
116
|
+
...renderSkillProjectionLines(context.skills),
|
|
117
|
+
"",
|
|
118
|
+
"Dependency support:",
|
|
119
|
+
...renderDependencyProjectionLines(context.dependencySupport),
|
|
120
|
+
"",
|
|
121
|
+
...renderPluginBriefingFragments(context.pluginFragments.briefing),
|
|
122
|
+
].join("\n");
|
|
123
|
+
}
|
|
124
|
+
export function materializeNexusWorkerContextBundle(options) {
|
|
125
|
+
const context = buildNexusWorkerContextBundle(options);
|
|
126
|
+
const contextDirectoryPath = nexusWorkerContextDirectoryPath(context.worktree.worktreePath);
|
|
127
|
+
const contextJsonPath = nexusWorkerContextJsonPath(context.worktree.worktreePath);
|
|
128
|
+
const briefingPath = nexusWorkerBriefingPath(context.worktree.worktreePath);
|
|
129
|
+
const briefingMarkdown = renderNexusWorkerBriefing(context);
|
|
130
|
+
fs.mkdirSync(contextDirectoryPath, { recursive: true });
|
|
131
|
+
fs.writeFileSync(contextJsonPath, `${JSON.stringify(context, null, 2)}\n`, "utf8");
|
|
132
|
+
fs.writeFileSync(briefingPath, briefingMarkdown, "utf8");
|
|
133
|
+
return {
|
|
134
|
+
contextDirectoryPath,
|
|
135
|
+
contextJsonPath,
|
|
136
|
+
briefingPath,
|
|
137
|
+
context,
|
|
138
|
+
briefingMarkdown,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
function projectContextReferences(options) {
|
|
142
|
+
const targetStatePath = resolveInsideProjectRoot(options.projectRoot, options.targetStatePath ?? defaultNexusWorkerTargetStatePath, "targetStatePath");
|
|
143
|
+
const agentsPath = path.join(options.projectRoot, "AGENTS.md");
|
|
144
|
+
const contextPath = path.join(options.projectRoot, "CONTEXT.md");
|
|
145
|
+
const planPath = path.join(options.projectRoot, "PLAN.md");
|
|
146
|
+
const files = [
|
|
147
|
+
{ id: "agents", path: agentsPath, access: "read_only" },
|
|
148
|
+
{ id: "context", path: contextPath, access: "read_only" },
|
|
149
|
+
{ id: "plan", path: planPath, access: "read_only" },
|
|
150
|
+
{ id: "target-state", path: targetStatePath, access: "read_only" },
|
|
151
|
+
];
|
|
152
|
+
return {
|
|
153
|
+
agentsPath,
|
|
154
|
+
contextPath,
|
|
155
|
+
planPath,
|
|
156
|
+
targetStatePath,
|
|
157
|
+
files,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function normalizeWorkerContextSkills(projectRoot, skills) {
|
|
161
|
+
const projectManagedRoot = skills?.projectManagedRoot
|
|
162
|
+
? normalizedAbsolutePath(skills.projectManagedRoot, "skills.projectManagedRoot")
|
|
163
|
+
: path.join(projectRoot, defaultNexusWorkerProjectManagedSkillsPath);
|
|
164
|
+
return {
|
|
165
|
+
projectManagedRoot,
|
|
166
|
+
agentNativeProjections: (skills?.agentNativeProjections ?? []).map((projection, projectionIndex) => ({
|
|
167
|
+
agent: requiredNonEmptyString(projection.agent, `skills.agentNativeProjections[${projectionIndex}].agent`),
|
|
168
|
+
skillsDirectory: normalizedAbsolutePath(projection.skillsDirectory, `skills.agentNativeProjections[${projectionIndex}].skillsDirectory`),
|
|
169
|
+
sourceControl: normalizeSkillSourceControl(projection.sourceControl, `skills.agentNativeProjections[${projectionIndex}].sourceControl`),
|
|
170
|
+
skills: projection.skills.map((skill, skillIndex) => ({
|
|
171
|
+
id: requiredNonEmptyString(skill.id, `skills.agentNativeProjections[${projectionIndex}].skills[${skillIndex}].id`),
|
|
172
|
+
sourceSkillRoot: normalizedAbsolutePath(skill.sourceSkillRoot, `skills.agentNativeProjections[${projectionIndex}].skills[${skillIndex}].sourceSkillRoot`),
|
|
173
|
+
projectedSkillRoot: normalizedAbsolutePath(skill.projectedSkillRoot, `skills.agentNativeProjections[${projectionIndex}].skills[${skillIndex}].projectedSkillRoot`),
|
|
174
|
+
skillPath: skill.skillPath === null
|
|
175
|
+
? null
|
|
176
|
+
: normalizedAbsolutePath(skill.skillPath, `skills.agentNativeProjections[${projectionIndex}].skills[${skillIndex}].skillPath`),
|
|
177
|
+
})),
|
|
178
|
+
})),
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
function normalizeSkillSourceControl(value, name) {
|
|
182
|
+
if (value === "support" || value === "source") {
|
|
183
|
+
return value;
|
|
184
|
+
}
|
|
185
|
+
throw new NexusWorkerContextBundleError(`${name} must be support or source`);
|
|
186
|
+
}
|
|
187
|
+
function renderSkillProjectionLines(skills) {
|
|
188
|
+
if (skills.agentNativeProjections.length === 0) {
|
|
189
|
+
return ["- agent-native projections: none"];
|
|
190
|
+
}
|
|
191
|
+
return skills.agentNativeProjections.map((projection) => `- ${projection.agent} skills: ${projection.skillsDirectory}`);
|
|
192
|
+
}
|
|
193
|
+
function normalizeWorkerDependencySupport(dependencyProjections) {
|
|
194
|
+
return {
|
|
195
|
+
pluginDependencyProjections: (dependencyProjections ?? []).map(normalizeWorkerDependencyProjection),
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
function normalizeWorkerDependencyProjection(projection) {
|
|
199
|
+
return {
|
|
200
|
+
id: requiredNonEmptyString(projection.id, "dependencyProjections.id"),
|
|
201
|
+
source: requiredNonEmptyString(projection.source, "dependencyProjections.source"),
|
|
202
|
+
target: requiredNonEmptyString(projection.target, "dependencyProjections.target"),
|
|
203
|
+
sourcePath: normalizedAbsolutePath(projection.sourcePath, "dependencyProjections.sourcePath"),
|
|
204
|
+
targetPath: normalizedAbsolutePath(projection.targetPath, "dependencyProjections.targetPath"),
|
|
205
|
+
required: projection.required === true,
|
|
206
|
+
sourceControl: normalizeDependencyProjectionSourceControl(projection.sourceControl, "dependencyProjections.sourceControl"),
|
|
207
|
+
reason: optionalNullableString(projection.reason, "dependencyProjections.reason") ??
|
|
208
|
+
null,
|
|
209
|
+
status: normalizeDependencyProjectionStatus(projection.status, "dependencyProjections.status"),
|
|
210
|
+
message: requiredNonEmptyString(projection.message, "dependencyProjections.message"),
|
|
211
|
+
sourceMetadata: normalizeDependencyProjectionSourceMetadata(projection.sourceMetadata),
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
function normalizeDependencyProjectionSourceControl(value, name) {
|
|
215
|
+
if (value === "support" || value === "source") {
|
|
216
|
+
return value;
|
|
217
|
+
}
|
|
218
|
+
throw new NexusWorkerContextBundleError(`${name} must be support or source`);
|
|
219
|
+
}
|
|
220
|
+
function normalizeDependencyProjectionStatus(value, name) {
|
|
221
|
+
if (value === "linked" || value === "present" || value === "skipped") {
|
|
222
|
+
return value;
|
|
223
|
+
}
|
|
224
|
+
throw new NexusWorkerContextBundleError(`${name} must be linked, present, or skipped`);
|
|
225
|
+
}
|
|
226
|
+
function normalizeDependencyProjectionSourceMetadata(sourceMetadata) {
|
|
227
|
+
return {
|
|
228
|
+
pluginId: requiredNonEmptyString(sourceMetadata.pluginId, "dependencyProjections.sourceMetadata.pluginId"),
|
|
229
|
+
pluginName: optionalNullableString(sourceMetadata.pluginName, "dependencyProjections.sourceMetadata.pluginName") ?? null,
|
|
230
|
+
version: optionalNullableString(sourceMetadata.version, "dependencyProjections.sourceMetadata.version") ?? null,
|
|
231
|
+
capabilityId: requiredNonEmptyString(sourceMetadata.capabilityId, "dependencyProjections.sourceMetadata.capabilityId"),
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
function renderDependencyProjectionLines(dependencySupport) {
|
|
235
|
+
if (dependencySupport.pluginDependencyProjections.length === 0) {
|
|
236
|
+
return ["- plugin dependency projections: none"];
|
|
237
|
+
}
|
|
238
|
+
return dependencySupport.pluginDependencyProjections.flatMap((projection) => [
|
|
239
|
+
`- ${projection.status} ${projection.id}: ${projection.target}`,
|
|
240
|
+
` Source: ${projection.sourceMetadata.pluginId}:${projection.sourceMetadata.capabilityId}`,
|
|
241
|
+
...(projection.reason ? [` Reason: ${projection.reason}`] : []),
|
|
242
|
+
]);
|
|
243
|
+
}
|
|
244
|
+
function normalizeWorkerContextWorkItem(workItem) {
|
|
245
|
+
if (workItem === null) {
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
return {
|
|
249
|
+
id: requiredNonEmptyString(workItem.id, "workItem.id"),
|
|
250
|
+
title: optionalNullableString(workItem.title, "workItem.title") ?? null,
|
|
251
|
+
...(workItem.status ? { status: workItem.status } : {}),
|
|
252
|
+
...(workItem.provider
|
|
253
|
+
? { provider: requiredNonEmptyString(workItem.provider, "workItem.provider") }
|
|
254
|
+
: {}),
|
|
255
|
+
...(workItem.labels ? { labels: [...workItem.labels] } : {}),
|
|
256
|
+
...(workItem.assignees ? { assignees: [...workItem.assignees] } : {}),
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
function normalizeWorkerPluginFragments(pluginFragments) {
|
|
260
|
+
return {
|
|
261
|
+
context: normalizeWorkerPluginFragmentList(pluginFragments?.context ?? [], "worker_context_fragment"),
|
|
262
|
+
briefing: normalizeWorkerPluginFragmentList(pluginFragments?.briefing ?? [], "worker_briefing_fragment"),
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
function normalizeWorkerPluginFragmentList(fragments, expectedKind) {
|
|
266
|
+
return fragments
|
|
267
|
+
.map((fragment) => normalizeWorkerPluginFragment(fragment, expectedKind))
|
|
268
|
+
.sort(compareWorkerPluginFragments);
|
|
269
|
+
}
|
|
270
|
+
function normalizeWorkerPluginFragment(fragment, expectedKind) {
|
|
271
|
+
const kind = requiredNonEmptyString(fragment.kind, "pluginFragments.kind");
|
|
272
|
+
if (kind !== expectedKind) {
|
|
273
|
+
throw new NexusWorkerContextBundleError(`pluginFragments.${expectedKind} entries must have kind ${expectedKind}`);
|
|
274
|
+
}
|
|
275
|
+
return {
|
|
276
|
+
kind,
|
|
277
|
+
id: requiredNonEmptyString(fragment.id, "pluginFragments.id"),
|
|
278
|
+
title: requiredNonEmptyString(fragment.title, "pluginFragments.title"),
|
|
279
|
+
body: requiredNonEmptyString(fragment.body, "pluginFragments.body"),
|
|
280
|
+
provenance: requiredNonEmptyString(fragment.provenance, "pluginFragments.provenance"),
|
|
281
|
+
advisory: true,
|
|
282
|
+
targetAgents: normalizeStringArray(fragment.targetAgents, "pluginFragments.targetAgents"),
|
|
283
|
+
targetComponents: normalizeStringArray(fragment.targetComponents, "pluginFragments.targetComponents"),
|
|
284
|
+
source: normalizeWorkerPluginFragmentSource(fragment.source),
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
function normalizeWorkerPluginFragmentSource(source) {
|
|
288
|
+
return {
|
|
289
|
+
pluginId: requiredNonEmptyString(source.pluginId, "pluginFragments.source.pluginId"),
|
|
290
|
+
pluginName: optionalNullableString(source.pluginName, "pluginFragments.source.pluginName") ?? null,
|
|
291
|
+
version: optionalNullableString(source.version, "pluginFragments.source.version") ?? null,
|
|
292
|
+
capabilityId: requiredNonEmptyString(source.capabilityId, "pluginFragments.source.capabilityId"),
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
function normalizeStringArray(value, name) {
|
|
296
|
+
if (!Array.isArray(value)) {
|
|
297
|
+
throw new NexusWorkerContextBundleError(`${name} must be an array`);
|
|
298
|
+
}
|
|
299
|
+
return value.map((entry, index) => requiredNonEmptyString(entry, `${name}[${index}]`));
|
|
300
|
+
}
|
|
301
|
+
function compareWorkerPluginFragments(left, right) {
|
|
302
|
+
return (compareStrings(left.source.pluginId, right.source.pluginId) ||
|
|
303
|
+
compareStrings(left.id, right.id) ||
|
|
304
|
+
compareStrings(left.kind, right.kind) ||
|
|
305
|
+
compareStrings(left.provenance, right.provenance));
|
|
306
|
+
}
|
|
307
|
+
function renderPluginBriefingFragments(fragments) {
|
|
308
|
+
if (fragments.length === 0) {
|
|
309
|
+
return [];
|
|
310
|
+
}
|
|
311
|
+
return [
|
|
312
|
+
"## Plugin Briefing Fragments",
|
|
313
|
+
"",
|
|
314
|
+
"These fragments are advisory setup/context only; they do not select work, launch subagents, or supervise implementation.",
|
|
315
|
+
"",
|
|
316
|
+
...fragments.flatMap(renderPluginBriefingFragment),
|
|
317
|
+
];
|
|
318
|
+
}
|
|
319
|
+
function renderPluginBriefingFragment(fragment) {
|
|
320
|
+
return [
|
|
321
|
+
`### ${fragment.title}`,
|
|
322
|
+
"",
|
|
323
|
+
`Source: ${fragment.source.pluginId}:${fragment.source.capabilityId}`,
|
|
324
|
+
`Provenance: ${fragment.provenance}`,
|
|
325
|
+
...renderTargetLine("Intended agents", fragment.targetAgents),
|
|
326
|
+
...renderTargetLine("Intended components", fragment.targetComponents),
|
|
327
|
+
"",
|
|
328
|
+
fragment.body.trim(),
|
|
329
|
+
"",
|
|
330
|
+
];
|
|
331
|
+
}
|
|
332
|
+
function renderTargetLine(label, targets) {
|
|
333
|
+
return targets.length > 0 ? [`${label}: ${targets.join(", ")}`] : [];
|
|
334
|
+
}
|
|
335
|
+
function resolveInsideProjectRoot(projectRoot, value, name) {
|
|
336
|
+
const resolved = path.resolve(projectRoot, requiredNonEmptyString(value, name));
|
|
337
|
+
assertPathInsideRoot(projectRoot, resolved, name, "projectRoot");
|
|
338
|
+
return resolved;
|
|
339
|
+
}
|
|
340
|
+
function assertPathInsideRoot(root, value, valueName, rootName) {
|
|
341
|
+
const relative = path.relative(root, value);
|
|
342
|
+
if (!relative || relative.startsWith("..") || path.isAbsolute(relative)) {
|
|
343
|
+
throw new NexusWorkerContextBundleError(`${valueName} must resolve inside ${rootName}: ${value}`);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
function uniquePaths(paths) {
|
|
347
|
+
return [...new Set(paths)];
|
|
348
|
+
}
|
|
349
|
+
function compareStrings(left, right) {
|
|
350
|
+
if (left < right) {
|
|
351
|
+
return -1;
|
|
352
|
+
}
|
|
353
|
+
if (left > right) {
|
|
354
|
+
return 1;
|
|
355
|
+
}
|
|
356
|
+
return 0;
|
|
357
|
+
}
|
|
358
|
+
function normalizedAbsolutePath(value, name) {
|
|
359
|
+
return path.resolve(requiredNonEmptyString(value, name));
|
|
360
|
+
}
|
|
361
|
+
function optionalNullableString(value, name) {
|
|
362
|
+
if (value === undefined) {
|
|
363
|
+
return undefined;
|
|
364
|
+
}
|
|
365
|
+
if (value === null) {
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
return requiredNonEmptyString(value, name);
|
|
369
|
+
}
|
|
370
|
+
function requiredNonEmptyString(value, name) {
|
|
371
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
372
|
+
throw new NexusWorkerContextBundleError(`${name} must be a non-empty string`);
|
|
373
|
+
}
|
|
374
|
+
return value.trim();
|
|
375
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { type ChildProcess } from "node:child_process";
|
|
2
|
+
export interface ProcessLogPaths {
|
|
3
|
+
stdout: string;
|
|
4
|
+
stderr: string;
|
|
5
|
+
lifecycle: string;
|
|
6
|
+
}
|
|
7
|
+
export interface StartManagedProcessOptions {
|
|
8
|
+
name: string;
|
|
9
|
+
command: string;
|
|
10
|
+
args?: readonly string[];
|
|
11
|
+
cwd?: string;
|
|
12
|
+
env?: NodeJS.ProcessEnv;
|
|
13
|
+
logDirectory: string;
|
|
14
|
+
appendLogs?: boolean;
|
|
15
|
+
detached?: boolean;
|
|
16
|
+
release?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface ManagedProcessHandle {
|
|
19
|
+
name: string;
|
|
20
|
+
command: string;
|
|
21
|
+
args: string[];
|
|
22
|
+
pid: number;
|
|
23
|
+
startedAt: string;
|
|
24
|
+
logPaths: ProcessLogPaths;
|
|
25
|
+
child: ChildProcess;
|
|
26
|
+
}
|
|
27
|
+
export interface StopProcessByPidOptions {
|
|
28
|
+
signal?: NodeJS.Signals;
|
|
29
|
+
timeoutMs?: number;
|
|
30
|
+
pollIntervalMs?: number;
|
|
31
|
+
force?: boolean;
|
|
32
|
+
killTree?: boolean;
|
|
33
|
+
}
|
|
34
|
+
export interface StopProcessByPidResult {
|
|
35
|
+
pid: number;
|
|
36
|
+
stopped: boolean;
|
|
37
|
+
alreadyExited: boolean;
|
|
38
|
+
method: "process.kill" | "taskkill";
|
|
39
|
+
}
|
|
40
|
+
export interface HttpPortHealthCheckOptions {
|
|
41
|
+
port: number;
|
|
42
|
+
host?: string;
|
|
43
|
+
path?: string;
|
|
44
|
+
timeoutMs?: number;
|
|
45
|
+
healthyStatusMin?: number;
|
|
46
|
+
healthyStatusMax?: number;
|
|
47
|
+
}
|
|
48
|
+
export interface HttpPortHealthCheckResult {
|
|
49
|
+
ok: boolean;
|
|
50
|
+
url: string;
|
|
51
|
+
statusCode?: number;
|
|
52
|
+
durationMs: number;
|
|
53
|
+
error?: string;
|
|
54
|
+
}
|
|
55
|
+
export interface WaitForHttpPortOptions extends HttpPortHealthCheckOptions {
|
|
56
|
+
intervalMs?: number;
|
|
57
|
+
totalTimeoutMs?: number;
|
|
58
|
+
}
|
|
59
|
+
export declare class ProcessSupervisorError extends Error {
|
|
60
|
+
constructor(message: string);
|
|
61
|
+
}
|
|
62
|
+
export type ProcessSupervisorPlatform = NodeJS.Platform;
|
|
63
|
+
export interface ProcessListeningOnPortLookupOptions {
|
|
64
|
+
platform?: ProcessSupervisorPlatform;
|
|
65
|
+
allowedProcessNames?: string[];
|
|
66
|
+
timeoutMs?: number;
|
|
67
|
+
}
|
|
68
|
+
export interface ProcessListeningOnPortResult {
|
|
69
|
+
pid: number;
|
|
70
|
+
platform: ProcessSupervisorPlatform;
|
|
71
|
+
method: "powershell";
|
|
72
|
+
}
|
|
73
|
+
export declare function isWindowsPlatform(platform?: ProcessSupervisorPlatform): boolean;
|
|
74
|
+
export declare function defaultDetachedForReleasedProcess(platform?: ProcessSupervisorPlatform): boolean;
|
|
75
|
+
export declare function defaultDetachedForPersistentService(platform?: ProcessSupervisorPlatform): boolean;
|
|
76
|
+
export declare function defaultKillTreeForPlatform(platform?: ProcessSupervisorPlatform): boolean;
|
|
77
|
+
export declare function stopMethodForPlatform(killTree: boolean, platform?: ProcessSupervisorPlatform): StopProcessByPidResult["method"];
|
|
78
|
+
export declare function envValue(env: NodeJS.ProcessEnv, key: string): string | undefined;
|
|
79
|
+
export declare function commandHasPathSegment(command: string): boolean;
|
|
80
|
+
export declare function executableExtensions(env: NodeJS.ProcessEnv, platform?: ProcessSupervisorPlatform): string[];
|
|
81
|
+
export declare function candidateExecutablePaths(command: string, env: NodeJS.ProcessEnv, platform?: ProcessSupervisorPlatform): string[];
|
|
82
|
+
export declare function findExecutablePath(command: string, env: NodeJS.ProcessEnv, platform?: ProcessSupervisorPlatform): string | undefined;
|
|
83
|
+
export declare function startManagedProcess(options: StartManagedProcessOptions): ManagedProcessHandle;
|
|
84
|
+
export declare function isProcessRunning(pid: number): boolean;
|
|
85
|
+
export declare function stopProcessByPid(pid: number, options?: StopProcessByPidOptions): Promise<StopProcessByPidResult>;
|
|
86
|
+
export declare function isTcpPortListening(port: number, timeoutMs?: number): Promise<boolean>;
|
|
87
|
+
export declare function findProcessListeningOnPort(port: number, options?: ProcessListeningOnPortLookupOptions): ProcessListeningOnPortResult | undefined;
|
|
88
|
+
export declare function checkHttpPort(options: HttpPortHealthCheckOptions): Promise<HttpPortHealthCheckResult>;
|
|
89
|
+
export declare function waitForHttpPort(options: WaitForHttpPortOptions): Promise<HttpPortHealthCheckResult>;
|