@lumenflow/core 2.3.1 → 2.4.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.
@@ -1,3 +1,4 @@
1
+ import { type OptionValues } from 'commander';
1
2
  /**
2
3
  * Predefined option configurations for WU management scripts.
3
4
  * Each option has: name, flags, description, and optional default.
@@ -19,6 +20,10 @@ interface WUOption {
19
20
  default?: string | boolean | string[];
20
21
  isNegated?: boolean;
21
22
  isRepeatable?: boolean;
23
+ /** Type hint for option parsing (e.g., 'boolean' for flags, 'string' for values) */
24
+ type?: 'boolean' | 'string';
25
+ /** Whether this option is required (used for validation hints) */
26
+ required?: boolean;
22
27
  }
23
28
  export declare const WU_OPTIONS: Record<string, WUOption>;
24
29
  /**
@@ -49,7 +54,14 @@ export declare const WU_CREATE_OPTIONS: Record<string, WUOption>;
49
54
  * console.log(opts.id); // 'WU-123'
50
55
  * console.log(opts.branchOnly); // true
51
56
  */
52
- export declare function createWUParser(config: any): import("commander").OptionValues;
57
+ export declare function createWUParser(config: {
58
+ name: string;
59
+ description: string;
60
+ options?: WUOption[];
61
+ required?: string[];
62
+ allowPositionalId?: boolean;
63
+ version?: string;
64
+ }): OptionValues;
53
65
  /**
54
66
  * Backward-compatible unified argument parser for WU management scripts.
55
67
  * Uses commander internally but maintains the same return format.
@@ -65,5 +77,5 @@ export declare function createWUParser(config: any): import("commander").OptionV
65
77
  * console.log(args.id); // 'WU-123'
66
78
  * console.log(args.branchOnly); // true
67
79
  */
68
- export declare function parseWUArgs(argv: any): import("commander").OptionValues;
80
+ export declare function parseWUArgs(argv: string[]): OptionValues;
69
81
  export {};
@@ -323,12 +323,26 @@ export const WU_OPTIONS = {
323
323
  description: 'Code paths (repeatable)',
324
324
  isRepeatable: true,
325
325
  },
326
+ // WU-1300: Alias for --code-paths (singular form for convenience)
327
+ codePath: {
328
+ name: 'codePath',
329
+ flags: '--code-path <path>',
330
+ description: 'Alias for --code-paths (repeatable)',
331
+ isRepeatable: true,
332
+ },
326
333
  testPathsManual: {
327
334
  name: 'testPathsManual',
328
335
  flags: '--test-paths-manual <tests>',
329
336
  description: 'Manual test descriptions (repeatable)',
330
337
  isRepeatable: true,
331
338
  },
339
+ // WU-1300: Alias for --test-paths-manual (shorter form for convenience)
340
+ manualTest: {
341
+ name: 'manualTest',
342
+ flags: '--manual-test <test>',
343
+ description: 'Alias for --test-paths-manual (repeatable)',
344
+ isRepeatable: true,
345
+ },
332
346
  testPathsUnit: {
333
347
  name: 'testPathsUnit',
334
348
  flags: '--test-paths-unit <paths>',
@@ -562,8 +576,57 @@ export function createWUParser(config) {
562
576
  if (allowPositionalId && program.args.length > 0 && !opts.id) {
563
577
  opts.id = program.args[0];
564
578
  }
579
+ // WU-1300: Merge CLI aliases into their canonical options
580
+ opts = mergeAliasOptions(opts);
565
581
  return opts;
566
582
  }
583
+ /**
584
+ * WU-1300: Option alias mappings (alias -> canonical)
585
+ * These allow users to use shorter/alternative flag names.
586
+ */
587
+ const OPTION_ALIASES = {
588
+ codePath: 'codePaths',
589
+ manualTest: 'testPathsManual',
590
+ };
591
+ /**
592
+ * WU-1300: Merge alias options into their canonical counterparts.
593
+ * Supports both singular aliases (--code-path -> --code-paths)
594
+ * and alternative names (--manual-test -> --test-paths-manual).
595
+ *
596
+ * For repeatable options, values are concatenated.
597
+ * For single-value options, alias value is used if canonical is not set.
598
+ *
599
+ * @param {object} opts - Parsed options from Commander
600
+ * @returns {object} Options with aliases merged into canonical names
601
+ */
602
+ function mergeAliasOptions(opts) {
603
+ const result = { ...opts };
604
+ for (const [alias, canonical] of Object.entries(OPTION_ALIASES)) {
605
+ const aliasValue = result[alias];
606
+ const canonicalValue = result[canonical];
607
+ if (aliasValue !== undefined && aliasValue !== null) {
608
+ // For arrays (repeatable options), concatenate
609
+ if (Array.isArray(aliasValue)) {
610
+ const existingArray = Array.isArray(canonicalValue) ? canonicalValue : [];
611
+ result[canonical] = [...existingArray, ...aliasValue];
612
+ }
613
+ else if (canonicalValue === undefined || canonicalValue === null) {
614
+ // For single values, only use alias if canonical not set
615
+ result[canonical] = aliasValue;
616
+ }
617
+ // Remove the alias from results (clean output)
618
+ // Build new result without the alias key to avoid dynamic delete
619
+ }
620
+ }
621
+ // Remove alias keys from final result
622
+ const finalResult = {};
623
+ for (const [key, value] of Object.entries(result)) {
624
+ if (!(key in OPTION_ALIASES)) {
625
+ finalResult[key] = value;
626
+ }
627
+ }
628
+ return finalResult;
629
+ }
567
630
  /**
568
631
  * Backward-compatible unified argument parser for WU management scripts.
569
632
  * Uses commander internally but maintains the same return format.
@@ -11,7 +11,7 @@
11
11
  import { existsSync, readFileSync } from 'node:fs';
12
12
  import { join } from 'node:path';
13
13
  import { parse as parseYaml } from 'yaml';
14
- import { DIRECTORIES } from '../wu-constants.js';
14
+ import { WU_PATHS } from '../wu-paths.js';
15
15
  /**
16
16
  * Normalize WU ID to uppercase format.
17
17
  */
@@ -24,10 +24,11 @@ function normalizeWuId(id) {
24
24
  }
25
25
  /**
26
26
  * Build the path to WU YAML file.
27
+ * WU-1301: Uses config-based paths instead of hardcoded DIRECTORIES.
27
28
  */
28
29
  function getWuYamlPath(wuId, repoRoot) {
29
30
  const normalizedId = normalizeWuId(wuId);
30
- return join(repoRoot, DIRECTORIES.WU_DIR, `${normalizedId}.yaml`);
31
+ return join(repoRoot, WU_PATHS.WU(normalizedId));
31
32
  }
32
33
  /**
33
34
  * Read WU state from YAML and detect inconsistencies.
@@ -31,7 +31,13 @@ function loadConfig(configPath = null) {
31
31
  configPath = path.join(projectRoot, '.lumenflow.lane-inference.yaml');
32
32
  }
33
33
  if (!existsSync(configPath)) {
34
- throw createError(ErrorCodes.FILE_NOT_FOUND, `Lane inference config not found: ${configPath}\n\nRun WU-906 to create infrastructure files.`, { path: configPath });
34
+ throw createError(ErrorCodes.FILE_NOT_FOUND, `Lane inference config not found: ${configPath}\n\n` +
35
+ `This file defines the lane taxonomy for sub-lane validation.\n\n` +
36
+ `To fix this:\n` +
37
+ ` 1. Generate a lane taxonomy from your codebase:\n` +
38
+ ` pnpm lane:suggest --output .lumenflow.lane-inference.yaml\n\n` +
39
+ ` 2. Or copy from an example project and customize.\n\n` +
40
+ `See: LUMENFLOW.md "Setup Notes" section for details.`, { path: configPath });
35
41
  }
36
42
  try {
37
43
  const configContent = readFileSync(configPath, { encoding: 'utf-8' });
@@ -167,7 +173,7 @@ export function getAllSubLanes(configPath = null) {
167
173
  subLanes.push(`${parentLane}: ${subLane}`);
168
174
  }
169
175
  }
170
- return subLanes.sort();
176
+ return subLanes.sort((a, b) => a.localeCompare(b));
171
177
  }
172
178
  /**
173
179
  * Get valid sub-lanes for a specific parent lane
@@ -35,6 +35,7 @@ export declare const DirectoriesSchema: z.ZodObject<{
35
35
  statusPath: z.ZodDefault<z.ZodString>;
36
36
  skillsDir: z.ZodDefault<z.ZodString>;
37
37
  agentsDir: z.ZodDefault<z.ZodString>;
38
+ plansDir: z.ZodDefault<z.ZodString>;
38
39
  }, z.core.$strip>;
39
40
  /**
40
41
  * Beacon paths configuration (.lumenflow directory structure)
@@ -67,6 +68,7 @@ export declare const GitConfigSchema: z.ZodObject<{
67
68
  maxBranchDrift: z.ZodDefault<z.ZodNumber>;
68
69
  branchDriftWarning: z.ZodDefault<z.ZodNumber>;
69
70
  branchDriftInfo: z.ZodDefault<z.ZodNumber>;
71
+ requireRemote: z.ZodDefault<z.ZodBoolean>;
70
72
  agentBranchPatterns: z.ZodDefault<z.ZodArray<z.ZodString>>;
71
73
  agentBranchPatternsOverride: z.ZodOptional<z.ZodArray<z.ZodString>>;
72
74
  disableAgentPatternRegistry: z.ZodDefault<z.ZodBoolean>;
@@ -329,6 +331,7 @@ export declare const LumenFlowConfigSchema: z.ZodObject<{
329
331
  statusPath: z.ZodDefault<z.ZodString>;
330
332
  skillsDir: z.ZodDefault<z.ZodString>;
331
333
  agentsDir: z.ZodDefault<z.ZodString>;
334
+ plansDir: z.ZodDefault<z.ZodString>;
332
335
  }, z.core.$strip>>;
333
336
  beacon: z.ZodDefault<z.ZodObject<{
334
337
  base: z.ZodDefault<z.ZodString>;
@@ -355,6 +358,7 @@ export declare const LumenFlowConfigSchema: z.ZodObject<{
355
358
  maxBranchDrift: z.ZodDefault<z.ZodNumber>;
356
359
  branchDriftWarning: z.ZodDefault<z.ZodNumber>;
357
360
  branchDriftInfo: z.ZodDefault<z.ZodNumber>;
361
+ requireRemote: z.ZodDefault<z.ZodBoolean>;
358
362
  agentBranchPatterns: z.ZodDefault<z.ZodArray<z.ZodString>>;
359
363
  agentBranchPatternsOverride: z.ZodOptional<z.ZodArray<z.ZodString>>;
360
364
  disableAgentPatternRegistry: z.ZodDefault<z.ZodBoolean>;
@@ -544,6 +548,7 @@ export declare function validateConfig(data: unknown): z.ZodSafeParseResult<{
544
548
  statusPath: string;
545
549
  skillsDir: string;
546
550
  agentsDir: string;
551
+ plansDir: string;
547
552
  };
548
553
  beacon: {
549
554
  base: string;
@@ -570,6 +575,7 @@ export declare function validateConfig(data: unknown): z.ZodSafeParseResult<{
570
575
  maxBranchDrift: number;
571
576
  branchDriftWarning: number;
572
577
  branchDriftInfo: number;
578
+ requireRemote: boolean;
573
579
  agentBranchPatterns: string[];
574
580
  disableAgentPatternRegistry: boolean;
575
581
  agentBranchPatternsOverride?: string[];
@@ -67,6 +67,8 @@ export const DirectoriesSchema = z.object({
67
67
  skillsDir: z.string().default('.claude/skills'),
68
68
  /** Agents directory (default: '.claude/agents') */
69
69
  agentsDir: z.string().default('.claude/agents'),
70
+ /** Plans directory (default: 'docs/04-operations/plans') - WU-1301 */
71
+ plansDir: z.string().default('docs/04-operations/plans'),
70
72
  });
71
73
  /**
72
74
  * Beacon paths configuration (.lumenflow directory structure)
@@ -118,6 +120,25 @@ export const GitConfigSchema = z.object({
118
120
  branchDriftWarning: z.number().int().positive().default(15),
119
121
  /** Info threshold for branch drift */
120
122
  branchDriftInfo: z.number().int().positive().default(10),
123
+ /**
124
+ * WU-1302: Require a remote repository for wu:create and wu:claim.
125
+ * When true (default), operations fail if no remote 'origin' exists.
126
+ * When false, operations can proceed locally without pushing.
127
+ *
128
+ * Use `git.requireRemote: false` for:
129
+ * - Local-only development before remote is set up
130
+ * - Air-gapped environments
131
+ * - Testing/evaluation of LumenFlow
132
+ *
133
+ * @default true
134
+ *
135
+ * @example
136
+ * ```yaml
137
+ * git:
138
+ * requireRemote: false # Allow offline/local mode
139
+ * ```
140
+ */
141
+ requireRemote: z.boolean().default(true),
121
142
  /**
122
143
  * Agent branch patterns to MERGE with the registry patterns.
123
144
  * These patterns are merged with patterns from lumenflow.dev/registry/agent-patterns.json.
@@ -71,6 +71,7 @@ export declare function getResolvedPaths(options?: {
71
71
  skillsDir: string;
72
72
  agentsDir: string;
73
73
  memoryBank: string;
74
+ plansDir: string;
74
75
  };
75
76
  /**
76
77
  * Validate a config file
@@ -93,3 +94,4 @@ export declare function createSampleConfig(outputPath: string, options?: {
93
94
  includeComments?: boolean;
94
95
  }): void;
95
96
  export type { LumenFlowConfig, Directories, BeaconPaths, GitConfig, WuConfig, GatesConfig, MemoryConfig, UiConfig, YamlConfig, } from './lumenflow-config-schema.js';
97
+ export { getDefaultConfig } from './lumenflow-config-schema.js';
@@ -144,6 +144,7 @@ export function getResolvedPaths(options = {}) {
144
144
  skillsDir: path.join(projectRoot, config.directories.skillsDir),
145
145
  agentsDir: path.join(projectRoot, config.directories.agentsDir),
146
146
  memoryBank: path.join(projectRoot, config.directories.memoryBank),
147
+ plansDir: path.join(projectRoot, config.directories.plansDir),
147
148
  };
148
149
  }
149
150
  /**
@@ -234,3 +235,5 @@ gates:
234
235
  : yaml.stringify(defaultConfig);
235
236
  fs.writeFileSync(outputPath, configContent, 'utf8');
236
237
  }
238
+ // Re-export getDefaultConfig for consumers
239
+ export { getDefaultConfig } from './lumenflow-config-schema.js';
@@ -77,6 +77,11 @@ export declare function createWuPaths(options?: {
77
77
  * @returns Path to worktrees directory
78
78
  */
79
79
  WORKTREES_DIR: () => string;
80
+ /**
81
+ * Get path to plans directory
82
+ * @returns Path to plans directory (WU-1301)
83
+ */
84
+ PLANS_DIR: () => string;
80
85
  };
81
86
  /**
82
87
  * Default WU paths using default config
@@ -130,6 +135,11 @@ export declare const WU_PATHS: {
130
135
  * @returns Path to worktrees directory
131
136
  */
132
137
  WORKTREES_DIR: () => string;
138
+ /**
139
+ * Get path to plans directory
140
+ * @returns Path to plans directory (WU-1301)
141
+ */
142
+ PLANS_DIR: () => string;
133
143
  };
134
144
  /**
135
145
  * Generate default worktree path from WU document
package/dist/wu-paths.js CHANGED
@@ -104,6 +104,11 @@ export function createWuPaths(options = {}) {
104
104
  * @returns Path to worktrees directory
105
105
  */
106
106
  WORKTREES_DIR: () => config.directories.worktrees,
107
+ /**
108
+ * Get path to plans directory
109
+ * @returns Path to plans directory (WU-1301)
110
+ */
111
+ PLANS_DIR: () => config.directories.plansDir,
107
112
  };
108
113
  }
109
114
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumenflow/core",
3
- "version": "2.3.1",
3
+ "version": "2.4.0",
4
4
  "description": "Core WU lifecycle tools for LumenFlow workflow framework",
5
5
  "keywords": [
6
6
  "lumenflow",
@@ -98,7 +98,7 @@
98
98
  "vitest": "^4.0.17"
99
99
  },
100
100
  "peerDependencies": {
101
- "@lumenflow/memory": "2.3.1"
101
+ "@lumenflow/memory": "2.4.0"
102
102
  },
103
103
  "peerDependenciesMeta": {
104
104
  "@lumenflow/memory": {