@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.
Files changed (100) hide show
  1. package/README.md +677 -0
  2. package/dist/browserOpener.d.ts +9 -0
  3. package/dist/browserOpener.js +47 -0
  4. package/dist/cli.d.ts +18 -0
  5. package/dist/cli.js +2374 -0
  6. package/dist/gitWorktreeService.d.ts +57 -0
  7. package/dist/gitWorktreeService.js +157 -0
  8. package/dist/index.d.ts +47 -0
  9. package/dist/index.js +47 -0
  10. package/dist/nexusAgentMcpConfig.d.ts +30 -0
  11. package/dist/nexusAgentMcpConfig.js +228 -0
  12. package/dist/nexusAutomation.d.ts +103 -0
  13. package/dist/nexusAutomation.js +390 -0
  14. package/dist/nexusAutomationAgentLaunch.d.ts +148 -0
  15. package/dist/nexusAutomationAgentLaunch.js +855 -0
  16. package/dist/nexusAutomationAgentProfile.d.ts +39 -0
  17. package/dist/nexusAutomationAgentProfile.js +103 -0
  18. package/dist/nexusAutomationAgentSurface.d.ts +62 -0
  19. package/dist/nexusAutomationAgentSurface.js +90 -0
  20. package/dist/nexusAutomationCommandExecutor.d.ts +29 -0
  21. package/dist/nexusAutomationCommandExecutor.js +251 -0
  22. package/dist/nexusAutomationConfig.d.ts +114 -0
  23. package/dist/nexusAutomationConfig.js +547 -0
  24. package/dist/nexusAutomationEnqueue.d.ts +37 -0
  25. package/dist/nexusAutomationEnqueue.js +128 -0
  26. package/dist/nexusAutomationRunOnce.d.ts +91 -0
  27. package/dist/nexusAutomationRunOnce.js +586 -0
  28. package/dist/nexusAutomationScheduler.d.ts +50 -0
  29. package/dist/nexusAutomationScheduler.js +196 -0
  30. package/dist/nexusAutomationStatus.d.ts +55 -0
  31. package/dist/nexusAutomationStatus.js +462 -0
  32. package/dist/nexusAutomationTarget.d.ts +19 -0
  33. package/dist/nexusAutomationTarget.js +33 -0
  34. package/dist/nexusAutomationTargetCycle.d.ts +90 -0
  35. package/dist/nexusAutomationTargetCycle.js +282 -0
  36. package/dist/nexusAutomationTargetReport.d.ts +136 -0
  37. package/dist/nexusAutomationTargetReport.js +504 -0
  38. package/dist/nexusAutomationWorktreeSetup.d.ts +89 -0
  39. package/dist/nexusAutomationWorktreeSetup.js +661 -0
  40. package/dist/nexusCoordination.d.ts +198 -0
  41. package/dist/nexusCoordination.js +1018 -0
  42. package/dist/nexusExtension.d.ts +31 -0
  43. package/dist/nexusExtension.js +1 -0
  44. package/dist/nexusHomeConfig.d.ts +38 -0
  45. package/dist/nexusHomeConfig.js +133 -0
  46. package/dist/nexusMcpServer.d.ts +31 -0
  47. package/dist/nexusMcpServer.js +1036 -0
  48. package/dist/nexusPluginCapabilities.d.ts +197 -0
  49. package/dist/nexusPluginCapabilities.js +201 -0
  50. package/dist/nexusProjectConfig.d.ts +95 -0
  51. package/dist/nexusProjectConfig.js +880 -0
  52. package/dist/nexusProjectHomeService.d.ts +121 -0
  53. package/dist/nexusProjectHomeService.js +171 -0
  54. package/dist/nexusProjectLifecycle.d.ts +62 -0
  55. package/dist/nexusProjectLifecycle.js +205 -0
  56. package/dist/nexusProjectOperations.d.ts +101 -0
  57. package/dist/nexusProjectOperations.js +296 -0
  58. package/dist/nexusProjectRegistry.d.ts +42 -0
  59. package/dist/nexusProjectRegistry.js +91 -0
  60. package/dist/nexusProjectScaffold.d.ts +25 -0
  61. package/dist/nexusProjectScaffold.js +61 -0
  62. package/dist/nexusProjectTemplate.d.ts +34 -0
  63. package/dist/nexusProjectTemplate.js +354 -0
  64. package/dist/nexusSkills.d.ts +134 -0
  65. package/dist/nexusSkills.js +647 -0
  66. package/dist/nexusWorkerContextBundle.d.ts +142 -0
  67. package/dist/nexusWorkerContextBundle.js +375 -0
  68. package/dist/processSupervisor.d.ts +89 -0
  69. package/dist/processSupervisor.js +440 -0
  70. package/dist/vibeKanbanApi.d.ts +11 -0
  71. package/dist/vibeKanbanApi.js +14 -0
  72. package/dist/vibeKanbanAuth.d.ts +25 -0
  73. package/dist/vibeKanbanAuth.js +101 -0
  74. package/dist/vibeKanbanBoardAdapter.d.ts +36 -0
  75. package/dist/vibeKanbanBoardAdapter.js +196 -0
  76. package/dist/vibeKanbanMcpConfig.d.ts +36 -0
  77. package/dist/vibeKanbanMcpConfig.js +191 -0
  78. package/dist/vibeKanbanProjectAdapter.d.ts +39 -0
  79. package/dist/vibeKanbanProjectAdapter.js +113 -0
  80. package/dist/vibeKanbanWorkspaceSetup.d.ts +1 -0
  81. package/dist/vibeKanbanWorkspaceSetup.js +96 -0
  82. package/dist/workItemService.d.ts +60 -0
  83. package/dist/workItemService.js +163 -0
  84. package/dist/workTrackingGitHubProvider.d.ts +71 -0
  85. package/dist/workTrackingGitHubProvider.js +663 -0
  86. package/dist/workTrackingGitLabProvider.d.ts +62 -0
  87. package/dist/workTrackingGitLabProvider.js +523 -0
  88. package/dist/workTrackingJiraProvider.d.ts +67 -0
  89. package/dist/workTrackingJiraProvider.js +652 -0
  90. package/dist/workTrackingLocalProvider.d.ts +49 -0
  91. package/dist/workTrackingLocalProvider.js +463 -0
  92. package/dist/workTrackingProviderService.d.ts +21 -0
  93. package/dist/workTrackingProviderService.js +117 -0
  94. package/dist/workTrackingTypes.d.ts +202 -0
  95. package/dist/workTrackingTypes.js +1 -0
  96. package/dist/workTrackingVibeProvider.d.ts +35 -0
  97. package/dist/workTrackingVibeProvider.js +119 -0
  98. package/dist/worktreeExecutionMetadata.d.ts +76 -0
  99. package/dist/worktreeExecutionMetadata.js +239 -0
  100. package/package.json +37 -0
@@ -0,0 +1,121 @@
1
+ import type { NexusProjectConfig } from "./nexusProjectConfig.js";
2
+ import { type ProjectGitCommandResult, type ProjectGitRunner } from "./nexusProjectLifecycle.js";
3
+ import { type ConfigureNexusProjectTrackerProvider, type NexusProjectRegistryWithRoot } from "./nexusProjectOperations.js";
4
+ import { type NexusProjectReference, type NexusProjectStatusBase } from "./nexusProjectRegistry.js";
5
+ import type { ScaffoldNexusProjectResult } from "./nexusProjectScaffold.js";
6
+ import type { WorkTrackingConfig } from "./workTrackingTypes.js";
7
+ export interface NexusProjectHomeStore<Registry extends NexusProjectRegistryWithRoot = NexusProjectRegistryWithRoot> {
8
+ resolveHomePath(homePath: string): string;
9
+ loadHomeConfig(homePath: string): Registry;
10
+ saveHomeConfig(homePath: string, registry: Registry): string;
11
+ }
12
+ export interface CreateNexusProjectOptions {
13
+ homePath: string;
14
+ homeStore: NexusProjectHomeStore;
15
+ name: string;
16
+ root?: string;
17
+ from?: string;
18
+ gitInit?: boolean;
19
+ vibeKanbanProjectId?: string;
20
+ gitRunner?: ProjectGitRunner;
21
+ }
22
+ export interface ImportNexusProjectOptions {
23
+ homePath: string;
24
+ homeStore: NexusProjectHomeStore;
25
+ root: string;
26
+ projectRoot?: string;
27
+ name?: string;
28
+ vibeKanbanProjectId?: string;
29
+ gitRunner?: ProjectGitRunner;
30
+ }
31
+ export interface CreateNexusProjectResult {
32
+ homePath: string;
33
+ projectRoot: string;
34
+ projectConfigPath: string;
35
+ worktreesRoot: string;
36
+ projectConfig: NexusProjectConfig;
37
+ reference: NexusProjectReference;
38
+ scaffold: ScaffoldNexusProjectResult;
39
+ git: {
40
+ operation: "clone" | "init";
41
+ remoteUrl: string | null;
42
+ defaultBranch: string | null;
43
+ commands: ProjectGitCommandResult[];
44
+ };
45
+ }
46
+ export interface ImportNexusProjectResult {
47
+ homePath: string;
48
+ projectRoot: string;
49
+ projectConfigPath: string;
50
+ worktreesRoot: string;
51
+ projectConfig: NexusProjectConfig;
52
+ reference: NexusProjectReference;
53
+ scaffold: ScaffoldNexusProjectResult;
54
+ git: {
55
+ operation: "import";
56
+ remoteUrl: string | null;
57
+ defaultBranch: string | null;
58
+ commands: ProjectGitCommandResult[];
59
+ };
60
+ }
61
+ export interface ListNexusProjectsOptions {
62
+ homePath: string;
63
+ homeStore: NexusProjectHomeStore;
64
+ }
65
+ export interface ListNexusProjectsResult {
66
+ homePath: string;
67
+ projects: NexusProjectStatusBase[];
68
+ }
69
+ export interface GetNexusProjectStatusOptions {
70
+ homePath: string;
71
+ homeStore: NexusProjectHomeStore;
72
+ project: string;
73
+ }
74
+ export interface GetNexusProjectStatusResult {
75
+ homePath: string;
76
+ project: NexusProjectStatusBase;
77
+ }
78
+ export interface LinkNexusProjectTrackerOptions {
79
+ homePath: string;
80
+ homeStore: NexusProjectHomeStore;
81
+ project: string;
82
+ trackerProjectId: string;
83
+ }
84
+ export interface LinkNexusProjectTrackerResult {
85
+ homePath: string;
86
+ vibeKanbanProjectId: string;
87
+ vibeKanbanRepoId: string | null;
88
+ project: NexusProjectStatusBase;
89
+ projectRoot: string;
90
+ projectConfigPath: string;
91
+ projectConfig: NexusProjectConfig;
92
+ }
93
+ export interface ConfigureNexusProjectTrackerOptions {
94
+ homePath: string;
95
+ homeStore: NexusProjectHomeStore;
96
+ project: string;
97
+ provider: ConfigureNexusProjectTrackerProvider;
98
+ host?: string;
99
+ repositoryOwner?: string;
100
+ repositoryName?: string;
101
+ repositoryId?: string;
102
+ projectKey?: string;
103
+ issueType?: string;
104
+ storePath?: string;
105
+ }
106
+ export interface ConfigureNexusProjectTrackerResult {
107
+ homePath: string;
108
+ project: NexusProjectStatusBase;
109
+ projectRoot: string;
110
+ projectConfigPath: string;
111
+ projectConfig: NexusProjectConfig;
112
+ reference: NexusProjectReference;
113
+ workTracking: WorkTrackingConfig;
114
+ }
115
+ export declare function statusForNexusProjectReference(reference: NexusProjectReference): NexusProjectStatusBase;
116
+ export declare function createNexusProject(options: CreateNexusProjectOptions): CreateNexusProjectResult;
117
+ export declare function importNexusProject(options: ImportNexusProjectOptions): ImportNexusProjectResult;
118
+ export declare function listNexusProjects(options: ListNexusProjectsOptions): ListNexusProjectsResult;
119
+ export declare function getNexusProjectStatus(options: GetNexusProjectStatusOptions): GetNexusProjectStatusResult;
120
+ export declare function linkNexusProjectTracker(options: LinkNexusProjectTrackerOptions): LinkNexusProjectTrackerResult;
121
+ export declare function configureNexusProjectTracker(options: ConfigureNexusProjectTrackerOptions): ConfigureNexusProjectTrackerResult;
@@ -0,0 +1,171 @@
1
+ import path from "node:path";
2
+ import { assertNonEmptyString, NexusProjectError, } from "./nexusProjectLifecycle.js";
3
+ import { configureNexusProjectTrackerInRegistry, createNexusProjectInRegistry, importNexusProjectInRegistry, linkNexusProjectTrackerInRegistry, } from "./nexusProjectOperations.js";
4
+ import { buildNexusProjectStatus, buildNexusProjectStatusForPath, findNexusProjectReferenceById, findNexusProjectReferenceByPath, projectRootFromInput, } from "./nexusProjectRegistry.js";
5
+ function loadProjectHome(options) {
6
+ const homePath = options.homeStore.resolveHomePath(options.homePath);
7
+ return {
8
+ homePath,
9
+ registry: options.homeStore.loadHomeConfig(homePath),
10
+ };
11
+ }
12
+ function saveProjectHome(homeStore, homePath, registry) {
13
+ homeStore.saveHomeConfig(homePath, registry);
14
+ }
15
+ export function statusForNexusProjectReference(reference) {
16
+ return buildNexusProjectStatus(reference);
17
+ }
18
+ function statusForNexusProjectPath(projectRoot) {
19
+ const baseStatus = buildNexusProjectStatusForPath(projectRoot);
20
+ return statusForNexusProjectReference({
21
+ id: baseStatus.id,
22
+ name: baseStatus.name,
23
+ projectRoot: baseStatus.projectRoot,
24
+ ...(baseStatus.vibeKanbanProjectId
25
+ ? { vibeKanbanProjectId: baseStatus.vibeKanbanProjectId }
26
+ : {}),
27
+ ...(baseStatus.vibeKanbanRepoId
28
+ ? { vibeKanbanRepoId: baseStatus.vibeKanbanRepoId }
29
+ : {}),
30
+ });
31
+ }
32
+ export function createNexusProject(options) {
33
+ const { homePath, registry } = loadProjectHome(options);
34
+ const result = createNexusProjectInRegistry({
35
+ homePath,
36
+ registry,
37
+ name: options.name,
38
+ ...(options.root !== undefined ? { root: options.root } : {}),
39
+ ...(options.from !== undefined ? { from: options.from } : {}),
40
+ ...(options.gitInit !== undefined ? { gitInit: options.gitInit } : {}),
41
+ ...(options.vibeKanbanProjectId !== undefined
42
+ ? { vibeKanbanProjectId: options.vibeKanbanProjectId }
43
+ : {}),
44
+ ...(options.gitRunner ? { gitRunner: options.gitRunner } : {}),
45
+ });
46
+ saveProjectHome(options.homeStore, homePath, registry);
47
+ return {
48
+ homePath,
49
+ projectRoot: result.projectRoot,
50
+ projectConfigPath: result.projectConfigPath,
51
+ worktreesRoot: result.worktreesRoot,
52
+ projectConfig: result.projectConfig,
53
+ reference: result.reference,
54
+ scaffold: result.scaffold,
55
+ git: result.git,
56
+ };
57
+ }
58
+ export function importNexusProject(options) {
59
+ const { homePath, registry } = loadProjectHome(options);
60
+ const result = importNexusProjectInRegistry({
61
+ homePath,
62
+ registry,
63
+ root: options.root,
64
+ ...(options.projectRoot !== undefined
65
+ ? { projectRoot: options.projectRoot }
66
+ : {}),
67
+ ...(options.name !== undefined ? { name: options.name } : {}),
68
+ ...(options.vibeKanbanProjectId !== undefined
69
+ ? { vibeKanbanProjectId: options.vibeKanbanProjectId }
70
+ : {}),
71
+ ...(options.gitRunner ? { gitRunner: options.gitRunner } : {}),
72
+ });
73
+ saveProjectHome(options.homeStore, homePath, registry);
74
+ return {
75
+ homePath,
76
+ projectRoot: result.projectRoot,
77
+ projectConfigPath: result.projectConfigPath,
78
+ worktreesRoot: result.worktreesRoot,
79
+ projectConfig: result.projectConfig,
80
+ reference: result.reference,
81
+ scaffold: result.scaffold,
82
+ git: result.git,
83
+ };
84
+ }
85
+ export function listNexusProjects(options) {
86
+ const { homePath, registry } = loadProjectHome(options);
87
+ return {
88
+ homePath,
89
+ projects: registry.projects.map(statusForNexusProjectReference),
90
+ };
91
+ }
92
+ export function getNexusProjectStatus(options) {
93
+ assertNonEmptyString(options.project, "project");
94
+ const { homePath, registry } = loadProjectHome(options);
95
+ const projectSelector = options.project.trim();
96
+ const reference = findNexusProjectReferenceById(registry, projectSelector) ??
97
+ findNexusProjectReferenceByPath(registry, projectSelector);
98
+ let project;
99
+ if (reference) {
100
+ project = statusForNexusProjectReference(reference);
101
+ }
102
+ else {
103
+ const projectRoot = projectRootFromInput(projectSelector);
104
+ try {
105
+ project = statusForNexusProjectPath(projectRoot);
106
+ }
107
+ catch (error) {
108
+ if (error instanceof NexusProjectError) {
109
+ throw new NexusProjectError(`No registered project matched "${projectSelector}". ` +
110
+ `Path fallback checked "${path.resolve(projectRoot)}" and failed: ${error.message}`);
111
+ }
112
+ throw error;
113
+ }
114
+ }
115
+ return {
116
+ homePath,
117
+ project,
118
+ };
119
+ }
120
+ export function linkNexusProjectTracker(options) {
121
+ assertNonEmptyString(options.project, "project");
122
+ const { homePath, registry } = loadProjectHome(options);
123
+ const result = linkNexusProjectTrackerInRegistry({
124
+ registry,
125
+ project: options.project,
126
+ trackerProjectId: options.trackerProjectId,
127
+ });
128
+ saveProjectHome(options.homeStore, homePath, registry);
129
+ return {
130
+ homePath,
131
+ vibeKanbanProjectId: result.vibeKanbanProjectId,
132
+ vibeKanbanRepoId: result.vibeKanbanRepoId,
133
+ project: statusForNexusProjectReference(result.reference),
134
+ projectRoot: result.projectRoot,
135
+ projectConfigPath: result.projectConfigPath,
136
+ projectConfig: result.projectConfig,
137
+ };
138
+ }
139
+ export function configureNexusProjectTracker(options) {
140
+ assertNonEmptyString(options.project, "project");
141
+ assertNonEmptyString(options.provider, "provider");
142
+ const { homePath, registry } = loadProjectHome(options);
143
+ const result = configureNexusProjectTrackerInRegistry({
144
+ registry,
145
+ project: options.project,
146
+ provider: options.provider,
147
+ ...(options.host !== undefined ? { host: options.host } : {}),
148
+ ...(options.repositoryOwner !== undefined
149
+ ? { repositoryOwner: options.repositoryOwner }
150
+ : {}),
151
+ ...(options.repositoryName !== undefined
152
+ ? { repositoryName: options.repositoryName }
153
+ : {}),
154
+ ...(options.repositoryId !== undefined
155
+ ? { repositoryId: options.repositoryId }
156
+ : {}),
157
+ ...(options.projectKey !== undefined ? { projectKey: options.projectKey } : {}),
158
+ ...(options.issueType !== undefined ? { issueType: options.issueType } : {}),
159
+ ...(options.storePath !== undefined ? { storePath: options.storePath } : {}),
160
+ });
161
+ saveProjectHome(options.homeStore, homePath, registry);
162
+ return {
163
+ homePath,
164
+ project: statusForNexusProjectReference(result.reference),
165
+ projectRoot: result.projectRoot,
166
+ projectConfigPath: result.projectConfigPath,
167
+ projectConfig: result.projectConfig,
168
+ reference: result.reference,
169
+ workTracking: result.workTracking,
170
+ };
171
+ }
@@ -0,0 +1,62 @@
1
+ import { type NexusProjectComponentConfig, type NexusProjectConfig } from "./nexusProjectConfig.js";
2
+ import type { TrackerCapabilities, WorkTrackerCapabilityReport } from "./workTrackingTypes.js";
3
+ export interface ProjectGitCommandResult {
4
+ args: string[];
5
+ stdout: string;
6
+ stderr: string;
7
+ exitCode: number | null;
8
+ }
9
+ export type ProjectGitRunner = (args: readonly string[], cwd?: string) => ProjectGitCommandResult;
10
+ export interface NexusProjectRegistryEntry {
11
+ id: string;
12
+ name?: string;
13
+ projectRoot: string;
14
+ }
15
+ export interface NexusProjectRegistryConfig {
16
+ paths: {
17
+ projectsRoot: string;
18
+ };
19
+ projects: NexusProjectRegistryEntry[];
20
+ }
21
+ export declare class NexusProjectError extends Error {
22
+ constructor(message: string);
23
+ }
24
+ export declare function assertNonEmptyString(value: string, name: string): void;
25
+ export declare function optionalNonEmptyString(value: string | undefined, name: string): string | undefined;
26
+ export declare function slugify(value: string): string;
27
+ export declare function safeProjectDirectoryName(value: string): string;
28
+ export declare function directoryExistsAndIsNonEmpty(directoryPath: string): boolean;
29
+ export declare function assertFileDoesNotExist(filePath: string): void;
30
+ export declare const defaultSourceCheckoutDirectoryName = "git";
31
+ export interface ResolvedNexusProjectComponent {
32
+ id: string;
33
+ name: string;
34
+ kind: NexusProjectComponentConfig["kind"];
35
+ role: NexusProjectComponentConfig["role"];
36
+ remoteUrl: string | null;
37
+ defaultBranch: string | null;
38
+ sourceRoot: string;
39
+ sourceRootExists: boolean;
40
+ worktreesRoot: string;
41
+ worktreesRootExists: boolean;
42
+ workTracking: NexusProjectComponentConfig["workTracking"] | null;
43
+ workTrackingCapabilities: TrackerCapabilities | null;
44
+ workTrackingCapabilityReport: WorkTrackerCapabilityReport | null;
45
+ verification: NexusProjectComponentConfig["verification"] | null;
46
+ publication: NexusProjectComponentConfig["publication"] | null;
47
+ relationships: NexusProjectComponentConfig["relationships"];
48
+ }
49
+ export declare function resolveProjectSourceRoot(projectRoot: string, projectConfig: NexusProjectConfig): string;
50
+ export declare function resolvePrimaryProjectComponent(projectRoot: string, projectConfig: NexusProjectConfig): ResolvedNexusProjectComponent;
51
+ export declare function resolveProjectComponents(projectRoot: string, projectConfig: NexusProjectConfig): ResolvedNexusProjectComponent[];
52
+ export declare function resolveProjectComponent(projectRoot: string, projectConfig: NexusProjectConfig, component: NexusProjectComponentConfig): ResolvedNexusProjectComponent;
53
+ export declare function defaultProjectGitRunner(args: readonly string[], cwd?: string): ProjectGitCommandResult;
54
+ export declare function runProjectGitCommand(gitRunner: ProjectGitRunner, commands: ProjectGitCommandResult[], args: readonly string[], cwd?: string): ProjectGitCommandResult;
55
+ export declare function detectDefaultBranch(gitRunner: ProjectGitRunner, commands: ProjectGitCommandResult[], projectRoot: string): string | null;
56
+ export declare function assertGitRepository(gitRunner: ProjectGitRunner, commands: ProjectGitCommandResult[], projectRoot: string): void;
57
+ export declare function detectOriginUrl(gitRunner: ProjectGitRunner, commands: ProjectGitCommandResult[], projectRoot: string): string | null;
58
+ export declare function samePath(left: string, right: string): boolean;
59
+ export declare function ensureUniqueProject(config: Pick<NexusProjectRegistryConfig, "projects">, projectId: string, projectRoot: string): void;
60
+ export declare function pathForProjectConfig(projectRoot: string, targetPath: string): string;
61
+ export declare function defaultImportedProjectRoot(homeConfig: NexusProjectRegistryConfig, projectName: string, sourceRoot: string): string;
62
+ export declare function loadProjectConfigIfExists(projectRoot: string): NexusProjectConfig | undefined;
@@ -0,0 +1,205 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { loadProjectConfig, projectConfigPath, projectWorktreesRootPath, validateProjectConfig, } from "./nexusProjectConfig.js";
5
+ import { workTrackerCapabilityReportForConfig, workTrackerCapabilitiesForConfig, } from "./workTrackingProviderService.js";
6
+ export class NexusProjectError extends Error {
7
+ constructor(message) {
8
+ super(message);
9
+ this.name = "NexusProjectError";
10
+ }
11
+ }
12
+ export function assertNonEmptyString(value, name) {
13
+ if (value.trim().length === 0) {
14
+ throw new NexusProjectError(`${name} must be a non-empty string`);
15
+ }
16
+ }
17
+ export function optionalNonEmptyString(value, name) {
18
+ if (value === undefined) {
19
+ return undefined;
20
+ }
21
+ assertNonEmptyString(value, name);
22
+ return value.trim();
23
+ }
24
+ export function slugify(value) {
25
+ const withWordBreaks = value
26
+ .trim()
27
+ .replace(/([a-z0-9])([A-Z])/g, "$1-$2")
28
+ .replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2");
29
+ const slug = withWordBreaks
30
+ .toLowerCase()
31
+ .replace(/[^a-z0-9._-]+/g, "-")
32
+ .replace(/^-+|-+$/g, "");
33
+ if (!slug) {
34
+ throw new NexusProjectError("Project name must contain at least one filesystem-safe character");
35
+ }
36
+ return slug;
37
+ }
38
+ export function safeProjectDirectoryName(value) {
39
+ const sanitized = value
40
+ .trim()
41
+ .replace(/[<>:"/\\|?*\u0000-\u001F]+/g, "-")
42
+ .replace(/\s+/g, "-")
43
+ .replace(/-+/g, "-")
44
+ .replace(/^-+|-+$/g, "");
45
+ return sanitized || slugify(value);
46
+ }
47
+ export function directoryExistsAndIsNonEmpty(directoryPath) {
48
+ if (!fs.existsSync(directoryPath)) {
49
+ return false;
50
+ }
51
+ const stat = fs.statSync(directoryPath);
52
+ if (!stat.isDirectory()) {
53
+ throw new NexusProjectError(`Project root exists and is not a directory: ${directoryPath}`);
54
+ }
55
+ return fs.readdirSync(directoryPath).length > 0;
56
+ }
57
+ export function assertFileDoesNotExist(filePath) {
58
+ if (fs.existsSync(filePath)) {
59
+ throw new NexusProjectError(`Refusing to overwrite existing file: ${filePath}`);
60
+ }
61
+ }
62
+ export const defaultSourceCheckoutDirectoryName = "git";
63
+ export function resolveProjectSourceRoot(projectRoot, projectConfig) {
64
+ return resolvePrimaryProjectComponent(projectRoot, projectConfig).sourceRoot;
65
+ }
66
+ export function resolvePrimaryProjectComponent(projectRoot, projectConfig) {
67
+ const components = resolveProjectComponents(projectRoot, projectConfig);
68
+ const primary = components.find((component) => component.role === "primary") ?? components[0];
69
+ if (!primary) {
70
+ throw new NexusProjectError("DevNexus project has no components");
71
+ }
72
+ return primary;
73
+ }
74
+ export function resolveProjectComponents(projectRoot, projectConfig) {
75
+ const normalizedConfig = validateProjectConfig(projectConfig);
76
+ return normalizedConfig.components.map((component) => resolveProjectComponent(projectRoot, normalizedConfig, component));
77
+ }
78
+ export function resolveProjectComponent(projectRoot, projectConfig, component) {
79
+ const sourceRoot = resolveProjectPath(projectRoot, component.sourceRoot ?? `components/${component.id}`);
80
+ const worktreesRoot = resolveComponentWorktreesRoot(projectRoot, projectConfig, component);
81
+ return {
82
+ id: component.id,
83
+ name: component.name,
84
+ kind: component.kind,
85
+ role: component.role,
86
+ remoteUrl: component.remoteUrl,
87
+ defaultBranch: component.defaultBranch,
88
+ sourceRoot,
89
+ sourceRootExists: directoryExists(sourceRoot),
90
+ worktreesRoot,
91
+ worktreesRootExists: directoryExists(worktreesRoot),
92
+ workTracking: component.workTracking ?? null,
93
+ workTrackingCapabilities: component.workTracking
94
+ ? workTrackerCapabilitiesForConfig(component.workTracking)
95
+ : null,
96
+ workTrackingCapabilityReport: component.workTracking
97
+ ? workTrackerCapabilityReportForConfig(component.workTracking)
98
+ : null,
99
+ verification: component.verification ?? null,
100
+ publication: component.publication ?? null,
101
+ relationships: component.relationships,
102
+ };
103
+ }
104
+ function resolveComponentWorktreesRoot(projectRoot, projectConfig, component) {
105
+ if (component.worktreesRoot) {
106
+ return resolveProjectPath(projectRoot, component.worktreesRoot);
107
+ }
108
+ return path.join(projectWorktreesRootPath(projectRoot, projectConfig), component.id);
109
+ }
110
+ function resolveProjectPath(projectRoot, value) {
111
+ return path.isAbsolute(value)
112
+ ? path.resolve(value)
113
+ : path.resolve(projectRoot, value);
114
+ }
115
+ function directoryExists(directoryPath) {
116
+ return fs.existsSync(directoryPath) && fs.statSync(directoryPath).isDirectory();
117
+ }
118
+ export function defaultProjectGitRunner(args, cwd) {
119
+ const result = spawnSync("git", [...args], {
120
+ cwd,
121
+ encoding: "utf8",
122
+ shell: false,
123
+ windowsHide: true,
124
+ });
125
+ if (result.error) {
126
+ throw new NexusProjectError(`Failed to run git ${args.join(" ")}: ${result.error.message}`);
127
+ }
128
+ const commandResult = {
129
+ args: [...args],
130
+ stdout: result.stdout ?? "",
131
+ stderr: result.stderr ?? "",
132
+ exitCode: result.status,
133
+ };
134
+ if (result.status !== 0) {
135
+ throw new NexusProjectError(`git ${args.join(" ")} failed with exit code ${result.status}: ${commandResult.stderr.trim() || commandResult.stdout.trim()}`);
136
+ }
137
+ return commandResult;
138
+ }
139
+ export function runProjectGitCommand(gitRunner, commands, args, cwd) {
140
+ const result = gitRunner(args, cwd);
141
+ commands.push(result);
142
+ if (result.exitCode !== 0) {
143
+ throw new NexusProjectError(`git ${args.join(" ")} failed with exit code ${result.exitCode}: ${result.stderr.trim() || result.stdout.trim()}`);
144
+ }
145
+ return result;
146
+ }
147
+ function tryGitCommand(gitRunner, commands, args, cwd) {
148
+ try {
149
+ return runProjectGitCommand(gitRunner, commands, args, cwd);
150
+ }
151
+ catch {
152
+ return undefined;
153
+ }
154
+ }
155
+ export function detectDefaultBranch(gitRunner, commands, projectRoot) {
156
+ const result = tryGitCommand(gitRunner, commands, ["-C", projectRoot, "symbolic-ref", "--short", "HEAD"]);
157
+ const branch = result?.stdout.trim();
158
+ return branch && branch !== "HEAD" ? branch : null;
159
+ }
160
+ export function assertGitRepository(gitRunner, commands, projectRoot) {
161
+ const result = runProjectGitCommand(gitRunner, commands, ["-C", projectRoot, "rev-parse", "--is-inside-work-tree"]);
162
+ if (result.stdout.trim() !== "true") {
163
+ throw new NexusProjectError(`Path is not inside a Git work tree: ${projectRoot}`);
164
+ }
165
+ }
166
+ export function detectOriginUrl(gitRunner, commands, projectRoot) {
167
+ const result = tryGitCommand(gitRunner, commands, ["-C", projectRoot, "config", "--get", "remote.origin.url"]);
168
+ const remoteUrl = result?.stdout.trim();
169
+ return remoteUrl || null;
170
+ }
171
+ function normalizedPathForCompare(filePath) {
172
+ const resolved = path.resolve(filePath);
173
+ return process.platform === "win32" ? resolved.toLowerCase() : resolved;
174
+ }
175
+ export function samePath(left, right) {
176
+ return normalizedPathForCompare(left) === normalizedPathForCompare(right);
177
+ }
178
+ export function ensureUniqueProject(config, projectId, projectRoot) {
179
+ const duplicate = config.projects.find((project) => project.id === projectId || samePath(project.projectRoot, projectRoot));
180
+ if (duplicate) {
181
+ throw new NexusProjectError(`Project is already registered: ${duplicate.id}`);
182
+ }
183
+ }
184
+ export function pathForProjectConfig(projectRoot, targetPath) {
185
+ const relative = path.relative(projectRoot, targetPath);
186
+ if (relative &&
187
+ !relative.startsWith("..") &&
188
+ !path.isAbsolute(relative)) {
189
+ return relative;
190
+ }
191
+ return path.resolve(targetPath);
192
+ }
193
+ export function defaultImportedProjectRoot(homeConfig, projectName, sourceRoot) {
194
+ const directoryName = safeProjectDirectoryName(projectName);
195
+ const candidate = path.join(homeConfig.paths.projectsRoot, directoryName);
196
+ return samePath(candidate, sourceRoot)
197
+ ? path.join(homeConfig.paths.projectsRoot, `${directoryName}-DevNexus`)
198
+ : candidate;
199
+ }
200
+ export function loadProjectConfigIfExists(projectRoot) {
201
+ if (!fs.existsSync(projectConfigPath(projectRoot))) {
202
+ return undefined;
203
+ }
204
+ return loadProjectConfig(projectRoot);
205
+ }
@@ -0,0 +1,101 @@
1
+ import type { NexusExtension } from "./nexusExtension.js";
2
+ import { type NexusProjectConfig, type NexusProjectExtensionsConfig } from "./nexusProjectConfig.js";
3
+ import { type ProjectGitCommandResult, type ProjectGitRunner } from "./nexusProjectLifecycle.js";
4
+ import { type NexusProjectReference, type NexusProjectRegistry } from "./nexusProjectRegistry.js";
5
+ import { type ScaffoldNexusProjectResult } from "./nexusProjectScaffold.js";
6
+ import type { WorkTrackingConfig } from "./workTrackingTypes.js";
7
+ export interface NexusProjectRegistryWithRoot extends NexusProjectRegistry {
8
+ paths: {
9
+ projectsRoot: string;
10
+ };
11
+ }
12
+ export interface CreateNexusProjectInRegistryOptions {
13
+ homePath: string;
14
+ registry: NexusProjectRegistryWithRoot;
15
+ name: string;
16
+ root?: string;
17
+ from?: string;
18
+ gitInit?: boolean;
19
+ vibeKanbanProjectId?: string;
20
+ gitRunner?: ProjectGitRunner;
21
+ extensions?: NexusProjectExtensionsConfig;
22
+ scaffoldExtensions?: NexusExtension<NexusProjectConfig>[];
23
+ }
24
+ export interface ImportNexusProjectInRegistryOptions {
25
+ homePath: string;
26
+ registry: NexusProjectRegistryWithRoot;
27
+ root: string;
28
+ projectRoot?: string;
29
+ name?: string;
30
+ vibeKanbanProjectId?: string;
31
+ gitRunner?: ProjectGitRunner;
32
+ extensions?: NexusProjectExtensionsConfig;
33
+ scaffoldExtensions?: NexusExtension<NexusProjectConfig>[];
34
+ }
35
+ export interface CreateNexusProjectInRegistryResult {
36
+ projectRoot: string;
37
+ projectConfigPath: string;
38
+ worktreesRoot: string;
39
+ projectConfig: NexusProjectConfig;
40
+ reference: NexusProjectReference;
41
+ scaffold: ScaffoldNexusProjectResult;
42
+ git: {
43
+ operation: "clone" | "init";
44
+ remoteUrl: string | null;
45
+ defaultBranch: string | null;
46
+ commands: ProjectGitCommandResult[];
47
+ };
48
+ }
49
+ export interface ImportNexusProjectInRegistryResult {
50
+ projectRoot: string;
51
+ projectConfigPath: string;
52
+ worktreesRoot: string;
53
+ projectConfig: NexusProjectConfig;
54
+ reference: NexusProjectReference;
55
+ scaffold: ScaffoldNexusProjectResult;
56
+ git: {
57
+ operation: "import";
58
+ remoteUrl: string | null;
59
+ defaultBranch: string | null;
60
+ commands: ProjectGitCommandResult[];
61
+ };
62
+ }
63
+ export type ConfigureNexusProjectTrackerProvider = "local" | "github" | "gitlab" | "jira";
64
+ export interface ConfigureNexusProjectTrackerInRegistryOptions {
65
+ registry: NexusProjectRegistry;
66
+ project: string;
67
+ provider: ConfigureNexusProjectTrackerProvider;
68
+ host?: string;
69
+ repositoryOwner?: string;
70
+ repositoryName?: string;
71
+ repositoryId?: string;
72
+ projectKey?: string;
73
+ issueType?: string;
74
+ storePath?: string;
75
+ }
76
+ export interface ConfigureNexusProjectTrackerInRegistryResult {
77
+ projectRoot: string;
78
+ projectConfigPath: string;
79
+ projectConfig: NexusProjectConfig;
80
+ reference: NexusProjectReference;
81
+ workTracking: WorkTrackingConfig;
82
+ }
83
+ export interface LinkNexusProjectTrackerInRegistryOptions {
84
+ registry: NexusProjectRegistry;
85
+ project: string;
86
+ trackerProjectId: string;
87
+ }
88
+ export interface LinkNexusProjectTrackerInRegistryResult {
89
+ projectRoot: string;
90
+ projectConfigPath: string;
91
+ projectConfig: NexusProjectConfig;
92
+ reference: NexusProjectReference;
93
+ vibeKanbanProjectId: string;
94
+ vibeKanbanRepoId: string | null;
95
+ }
96
+ export declare function buildProjectConfig(name: string, projectId: string, from: string | undefined, defaultBranch: string | null, vibeKanbanProjectId?: string | null, sourceRoot?: string | null, forceGit?: boolean, extensions?: NexusProjectExtensionsConfig): NexusProjectConfig;
97
+ export declare function buildConfiguredWorkTracking(options: ConfigureNexusProjectTrackerInRegistryOptions): WorkTrackingConfig;
98
+ export declare function createNexusProjectInRegistry(options: CreateNexusProjectInRegistryOptions): CreateNexusProjectInRegistryResult;
99
+ export declare function importNexusProjectInRegistry(options: ImportNexusProjectInRegistryOptions): ImportNexusProjectInRegistryResult;
100
+ export declare function configureNexusProjectTrackerInRegistry(options: ConfigureNexusProjectTrackerInRegistryOptions): ConfigureNexusProjectTrackerInRegistryResult;
101
+ export declare function linkNexusProjectTrackerInRegistry(options: LinkNexusProjectTrackerInRegistryOptions): LinkNexusProjectTrackerInRegistryResult;