@lumenflow/core 2.5.1 → 2.7.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.
@@ -204,3 +204,75 @@ export interface TestPolicy extends CoverageConfig {
204
204
  * @returns Resolved test policy including tests_required
205
205
  */
206
206
  export declare function resolveTestPolicy(projectRoot: string): TestPolicy;
207
+ /**
208
+ * WU-1356: Supported package managers type
209
+ * Re-exported from lumenflow-config-schema to avoid circular import
210
+ */
211
+ type PackageManager = 'pnpm' | 'npm' | 'yarn' | 'bun';
212
+ /**
213
+ * WU-1356: Supported test runners type
214
+ * Re-exported from lumenflow-config-schema to avoid circular import
215
+ */
216
+ type TestRunner = 'vitest' | 'jest' | 'mocha';
217
+ /**
218
+ * WU-1356: Gates commands configuration type
219
+ */
220
+ export interface GatesCommands {
221
+ test_full: string;
222
+ test_docs_only: string;
223
+ test_incremental: string;
224
+ lint?: string;
225
+ typecheck?: string;
226
+ format?: string;
227
+ }
228
+ /**
229
+ * WU-1356: Resolve package manager from configuration
230
+ *
231
+ * Reads the package_manager field from .lumenflow.config.yaml.
232
+ * Returns 'pnpm' as default if not configured.
233
+ *
234
+ * @param projectRoot - Project root directory
235
+ * @returns Resolved package manager ('pnpm', 'npm', 'yarn', or 'bun')
236
+ */
237
+ export declare function resolvePackageManager(projectRoot: string): PackageManager;
238
+ /**
239
+ * WU-1356: Resolve test runner from configuration
240
+ *
241
+ * Reads the test_runner field from .lumenflow.config.yaml.
242
+ * Returns 'vitest' as default if not configured.
243
+ *
244
+ * @param projectRoot - Project root directory
245
+ * @returns Resolved test runner ('vitest', 'jest', or 'mocha')
246
+ */
247
+ export declare function resolveTestRunner(projectRoot: string): TestRunner;
248
+ /**
249
+ * WU-1356: Resolve build command from configuration
250
+ *
251
+ * Reads the build_command field from .lumenflow.config.yaml.
252
+ * If not configured, uses default based on package_manager.
253
+ *
254
+ * @param projectRoot - Project root directory
255
+ * @returns Resolved build command
256
+ */
257
+ export declare function resolveBuildCommand(projectRoot: string): string;
258
+ /**
259
+ * WU-1356: Resolve gates commands from configuration
260
+ *
261
+ * Reads gates.commands from .lumenflow.config.yaml.
262
+ * Merges with defaults based on package_manager if not fully specified.
263
+ *
264
+ * @param projectRoot - Project root directory
265
+ * @returns Resolved gates commands configuration
266
+ */
267
+ export declare function resolveGatesCommands(projectRoot: string): GatesCommands;
268
+ /**
269
+ * WU-1356: Get ignore patterns for test runner
270
+ *
271
+ * Returns patterns to ignore when detecting changed tests,
272
+ * based on the test runner configuration.
273
+ *
274
+ * @param testRunner - Test runner type (vitest, jest, mocha)
275
+ * @returns Array of ignore patterns
276
+ */
277
+ export declare function getIgnorePatterns(testRunner: TestRunner): string[];
278
+ export {};
@@ -453,3 +453,193 @@ export function resolveTestPolicy(projectRoot) {
453
453
  tests_required: policy.tests_required,
454
454
  };
455
455
  }
456
+ /**
457
+ * WU-1356: Default gates commands by package manager
458
+ *
459
+ * Provides sensible defaults for different package manager and test runner combinations.
460
+ */
461
+ const DEFAULT_GATES_COMMANDS = {
462
+ pnpm: {
463
+ test_full: 'pnpm turbo run test',
464
+ test_docs_only: '',
465
+ test_incremental: 'pnpm vitest run --changed origin/main',
466
+ lint: 'pnpm lint',
467
+ typecheck: 'pnpm typecheck',
468
+ format: 'pnpm format:check',
469
+ },
470
+ npm: {
471
+ test_full: 'npm test',
472
+ test_docs_only: '',
473
+ test_incremental: 'npm test -- --onlyChanged',
474
+ lint: 'npm run lint',
475
+ typecheck: 'npm run typecheck',
476
+ format: 'npm run format:check',
477
+ },
478
+ yarn: {
479
+ test_full: 'yarn test',
480
+ test_docs_only: '',
481
+ test_incremental: 'yarn test --onlyChanged',
482
+ lint: 'yarn lint',
483
+ typecheck: 'yarn typecheck',
484
+ format: 'yarn format:check',
485
+ },
486
+ bun: {
487
+ test_full: 'bun test',
488
+ test_docs_only: '',
489
+ test_incremental: 'bun test --changed',
490
+ lint: 'bun run lint',
491
+ typecheck: 'bun run typecheck',
492
+ format: 'bun run format:check',
493
+ },
494
+ };
495
+ /**
496
+ * WU-1356: Default build commands by package manager
497
+ */
498
+ const DEFAULT_BUILD_COMMANDS = {
499
+ pnpm: 'pnpm --filter @lumenflow/cli build',
500
+ npm: 'npm run build --workspace @lumenflow/cli',
501
+ yarn: 'yarn workspace @lumenflow/cli build',
502
+ bun: 'bun run --filter @lumenflow/cli build',
503
+ };
504
+ /**
505
+ * WU-1356: Ignore patterns by test runner
506
+ *
507
+ * Different test runners use different cache directories that should be ignored.
508
+ */
509
+ const IGNORE_PATTERNS_BY_RUNNER = {
510
+ vitest: ['.turbo'],
511
+ jest: ['coverage', '.jest-cache'],
512
+ mocha: ['coverage', '.nyc_output'],
513
+ };
514
+ /**
515
+ * WU-1356: Resolve package manager from configuration
516
+ *
517
+ * Reads the package_manager field from .lumenflow.config.yaml.
518
+ * Returns 'pnpm' as default if not configured.
519
+ *
520
+ * @param projectRoot - Project root directory
521
+ * @returns Resolved package manager ('pnpm', 'npm', 'yarn', or 'bun')
522
+ */
523
+ export function resolvePackageManager(projectRoot) {
524
+ const configPath = path.join(projectRoot, CONFIG_FILE_NAME);
525
+ if (!fs.existsSync(configPath)) {
526
+ return 'pnpm';
527
+ }
528
+ try {
529
+ const content = fs.readFileSync(configPath, 'utf8');
530
+ const data = yaml.parse(content);
531
+ const pm = data?.package_manager;
532
+ if (pm && ['pnpm', 'npm', 'yarn', 'bun'].includes(pm)) {
533
+ return pm;
534
+ }
535
+ return 'pnpm';
536
+ }
537
+ catch {
538
+ return 'pnpm';
539
+ }
540
+ }
541
+ /**
542
+ * WU-1356: Resolve test runner from configuration
543
+ *
544
+ * Reads the test_runner field from .lumenflow.config.yaml.
545
+ * Returns 'vitest' as default if not configured.
546
+ *
547
+ * @param projectRoot - Project root directory
548
+ * @returns Resolved test runner ('vitest', 'jest', or 'mocha')
549
+ */
550
+ export function resolveTestRunner(projectRoot) {
551
+ const configPath = path.join(projectRoot, CONFIG_FILE_NAME);
552
+ if (!fs.existsSync(configPath)) {
553
+ return 'vitest';
554
+ }
555
+ try {
556
+ const content = fs.readFileSync(configPath, 'utf8');
557
+ const data = yaml.parse(content);
558
+ const runner = data?.test_runner;
559
+ if (runner && ['vitest', 'jest', 'mocha'].includes(runner)) {
560
+ return runner;
561
+ }
562
+ return 'vitest';
563
+ }
564
+ catch {
565
+ return 'vitest';
566
+ }
567
+ }
568
+ /**
569
+ * WU-1356: Resolve build command from configuration
570
+ *
571
+ * Reads the build_command field from .lumenflow.config.yaml.
572
+ * If not configured, uses default based on package_manager.
573
+ *
574
+ * @param projectRoot - Project root directory
575
+ * @returns Resolved build command
576
+ */
577
+ export function resolveBuildCommand(projectRoot) {
578
+ const configPath = path.join(projectRoot, CONFIG_FILE_NAME);
579
+ const defaultPm = resolvePackageManager(projectRoot);
580
+ if (!fs.existsSync(configPath)) {
581
+ return DEFAULT_BUILD_COMMANDS[defaultPm];
582
+ }
583
+ try {
584
+ const content = fs.readFileSync(configPath, 'utf8');
585
+ const data = yaml.parse(content);
586
+ // If explicit build_command is set, use it
587
+ if (data?.build_command && typeof data.build_command === 'string') {
588
+ return data.build_command;
589
+ }
590
+ // Otherwise, use default for the configured package manager
591
+ return DEFAULT_BUILD_COMMANDS[defaultPm];
592
+ }
593
+ catch {
594
+ return DEFAULT_BUILD_COMMANDS[defaultPm];
595
+ }
596
+ }
597
+ /**
598
+ * WU-1356: Resolve gates commands from configuration
599
+ *
600
+ * Reads gates.commands from .lumenflow.config.yaml.
601
+ * Merges with defaults based on package_manager if not fully specified.
602
+ *
603
+ * @param projectRoot - Project root directory
604
+ * @returns Resolved gates commands configuration
605
+ */
606
+ export function resolveGatesCommands(projectRoot) {
607
+ const configPath = path.join(projectRoot, CONFIG_FILE_NAME);
608
+ const pm = resolvePackageManager(projectRoot);
609
+ const defaults = DEFAULT_GATES_COMMANDS[pm];
610
+ if (!fs.existsSync(configPath)) {
611
+ return defaults;
612
+ }
613
+ try {
614
+ const content = fs.readFileSync(configPath, 'utf8');
615
+ const data = yaml.parse(content);
616
+ const commands = data?.gates?.commands;
617
+ if (!commands) {
618
+ return defaults;
619
+ }
620
+ // Merge user config with defaults (user config wins)
621
+ return {
622
+ test_full: commands.test_full ?? defaults.test_full,
623
+ test_docs_only: commands.test_docs_only ?? defaults.test_docs_only,
624
+ test_incremental: commands.test_incremental ?? defaults.test_incremental,
625
+ lint: commands.lint ?? defaults.lint,
626
+ typecheck: commands.typecheck ?? defaults.typecheck,
627
+ format: commands.format ?? defaults.format,
628
+ };
629
+ }
630
+ catch {
631
+ return defaults;
632
+ }
633
+ }
634
+ /**
635
+ * WU-1356: Get ignore patterns for test runner
636
+ *
637
+ * Returns patterns to ignore when detecting changed tests,
638
+ * based on the test runner configuration.
639
+ *
640
+ * @param testRunner - Test runner type (vitest, jest, mocha)
641
+ * @returns Array of ignore patterns
642
+ */
643
+ export function getIgnorePatterns(testRunner) {
644
+ return IGNORE_PATTERNS_BY_RUNNER[testRunner] ?? ['.turbo'];
645
+ }
@@ -7,6 +7,68 @@
7
7
  * @module lumenflow-config-schema
8
8
  */
9
9
  import { z } from 'zod';
10
+ /**
11
+ * WU-1356: Package manager options
12
+ *
13
+ * Supported package managers for LumenFlow CLI operations.
14
+ * Used for build commands, dependency installation, and script execution.
15
+ *
16
+ * @example
17
+ * ```yaml
18
+ * package_manager: npm
19
+ * ```
20
+ */
21
+ export declare const PackageManagerSchema: z.ZodDefault<z.ZodEnum<{
22
+ pnpm: "pnpm";
23
+ npm: "npm";
24
+ yarn: "yarn";
25
+ bun: "bun";
26
+ }>>;
27
+ /** WU-1356: TypeScript type for package manager */
28
+ export type PackageManager = z.infer<typeof PackageManagerSchema>;
29
+ /**
30
+ * WU-1356: Test runner options
31
+ *
32
+ * Supported test runners for incremental test detection and execution.
33
+ * Determines how changed tests are detected and ignore patterns are derived.
34
+ *
35
+ * @example
36
+ * ```yaml
37
+ * test_runner: jest
38
+ * ```
39
+ */
40
+ export declare const TestRunnerSchema: z.ZodDefault<z.ZodEnum<{
41
+ vitest: "vitest";
42
+ jest: "jest";
43
+ mocha: "mocha";
44
+ }>>;
45
+ /** WU-1356: TypeScript type for test runner */
46
+ export type TestRunner = z.infer<typeof TestRunnerSchema>;
47
+ /**
48
+ * WU-1356: Gates commands configuration
49
+ *
50
+ * Configurable test commands for gates execution.
51
+ * Replaces hard-coded turbo/vitest commands with user-configurable alternatives.
52
+ *
53
+ * @example
54
+ * ```yaml
55
+ * gates:
56
+ * commands:
57
+ * test_full: 'npm test'
58
+ * test_docs_only: 'npm test -- --testPathPattern=docs'
59
+ * test_incremental: 'npm test -- --onlyChanged'
60
+ * ```
61
+ */
62
+ export declare const GatesCommandsConfigSchema: z.ZodObject<{
63
+ test_full: z.ZodDefault<z.ZodString>;
64
+ test_docs_only: z.ZodDefault<z.ZodString>;
65
+ test_incremental: z.ZodDefault<z.ZodString>;
66
+ lint: z.ZodOptional<z.ZodString>;
67
+ typecheck: z.ZodOptional<z.ZodString>;
68
+ format: z.ZodOptional<z.ZodString>;
69
+ }, z.core.$strip>;
70
+ /** WU-1356: TypeScript type for gates commands config */
71
+ export type GatesCommandsConfig = z.infer<typeof GatesCommandsConfigSchema>;
10
72
  /**
11
73
  * WU-1325: Lock policy for lane-level WIP enforcement
12
74
  *
@@ -172,6 +234,20 @@ export declare const GatesConfigSchema: z.ZodObject<{
172
234
  threshold: z.ZodOptional<z.ZodNumber>;
173
235
  }, z.core.$strip>]>>;
174
236
  }, z.core.$strip>>;
237
+ commands: z.ZodDefault<z.ZodObject<{
238
+ test_full: z.ZodDefault<z.ZodString>;
239
+ test_docs_only: z.ZodDefault<z.ZodString>;
240
+ test_incremental: z.ZodDefault<z.ZodString>;
241
+ lint: z.ZodOptional<z.ZodString>;
242
+ typecheck: z.ZodOptional<z.ZodString>;
243
+ format: z.ZodOptional<z.ZodString>;
244
+ }, z.core.$strip>>;
245
+ ignore_patterns: z.ZodOptional<z.ZodArray<z.ZodString>>;
246
+ lane_health: z.ZodDefault<z.ZodEnum<{
247
+ error: "error";
248
+ off: "off";
249
+ warn: "warn";
250
+ }>>;
175
251
  }, z.core.$strip>;
176
252
  /**
177
253
  * WU-1203: Progress signals configuration for sub-agent coordination
@@ -550,6 +626,20 @@ export declare const LumenFlowConfigSchema: z.ZodObject<{
550
626
  threshold: z.ZodOptional<z.ZodNumber>;
551
627
  }, z.core.$strip>]>>;
552
628
  }, z.core.$strip>>;
629
+ commands: z.ZodDefault<z.ZodObject<{
630
+ test_full: z.ZodDefault<z.ZodString>;
631
+ test_docs_only: z.ZodDefault<z.ZodString>;
632
+ test_incremental: z.ZodDefault<z.ZodString>;
633
+ lint: z.ZodOptional<z.ZodString>;
634
+ typecheck: z.ZodOptional<z.ZodString>;
635
+ format: z.ZodOptional<z.ZodString>;
636
+ }, z.core.$strip>>;
637
+ ignore_patterns: z.ZodOptional<z.ZodArray<z.ZodString>>;
638
+ lane_health: z.ZodDefault<z.ZodEnum<{
639
+ error: "error";
640
+ off: "off";
641
+ warn: "warn";
642
+ }>>;
553
643
  }, z.core.$strip>>;
554
644
  memory: z.ZodDefault<z.ZodObject<{
555
645
  directory: z.ZodDefault<z.ZodString>;
@@ -679,6 +769,18 @@ export declare const LumenFlowConfigSchema: z.ZodObject<{
679
769
  code_paths: z.ZodOptional<z.ZodArray<z.ZodString>>;
680
770
  }, z.core.$strip>>>;
681
771
  }, z.core.$strip>>;
772
+ package_manager: z.ZodDefault<z.ZodEnum<{
773
+ pnpm: "pnpm";
774
+ npm: "npm";
775
+ yarn: "yarn";
776
+ bun: "bun";
777
+ }>>;
778
+ test_runner: z.ZodDefault<z.ZodEnum<{
779
+ vitest: "vitest";
780
+ jest: "jest";
781
+ mocha: "mocha";
782
+ }>>;
783
+ build_command: z.ZodDefault<z.ZodString>;
682
784
  }, z.core.$strip>;
683
785
  /**
684
786
  * TypeScript types inferred from schemas
@@ -787,6 +889,15 @@ export declare function validateConfig(data: unknown): z.ZodSafeParseResult<{
787
889
  minCoverage: number;
788
890
  enableSafetyCriticalTests: boolean;
789
891
  enableInvariants: boolean;
892
+ commands: {
893
+ test_full: string;
894
+ test_docs_only: string;
895
+ test_incremental: string;
896
+ lint?: string;
897
+ typecheck?: string;
898
+ format?: string;
899
+ };
900
+ lane_health: "error" | "off" | "warn";
790
901
  execution?: {
791
902
  preset?: string;
792
903
  setup?: string | {
@@ -819,6 +930,7 @@ export declare function validateConfig(data: unknown): z.ZodSafeParseResult<{
819
930
  threshold?: number;
820
931
  };
821
932
  };
933
+ ignore_patterns?: string[];
822
934
  };
823
935
  memory: {
824
936
  directory: string;
@@ -882,6 +994,9 @@ export declare function validateConfig(data: unknown): z.ZodSafeParseResult<{
882
994
  enabled: boolean;
883
995
  };
884
996
  };
997
+ package_manager: "pnpm" | "npm" | "yarn" | "bun";
998
+ test_runner: "vitest" | "jest" | "mocha";
999
+ build_command: string;
885
1000
  methodology?: {
886
1001
  testing: "tdd" | "test-after" | "none";
887
1002
  architecture: "none" | "hexagonal" | "layered";
@@ -11,6 +11,77 @@ import { z } from 'zod';
11
11
  import { GatesExecutionConfigSchema } from './gates-config.js';
12
12
  // WU-1259: Import methodology config schema for resolvePolicy()
13
13
  import { MethodologyConfigSchema } from './resolve-policy.js';
14
+ /**
15
+ * WU-1356: Package manager options
16
+ *
17
+ * Supported package managers for LumenFlow CLI operations.
18
+ * Used for build commands, dependency installation, and script execution.
19
+ *
20
+ * @example
21
+ * ```yaml
22
+ * package_manager: npm
23
+ * ```
24
+ */
25
+ export const PackageManagerSchema = z.enum(['pnpm', 'npm', 'yarn', 'bun']).default('pnpm');
26
+ /**
27
+ * WU-1356: Test runner options
28
+ *
29
+ * Supported test runners for incremental test detection and execution.
30
+ * Determines how changed tests are detected and ignore patterns are derived.
31
+ *
32
+ * @example
33
+ * ```yaml
34
+ * test_runner: jest
35
+ * ```
36
+ */
37
+ export const TestRunnerSchema = z.enum(['vitest', 'jest', 'mocha']).default('vitest');
38
+ /**
39
+ * WU-1356: Gates commands configuration
40
+ *
41
+ * Configurable test commands for gates execution.
42
+ * Replaces hard-coded turbo/vitest commands with user-configurable alternatives.
43
+ *
44
+ * @example
45
+ * ```yaml
46
+ * gates:
47
+ * commands:
48
+ * test_full: 'npm test'
49
+ * test_docs_only: 'npm test -- --testPathPattern=docs'
50
+ * test_incremental: 'npm test -- --onlyChanged'
51
+ * ```
52
+ */
53
+ export const GatesCommandsConfigSchema = z.object({
54
+ /**
55
+ * Command to run full test suite.
56
+ * Default: 'pnpm turbo run test'
57
+ */
58
+ test_full: z.string().default('pnpm turbo run test'),
59
+ /**
60
+ * Command to run tests in docs-only mode.
61
+ * Default: empty (skip tests in docs-only mode)
62
+ */
63
+ test_docs_only: z.string().default(''),
64
+ /**
65
+ * Command to run incremental tests (changed files only).
66
+ * Default: 'pnpm vitest run --changed origin/main'
67
+ */
68
+ test_incremental: z.string().default('pnpm vitest run --changed origin/main'),
69
+ /**
70
+ * Command to run lint checks.
71
+ * Default: 'pnpm lint'
72
+ */
73
+ lint: z.string().optional(),
74
+ /**
75
+ * Command to run type checks.
76
+ * Default: 'pnpm typecheck'
77
+ */
78
+ typecheck: z.string().optional(),
79
+ /**
80
+ * Command to run format checks.
81
+ * Default: 'pnpm format:check'
82
+ */
83
+ format: z.string().optional(),
84
+ });
14
85
  /**
15
86
  * WU-1325: Lock policy for lane-level WIP enforcement
16
87
  *
@@ -306,6 +377,26 @@ export const GatesConfigSchema = z.object({
306
377
  * When set, gates runner uses these instead of hardcoded commands.
307
378
  */
308
379
  execution: GatesExecutionConfigSchema.optional(),
380
+ /**
381
+ * WU-1356: Configurable gate commands
382
+ * Replaces hard-coded turbo/vitest commands with user-configurable alternatives.
383
+ * Enables LumenFlow to work with npm/yarn/bun, Nx/plain scripts, Jest/Mocha, etc.
384
+ */
385
+ commands: GatesCommandsConfigSchema.default(() => GatesCommandsConfigSchema.parse({})),
386
+ /**
387
+ * WU-1356: Ignore patterns for test runners
388
+ * Patterns to ignore when detecting changed tests.
389
+ * Default: ['.turbo'] for vitest (derived from test_runner if not specified)
390
+ */
391
+ ignore_patterns: z.array(z.string()).optional(),
392
+ /**
393
+ * WU-1191: Lane health gate mode
394
+ * Controls how lane health check behaves during gates.
395
+ * - 'warn': Log warning if issues found (default)
396
+ * - 'error': Fail gates if issues found
397
+ * - 'off': Skip lane health check
398
+ */
399
+ lane_health: z.enum(['warn', 'error', 'off']).default('warn'),
309
400
  });
310
401
  /**
311
402
  * WU-1203: Progress signals configuration for sub-agent coordination
@@ -731,6 +822,43 @@ export const LumenFlowConfigSchema = z.object({
731
822
  * ```
732
823
  */
733
824
  lanes: LanesConfigSchema.optional(),
825
+ /**
826
+ * WU-1356: Package manager for CLI operations
827
+ * Determines which package manager is used for build commands,
828
+ * dependency installation, and script execution.
829
+ *
830
+ * @default 'pnpm'
831
+ *
832
+ * @example
833
+ * ```yaml
834
+ * package_manager: npm
835
+ * ```
836
+ */
837
+ package_manager: PackageManagerSchema,
838
+ /**
839
+ * WU-1356: Test runner for incremental test detection
840
+ * Determines how changed tests are detected and which ignore patterns to use.
841
+ *
842
+ * @default 'vitest'
843
+ *
844
+ * @example
845
+ * ```yaml
846
+ * test_runner: jest
847
+ * ```
848
+ */
849
+ test_runner: TestRunnerSchema,
850
+ /**
851
+ * WU-1356: Custom build command for CLI bootstrap
852
+ * Overrides the default build command used in cli-entry.mjs.
853
+ *
854
+ * @default 'pnpm --filter @lumenflow/cli build'
855
+ *
856
+ * @example
857
+ * ```yaml
858
+ * build_command: 'npm run build'
859
+ * ```
860
+ */
861
+ build_command: z.string().default('pnpm --filter @lumenflow/cli build'),
734
862
  });
735
863
  /**
736
864
  * Validate configuration data
@@ -1718,6 +1718,75 @@ export declare const CONTEXT_VALIDATION: {
1718
1718
  readonly GATES: "gates";
1719
1719
  };
1720
1720
  };
1721
+ /**
1722
+ * Git hook error messages (WU-1357)
1723
+ *
1724
+ * Educational, structured messages for git hook blocks.
1725
+ * Follows the "message bag" pattern: TITLE, WHY, ACTIONS, HELP, BYPASS.
1726
+ *
1727
+ * Design principles:
1728
+ * - Explain WHY before showing WHAT to do
1729
+ * - Provide multiple paths forward (not just one command)
1730
+ * - Put emergency bypass LAST with clear warnings
1731
+ * - Include help resources for learning
1732
+ *
1733
+ * @see .husky/hooks/pre-commit.mjs - Primary consumer
1734
+ */
1735
+ export declare const HOOK_MESSAGES: {
1736
+ /**
1737
+ * Main branch protection block message components
1738
+ */
1739
+ readonly MAIN_BRANCH_BLOCK: {
1740
+ /** Box drawing for visual structure */
1741
+ readonly BOX: {
1742
+ readonly TOP: "══════════════════════════════════════════════════════════════";
1743
+ readonly DIVIDER: "──────────────────────────────────────────────────────────────";
1744
+ };
1745
+ /** Title shown at the top */
1746
+ readonly TITLE: (branch: string) => string;
1747
+ /** Educational explanation of WHY the block exists */
1748
+ readonly WHY: {
1749
+ readonly HEADER: "WHY THIS HAPPENS";
1750
+ readonly LINES: readonly ["LumenFlow protects main from direct commits to ensure:", " • All work is tracked in Work Units (WUs)", " • Changes can be reviewed and coordinated", " • Parallel work across lanes stays isolated"];
1751
+ };
1752
+ /** Action paths - multiple ways forward */
1753
+ readonly ACTIONS: {
1754
+ readonly HEADER: "WHAT TO DO";
1755
+ readonly HAVE_WU: {
1756
+ readonly HEADER: "1. If you have a Work Unit to implement:";
1757
+ readonly COMMANDS: readonly ["pnpm wu:claim --id WU-XXXX --lane \"<Lane>\"", "cd worktrees/<lane>-wu-xxxx"];
1758
+ readonly NOTE: "Then make your commits there";
1759
+ };
1760
+ readonly NEED_WU: {
1761
+ readonly HEADER: "2. If you need to create a new Work Unit:";
1762
+ readonly COMMANDS: readonly ["pnpm wu:create --lane \"<Lane>\" --title \"Your task\""];
1763
+ readonly NOTE: "This generates a WU ID, then claim it as above";
1764
+ };
1765
+ readonly LIST_LANES: {
1766
+ readonly HEADER: "3. Not sure what lane to use?";
1767
+ readonly COMMANDS: readonly ["pnpm wu:list-lanes"];
1768
+ };
1769
+ };
1770
+ /** Help resources */
1771
+ readonly HELP: {
1772
+ readonly HEADER: "NEED HELP?";
1773
+ readonly RESOURCES: readonly ["• Read: LUMENFLOW.md (workflow overview)", "• Read: docs/04-operations/_frameworks/lumenflow/agent/onboarding/", "• Run: pnpm wu:help"];
1774
+ };
1775
+ /** Emergency bypass (shown last, with warnings) */
1776
+ readonly BYPASS: {
1777
+ readonly HEADER: "EMERGENCY BYPASS (logged, use sparingly)";
1778
+ readonly WARNING: "Bypasses are audit-logged. Only use for genuine emergencies.";
1779
+ readonly COMMAND: "LUMENFLOW_FORCE=1 LUMENFLOW_FORCE_REASON=\"<reason>\" git commit ...";
1780
+ };
1781
+ };
1782
+ /**
1783
+ * Worktree discipline block message components
1784
+ */
1785
+ readonly WORKTREE_BLOCK: {
1786
+ readonly TITLE: "LANE BRANCH WORK SHOULD BE IN WORKTREE";
1787
+ readonly WHY: "Worktrees provide isolation for parallel work. Working on a lane branch from the main checkout bypasses this isolation.";
1788
+ };
1789
+ };
1721
1790
  /** Type for location types */
1722
1791
  export type LocationType = (typeof CONTEXT_VALIDATION.LOCATION_TYPES)[keyof typeof CONTEXT_VALIDATION.LOCATION_TYPES];
1723
1792
  /** Type for validation error codes */
@@ -1789,3 +1789,81 @@ export const CONTEXT_VALIDATION = {
1789
1789
  GATES: 'gates',
1790
1790
  },
1791
1791
  };
1792
+ /**
1793
+ * Git hook error messages (WU-1357)
1794
+ *
1795
+ * Educational, structured messages for git hook blocks.
1796
+ * Follows the "message bag" pattern: TITLE, WHY, ACTIONS, HELP, BYPASS.
1797
+ *
1798
+ * Design principles:
1799
+ * - Explain WHY before showing WHAT to do
1800
+ * - Provide multiple paths forward (not just one command)
1801
+ * - Put emergency bypass LAST with clear warnings
1802
+ * - Include help resources for learning
1803
+ *
1804
+ * @see .husky/hooks/pre-commit.mjs - Primary consumer
1805
+ */
1806
+ export const HOOK_MESSAGES = {
1807
+ /**
1808
+ * Main branch protection block message components
1809
+ */
1810
+ MAIN_BRANCH_BLOCK: {
1811
+ /** Box drawing for visual structure */
1812
+ BOX: {
1813
+ TOP: '══════════════════════════════════════════════════════════════',
1814
+ DIVIDER: '──────────────────────────────────────────────────────────────',
1815
+ },
1816
+ /** Title shown at the top */
1817
+ TITLE: (branch) => `DIRECT COMMIT TO ${branch.toUpperCase()} BLOCKED`,
1818
+ /** Educational explanation of WHY the block exists */
1819
+ WHY: {
1820
+ HEADER: 'WHY THIS HAPPENS',
1821
+ LINES: [
1822
+ 'LumenFlow protects main from direct commits to ensure:',
1823
+ ' • All work is tracked in Work Units (WUs)',
1824
+ ' • Changes can be reviewed and coordinated',
1825
+ ' • Parallel work across lanes stays isolated',
1826
+ ],
1827
+ },
1828
+ /** Action paths - multiple ways forward */
1829
+ ACTIONS: {
1830
+ HEADER: 'WHAT TO DO',
1831
+ HAVE_WU: {
1832
+ HEADER: '1. If you have a Work Unit to implement:',
1833
+ COMMANDS: ['pnpm wu:claim --id WU-XXXX --lane "<Lane>"', 'cd worktrees/<lane>-wu-xxxx'],
1834
+ NOTE: 'Then make your commits there',
1835
+ },
1836
+ NEED_WU: {
1837
+ HEADER: '2. If you need to create a new Work Unit:',
1838
+ COMMANDS: ['pnpm wu:create --lane "<Lane>" --title "Your task"'],
1839
+ NOTE: 'This generates a WU ID, then claim it as above',
1840
+ },
1841
+ LIST_LANES: {
1842
+ HEADER: '3. Not sure what lane to use?',
1843
+ COMMANDS: ['pnpm wu:list-lanes'],
1844
+ },
1845
+ },
1846
+ /** Help resources */
1847
+ HELP: {
1848
+ HEADER: 'NEED HELP?',
1849
+ RESOURCES: [
1850
+ '• Read: LUMENFLOW.md (workflow overview)',
1851
+ '• Read: docs/04-operations/_frameworks/lumenflow/agent/onboarding/',
1852
+ '• Run: pnpm wu:help',
1853
+ ],
1854
+ },
1855
+ /** Emergency bypass (shown last, with warnings) */
1856
+ BYPASS: {
1857
+ HEADER: 'EMERGENCY BYPASS (logged, use sparingly)',
1858
+ WARNING: 'Bypasses are audit-logged. Only use for genuine emergencies.',
1859
+ COMMAND: 'LUMENFLOW_FORCE=1 LUMENFLOW_FORCE_REASON="<reason>" git commit ...',
1860
+ },
1861
+ },
1862
+ /**
1863
+ * Worktree discipline block message components
1864
+ */
1865
+ WORKTREE_BLOCK: {
1866
+ TITLE: 'LANE BRANCH WORK SHOULD BE IN WORKTREE',
1867
+ WHY: 'Worktrees provide isolation for parallel work. Working on a lane branch from the main checkout bypasses this isolation.',
1868
+ },
1869
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumenflow/core",
3
- "version": "2.5.1",
3
+ "version": "2.7.0",
4
4
  "description": "Core WU lifecycle tools for LumenFlow workflow framework",
5
5
  "keywords": [
6
6
  "lumenflow",
@@ -99,7 +99,7 @@
99
99
  "vitest": "^4.0.17"
100
100
  },
101
101
  "peerDependencies": {
102
- "@lumenflow/memory": "2.5.1"
102
+ "@lumenflow/memory": "2.7.0"
103
103
  },
104
104
  "peerDependenciesMeta": {
105
105
  "@lumenflow/memory": {