@lumenflow/core 1.0.0 → 1.3.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/dist/arg-parser.js +31 -1
- package/dist/backlog-generator.js +1 -1
- package/dist/backlog-sync-validator.js +3 -3
- package/dist/branch-check.d.ts +21 -0
- package/dist/branch-check.js +77 -0
- package/dist/cli/is-agent-branch.d.ts +11 -0
- package/dist/cli/is-agent-branch.js +15 -0
- package/dist/code-paths-overlap.js +2 -2
- package/dist/error-handler.d.ts +1 -0
- package/dist/error-handler.js +4 -1
- package/dist/git-adapter.d.ts +16 -0
- package/dist/git-adapter.js +23 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/lane-checker.d.ts +36 -3
- package/dist/lane-checker.js +128 -17
- package/dist/lane-inference.js +3 -4
- package/dist/lumenflow-config-schema.d.ts +125 -0
- package/dist/lumenflow-config-schema.js +76 -0
- package/dist/orchestration-rules.d.ts +1 -1
- package/dist/orchestration-rules.js +2 -2
- package/dist/path-classifiers.d.ts +1 -1
- package/dist/path-classifiers.js +1 -1
- package/dist/rebase-artifact-cleanup.d.ts +17 -0
- package/dist/rebase-artifact-cleanup.js +49 -8
- package/dist/spawn-strategy.d.ts +53 -0
- package/dist/spawn-strategy.js +106 -0
- package/dist/stamp-utils.d.ts +10 -0
- package/dist/stamp-utils.js +17 -19
- package/dist/token-counter.js +2 -2
- package/dist/wu-consistency-checker.js +5 -5
- package/dist/wu-constants.d.ts +21 -3
- package/dist/wu-constants.js +28 -3
- package/dist/wu-done-branch-utils.d.ts +10 -0
- package/dist/wu-done-branch-utils.js +31 -0
- package/dist/wu-done-cleanup.d.ts +8 -0
- package/dist/wu-done-cleanup.js +122 -0
- package/dist/wu-done-docs-only.d.ts +20 -0
- package/dist/wu-done-docs-only.js +65 -0
- package/dist/wu-done-errors.d.ts +17 -0
- package/dist/wu-done-errors.js +24 -0
- package/dist/wu-done-inputs.d.ts +12 -0
- package/dist/wu-done-inputs.js +51 -0
- package/dist/wu-done-metadata.d.ts +100 -0
- package/dist/wu-done-metadata.js +193 -0
- package/dist/wu-done-paths.d.ts +69 -0
- package/dist/wu-done-paths.js +237 -0
- package/dist/wu-done-preflight.d.ts +48 -0
- package/dist/wu-done-preflight.js +185 -0
- package/dist/wu-done-validation.d.ts +82 -0
- package/dist/wu-done-validation.js +340 -0
- package/dist/wu-done-validators.d.ts +13 -409
- package/dist/wu-done-validators.js +9 -1225
- package/dist/wu-done-worktree.d.ts +0 -1
- package/dist/wu-done-worktree.js +12 -30
- package/dist/wu-schema.js +1 -3
- package/dist/wu-spawn-skills.d.ts +19 -0
- package/dist/wu-spawn-skills.js +148 -0
- package/dist/wu-spawn.d.ts +17 -4
- package/dist/wu-spawn.js +99 -176
- package/dist/wu-validation.d.ts +1 -0
- package/dist/wu-validation.js +21 -1
- package/dist/wu-validator.d.ts +51 -0
- package/dist/wu-validator.js +108 -0
- package/package.json +11 -8
package/dist/lane-checker.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { existsSync, readFileSync } from 'node:fs';
|
|
9
9
|
import path from 'node:path';
|
|
10
|
-
import
|
|
10
|
+
import { parseYAML } from './wu-yaml.js';
|
|
11
11
|
import { getSubLanesForParent } from './lane-inference.js';
|
|
12
12
|
import { createError, ErrorCodes } from './error-handler.js';
|
|
13
13
|
import { isInProgressHeader, WU_LINK_PATTERN } from './constants/backlog-patterns.js';
|
|
@@ -48,7 +48,7 @@ function hasSubLaneTaxonomy(parent) {
|
|
|
48
48
|
}
|
|
49
49
|
try {
|
|
50
50
|
const taxonomyContent = readFileSync(taxonomyPath, { encoding: 'utf-8' });
|
|
51
|
-
const taxonomy =
|
|
51
|
+
const taxonomy = parseYAML(taxonomyContent);
|
|
52
52
|
// Check if parent exists as top-level key in taxonomy
|
|
53
53
|
const normalizedParent = parent.trim();
|
|
54
54
|
return Object.keys(taxonomy).some((key) => key.toLowerCase().trim() === normalizedParent.toLowerCase());
|
|
@@ -71,7 +71,7 @@ function isValidSubLane(parent, subdomain) {
|
|
|
71
71
|
}
|
|
72
72
|
try {
|
|
73
73
|
const taxonomyContent = readFileSync(taxonomyPath, { encoding: 'utf-8' });
|
|
74
|
-
const taxonomy =
|
|
74
|
+
const taxonomy = parseYAML(taxonomyContent);
|
|
75
75
|
// Find parent key (case-insensitive)
|
|
76
76
|
const normalizedParent = parent.trim().toLowerCase();
|
|
77
77
|
const parentKey = Object.keys(taxonomy).find((key) => key.toLowerCase().trim() === normalizedParent);
|
|
@@ -188,6 +188,10 @@ export function validateLaneFormat(lane, configPath = null, options = {}) {
|
|
|
188
188
|
}
|
|
189
189
|
/**
|
|
190
190
|
* Check if a parent lane exists in LumenFlow config
|
|
191
|
+
*
|
|
192
|
+
* WU-1022: Updated to support lanes.definitions with full "Parent: Sublane" format.
|
|
193
|
+
* When lanes.definitions exists, parent lanes are extracted from full lane names.
|
|
194
|
+
*
|
|
191
195
|
* @param {string} parent - Parent lane name to check
|
|
192
196
|
* @param {string} configPath - Path to config file (optional)
|
|
193
197
|
* @returns {boolean} True if parent lane exists
|
|
@@ -206,16 +210,26 @@ function isValidParentLane(parent, configPath = null) {
|
|
|
206
210
|
});
|
|
207
211
|
}
|
|
208
212
|
const configContent = readFileSync(resolvedConfigPath, { encoding: 'utf-8' });
|
|
209
|
-
const config =
|
|
210
|
-
// Extract all lane names - handle
|
|
213
|
+
const config = parseYAML(configContent);
|
|
214
|
+
// Extract all lane names - handle multiple config formats
|
|
211
215
|
const allLanes = [];
|
|
216
|
+
const parentLanes = new Set();
|
|
212
217
|
if (config.lanes) {
|
|
213
218
|
if (Array.isArray(config.lanes)) {
|
|
214
219
|
// Flat array format: lanes: [{name: "Core"}, {name: "CLI"}, ...]
|
|
215
220
|
allLanes.push(...config.lanes.map((l) => l.name));
|
|
216
221
|
}
|
|
217
222
|
else {
|
|
218
|
-
//
|
|
223
|
+
// WU-1022: New format with lanes.definitions containing full "Parent: Sublane" names
|
|
224
|
+
if (config.lanes.definitions) {
|
|
225
|
+
for (const lane of config.lanes.definitions) {
|
|
226
|
+
allLanes.push(lane.name);
|
|
227
|
+
// Extract parent from full lane name for parent validation
|
|
228
|
+
const extractedParent = extractParent(lane.name);
|
|
229
|
+
parentLanes.add(extractedParent.toLowerCase().trim());
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
// Legacy nested format: lanes: {engineering: [...], business: [...]}
|
|
219
233
|
if (config.lanes.engineering) {
|
|
220
234
|
allLanes.push(...config.lanes.engineering.map((l) => l.name));
|
|
221
235
|
}
|
|
@@ -226,16 +240,86 @@ function isValidParentLane(parent, configPath = null) {
|
|
|
226
240
|
}
|
|
227
241
|
// Case-insensitive comparison
|
|
228
242
|
const normalizedParent = parent.toLowerCase().trim();
|
|
243
|
+
// WU-1022: If we have extracted parent lanes (from full lane names), check against those
|
|
244
|
+
if (parentLanes.size > 0) {
|
|
245
|
+
return parentLanes.has(normalizedParent);
|
|
246
|
+
}
|
|
247
|
+
// Legacy: check against direct lane names
|
|
229
248
|
return allLanes.some((lane) => lane.toLowerCase().trim() === normalizedParent);
|
|
230
249
|
}
|
|
250
|
+
/** WU-1016: Default WIP limit when not specified in config */
|
|
251
|
+
const DEFAULT_WIP_LIMIT = 1;
|
|
231
252
|
/**
|
|
232
|
-
*
|
|
253
|
+
* WU-1016: Get WIP limit for a lane from config
|
|
254
|
+
*
|
|
255
|
+
* Reads the wip_limit field from .lumenflow.config.yaml for the specified lane.
|
|
256
|
+
* Returns DEFAULT_WIP_LIMIT (1) if the lane is not found or wip_limit is not specified.
|
|
257
|
+
*
|
|
258
|
+
* @param {string} lane - Lane name (e.g., "Core", "CLI")
|
|
259
|
+
* @param {GetWipLimitOptions} options - Options including configPath for testing
|
|
260
|
+
* @returns {number} The WIP limit for the lane (default: 1)
|
|
261
|
+
*/
|
|
262
|
+
export function getWipLimitForLane(lane, options = {}) {
|
|
263
|
+
// Determine config path
|
|
264
|
+
let resolvedConfigPath = options.configPath;
|
|
265
|
+
if (!resolvedConfigPath) {
|
|
266
|
+
const projectRoot = findProjectRoot();
|
|
267
|
+
resolvedConfigPath = path.join(projectRoot, CONFIG_FILES.LUMENFLOW_CONFIG);
|
|
268
|
+
}
|
|
269
|
+
// Check if config file exists
|
|
270
|
+
if (!existsSync(resolvedConfigPath)) {
|
|
271
|
+
return DEFAULT_WIP_LIMIT;
|
|
272
|
+
}
|
|
273
|
+
try {
|
|
274
|
+
const configContent = readFileSync(resolvedConfigPath, { encoding: 'utf-8' });
|
|
275
|
+
const config = parseYAML(configContent);
|
|
276
|
+
if (!config.lanes) {
|
|
277
|
+
return DEFAULT_WIP_LIMIT;
|
|
278
|
+
}
|
|
279
|
+
// Normalize lane name for case-insensitive comparison
|
|
280
|
+
const normalizedLane = lane.toLowerCase().trim();
|
|
281
|
+
// Extract all lanes with their wip_limit
|
|
282
|
+
let allLanes = [];
|
|
283
|
+
if (Array.isArray(config.lanes)) {
|
|
284
|
+
// Flat array format: lanes: [{name: "Core", wip_limit: 2}, ...]
|
|
285
|
+
allLanes = config.lanes;
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
// Nested format: lanes: {engineering: [...], business: [...]}
|
|
289
|
+
if (config.lanes.engineering) {
|
|
290
|
+
allLanes.push(...config.lanes.engineering);
|
|
291
|
+
}
|
|
292
|
+
if (config.lanes.business) {
|
|
293
|
+
allLanes.push(...config.lanes.business);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
// Find matching lane (case-insensitive)
|
|
297
|
+
const matchingLane = allLanes.find((l) => l.name.toLowerCase().trim() === normalizedLane);
|
|
298
|
+
if (!matchingLane) {
|
|
299
|
+
return DEFAULT_WIP_LIMIT;
|
|
300
|
+
}
|
|
301
|
+
// Return wip_limit if specified, otherwise default
|
|
302
|
+
return matchingLane.wip_limit ?? DEFAULT_WIP_LIMIT;
|
|
303
|
+
}
|
|
304
|
+
catch {
|
|
305
|
+
// If config parsing fails, return default
|
|
306
|
+
return DEFAULT_WIP_LIMIT;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Check if a lane is free (in_progress WU count is below wip_limit)
|
|
311
|
+
*
|
|
312
|
+
* WU-1016: Now respects configurable wip_limit per lane from .lumenflow.config.yaml.
|
|
313
|
+
* Lane is considered "free" if current in_progress count < wip_limit.
|
|
314
|
+
* Default wip_limit is 1 if not specified in config (backward compatible).
|
|
315
|
+
*
|
|
233
316
|
* @param {string} statusPath - Path to status.md
|
|
234
317
|
* @param {string} lane - Lane name (e.g., "Operations", "Intelligence")
|
|
235
318
|
* @param {string} wuid - WU ID being claimed (e.g., "WU-419")
|
|
236
|
-
* @
|
|
319
|
+
* @param {CheckLaneFreeOptions} options - Options including configPath for testing
|
|
320
|
+
* @returns {{ free: boolean, occupiedBy: string | null, error: string | null, inProgressWUs?: string[], wipLimit?: number, currentCount?: number }}
|
|
237
321
|
*/
|
|
238
|
-
export function checkLaneFree(statusPath, lane, wuid) {
|
|
322
|
+
export function checkLaneFree(statusPath, lane, wuid, options = {}) {
|
|
239
323
|
/** Section heading marker for H2 headings */
|
|
240
324
|
const SECTION_HEADING_PREFIX = '## ';
|
|
241
325
|
try {
|
|
@@ -264,19 +348,38 @@ export function checkLaneFree(statusPath, lane, wuid) {
|
|
|
264
348
|
endIdx = inProgressIdx + 1 + endIdx;
|
|
265
349
|
// Extract WU links from In Progress section
|
|
266
350
|
const section = lines.slice(inProgressIdx + 1, endIdx).join(STRING_LITERALS.NEWLINE);
|
|
351
|
+
// WU-1016: Get WIP limit for this lane from config
|
|
352
|
+
const wipLimit = getWipLimitForLane(lane, { configPath: options.configPath });
|
|
267
353
|
// Check for "No items" marker
|
|
268
354
|
if (section.includes(NO_ITEMS_MARKER)) {
|
|
269
|
-
return {
|
|
355
|
+
return {
|
|
356
|
+
free: true,
|
|
357
|
+
occupiedBy: null,
|
|
358
|
+
error: null,
|
|
359
|
+
inProgressWUs: [],
|
|
360
|
+
wipLimit,
|
|
361
|
+
currentCount: 0,
|
|
362
|
+
};
|
|
270
363
|
}
|
|
271
364
|
// Extract WU IDs from links like [WU-334 — Title](wu/WU-334.yaml)
|
|
272
365
|
WU_LINK_PATTERN.lastIndex = 0; // Reset global regex state
|
|
273
366
|
const matches = [...section.matchAll(WU_LINK_PATTERN)];
|
|
274
367
|
if (matches.length === 0) {
|
|
275
|
-
return {
|
|
368
|
+
return {
|
|
369
|
+
free: true,
|
|
370
|
+
occupiedBy: null,
|
|
371
|
+
error: null,
|
|
372
|
+
inProgressWUs: [],
|
|
373
|
+
wipLimit,
|
|
374
|
+
currentCount: 0,
|
|
375
|
+
};
|
|
276
376
|
}
|
|
277
377
|
// Get project root from statusPath (docs/04-operations/tasks/status.md)
|
|
278
378
|
// Use path.dirname 4 times: status.md -> tasks -> 04-operations -> docs -> root
|
|
279
379
|
const projectRoot = path.dirname(path.dirname(path.dirname(path.dirname(statusPath))));
|
|
380
|
+
// WU-1016: Collect all WUs in the target lane
|
|
381
|
+
const inProgressWUs = [];
|
|
382
|
+
const targetLane = lane.toString().trim().toLowerCase();
|
|
280
383
|
for (const match of matches) {
|
|
281
384
|
const activeWuid = match[1]; // e.g., "WU-334"
|
|
282
385
|
// Skip if it's the same WU we're trying to claim (shouldn't happen, but be safe)
|
|
@@ -290,17 +393,16 @@ export function checkLaneFree(statusPath, lane, wuid) {
|
|
|
290
393
|
}
|
|
291
394
|
try {
|
|
292
395
|
const wuContent = readFileSync(wuPath, { encoding: 'utf-8' });
|
|
293
|
-
const wuDoc =
|
|
396
|
+
const wuDoc = parseYAML(wuContent);
|
|
294
397
|
if (!wuDoc || !wuDoc.lane) {
|
|
295
398
|
console.warn(`${PREFIX} Warning: ${activeWuid} has no lane field`);
|
|
296
399
|
continue;
|
|
297
400
|
}
|
|
298
401
|
// Normalize lane names for comparison (case-insensitive, trim whitespace)
|
|
299
402
|
const activeLane = wuDoc.lane.toString().trim().toLowerCase();
|
|
300
|
-
const targetLane = lane.toString().trim().toLowerCase();
|
|
301
403
|
if (activeLane === targetLane) {
|
|
302
|
-
//
|
|
303
|
-
|
|
404
|
+
// WU-1016: Add to list of in-progress WUs in this lane
|
|
405
|
+
inProgressWUs.push(activeWuid);
|
|
304
406
|
}
|
|
305
407
|
}
|
|
306
408
|
catch (e) {
|
|
@@ -309,8 +411,17 @@ export function checkLaneFree(statusPath, lane, wuid) {
|
|
|
309
411
|
continue;
|
|
310
412
|
}
|
|
311
413
|
}
|
|
312
|
-
//
|
|
313
|
-
|
|
414
|
+
// WU-1016: Check if lane is free based on WIP limit
|
|
415
|
+
const currentCount = inProgressWUs.length;
|
|
416
|
+
const isFree = currentCount < wipLimit;
|
|
417
|
+
return {
|
|
418
|
+
free: isFree,
|
|
419
|
+
occupiedBy: isFree ? null : inProgressWUs[0] || null,
|
|
420
|
+
error: null,
|
|
421
|
+
inProgressWUs,
|
|
422
|
+
wipLimit,
|
|
423
|
+
currentCount,
|
|
424
|
+
};
|
|
314
425
|
}
|
|
315
426
|
catch (error) {
|
|
316
427
|
const errMessage = error instanceof Error ? error.message : String(error);
|
package/dist/lane-inference.js
CHANGED
|
@@ -12,12 +12,12 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { readFileSync, existsSync } from 'node:fs';
|
|
14
14
|
import path from 'node:path';
|
|
15
|
-
import { fileURLToPath } from 'node:url';
|
|
16
15
|
import YAML from 'yaml'; // Modern YAML library (not js-yaml)
|
|
17
16
|
import micromatch from 'micromatch'; // Industry-standard glob matching (CommonJS)
|
|
18
17
|
import { extractParent } from './lane-checker.js'; // Shared utility (WU-1137: consolidation)
|
|
19
18
|
import { createError, ErrorCodes } from './error-handler.js';
|
|
20
19
|
import { WEIGHTS, CONFIDENCE } from './wu-validation-constants.js';
|
|
20
|
+
import { findProjectRoot } from './lumenflow-config.js';
|
|
21
21
|
/**
|
|
22
22
|
* Load lane inference config from project root
|
|
23
23
|
* @param {string|null} configPath - Optional path to config file (defaults to project root)
|
|
@@ -26,9 +26,8 @@ import { WEIGHTS, CONFIDENCE } from './wu-validation-constants.js';
|
|
|
26
26
|
*/
|
|
27
27
|
function loadConfig(configPath = null) {
|
|
28
28
|
if (!configPath) {
|
|
29
|
-
//
|
|
30
|
-
const
|
|
31
|
-
const projectRoot = path.resolve(currentDir, '../..');
|
|
29
|
+
// Use findProjectRoot() to locate config from cwd
|
|
30
|
+
const projectRoot = findProjectRoot();
|
|
32
31
|
configPath = path.join(projectRoot, '.lumenflow.lane-inference.yaml');
|
|
33
32
|
}
|
|
34
33
|
if (!existsSync(configPath)) {
|
|
@@ -52,6 +52,7 @@ export declare const GitConfigSchema: z.ZodObject<{
|
|
|
52
52
|
maxBranchDrift: z.ZodDefault<z.ZodNumber>;
|
|
53
53
|
branchDriftWarning: z.ZodDefault<z.ZodNumber>;
|
|
54
54
|
branchDriftInfo: z.ZodDefault<z.ZodNumber>;
|
|
55
|
+
agentBranchPatterns: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
55
56
|
}, z.core.$strip>;
|
|
56
57
|
/**
|
|
57
58
|
* WU (Work Unit) configuration
|
|
@@ -91,12 +92,84 @@ export declare const UiConfigSchema: z.ZodObject<{
|
|
|
91
92
|
statusPreviewLines: z.ZodDefault<z.ZodNumber>;
|
|
92
93
|
readinessBoxWidth: z.ZodDefault<z.ZodNumber>;
|
|
93
94
|
}, z.core.$strip>;
|
|
95
|
+
/**
|
|
96
|
+
* YAML serialization configuration
|
|
97
|
+
*/
|
|
94
98
|
/**
|
|
95
99
|
* YAML serialization configuration
|
|
96
100
|
*/
|
|
97
101
|
export declare const YamlConfigSchema: z.ZodObject<{
|
|
98
102
|
lineWidth: z.ZodDefault<z.ZodNumber>;
|
|
99
103
|
}, z.core.$strip>;
|
|
104
|
+
/**
|
|
105
|
+
* Methodology defaults (agent-facing project defaults)
|
|
106
|
+
*/
|
|
107
|
+
export declare const DEFAULT_METHODOLOGY_PRINCIPLES: string[];
|
|
108
|
+
export declare const MethodologyDefaultsSchema: z.ZodObject<{
|
|
109
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
110
|
+
enforcement: z.ZodDefault<z.ZodEnum<{
|
|
111
|
+
required: "required";
|
|
112
|
+
recommended: "recommended";
|
|
113
|
+
}>>;
|
|
114
|
+
principles: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
115
|
+
notes: z.ZodOptional<z.ZodString>;
|
|
116
|
+
}, z.core.$strip>;
|
|
117
|
+
/**
|
|
118
|
+
* Client-specific blocks (agent-facing spawn blocks)
|
|
119
|
+
*/
|
|
120
|
+
export declare const ClientBlockSchema: z.ZodObject<{
|
|
121
|
+
title: z.ZodString;
|
|
122
|
+
content: z.ZodString;
|
|
123
|
+
}, z.core.$strip>;
|
|
124
|
+
/**
|
|
125
|
+
* Client-specific skills guidance
|
|
126
|
+
*/
|
|
127
|
+
export declare const ClientSkillsSchema: z.ZodObject<{
|
|
128
|
+
instructions: z.ZodOptional<z.ZodString>;
|
|
129
|
+
recommended: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
130
|
+
}, z.core.$strip>;
|
|
131
|
+
/**
|
|
132
|
+
* Client configuration (per-client settings)
|
|
133
|
+
*/
|
|
134
|
+
export declare const ClientConfigSchema: z.ZodObject<{
|
|
135
|
+
preamble: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodBoolean]>>;
|
|
136
|
+
skillsDir: z.ZodOptional<z.ZodString>;
|
|
137
|
+
blocks: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
138
|
+
title: z.ZodString;
|
|
139
|
+
content: z.ZodString;
|
|
140
|
+
}, z.core.$strip>>>;
|
|
141
|
+
skills: z.ZodOptional<z.ZodObject<{
|
|
142
|
+
instructions: z.ZodOptional<z.ZodString>;
|
|
143
|
+
recommended: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
144
|
+
}, z.core.$strip>>;
|
|
145
|
+
}, z.core.$strip>;
|
|
146
|
+
/**
|
|
147
|
+
* Agents configuration
|
|
148
|
+
*/
|
|
149
|
+
export declare const AgentsConfigSchema: z.ZodObject<{
|
|
150
|
+
defaultClient: z.ZodDefault<z.ZodString>;
|
|
151
|
+
clients: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
152
|
+
preamble: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodBoolean]>>;
|
|
153
|
+
skillsDir: z.ZodOptional<z.ZodString>;
|
|
154
|
+
blocks: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
155
|
+
title: z.ZodString;
|
|
156
|
+
content: z.ZodString;
|
|
157
|
+
}, z.core.$strip>>>;
|
|
158
|
+
skills: z.ZodOptional<z.ZodObject<{
|
|
159
|
+
instructions: z.ZodOptional<z.ZodString>;
|
|
160
|
+
recommended: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
161
|
+
}, z.core.$strip>>;
|
|
162
|
+
}, z.core.$strip>>>;
|
|
163
|
+
methodology: z.ZodDefault<z.ZodObject<{
|
|
164
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
165
|
+
enforcement: z.ZodDefault<z.ZodEnum<{
|
|
166
|
+
required: "required";
|
|
167
|
+
recommended: "recommended";
|
|
168
|
+
}>>;
|
|
169
|
+
principles: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
170
|
+
notes: z.ZodOptional<z.ZodString>;
|
|
171
|
+
}, z.core.$strip>>;
|
|
172
|
+
}, z.core.$strip>;
|
|
100
173
|
/**
|
|
101
174
|
* Complete LumenFlow configuration schema
|
|
102
175
|
*/
|
|
@@ -138,6 +211,7 @@ export declare const LumenFlowConfigSchema: z.ZodObject<{
|
|
|
138
211
|
maxBranchDrift: z.ZodDefault<z.ZodNumber>;
|
|
139
212
|
branchDriftWarning: z.ZodDefault<z.ZodNumber>;
|
|
140
213
|
branchDriftInfo: z.ZodDefault<z.ZodNumber>;
|
|
214
|
+
agentBranchPatterns: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
141
215
|
}, z.core.$strip>>;
|
|
142
216
|
wu: z.ZodDefault<z.ZodObject<{
|
|
143
217
|
idPattern: z.ZodDefault<z.ZodString>;
|
|
@@ -168,6 +242,30 @@ export declare const LumenFlowConfigSchema: z.ZodObject<{
|
|
|
168
242
|
yaml: z.ZodDefault<z.ZodObject<{
|
|
169
243
|
lineWidth: z.ZodDefault<z.ZodNumber>;
|
|
170
244
|
}, z.core.$strip>>;
|
|
245
|
+
agents: z.ZodDefault<z.ZodObject<{
|
|
246
|
+
defaultClient: z.ZodDefault<z.ZodString>;
|
|
247
|
+
clients: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
248
|
+
preamble: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodBoolean]>>;
|
|
249
|
+
skillsDir: z.ZodOptional<z.ZodString>;
|
|
250
|
+
blocks: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
251
|
+
title: z.ZodString;
|
|
252
|
+
content: z.ZodString;
|
|
253
|
+
}, z.core.$strip>>>;
|
|
254
|
+
skills: z.ZodOptional<z.ZodObject<{
|
|
255
|
+
instructions: z.ZodOptional<z.ZodString>;
|
|
256
|
+
recommended: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
257
|
+
}, z.core.$strip>>;
|
|
258
|
+
}, z.core.$strip>>>;
|
|
259
|
+
methodology: z.ZodDefault<z.ZodObject<{
|
|
260
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
261
|
+
enforcement: z.ZodDefault<z.ZodEnum<{
|
|
262
|
+
required: "required";
|
|
263
|
+
recommended: "recommended";
|
|
264
|
+
}>>;
|
|
265
|
+
principles: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
266
|
+
notes: z.ZodOptional<z.ZodString>;
|
|
267
|
+
}, z.core.$strip>>;
|
|
268
|
+
}, z.core.$strip>>;
|
|
171
269
|
}, z.core.$strip>;
|
|
172
270
|
/**
|
|
173
271
|
* TypeScript types inferred from schemas
|
|
@@ -180,6 +278,11 @@ export type GatesConfig = z.infer<typeof GatesConfigSchema>;
|
|
|
180
278
|
export type MemoryConfig = z.infer<typeof MemoryConfigSchema>;
|
|
181
279
|
export type UiConfig = z.infer<typeof UiConfigSchema>;
|
|
182
280
|
export type YamlConfig = z.infer<typeof YamlConfigSchema>;
|
|
281
|
+
export type MethodologyDefaults = z.infer<typeof MethodologyDefaultsSchema>;
|
|
282
|
+
export type ClientBlock = z.infer<typeof ClientBlockSchema>;
|
|
283
|
+
export type ClientSkills = z.infer<typeof ClientSkillsSchema>;
|
|
284
|
+
export type ClientConfig = z.infer<typeof ClientConfigSchema>;
|
|
285
|
+
export type AgentsConfig = z.infer<typeof AgentsConfigSchema>;
|
|
183
286
|
export type LumenFlowConfig = z.infer<typeof LumenFlowConfigSchema>;
|
|
184
287
|
/**
|
|
185
288
|
* Validate configuration data
|
|
@@ -225,6 +328,7 @@ export declare function validateConfig(data: unknown): z.ZodSafeParseResult<{
|
|
|
225
328
|
maxBranchDrift: number;
|
|
226
329
|
branchDriftWarning: number;
|
|
227
330
|
branchDriftInfo: number;
|
|
331
|
+
agentBranchPatterns: string[];
|
|
228
332
|
};
|
|
229
333
|
wu: {
|
|
230
334
|
idPattern: string;
|
|
@@ -255,6 +359,27 @@ export declare function validateConfig(data: unknown): z.ZodSafeParseResult<{
|
|
|
255
359
|
yaml: {
|
|
256
360
|
lineWidth: number;
|
|
257
361
|
};
|
|
362
|
+
agents: {
|
|
363
|
+
defaultClient: string;
|
|
364
|
+
clients: Record<string, {
|
|
365
|
+
blocks: {
|
|
366
|
+
title: string;
|
|
367
|
+
content: string;
|
|
368
|
+
}[];
|
|
369
|
+
preamble?: string | boolean;
|
|
370
|
+
skillsDir?: string;
|
|
371
|
+
skills?: {
|
|
372
|
+
recommended: string[];
|
|
373
|
+
instructions?: string;
|
|
374
|
+
};
|
|
375
|
+
}>;
|
|
376
|
+
methodology: {
|
|
377
|
+
enabled: boolean;
|
|
378
|
+
enforcement: "required" | "recommended";
|
|
379
|
+
principles: string[];
|
|
380
|
+
notes?: string;
|
|
381
|
+
};
|
|
382
|
+
};
|
|
258
383
|
}>;
|
|
259
384
|
/**
|
|
260
385
|
* Parse configuration with defaults
|
|
@@ -83,6 +83,13 @@ export const GitConfigSchema = z.object({
|
|
|
83
83
|
branchDriftWarning: z.number().int().positive().default(15),
|
|
84
84
|
/** Info threshold for branch drift */
|
|
85
85
|
branchDriftInfo: z.number().int().positive().default(10),
|
|
86
|
+
/**
|
|
87
|
+
* Agent branch patterns that bypass worktree requirements.
|
|
88
|
+
* Branches matching these glob patterns can work in the main checkout.
|
|
89
|
+
* Default: ['agent/*'] - narrow default, add vendor patterns as needed.
|
|
90
|
+
* Protected branches (mainBranch + 'master') are NEVER bypassed.
|
|
91
|
+
*/
|
|
92
|
+
agentBranchPatterns: z.array(z.string()).default(['agent/*']),
|
|
86
93
|
});
|
|
87
94
|
/**
|
|
88
95
|
* WU (Work Unit) configuration
|
|
@@ -148,6 +155,9 @@ export const UiConfigSchema = z.object({
|
|
|
148
155
|
/** Readiness box width (default: 50) */
|
|
149
156
|
readinessBoxWidth: z.number().int().positive().default(50),
|
|
150
157
|
});
|
|
158
|
+
/**
|
|
159
|
+
* YAML serialization configuration
|
|
160
|
+
*/
|
|
151
161
|
/**
|
|
152
162
|
* YAML serialization configuration
|
|
153
163
|
*/
|
|
@@ -155,6 +165,70 @@ export const YamlConfigSchema = z.object({
|
|
|
155
165
|
/** Line width for YAML output (default: 100, -1 for no wrap) */
|
|
156
166
|
lineWidth: z.number().int().default(100),
|
|
157
167
|
});
|
|
168
|
+
/**
|
|
169
|
+
* Methodology defaults (agent-facing project defaults)
|
|
170
|
+
*/
|
|
171
|
+
export const DEFAULT_METHODOLOGY_PRINCIPLES = [
|
|
172
|
+
'TDD',
|
|
173
|
+
'Hexagonal Architecture',
|
|
174
|
+
'SOLID',
|
|
175
|
+
'DRY',
|
|
176
|
+
'YAGNI',
|
|
177
|
+
'KISS',
|
|
178
|
+
'Library-First',
|
|
179
|
+
];
|
|
180
|
+
export const MethodologyDefaultsSchema = z.object({
|
|
181
|
+
/** Enable or disable project defaults output */
|
|
182
|
+
enabled: z.boolean().default(true),
|
|
183
|
+
/** Whether defaults are required or recommended */
|
|
184
|
+
enforcement: z.enum(['required', 'recommended']).default('required'),
|
|
185
|
+
/** Default methodology principles to apply */
|
|
186
|
+
principles: z.array(z.string()).default(DEFAULT_METHODOLOGY_PRINCIPLES),
|
|
187
|
+
/** Optional notes appended to Project Defaults */
|
|
188
|
+
notes: z.string().optional(),
|
|
189
|
+
});
|
|
190
|
+
/**
|
|
191
|
+
* Client-specific blocks (agent-facing spawn blocks)
|
|
192
|
+
*/
|
|
193
|
+
export const ClientBlockSchema = z.object({
|
|
194
|
+
/** Block title */
|
|
195
|
+
title: z.string(),
|
|
196
|
+
/** Block content (markdown allowed) */
|
|
197
|
+
content: z.string(),
|
|
198
|
+
});
|
|
199
|
+
/**
|
|
200
|
+
* Client-specific skills guidance
|
|
201
|
+
*/
|
|
202
|
+
export const ClientSkillsSchema = z.object({
|
|
203
|
+
/** Optional skills selection guidance text */
|
|
204
|
+
instructions: z.string().optional(),
|
|
205
|
+
/** Recommended skills to load for this client */
|
|
206
|
+
recommended: z.array(z.string()).default([]),
|
|
207
|
+
});
|
|
208
|
+
/**
|
|
209
|
+
* Client configuration (per-client settings)
|
|
210
|
+
*/
|
|
211
|
+
export const ClientConfigSchema = z.object({
|
|
212
|
+
/** Preamble file path (e.g. 'CLAUDE.md') or false to disable */
|
|
213
|
+
preamble: z.union([z.string(), z.boolean()]).optional(),
|
|
214
|
+
/** Skills directory path */
|
|
215
|
+
skillsDir: z.string().optional(),
|
|
216
|
+
/** Client-specific blocks injected into wu:spawn output */
|
|
217
|
+
blocks: z.array(ClientBlockSchema).default([]),
|
|
218
|
+
/** Client-specific skills guidance for wu:spawn */
|
|
219
|
+
skills: ClientSkillsSchema.optional(),
|
|
220
|
+
});
|
|
221
|
+
/**
|
|
222
|
+
* Agents configuration
|
|
223
|
+
*/
|
|
224
|
+
export const AgentsConfigSchema = z.object({
|
|
225
|
+
/** Default client to use if not specified (default: 'claude-code') */
|
|
226
|
+
defaultClient: z.string().default('claude-code'),
|
|
227
|
+
/** Client-specific configurations */
|
|
228
|
+
clients: z.record(z.string(), ClientConfigSchema).default({}),
|
|
229
|
+
/** Project methodology defaults (agent-facing) */
|
|
230
|
+
methodology: MethodologyDefaultsSchema.default(() => MethodologyDefaultsSchema.parse({})),
|
|
231
|
+
});
|
|
158
232
|
/**
|
|
159
233
|
* Complete LumenFlow configuration schema
|
|
160
234
|
*/
|
|
@@ -177,6 +251,8 @@ export const LumenFlowConfigSchema = z.object({
|
|
|
177
251
|
ui: UiConfigSchema.default(() => UiConfigSchema.parse({})),
|
|
178
252
|
/** YAML configuration */
|
|
179
253
|
yaml: YamlConfigSchema.default(() => YamlConfigSchema.parse({})),
|
|
254
|
+
/** Agents configuration */
|
|
255
|
+
agents: AgentsConfigSchema.default(() => AgentsConfigSchema.parse({})),
|
|
180
256
|
});
|
|
181
257
|
/**
|
|
182
258
|
* Validate configuration data
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @module orchestration-rules
|
|
8
8
|
* @see {@link ./domain/orchestration.constants.mjs} - MANDATORY_TRIGGERS patterns
|
|
9
|
-
* @see {@link ../../../
|
|
9
|
+
* @see {@link ../../../docs/04-operations/_frameworks/lumenflow/agent/onboarding/agent-selection-guide.md} - Agent selection rules
|
|
10
10
|
*/
|
|
11
11
|
import { type MandatoryAgentName } from './domain/orchestration.constants.js';
|
|
12
12
|
/**
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @module orchestration-rules
|
|
8
8
|
* @see {@link ./domain/orchestration.constants.mjs} - MANDATORY_TRIGGERS patterns
|
|
9
|
-
* @see {@link ../../../
|
|
9
|
+
* @see {@link ../../../docs/04-operations/_frameworks/lumenflow/agent/onboarding/agent-selection-guide.md} - Agent selection rules
|
|
10
10
|
*/
|
|
11
11
|
import { minimatch } from 'minimatch';
|
|
12
12
|
import { MANDATORY_TRIGGERS } from './domain/orchestration.constants.js';
|
|
@@ -162,7 +162,7 @@ export function buildMandatoryAgentsErrorMessage(wuId, missingAgents, codePaths)
|
|
|
162
162
|
lines.push('To bypass (NOT RECOMMENDED for PHI/auth work):');
|
|
163
163
|
lines.push(' Remove --require-agents flag from wu:done command');
|
|
164
164
|
lines.push('');
|
|
165
|
-
lines.push('See:
|
|
165
|
+
lines.push('See: docs/04-operations/_frameworks/lumenflow/agent/onboarding/agent-selection-guide.md for agent invocation guidance');
|
|
166
166
|
lines.push('='.repeat(70));
|
|
167
167
|
return lines.join('\n');
|
|
168
168
|
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* - Documentation: docs/, ai/, .claude/, README*, CLAUDE*.md
|
|
10
10
|
* - Tooling: tools/, scripts/
|
|
11
11
|
*
|
|
12
|
-
* @see {@link
|
|
12
|
+
* @see {@link ./wu-done-paths.js} - Consumer for detectDocsOnlyByPaths
|
|
13
13
|
*/
|
|
14
14
|
/**
|
|
15
15
|
* Prefixes for paths that should skip web app tests.
|
package/dist/path-classifiers.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* - Documentation: docs/, ai/, .claude/, README*, CLAUDE*.md
|
|
10
10
|
* - Tooling: tools/, scripts/
|
|
11
11
|
*
|
|
12
|
-
* @see {@link
|
|
12
|
+
* @see {@link ./wu-done-paths.js} - Consumer for detectDocsOnlyByPaths
|
|
13
13
|
*/
|
|
14
14
|
/**
|
|
15
15
|
* Prefixes for paths that should skip web app tests.
|
|
@@ -143,3 +143,20 @@ export declare function deduplicateBacklogAfterRebase(worktreePath: any, wuId: a
|
|
|
143
143
|
cleaned: boolean;
|
|
144
144
|
errors: any[];
|
|
145
145
|
}>;
|
|
146
|
+
/**
|
|
147
|
+
* Clean up build artifacts in a worktree (dist folders + tsbuildinfo files)
|
|
148
|
+
*
|
|
149
|
+
* WU-1042: Provide a safe helper for clearing build artifacts that can
|
|
150
|
+
* trigger TS5055 or stale build issues in worktrees.
|
|
151
|
+
*
|
|
152
|
+
* @param {string} worktreePath - Path to the worktree directory
|
|
153
|
+
* @returns {Promise<object>} Cleanup result
|
|
154
|
+
* @returns {string[]} result.distDirectories - Removed dist directories (relative paths)
|
|
155
|
+
* @returns {string[]} result.tsbuildinfoFiles - Removed tsbuildinfo files (relative paths)
|
|
156
|
+
* @returns {number} result.removedCount - Total removed artifacts
|
|
157
|
+
*/
|
|
158
|
+
export declare function cleanupWorktreeBuildArtifacts(worktreePath: any): Promise<{
|
|
159
|
+
distDirectories: string[];
|
|
160
|
+
tsbuildinfoFiles: string[];
|
|
161
|
+
removedCount: number;
|
|
162
|
+
}>;
|