@lumenflow/core 1.0.0 → 1.3.2
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.d.ts +6 -0
- package/dist/arg-parser.js +57 -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 +23 -0
- package/dist/git-adapter.js +38 -2
- package/dist/index.d.ts +3 -0
- package/dist/index.js +5 -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/lumenflow-home.d.ts +130 -0
- package/dist/lumenflow-home.js +208 -0
- package/dist/manual-test-validator.js +1 -1
- package/dist/orchestration-rules.d.ts +1 -1
- package/dist/orchestration-rules.js +2 -2
- package/dist/orphan-detector.d.ts +16 -0
- package/dist/orphan-detector.js +24 -0
- 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/spec-branch-helpers.d.ts +118 -0
- package/dist/spec-branch-helpers.js +192 -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.d.ts +2 -0
- package/dist/wu-consistency-checker.js +40 -6
- package/dist/wu-constants.d.ts +98 -3
- package/dist/wu-constants.js +108 -3
- package/dist/wu-create-validators.d.ts +40 -2
- package/dist/wu-create-validators.js +76 -2
- package/dist/wu-done-branch-only.js +9 -0
- 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-generate.d.ts +73 -0
- package/dist/wu-done-docs-generate.js +108 -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 +24 -30
- package/dist/wu-schema.js +4 -4
- 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 +113 -177
- 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 +12 -8
package/dist/arg-parser.d.ts
CHANGED
|
@@ -21,6 +21,12 @@ interface WUOption {
|
|
|
21
21
|
isRepeatable?: boolean;
|
|
22
22
|
}
|
|
23
23
|
export declare const WU_OPTIONS: Record<string, WUOption>;
|
|
24
|
+
/**
|
|
25
|
+
* WU-1062: Additional options for wu:create command
|
|
26
|
+
*
|
|
27
|
+
* These options control how wu:create handles spec storage and branch creation.
|
|
28
|
+
*/
|
|
29
|
+
export declare const WU_CREATE_OPTIONS: Record<string, WUOption>;
|
|
24
30
|
/**
|
|
25
31
|
* Create a commander-based CLI parser for a WU script.
|
|
26
32
|
*
|
package/dist/arg-parser.js
CHANGED
|
@@ -15,6 +15,16 @@ function collectRepeatable(value, previous) {
|
|
|
15
15
|
}
|
|
16
16
|
export const WU_OPTIONS = {
|
|
17
17
|
// String options (require values)
|
|
18
|
+
client: {
|
|
19
|
+
name: 'client',
|
|
20
|
+
flags: '--client <client>',
|
|
21
|
+
description: 'Client name (claude-code, gemini-cli, etc)',
|
|
22
|
+
},
|
|
23
|
+
vendor: {
|
|
24
|
+
name: 'vendor',
|
|
25
|
+
flags: '--vendor <vendor>',
|
|
26
|
+
description: 'Deprecated alias for --client',
|
|
27
|
+
},
|
|
18
28
|
id: {
|
|
19
29
|
name: 'id',
|
|
20
30
|
flags: '-i, --id <wuId>',
|
|
@@ -124,6 +134,11 @@ export const WU_OPTIONS = {
|
|
|
124
134
|
flags: '--skip-gates',
|
|
125
135
|
description: 'Skip gates check (requires --reason and --fix-wu)',
|
|
126
136
|
},
|
|
137
|
+
docsOnly: {
|
|
138
|
+
name: 'docsOnly',
|
|
139
|
+
flags: '--docs-only',
|
|
140
|
+
description: 'Run docs-only gates (requires exposure: documentation or docs-only code_paths)',
|
|
141
|
+
},
|
|
127
142
|
allowTodo: {
|
|
128
143
|
name: 'allowTodo',
|
|
129
144
|
flags: '--allow-todo',
|
|
@@ -154,6 +169,12 @@ export const WU_OPTIONS = {
|
|
|
154
169
|
flags: '--fix',
|
|
155
170
|
description: 'Auto-fix common YAML validation issues (WU-1359)',
|
|
156
171
|
},
|
|
172
|
+
noPush: {
|
|
173
|
+
name: 'noPush',
|
|
174
|
+
flags: '--no-push',
|
|
175
|
+
description: 'Skip pushing claim branch or canonical updates (air-gapped/offline)',
|
|
176
|
+
isNegated: true,
|
|
177
|
+
},
|
|
157
178
|
createPr: {
|
|
158
179
|
name: 'createPr',
|
|
159
180
|
flags: '--create-pr',
|
|
@@ -368,12 +389,44 @@ export const WU_OPTIONS = {
|
|
|
368
389
|
flags: '--resume',
|
|
369
390
|
description: 'Resume a WU from a crashed/killed agent (handoff) by taking over the existing worktree and updating the lock with new PID. Fails if original PID is still running (safety) or worktree does not exist.',
|
|
370
391
|
},
|
|
392
|
+
// WU-1023: Skip auto-setup for fast claims
|
|
393
|
+
skipSetup: {
|
|
394
|
+
name: 'skipSetup',
|
|
395
|
+
flags: '--skip-setup',
|
|
396
|
+
description: 'Skip automatic pnpm install in worktree after creation (faster claims when deps already built)',
|
|
397
|
+
},
|
|
398
|
+
};
|
|
399
|
+
/**
|
|
400
|
+
* WU-1062: Additional options for wu:create command
|
|
401
|
+
*
|
|
402
|
+
* These options control how wu:create handles spec storage and branch creation.
|
|
403
|
+
*/
|
|
404
|
+
export const WU_CREATE_OPTIONS = {
|
|
405
|
+
/**
|
|
406
|
+
* Create plan template in $LUMENFLOW_HOME/plans/
|
|
407
|
+
* Used with spec branch mode to store plans externally.
|
|
408
|
+
*/
|
|
409
|
+
plan: {
|
|
410
|
+
name: 'plan',
|
|
411
|
+
flags: '--plan',
|
|
412
|
+
description: 'Create plan template in $LUMENFLOW_HOME/plans/ (external plan storage for traceability)',
|
|
413
|
+
},
|
|
414
|
+
/**
|
|
415
|
+
* Direct main-write mode (legacy behavior)
|
|
416
|
+
* Writes WU spec directly to main branch instead of spec branch.
|
|
417
|
+
* Use for emergency/hotfix scenarios or when spec branch workflow is not desired.
|
|
418
|
+
*/
|
|
419
|
+
direct: {
|
|
420
|
+
name: 'direct',
|
|
421
|
+
flags: '--direct',
|
|
422
|
+
description: 'Write to main directly (legacy behavior). Default is spec branch mode (spec/wu-XXXX)',
|
|
423
|
+
},
|
|
371
424
|
};
|
|
372
425
|
/**
|
|
373
426
|
* Negated options that commander handles specially.
|
|
374
427
|
* --no-foo creates opts.foo = false. We convert to noFoo = true.
|
|
375
428
|
*/
|
|
376
|
-
const NEGATED_OPTIONS = ['auto', 'remove', 'merge', 'autoRebase'];
|
|
429
|
+
const NEGATED_OPTIONS = ['auto', 'remove', 'merge', 'autoRebase', 'push'];
|
|
377
430
|
/**
|
|
378
431
|
* Post-process commander opts to handle negated boolean options.
|
|
379
432
|
* Commander's --no-* flags create opts.foo = false.
|
|
@@ -527,6 +580,7 @@ export function parseWUArgs(argv) {
|
|
|
527
580
|
WU_OPTIONS.noMerge,
|
|
528
581
|
WU_OPTIONS.help,
|
|
529
582
|
WU_OPTIONS.skipGates,
|
|
583
|
+
WU_OPTIONS.docsOnly,
|
|
530
584
|
WU_OPTIONS.allowTodo,
|
|
531
585
|
WU_OPTIONS.skipExposureCheck,
|
|
532
586
|
WU_OPTIONS.skipAccessibilityCheck,
|
|
@@ -535,6 +589,8 @@ export function parseWUArgs(argv) {
|
|
|
535
589
|
WU_OPTIONS.overrideOwner,
|
|
536
590
|
WU_OPTIONS.noAutoRebase,
|
|
537
591
|
WU_OPTIONS.requireAgents,
|
|
592
|
+
WU_OPTIONS.client,
|
|
593
|
+
WU_OPTIONS.vendor,
|
|
538
594
|
];
|
|
539
595
|
for (const opt of allOptions) {
|
|
540
596
|
program.option(opt.flags, opt.description, opt.default);
|
|
@@ -54,7 +54,7 @@ sections:
|
|
|
54
54
|
insertion: after_heading_blank_line
|
|
55
55
|
---
|
|
56
56
|
|
|
57
|
-
> Agent: Read **
|
|
57
|
+
> Agent: Read **docs/04-operations/_frameworks/lumenflow/agent/onboarding/starting-prompt.md** first, then follow **docs/04-operations/\\_frameworks/lumenflow/lumenflow-complete.md** for execution.
|
|
58
58
|
|
|
59
59
|
# Backlog (single source of truth)
|
|
60
60
|
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { readFileSync, writeFileSync, existsSync, copyFileSync } from 'node:fs';
|
|
9
9
|
import path from 'node:path';
|
|
10
|
-
import
|
|
10
|
+
import { parseYAML } from './wu-yaml.js';
|
|
11
11
|
import { parseBacklogFrontmatter, getSectionHeadings } from './backlog-parser.js';
|
|
12
12
|
import { extractParent } from './lane-checker.js';
|
|
13
13
|
import { CONFIG_FILES, STRING_LITERALS, getProjectRoot, } from './wu-constants.js';
|
|
@@ -24,7 +24,7 @@ function hasSubLaneTaxonomy(parent, projectRoot) {
|
|
|
24
24
|
}
|
|
25
25
|
try {
|
|
26
26
|
const taxonomyContent = readFileSync(taxonomyPath, { encoding: 'utf-8' });
|
|
27
|
-
const taxonomy =
|
|
27
|
+
const taxonomy = parseYAML(taxonomyContent);
|
|
28
28
|
const normalizedParent = parent.trim().toLowerCase();
|
|
29
29
|
return Object.keys(taxonomy).some((key) => key.toLowerCase().trim() === normalizedParent);
|
|
30
30
|
}
|
|
@@ -126,7 +126,7 @@ export function validateBacklogSync(backlogPath) {
|
|
|
126
126
|
}
|
|
127
127
|
try {
|
|
128
128
|
const wuContent = readFileSync(wuPath, { encoding: 'utf-8' });
|
|
129
|
-
const wuDoc =
|
|
129
|
+
const wuDoc = parseYAML(wuContent);
|
|
130
130
|
if (wuDoc && wuDoc.lane) {
|
|
131
131
|
const lane = wuDoc.lane.toString().trim();
|
|
132
132
|
const hasColon = lane.includes(':');
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Branch-aware bypass detection for cloud/automation agents.
|
|
3
|
+
*
|
|
4
|
+
* Provides functions to check if a branch is an agent branch that can
|
|
5
|
+
* bypass worktree requirements, and if headless mode is allowed.
|
|
6
|
+
*
|
|
7
|
+
* @module branch-check
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Check if branch is an agent branch that can bypass worktree requirements.
|
|
11
|
+
* Uses the existing config loader (which handles caching/validation).
|
|
12
|
+
*
|
|
13
|
+
* @param branch - Branch name to check
|
|
14
|
+
* @returns True if branch matches agent patterns
|
|
15
|
+
*/
|
|
16
|
+
export declare function isAgentBranch(branch: string | null | undefined): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Check if headless mode is allowed (guarded).
|
|
19
|
+
* Requires LUMENFLOW_HEADLESS=1 AND (LUMENFLOW_ADMIN=1 OR CI truthy OR GITHUB_ACTIONS truthy)
|
|
20
|
+
*/
|
|
21
|
+
export declare function isHeadlessAllowed(): boolean;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Branch-aware bypass detection for cloud/automation agents.
|
|
3
|
+
*
|
|
4
|
+
* Provides functions to check if a branch is an agent branch that can
|
|
5
|
+
* bypass worktree requirements, and if headless mode is allowed.
|
|
6
|
+
*
|
|
7
|
+
* @module branch-check
|
|
8
|
+
*/
|
|
9
|
+
import micromatch from 'micromatch';
|
|
10
|
+
import { getConfig } from './lumenflow-config.js';
|
|
11
|
+
/** Default agent branch patterns (narrow: just agent/*) */
|
|
12
|
+
const DEFAULT_AGENT_BRANCH_PATTERNS = ['agent/*'];
|
|
13
|
+
/** Legacy protected branch (always protected regardless of mainBranch setting) */
|
|
14
|
+
const LEGACY_PROTECTED = 'master';
|
|
15
|
+
/**
|
|
16
|
+
* Get lane branch pattern from config (or default).
|
|
17
|
+
* Lane branches always require worktrees - never bypassed.
|
|
18
|
+
*/
|
|
19
|
+
function getLaneBranchPattern() {
|
|
20
|
+
const config = getConfig();
|
|
21
|
+
const prefix = config?.git?.laneBranchPrefix ?? 'lane/';
|
|
22
|
+
// Escape regex special chars in prefix, then anchor to start
|
|
23
|
+
const escaped = prefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
24
|
+
return new RegExp(`^${escaped}`);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Get protected branches derived from config.
|
|
28
|
+
* Returns [mainBranch, 'master'] to avoid config duplication.
|
|
29
|
+
*/
|
|
30
|
+
function getProtectedBranches() {
|
|
31
|
+
const config = getConfig();
|
|
32
|
+
const mainBranch = config?.git?.mainBranch ?? 'main';
|
|
33
|
+
// Deduplicate in case mainBranch is 'master'
|
|
34
|
+
const protectedSet = new Set([mainBranch, LEGACY_PROTECTED]);
|
|
35
|
+
return Array.from(protectedSet);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Check if branch is an agent branch that can bypass worktree requirements.
|
|
39
|
+
* Uses the existing config loader (which handles caching/validation).
|
|
40
|
+
*
|
|
41
|
+
* @param branch - Branch name to check
|
|
42
|
+
* @returns True if branch matches agent patterns
|
|
43
|
+
*/
|
|
44
|
+
export function isAgentBranch(branch) {
|
|
45
|
+
// Fail-closed: no branch = protected
|
|
46
|
+
if (!branch)
|
|
47
|
+
return false;
|
|
48
|
+
// Detached HEAD = protected (fail-closed)
|
|
49
|
+
if (branch === 'HEAD')
|
|
50
|
+
return false;
|
|
51
|
+
// Load config (uses existing loader with caching)
|
|
52
|
+
const config = getConfig();
|
|
53
|
+
const protectedBranches = getProtectedBranches();
|
|
54
|
+
const patterns = config?.git?.agentBranchPatterns?.length > 0
|
|
55
|
+
? config.git.agentBranchPatterns
|
|
56
|
+
: DEFAULT_AGENT_BRANCH_PATTERNS;
|
|
57
|
+
// Protected branches are NEVER bypassed (mainBranch + 'master')
|
|
58
|
+
if (protectedBranches.includes(branch))
|
|
59
|
+
return false;
|
|
60
|
+
// LumenFlow lane branches require worktrees (uses config's laneBranchPrefix)
|
|
61
|
+
if (getLaneBranchPattern().test(branch))
|
|
62
|
+
return false;
|
|
63
|
+
// Use micromatch for proper glob matching
|
|
64
|
+
return micromatch.isMatch(branch, patterns);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Check if headless mode is allowed (guarded).
|
|
68
|
+
* Requires LUMENFLOW_HEADLESS=1 AND (LUMENFLOW_ADMIN=1 OR CI truthy OR GITHUB_ACTIONS truthy)
|
|
69
|
+
*/
|
|
70
|
+
export function isHeadlessAllowed() {
|
|
71
|
+
if (process.env.LUMENFLOW_HEADLESS !== '1')
|
|
72
|
+
return false;
|
|
73
|
+
return (process.env.LUMENFLOW_ADMIN === '1' ||
|
|
74
|
+
Boolean(process.env.CI) || // Any truthy CI value (true, 1, yes, etc.)
|
|
75
|
+
Boolean(process.env.GITHUB_ACTIONS) // Any truthy value
|
|
76
|
+
);
|
|
77
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CLI helper for bash hooks to check if branch is an agent branch.
|
|
4
|
+
* Uses the same isAgentBranch() logic as TypeScript code.
|
|
5
|
+
*
|
|
6
|
+
* Usage: node dist/cli/is-agent-branch.js [branch-name]
|
|
7
|
+
* Exit codes: 0 = agent branch (allowed), 1 = not agent branch (protected)
|
|
8
|
+
*
|
|
9
|
+
* @module cli/is-agent-branch
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CLI helper for bash hooks to check if branch is an agent branch.
|
|
4
|
+
* Uses the same isAgentBranch() logic as TypeScript code.
|
|
5
|
+
*
|
|
6
|
+
* Usage: node dist/cli/is-agent-branch.js [branch-name]
|
|
7
|
+
* Exit codes: 0 = agent branch (allowed), 1 = not agent branch (protected)
|
|
8
|
+
*
|
|
9
|
+
* @module cli/is-agent-branch
|
|
10
|
+
*/
|
|
11
|
+
import { isAgentBranch } from '../branch-check.js';
|
|
12
|
+
const branch = process.argv[2] || null;
|
|
13
|
+
const result = isAgentBranch(branch);
|
|
14
|
+
// Exit 0 = agent branch (truthy), Exit 1 = not agent branch
|
|
15
|
+
process.exit(result ? 0 : 1);
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { readFileSync, existsSync } from 'fs';
|
|
11
11
|
import path from 'path';
|
|
12
|
-
import
|
|
12
|
+
import { parseYAML } from './wu-yaml.js';
|
|
13
13
|
import fg from 'fast-glob';
|
|
14
14
|
import micromatch from 'micromatch';
|
|
15
15
|
import { STATUS_SECTIONS, BACKLOG_SECTIONS, STRING_LITERALS } from './wu-constants.js';
|
|
@@ -166,7 +166,7 @@ export function detectConflicts(statusPath, claimingPaths, claimingWU) {
|
|
|
166
166
|
continue; // Skip if YAML doesn't exist
|
|
167
167
|
}
|
|
168
168
|
const wuContent = readFileSync(wuPath, { encoding: 'utf-8' });
|
|
169
|
-
const wuDoc =
|
|
169
|
+
const wuDoc = parseYAML(wuContent);
|
|
170
170
|
// Extract code_paths (skip if not defined)
|
|
171
171
|
const existingPaths = wuDoc?.code_paths;
|
|
172
172
|
if (!existingPaths || existingPaths.length === 0) {
|
package/dist/error-handler.d.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* @file error-handler.mjs
|
|
3
3
|
* @description Structured error handling with error codes
|
|
4
4
|
* WU-1082: Extract shared utilities (eliminate die() duplication)
|
|
5
|
+
* WU-1006: Library-First - use path.basename() instead of manual split
|
|
5
6
|
*
|
|
6
7
|
* Replaces die() function in:
|
|
7
8
|
* - tools/wu-claim.mjs
|
package/dist/error-handler.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* @file error-handler.mjs
|
|
3
3
|
* @description Structured error handling with error codes
|
|
4
4
|
* WU-1082: Extract shared utilities (eliminate die() duplication)
|
|
5
|
+
* WU-1006: Library-First - use path.basename() instead of manual split
|
|
5
6
|
*
|
|
6
7
|
* Replaces die() function in:
|
|
7
8
|
* - tools/wu-claim.mjs
|
|
@@ -14,6 +15,7 @@
|
|
|
14
15
|
* - tools/validate.mjs
|
|
15
16
|
* - tools/guard-worktree-commit.mjs
|
|
16
17
|
*/
|
|
18
|
+
import path from 'node:path';
|
|
17
19
|
/**
|
|
18
20
|
* Structured error class with error codes and details
|
|
19
21
|
* @class WUError
|
|
@@ -56,8 +58,9 @@ export class WUError extends Error {
|
|
|
56
58
|
*/
|
|
57
59
|
export function die(message, exitCode = 1) {
|
|
58
60
|
// Auto-detect script name from process.argv[1] (eliminates string literal duplication)
|
|
61
|
+
// WU-1006: Use path.basename() instead of manual split (Library-First principle)
|
|
59
62
|
const scriptPath = process.argv[1] || 'unknown';
|
|
60
|
-
const scriptName =
|
|
63
|
+
const scriptName = path.basename(scriptPath, '.js');
|
|
61
64
|
console.error(`[${scriptName}] ${message}`);
|
|
62
65
|
process.exit(exitCode);
|
|
63
66
|
}
|
package/dist/git-adapter.d.ts
CHANGED
|
@@ -66,6 +66,13 @@ export declare class GitAdapter {
|
|
|
66
66
|
* await git.getStatus(); // " M file.txt\n?? untracked.txt"
|
|
67
67
|
*/
|
|
68
68
|
getStatus(): Promise<string>;
|
|
69
|
+
/**
|
|
70
|
+
* Get unpushed commits (compared to upstream)
|
|
71
|
+
* @returns {Promise<string>} Oneline log output for unpushed commits
|
|
72
|
+
* @example
|
|
73
|
+
* await git.getUnpushedCommits(); // "abc123 fix: ...\n"
|
|
74
|
+
*/
|
|
75
|
+
getUnpushedCommits(): Promise<string>;
|
|
69
76
|
/**
|
|
70
77
|
* Check if a branch exists
|
|
71
78
|
* @param {string} branch - Branch name
|
|
@@ -77,6 +84,17 @@ export declare class GitAdapter {
|
|
|
77
84
|
* await git.branchExists('nonexistent'); // false
|
|
78
85
|
*/
|
|
79
86
|
branchExists(branch: string): Promise<boolean>;
|
|
87
|
+
/**
|
|
88
|
+
* Check if a remote branch exists via git ls-remote --heads
|
|
89
|
+
* @param {string} remote - Remote name (e.g., 'origin')
|
|
90
|
+
* @param {string} branch - Branch name
|
|
91
|
+
* @returns {Promise<boolean>} True if remote branch exists
|
|
92
|
+
* @throws {TypeError} If remote or branch is not a string
|
|
93
|
+
* @throws {Error} If remote or branch is empty
|
|
94
|
+
* @example
|
|
95
|
+
* await git.remoteBranchExists('origin', 'lane/operations/wu-123'); // true/false
|
|
96
|
+
*/
|
|
97
|
+
remoteBranchExists(remote: string, branch: string): Promise<boolean>;
|
|
80
98
|
/**
|
|
81
99
|
* Fetch from remote
|
|
82
100
|
* @param {string} [remote] - Remote name (defaults to fetching all)
|
|
@@ -391,5 +409,10 @@ export declare function createGitForPath(baseDir: string): GitAdapter;
|
|
|
391
409
|
* const git = getGitForCwd(); // Uses new directory
|
|
392
410
|
*/
|
|
393
411
|
export declare function getGitForCwd(): GitAdapter;
|
|
412
|
+
/**
|
|
413
|
+
* Reset singleton warning flag (for testing only)
|
|
414
|
+
* @internal
|
|
415
|
+
*/
|
|
416
|
+
export declare function _resetSingletonWarning(): void;
|
|
394
417
|
export declare const git: GitAdapter;
|
|
395
418
|
export {};
|
package/dist/git-adapter.js
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import { simpleGit } from 'simple-git';
|
|
20
20
|
import { existsSync, rmSync } from 'node:fs';
|
|
21
|
-
import { GIT_FLAGS } from './wu-constants.js';
|
|
21
|
+
import { GIT_COMMANDS, GIT_FLAGS, GIT_REFS } from './wu-constants.js';
|
|
22
22
|
// WU-2242: Runtime assertion helpers
|
|
23
23
|
/**
|
|
24
24
|
* Assert that a value is a non-empty string
|
|
@@ -117,6 +117,20 @@ export class GitAdapter {
|
|
|
117
117
|
const result = await this.git.raw(['status', GIT_FLAGS.PORCELAIN]);
|
|
118
118
|
return result.trim();
|
|
119
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* Get unpushed commits (compared to upstream)
|
|
122
|
+
* @returns {Promise<string>} Oneline log output for unpushed commits
|
|
123
|
+
* @example
|
|
124
|
+
* await git.getUnpushedCommits(); // "abc123 fix: ...\n"
|
|
125
|
+
*/
|
|
126
|
+
async getUnpushedCommits() {
|
|
127
|
+
const result = await this.git.raw([
|
|
128
|
+
GIT_COMMANDS.LOG,
|
|
129
|
+
GIT_REFS.UPSTREAM_RANGE,
|
|
130
|
+
GIT_FLAGS.ONELINE,
|
|
131
|
+
]);
|
|
132
|
+
return result.trim();
|
|
133
|
+
}
|
|
120
134
|
/**
|
|
121
135
|
* Check if a branch exists
|
|
122
136
|
* @param {string} branch - Branch name
|
|
@@ -137,6 +151,22 @@ export class GitAdapter {
|
|
|
137
151
|
return false;
|
|
138
152
|
}
|
|
139
153
|
}
|
|
154
|
+
/**
|
|
155
|
+
* Check if a remote branch exists via git ls-remote --heads
|
|
156
|
+
* @param {string} remote - Remote name (e.g., 'origin')
|
|
157
|
+
* @param {string} branch - Branch name
|
|
158
|
+
* @returns {Promise<boolean>} True if remote branch exists
|
|
159
|
+
* @throws {TypeError} If remote or branch is not a string
|
|
160
|
+
* @throws {Error} If remote or branch is empty
|
|
161
|
+
* @example
|
|
162
|
+
* await git.remoteBranchExists('origin', 'lane/operations/wu-123'); // true/false
|
|
163
|
+
*/
|
|
164
|
+
async remoteBranchExists(remote, branch) {
|
|
165
|
+
assertNonEmptyString(remote, 'remote');
|
|
166
|
+
assertNonEmptyString(branch, 'branch');
|
|
167
|
+
const result = await this.git.raw(['ls-remote', '--heads', remote, branch]);
|
|
168
|
+
return result.trim().length > 0;
|
|
169
|
+
}
|
|
140
170
|
/**
|
|
141
171
|
* Fetch from remote
|
|
142
172
|
* @param {string} [remote] - Remote name (defaults to fetching all)
|
|
@@ -465,7 +495,13 @@ export class GitAdapter {
|
|
|
465
495
|
// This handles edge cases where git worktree remove succeeds but leaves the directory
|
|
466
496
|
// eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool with validated worktree path
|
|
467
497
|
if (existsSync(worktreePath)) {
|
|
468
|
-
|
|
498
|
+
try {
|
|
499
|
+
rmSync(worktreePath, { recursive: true, force: true });
|
|
500
|
+
}
|
|
501
|
+
catch (rmErr) {
|
|
502
|
+
// WU-1014: Log but don't throw - git worktree remove succeeded, directory cleanup is best-effort
|
|
503
|
+
console.warn(`[git-adapter] worktreeRemove: git succeeded but directory cleanup failed for ${worktreePath}: ${rmErr instanceof Error ? rmErr.message : String(rmErr)}`);
|
|
504
|
+
}
|
|
469
505
|
}
|
|
470
506
|
}
|
|
471
507
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -40,3 +40,6 @@ export * from './dependency-guard.js';
|
|
|
40
40
|
export * from './stamp-utils.js';
|
|
41
41
|
export * from './lumenflow-config.js';
|
|
42
42
|
export * from './lumenflow-config-schema.js';
|
|
43
|
+
export * from './branch-check.js';
|
|
44
|
+
export * from './lumenflow-home.js';
|
|
45
|
+
export * from './spec-branch-helpers.js';
|
package/dist/index.js
CHANGED
|
@@ -59,3 +59,8 @@ export * from './stamp-utils.js';
|
|
|
59
59
|
// Configuration
|
|
60
60
|
export * from './lumenflow-config.js';
|
|
61
61
|
export * from './lumenflow-config-schema.js';
|
|
62
|
+
// Branch check utilities
|
|
63
|
+
export * from './branch-check.js';
|
|
64
|
+
// WU-1062: External plan storage and spec branch helpers
|
|
65
|
+
export * from './lumenflow-home.js';
|
|
66
|
+
export * from './spec-branch-helpers.js';
|
package/dist/lane-checker.d.ts
CHANGED
|
@@ -18,6 +18,22 @@ interface CheckLaneFreeResult {
|
|
|
18
18
|
free: boolean;
|
|
19
19
|
occupiedBy: string | null;
|
|
20
20
|
error: string | null;
|
|
21
|
+
/** WU-1016: List of WU IDs currently in progress in this lane */
|
|
22
|
+
inProgressWUs?: string[];
|
|
23
|
+
/** WU-1016: The configured WIP limit for this lane */
|
|
24
|
+
wipLimit?: number;
|
|
25
|
+
/** WU-1016: Current count of in-progress WUs in this lane */
|
|
26
|
+
currentCount?: number;
|
|
27
|
+
}
|
|
28
|
+
/** WU-1016: Options for checkLaneFree */
|
|
29
|
+
interface CheckLaneFreeOptions {
|
|
30
|
+
/** Path to .lumenflow.config.yaml (for testing) */
|
|
31
|
+
configPath?: string;
|
|
32
|
+
}
|
|
33
|
+
/** WU-1016: Options for getWipLimitForLane */
|
|
34
|
+
interface GetWipLimitOptions {
|
|
35
|
+
/** Path to .lumenflow.config.yaml (for testing) */
|
|
36
|
+
configPath?: string;
|
|
21
37
|
}
|
|
22
38
|
export { getSubLanesForParent };
|
|
23
39
|
/**
|
|
@@ -41,10 +57,27 @@ export declare function extractParent(lane: string): string;
|
|
|
41
57
|
*/
|
|
42
58
|
export declare function validateLaneFormat(lane: string, configPath?: string | null, options?: ValidateLaneOptions): ValidateLaneResult;
|
|
43
59
|
/**
|
|
44
|
-
*
|
|
60
|
+
* WU-1016: Get WIP limit for a lane from config
|
|
61
|
+
*
|
|
62
|
+
* Reads the wip_limit field from .lumenflow.config.yaml for the specified lane.
|
|
63
|
+
* Returns DEFAULT_WIP_LIMIT (1) if the lane is not found or wip_limit is not specified.
|
|
64
|
+
*
|
|
65
|
+
* @param {string} lane - Lane name (e.g., "Core", "CLI")
|
|
66
|
+
* @param {GetWipLimitOptions} options - Options including configPath for testing
|
|
67
|
+
* @returns {number} The WIP limit for the lane (default: 1)
|
|
68
|
+
*/
|
|
69
|
+
export declare function getWipLimitForLane(lane: string, options?: GetWipLimitOptions): number;
|
|
70
|
+
/**
|
|
71
|
+
* Check if a lane is free (in_progress WU count is below wip_limit)
|
|
72
|
+
*
|
|
73
|
+
* WU-1016: Now respects configurable wip_limit per lane from .lumenflow.config.yaml.
|
|
74
|
+
* Lane is considered "free" if current in_progress count < wip_limit.
|
|
75
|
+
* Default wip_limit is 1 if not specified in config (backward compatible).
|
|
76
|
+
*
|
|
45
77
|
* @param {string} statusPath - Path to status.md
|
|
46
78
|
* @param {string} lane - Lane name (e.g., "Operations", "Intelligence")
|
|
47
79
|
* @param {string} wuid - WU ID being claimed (e.g., "WU-419")
|
|
48
|
-
* @
|
|
80
|
+
* @param {CheckLaneFreeOptions} options - Options including configPath for testing
|
|
81
|
+
* @returns {{ free: boolean, occupiedBy: string | null, error: string | null, inProgressWUs?: string[], wipLimit?: number, currentCount?: number }}
|
|
49
82
|
*/
|
|
50
|
-
export declare function checkLaneFree(statusPath: string, lane: string, wuid: string): CheckLaneFreeResult;
|
|
83
|
+
export declare function checkLaneFree(statusPath: string, lane: string, wuid: string, options?: CheckLaneFreeOptions): CheckLaneFreeResult;
|