@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,462 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { buildNexusAutomationWorkItemQuery, eligibleNexusAutomationWorkItems, evaluateNexusAutomationLedgerBackoff, nexusAutomationLockPath, readNexusAutomationRunLedger, selectNexusAutomationWorkItem, } from "./nexusAutomation.js";
4
+ import { preflightNexusAutomationAgentLaunch, } from "./nexusAutomationAgentLaunch.js";
5
+ import { normalizeNexusAutomationAgentPolicy, } from "./nexusAutomationAgentProfile.js";
6
+ import { loadProjectConfig, } from "./nexusProjectConfig.js";
7
+ import { readNexusAutomationTargetContext, } from "./nexusAutomationTarget.js";
8
+ import { summarizeNexusAutomationTargetCycles, } from "./nexusAutomationTargetCycle.js";
9
+ import { resolvePrimaryProjectComponent, resolveProjectComponents, } from "./nexusProjectLifecycle.js";
10
+ import { preflightNexusAutomationRunOnce, } from "./nexusAutomationRunOnce.js";
11
+ import { createWorkTrackerProvider, } from "./workTrackingProviderService.js";
12
+ export class NexusAutomationStatusError extends Error {
13
+ constructor(message) {
14
+ super(message);
15
+ this.name = "NexusAutomationStatusError";
16
+ }
17
+ }
18
+ export async function getNexusAutomationStatus(options) {
19
+ const projectRoot = path.resolve(requiredNonEmptyString(options.projectRoot, "projectRoot"));
20
+ const projectConfig = loadProjectConfig(projectRoot);
21
+ const automationConfig = projectConfig.automation ?? null;
22
+ const components = resolveProjectComponents(projectRoot, projectConfig);
23
+ const primaryComponent = resolvePrimaryProjectComponent(projectRoot, projectConfig);
24
+ const sourceRoot = primaryComponent.sourceRoot;
25
+ if (!automationConfig?.enabled) {
26
+ return statusResult({
27
+ projectRoot,
28
+ sourceRoot,
29
+ components,
30
+ projectConfig,
31
+ automationConfig,
32
+ status: "disabled",
33
+ summary: "Automation is not enabled for this project",
34
+ lock: null,
35
+ ledger: null,
36
+ backoff: null,
37
+ preflight: [],
38
+ selectorQuery: null,
39
+ candidateCount: null,
40
+ eligibleWorkItems: null,
41
+ selectedWorkItem: null,
42
+ });
43
+ }
44
+ const now = currentIso(options.now);
45
+ const ledger = readNexusAutomationRunLedger(projectRoot, automationConfig);
46
+ const lock = readNexusAutomationStatusLock(projectRoot, automationConfig, now);
47
+ if (lock.status === "active") {
48
+ return statusResult({
49
+ projectRoot,
50
+ sourceRoot,
51
+ components,
52
+ projectConfig,
53
+ automationConfig,
54
+ status: "locked",
55
+ summary: lock.message,
56
+ lock,
57
+ ledger,
58
+ backoff: null,
59
+ preflight: [],
60
+ selectorQuery: null,
61
+ candidateCount: null,
62
+ eligibleWorkItems: null,
63
+ selectedWorkItem: null,
64
+ });
65
+ }
66
+ if (lock.status === "invalid") {
67
+ return statusResult({
68
+ projectRoot,
69
+ sourceRoot,
70
+ components,
71
+ projectConfig,
72
+ automationConfig,
73
+ status: "blocked",
74
+ summary: lock.message,
75
+ lock,
76
+ ledger,
77
+ backoff: null,
78
+ preflight: [],
79
+ selectorQuery: null,
80
+ candidateCount: null,
81
+ eligibleWorkItems: null,
82
+ selectedWorkItem: null,
83
+ });
84
+ }
85
+ const backoff = evaluateNexusAutomationLedgerBackoff(automationConfig, ledger, now);
86
+ if (!backoff.shouldRun) {
87
+ return statusResult({
88
+ projectRoot,
89
+ sourceRoot,
90
+ components,
91
+ projectConfig,
92
+ automationConfig,
93
+ status: "backoff",
94
+ summary: backoff.reason ?? "Automation retry backoff is active",
95
+ lock,
96
+ ledger,
97
+ backoff,
98
+ preflight: [],
99
+ selectorQuery: null,
100
+ candidateCount: null,
101
+ eligibleWorkItems: null,
102
+ selectedWorkItem: null,
103
+ });
104
+ }
105
+ if (automationConfig.mode === "agent_launch") {
106
+ const componentProviders = createStatusComponentProviders({
107
+ options,
108
+ projectRoot,
109
+ projectConfig,
110
+ components,
111
+ });
112
+ if (componentProviders.length === 0) {
113
+ const summary = "No project component has work tracking configured";
114
+ return statusResult({
115
+ projectRoot,
116
+ sourceRoot,
117
+ components,
118
+ projectConfig,
119
+ automationConfig,
120
+ status: "blocked",
121
+ summary,
122
+ lock,
123
+ ledger,
124
+ backoff,
125
+ preflight: [
126
+ {
127
+ name: "workTracking",
128
+ status: "failed",
129
+ message: summary,
130
+ },
131
+ ],
132
+ selectorQuery: null,
133
+ candidateCount: null,
134
+ eligibleWorkItems: null,
135
+ selectedWorkItem: null,
136
+ });
137
+ }
138
+ const preflight = preflightNexusAutomationAgentLaunch({
139
+ components,
140
+ componentProviders,
141
+ automationConfig,
142
+ });
143
+ const failedChecks = preflight.filter((check) => check.status === "failed");
144
+ if (failedChecks.length > 0) {
145
+ return statusResult({
146
+ projectRoot,
147
+ sourceRoot,
148
+ components,
149
+ projectConfig,
150
+ automationConfig,
151
+ status: "blocked",
152
+ summary: failedChecks.map((check) => check.message).join("; "),
153
+ lock,
154
+ ledger,
155
+ backoff,
156
+ preflight,
157
+ selectorQuery: null,
158
+ candidateCount: null,
159
+ eligibleWorkItems: null,
160
+ selectedWorkItem: null,
161
+ });
162
+ }
163
+ const selectorQuery = buildNexusAutomationWorkItemQuery(automationConfig);
164
+ const componentEligibleWorkItems = await listStatusEligibleWorkItemsByComponent(componentProviders, selectorQuery, automationConfig, projectRoot);
165
+ const eligibleWorkItems = componentEligibleWorkItems.flatMap((component) => component.workItems);
166
+ const status = eligibleWorkItems.length > 0 ? "ready" : "idle";
167
+ const summary = eligibleWorkItems.length > 0
168
+ ? `Agent launch ready with ${eligibleWorkItems.length} eligible work item(s)`
169
+ : "No eligible work item matched the automation selector";
170
+ return statusResult({
171
+ projectRoot,
172
+ sourceRoot,
173
+ components,
174
+ projectConfig,
175
+ automationConfig,
176
+ status,
177
+ summary,
178
+ lock,
179
+ ledger,
180
+ backoff,
181
+ preflight,
182
+ selectorQuery,
183
+ candidateCount: eligibleWorkItems.length,
184
+ eligibleWorkItems,
185
+ componentEligibleWorkItems,
186
+ selectedWorkItem: null,
187
+ });
188
+ }
189
+ if (!primaryComponent.workTracking) {
190
+ const summary = "Primary component work tracking is not configured";
191
+ return statusResult({
192
+ projectRoot,
193
+ sourceRoot,
194
+ components,
195
+ projectConfig,
196
+ automationConfig,
197
+ status: "blocked",
198
+ summary,
199
+ lock,
200
+ ledger,
201
+ backoff,
202
+ preflight: [
203
+ {
204
+ name: "workTracking",
205
+ status: "failed",
206
+ message: summary,
207
+ },
208
+ ],
209
+ selectorQuery: null,
210
+ candidateCount: null,
211
+ eligibleWorkItems: null,
212
+ selectedWorkItem: null,
213
+ });
214
+ }
215
+ const provider = createStatusProvider({
216
+ options,
217
+ projectRoot,
218
+ sourceRoot,
219
+ projectConfig,
220
+ component: primaryComponent,
221
+ });
222
+ const preflight = preflightNexusAutomationRunOnce({
223
+ projectRoot,
224
+ sourceRoot,
225
+ projectConfig,
226
+ component: primaryComponent,
227
+ automationConfig,
228
+ provider,
229
+ });
230
+ const failedChecks = preflight.filter((check) => check.status === "failed");
231
+ if (failedChecks.length > 0) {
232
+ return statusResult({
233
+ projectRoot,
234
+ sourceRoot,
235
+ components,
236
+ projectConfig,
237
+ automationConfig,
238
+ status: "blocked",
239
+ summary: failedChecks.map((check) => check.message).join("; "),
240
+ lock,
241
+ ledger,
242
+ backoff,
243
+ preflight,
244
+ selectorQuery: null,
245
+ candidateCount: null,
246
+ eligibleWorkItems: null,
247
+ selectedWorkItem: null,
248
+ });
249
+ }
250
+ const selectorQuery = buildNexusAutomationWorkItemQuery(automationConfig);
251
+ const candidates = await provider.listWorkItems({
252
+ ...selectorQuery,
253
+ projectRoot,
254
+ });
255
+ const selectedWorkItem = selectNexusAutomationWorkItem(candidates, automationConfig) ?? null;
256
+ const status = selectedWorkItem ? "ready" : "idle";
257
+ const summary = selectedWorkItem
258
+ ? `Selected work item ${selectedWorkItem.id}: ${selectedWorkItem.title}`
259
+ : "No eligible work item matched the automation selector";
260
+ return statusResult({
261
+ projectRoot,
262
+ sourceRoot,
263
+ components,
264
+ projectConfig,
265
+ automationConfig,
266
+ status,
267
+ summary,
268
+ lock,
269
+ ledger,
270
+ backoff,
271
+ preflight,
272
+ selectorQuery,
273
+ candidateCount: candidates.length,
274
+ eligibleWorkItems: null,
275
+ selectedWorkItem,
276
+ });
277
+ }
278
+ export function readNexusAutomationStatusLock(projectRoot, config, now = new Date()) {
279
+ const lockPath = nexusAutomationLockPath(projectRoot, config);
280
+ if (!fs.existsSync(lockPath)) {
281
+ return {
282
+ path: lockPath,
283
+ status: "none",
284
+ runId: null,
285
+ owner: null,
286
+ acquiredAt: null,
287
+ expiresAt: null,
288
+ message: "No automation run lock is present",
289
+ };
290
+ }
291
+ try {
292
+ const record = JSON.parse(fs.readFileSync(lockPath, "utf8").replace(/^\uFEFF/, ""));
293
+ const runId = requiredLockString(record.runId, "automation lock.runId");
294
+ const acquiredAt = requiredLockIsoString(record.acquiredAt, "automation lock.acquiredAt");
295
+ const expiresAt = requiredLockIsoString(record.expiresAt, "automation lock.expiresAt");
296
+ const owner = optionalLockString(record.owner);
297
+ const expiresAtDate = dateFrom(expiresAt, "automation lock.expiresAt");
298
+ const status = expiresAtDate.getTime() > dateFrom(now, "now").getTime()
299
+ ? "active"
300
+ : "stale";
301
+ const message = status === "active"
302
+ ? `Automation run lock is held by ${runId} until ${expiresAt}`
303
+ : `Automation run lock is stale and can be replaced: ${runId}`;
304
+ return {
305
+ path: lockPath,
306
+ status,
307
+ runId,
308
+ owner,
309
+ acquiredAt,
310
+ expiresAt,
311
+ message,
312
+ };
313
+ }
314
+ catch (error) {
315
+ return {
316
+ path: lockPath,
317
+ status: "invalid",
318
+ runId: null,
319
+ owner: null,
320
+ acquiredAt: null,
321
+ expiresAt: null,
322
+ message: `Automation run lock is invalid: ${errorMessage(error)}`,
323
+ };
324
+ }
325
+ }
326
+ function createStatusProvider(options) {
327
+ const workTracking = options.component.workTracking;
328
+ if (!workTracking) {
329
+ throw new NexusAutomationStatusError("Primary component work tracking is not configured");
330
+ }
331
+ if (options.options.provider) {
332
+ return options.options.provider;
333
+ }
334
+ if (options.options.providerFactory) {
335
+ return options.options.providerFactory({
336
+ projectRoot: options.projectRoot,
337
+ sourceRoot: options.sourceRoot,
338
+ projectConfig: options.projectConfig,
339
+ component: options.component,
340
+ workTracking,
341
+ });
342
+ }
343
+ return createWorkTrackerProvider(workTracking, {
344
+ ...options.options.providerOptions,
345
+ projectRoot: options.projectRoot,
346
+ now: options.options.now,
347
+ });
348
+ }
349
+ function createStatusComponentProviders(options) {
350
+ return options.components
351
+ .filter((component) => component.workTracking)
352
+ .map((component) => ({
353
+ component,
354
+ provider: createStatusComponentProvider({
355
+ options: options.options,
356
+ projectRoot: options.projectRoot,
357
+ projectConfig: options.projectConfig,
358
+ component,
359
+ }),
360
+ }));
361
+ }
362
+ function createStatusComponentProvider(options) {
363
+ const workTracking = options.component.workTracking;
364
+ if (!workTracking) {
365
+ throw new NexusAutomationStatusError(`Component ${options.component.id} work tracking is not configured`);
366
+ }
367
+ if (options.options.provider) {
368
+ return options.options.provider;
369
+ }
370
+ if (options.options.providerFactory) {
371
+ return options.options.providerFactory({
372
+ projectRoot: options.projectRoot,
373
+ sourceRoot: options.component.sourceRoot,
374
+ projectConfig: options.projectConfig,
375
+ component: options.component,
376
+ workTracking,
377
+ });
378
+ }
379
+ return createWorkTrackerProvider(workTracking, {
380
+ ...options.options.providerOptions,
381
+ projectRoot: options.projectRoot,
382
+ now: options.options.now,
383
+ });
384
+ }
385
+ async function listStatusEligibleWorkItemsByComponent(componentProviders, selectorQuery, automationConfig, projectRoot) {
386
+ const grouped = [];
387
+ for (const { component, provider } of componentProviders) {
388
+ grouped.push({
389
+ componentId: component.id,
390
+ workItems: eligibleNexusAutomationWorkItems(await provider.listWorkItems({
391
+ ...selectorQuery,
392
+ projectRoot,
393
+ }), automationConfig),
394
+ });
395
+ }
396
+ return grouped;
397
+ }
398
+ function statusResult(result) {
399
+ const target = result.target ??
400
+ (result.automationConfig
401
+ ? readNexusAutomationTargetContext({
402
+ projectRoot: result.projectRoot,
403
+ config: result.automationConfig,
404
+ })
405
+ : null);
406
+ const agent = result.agent ??
407
+ (result.automationConfig
408
+ ? normalizeNexusAutomationAgentPolicy(result.automationConfig)
409
+ : null);
410
+ const targetCycles = result.targetCycles ??
411
+ (result.automationConfig
412
+ ? summarizeNexusAutomationTargetCycles({
413
+ projectRoot: result.projectRoot,
414
+ config: result.automationConfig,
415
+ })
416
+ : null);
417
+ return {
418
+ ...result,
419
+ target,
420
+ targetCycles,
421
+ agent,
422
+ components: result.components ?? [],
423
+ componentEligibleWorkItems: result.componentEligibleWorkItems ?? null,
424
+ };
425
+ }
426
+ function currentIso(now) {
427
+ const value = now ? now() : new Date();
428
+ return dateFrom(value, "now").toISOString();
429
+ }
430
+ function dateFrom(value, name) {
431
+ const date = value instanceof Date ? value : new Date(value);
432
+ if (Number.isNaN(date.getTime())) {
433
+ throw new NexusAutomationStatusError(`${name} must be a valid date`);
434
+ }
435
+ return date;
436
+ }
437
+ function requiredNonEmptyString(value, name) {
438
+ if (typeof value !== "string" || value.trim().length === 0) {
439
+ throw new NexusAutomationStatusError(`${name} must be a non-empty string`);
440
+ }
441
+ return value.trim();
442
+ }
443
+ function requiredLockString(value, name) {
444
+ if (typeof value !== "string" || value.trim().length === 0) {
445
+ throw new NexusAutomationStatusError(`${name} must be a non-empty string`);
446
+ }
447
+ return value.trim();
448
+ }
449
+ function requiredLockIsoString(value, name) {
450
+ const stringValue = requiredLockString(value, name);
451
+ dateFrom(stringValue, name);
452
+ return stringValue;
453
+ }
454
+ function optionalLockString(value) {
455
+ if (value === undefined || value === null) {
456
+ return null;
457
+ }
458
+ return requiredLockString(value, "automation lock.owner");
459
+ }
460
+ function errorMessage(error) {
461
+ return error instanceof Error ? error.message : String(error);
462
+ }
@@ -0,0 +1,19 @@
1
+ import type { NexusAutomationConfig } from "./nexusAutomationConfig.js";
2
+ export interface NexusAutomationTargetContext {
3
+ id: string | null;
4
+ objective: string | null;
5
+ statePath: string;
6
+ cycleLedgerPath: string;
7
+ stateExists: boolean;
8
+ stateMarkdown: string | null;
9
+ stopWhenNoEligibleWork: boolean;
10
+ maxCycles: number | null;
11
+ maxWorkItems: number | null;
12
+ }
13
+ export declare class NexusAutomationTargetError extends Error {
14
+ constructor(message: string);
15
+ }
16
+ export declare function readNexusAutomationTargetContext(options: {
17
+ projectRoot: string;
18
+ config: NexusAutomationConfig;
19
+ }): NexusAutomationTargetContext;
@@ -0,0 +1,33 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ export class NexusAutomationTargetError extends Error {
4
+ constructor(message) {
5
+ super(message);
6
+ this.name = "NexusAutomationTargetError";
7
+ }
8
+ }
9
+ export function readNexusAutomationTargetContext(options) {
10
+ const statePath = resolveProjectRelativePath(options.projectRoot, options.config.target.statePath, "automation.target.statePath");
11
+ const cycleLedgerPath = resolveProjectRelativePath(options.projectRoot, options.config.target.cycleLedgerPath, "automation.target.cycleLedgerPath");
12
+ const stateExists = fs.existsSync(statePath);
13
+ return {
14
+ id: options.config.target.id,
15
+ objective: options.config.target.objective,
16
+ statePath,
17
+ cycleLedgerPath,
18
+ stateExists,
19
+ stateMarkdown: stateExists ? fs.readFileSync(statePath, "utf8") : null,
20
+ stopWhenNoEligibleWork: options.config.target.stopWhenNoEligibleWork,
21
+ maxCycles: options.config.target.maxCycles,
22
+ maxWorkItems: options.config.target.maxWorkItems,
23
+ };
24
+ }
25
+ function resolveProjectRelativePath(projectRoot, configuredPath, fieldName) {
26
+ const root = path.resolve(projectRoot);
27
+ const target = path.resolve(root, configuredPath);
28
+ const relative = path.relative(root, target);
29
+ if (relative.startsWith("..") || path.isAbsolute(relative)) {
30
+ throw new NexusAutomationTargetError(`${fieldName} must resolve inside the project root: ${target}`);
31
+ }
32
+ return target;
33
+ }
@@ -0,0 +1,90 @@
1
+ import type { NexusAutomationConfig } from "./nexusAutomationConfig.js";
2
+ import type { WorkStatus } from "./workTrackingTypes.js";
3
+ export declare const maxNexusAutomationTargetCycleNoteLength = 1000;
4
+ export type NexusAutomationTargetCycleStatus = "started" | "dispatched" | "completed" | "blocked" | "failed" | "skipped";
5
+ export type NexusAutomationTargetCycleWorkItemStatus = "eligible" | "selected" | "dispatched" | "in_progress" | "completed" | "blocked" | "skipped";
6
+ export interface NexusAutomationTargetCycleWorkItem {
7
+ componentId: string | null;
8
+ id: string;
9
+ title: string | null;
10
+ status: WorkStatus | null;
11
+ cycleStatus: NexusAutomationTargetCycleWorkItemStatus | null;
12
+ agentProfileId: string | null;
13
+ notes: string | null;
14
+ }
15
+ export interface NexusAutomationTargetCycleRecord {
16
+ id: string;
17
+ projectId: string;
18
+ targetId: string | null;
19
+ runId: string | null;
20
+ status: NexusAutomationTargetCycleStatus;
21
+ startedAt: string;
22
+ finishedAt: string | null;
23
+ objective: string | null;
24
+ summary: string | null;
25
+ eligibleWorkItemCount: number | null;
26
+ workItems: NexusAutomationTargetCycleWorkItem[];
27
+ blockers: string[];
28
+ notes: string[];
29
+ nextCycleNotBefore: string | null;
30
+ }
31
+ export interface NexusAutomationTargetCycleRecordInput {
32
+ id?: string;
33
+ projectId: string;
34
+ targetId?: string | null;
35
+ runId?: string | null;
36
+ status: NexusAutomationTargetCycleStatus;
37
+ startedAt?: string;
38
+ finishedAt?: string | null;
39
+ objective?: string | null;
40
+ summary?: string | null;
41
+ eligibleWorkItemCount?: number | null;
42
+ workItems?: NexusAutomationTargetCycleWorkItemInput[];
43
+ blockers?: string[];
44
+ notes?: string[];
45
+ nextCycleNotBefore?: string | null;
46
+ }
47
+ export interface NexusAutomationTargetCycleWorkItemInput {
48
+ componentId?: string | null;
49
+ id: string;
50
+ title?: string | null;
51
+ status?: WorkStatus | null;
52
+ cycleStatus?: NexusAutomationTargetCycleWorkItemStatus | null;
53
+ agentProfileId?: string | null;
54
+ notes?: string | null;
55
+ }
56
+ export interface NexusAutomationTargetCycleLedger {
57
+ version: 1;
58
+ cycles: NexusAutomationTargetCycleRecord[];
59
+ updatedAt: string | null;
60
+ }
61
+ export interface AppendNexusAutomationTargetCycleRecordOptions {
62
+ projectRoot: string;
63
+ config: NexusAutomationConfig;
64
+ record: NexusAutomationTargetCycleRecordInput;
65
+ now?: Date | string;
66
+ }
67
+ export interface NexusAutomationTargetCycleSummary {
68
+ ledgerPath: string;
69
+ cycleCount: number;
70
+ activeCycleCount: number;
71
+ completedCycleCount: number;
72
+ blockedCycleCount: number;
73
+ failedCycleCount: number;
74
+ skippedCycleCount: number;
75
+ lastCycle: NexusAutomationTargetCycleRecord | null;
76
+ }
77
+ export declare class NexusAutomationTargetCycleError extends Error {
78
+ constructor(message: string);
79
+ }
80
+ export declare function nexusAutomationTargetCycleLedgerPath(projectRoot: string, config: NexusAutomationConfig): string;
81
+ export declare function emptyNexusAutomationTargetCycleLedger(): NexusAutomationTargetCycleLedger;
82
+ export declare function readNexusAutomationTargetCycleLedger(projectRoot: string, config: NexusAutomationConfig): NexusAutomationTargetCycleLedger;
83
+ export declare function writeNexusAutomationTargetCycleLedger(projectRoot: string, config: NexusAutomationConfig, ledger: NexusAutomationTargetCycleLedger): string;
84
+ export declare function appendNexusAutomationTargetCycleRecord(options: AppendNexusAutomationTargetCycleRecordOptions): NexusAutomationTargetCycleLedger;
85
+ export declare function summarizeNexusAutomationTargetCycles(options: {
86
+ projectRoot: string;
87
+ config: NexusAutomationConfig;
88
+ }): NexusAutomationTargetCycleSummary;
89
+ export declare function normalizeNexusAutomationTargetCycleLedger(value: unknown): NexusAutomationTargetCycleLedger;
90
+ export declare function generateNexusAutomationTargetCycleId(now?: Date | string): string;