@lumenflow/core 1.0.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/LICENSE +190 -0
- package/README.md +119 -0
- package/dist/active-wu-detector.d.ts +33 -0
- package/dist/active-wu-detector.js +106 -0
- package/dist/adapters/filesystem-metrics.adapter.d.ts +108 -0
- package/dist/adapters/filesystem-metrics.adapter.js +519 -0
- package/dist/adapters/terminal-renderer.adapter.d.ts +106 -0
- package/dist/adapters/terminal-renderer.adapter.js +337 -0
- package/dist/arg-parser.d.ts +63 -0
- package/dist/arg-parser.js +560 -0
- package/dist/backlog-editor.d.ts +98 -0
- package/dist/backlog-editor.js +179 -0
- package/dist/backlog-generator.d.ts +111 -0
- package/dist/backlog-generator.js +381 -0
- package/dist/backlog-parser.d.ts +45 -0
- package/dist/backlog-parser.js +102 -0
- package/dist/backlog-sync-validator.d.ts +78 -0
- package/dist/backlog-sync-validator.js +294 -0
- package/dist/branch-drift.d.ts +34 -0
- package/dist/branch-drift.js +51 -0
- package/dist/cleanup-install-config.d.ts +33 -0
- package/dist/cleanup-install-config.js +37 -0
- package/dist/cleanup-lock.d.ts +139 -0
- package/dist/cleanup-lock.js +313 -0
- package/dist/code-path-validator.d.ts +146 -0
- package/dist/code-path-validator.js +537 -0
- package/dist/code-paths-overlap.d.ts +55 -0
- package/dist/code-paths-overlap.js +245 -0
- package/dist/commands-logger.d.ts +77 -0
- package/dist/commands-logger.js +254 -0
- package/dist/commit-message-utils.d.ts +25 -0
- package/dist/commit-message-utils.js +41 -0
- package/dist/compliance-parser.d.ts +150 -0
- package/dist/compliance-parser.js +507 -0
- package/dist/constants/backlog-patterns.d.ts +20 -0
- package/dist/constants/backlog-patterns.js +23 -0
- package/dist/constants/dora-constants.d.ts +49 -0
- package/dist/constants/dora-constants.js +53 -0
- package/dist/constants/gate-constants.d.ts +15 -0
- package/dist/constants/gate-constants.js +15 -0
- package/dist/constants/linter-constants.d.ts +16 -0
- package/dist/constants/linter-constants.js +16 -0
- package/dist/constants/tokenizer-constants.d.ts +15 -0
- package/dist/constants/tokenizer-constants.js +15 -0
- package/dist/core/scope-checker.d.ts +97 -0
- package/dist/core/scope-checker.js +163 -0
- package/dist/core/tool-runner.d.ts +161 -0
- package/dist/core/tool-runner.js +393 -0
- package/dist/core/tool.constants.d.ts +105 -0
- package/dist/core/tool.constants.js +101 -0
- package/dist/core/tool.schemas.d.ts +226 -0
- package/dist/core/tool.schemas.js +226 -0
- package/dist/core/worktree-guard.d.ts +130 -0
- package/dist/core/worktree-guard.js +242 -0
- package/dist/coverage-gate.d.ts +108 -0
- package/dist/coverage-gate.js +196 -0
- package/dist/date-utils.d.ts +75 -0
- package/dist/date-utils.js +140 -0
- package/dist/dependency-graph.d.ts +142 -0
- package/dist/dependency-graph.js +550 -0
- package/dist/dependency-guard.d.ts +54 -0
- package/dist/dependency-guard.js +142 -0
- package/dist/dependency-validator.d.ts +105 -0
- package/dist/dependency-validator.js +154 -0
- package/dist/docs-path-validator.d.ts +36 -0
- package/dist/docs-path-validator.js +95 -0
- package/dist/domain/orchestration.constants.d.ts +99 -0
- package/dist/domain/orchestration.constants.js +97 -0
- package/dist/domain/orchestration.schemas.d.ts +280 -0
- package/dist/domain/orchestration.schemas.js +211 -0
- package/dist/domain/orchestration.types.d.ts +133 -0
- package/dist/domain/orchestration.types.js +12 -0
- package/dist/error-handler.d.ts +116 -0
- package/dist/error-handler.js +136 -0
- package/dist/file-classifiers.d.ts +62 -0
- package/dist/file-classifiers.js +108 -0
- package/dist/gates-agent-mode.d.ts +81 -0
- package/dist/gates-agent-mode.js +94 -0
- package/dist/generate-traceability.d.ts +107 -0
- package/dist/generate-traceability.js +411 -0
- package/dist/git-adapter.d.ts +395 -0
- package/dist/git-adapter.js +649 -0
- package/dist/git-staged-validator.d.ts +32 -0
- package/dist/git-staged-validator.js +48 -0
- package/dist/hardcoded-strings.d.ts +61 -0
- package/dist/hardcoded-strings.js +270 -0
- package/dist/incremental-lint.d.ts +78 -0
- package/dist/incremental-lint.js +129 -0
- package/dist/incremental-test.d.ts +39 -0
- package/dist/incremental-test.js +61 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.js +61 -0
- package/dist/invariants/check-automated-tests.d.ts +50 -0
- package/dist/invariants/check-automated-tests.js +166 -0
- package/dist/invariants-runner.d.ts +103 -0
- package/dist/invariants-runner.js +527 -0
- package/dist/lane-checker.d.ts +50 -0
- package/dist/lane-checker.js +319 -0
- package/dist/lane-inference.d.ts +39 -0
- package/dist/lane-inference.js +195 -0
- package/dist/lane-lock.d.ts +211 -0
- package/dist/lane-lock.js +474 -0
- package/dist/lane-validator.d.ts +48 -0
- package/dist/lane-validator.js +114 -0
- package/dist/logs-lib.d.ts +104 -0
- package/dist/logs-lib.js +207 -0
- package/dist/lumenflow-config-schema.d.ts +272 -0
- package/dist/lumenflow-config-schema.js +207 -0
- package/dist/lumenflow-config.d.ts +95 -0
- package/dist/lumenflow-config.js +236 -0
- package/dist/manual-test-validator.d.ts +80 -0
- package/dist/manual-test-validator.js +200 -0
- package/dist/merge-lock.d.ts +115 -0
- package/dist/merge-lock.js +251 -0
- package/dist/micro-worktree.d.ts +159 -0
- package/dist/micro-worktree.js +427 -0
- package/dist/migration-deployer.d.ts +69 -0
- package/dist/migration-deployer.js +151 -0
- package/dist/orchestration-advisory-loader.d.ts +28 -0
- package/dist/orchestration-advisory-loader.js +87 -0
- package/dist/orchestration-advisory.d.ts +58 -0
- package/dist/orchestration-advisory.js +94 -0
- package/dist/orchestration-di.d.ts +48 -0
- package/dist/orchestration-di.js +57 -0
- package/dist/orchestration-rules.d.ts +57 -0
- package/dist/orchestration-rules.js +201 -0
- package/dist/orphan-detector.d.ts +131 -0
- package/dist/orphan-detector.js +226 -0
- package/dist/path-classifiers.d.ts +57 -0
- package/dist/path-classifiers.js +93 -0
- package/dist/piped-command-detector.d.ts +34 -0
- package/dist/piped-command-detector.js +64 -0
- package/dist/ports/dashboard-renderer.port.d.ts +112 -0
- package/dist/ports/dashboard-renderer.port.js +25 -0
- package/dist/ports/metrics-collector.port.d.ts +132 -0
- package/dist/ports/metrics-collector.port.js +26 -0
- package/dist/process-detector.d.ts +84 -0
- package/dist/process-detector.js +172 -0
- package/dist/prompt-linter.d.ts +72 -0
- package/dist/prompt-linter.js +312 -0
- package/dist/prompt-monitor.d.ts +15 -0
- package/dist/prompt-monitor.js +205 -0
- package/dist/rebase-artifact-cleanup.d.ts +145 -0
- package/dist/rebase-artifact-cleanup.js +433 -0
- package/dist/retry-strategy.d.ts +189 -0
- package/dist/retry-strategy.js +283 -0
- package/dist/risk-detector.d.ts +108 -0
- package/dist/risk-detector.js +252 -0
- package/dist/rollback-utils.d.ts +76 -0
- package/dist/rollback-utils.js +104 -0
- package/dist/section-headings.d.ts +43 -0
- package/dist/section-headings.js +49 -0
- package/dist/spawn-escalation.d.ts +90 -0
- package/dist/spawn-escalation.js +253 -0
- package/dist/spawn-monitor.d.ts +229 -0
- package/dist/spawn-monitor.js +672 -0
- package/dist/spawn-recovery.d.ts +82 -0
- package/dist/spawn-recovery.js +298 -0
- package/dist/spawn-registry-schema.d.ts +98 -0
- package/dist/spawn-registry-schema.js +108 -0
- package/dist/spawn-registry-store.d.ts +146 -0
- package/dist/spawn-registry-store.js +273 -0
- package/dist/spawn-tree.d.ts +121 -0
- package/dist/spawn-tree.js +285 -0
- package/dist/stamp-status-validator.d.ts +84 -0
- package/dist/stamp-status-validator.js +134 -0
- package/dist/stamp-utils.d.ts +100 -0
- package/dist/stamp-utils.js +229 -0
- package/dist/state-machine.d.ts +26 -0
- package/dist/state-machine.js +83 -0
- package/dist/system-map-validator.d.ts +80 -0
- package/dist/system-map-validator.js +272 -0
- package/dist/telemetry.d.ts +80 -0
- package/dist/telemetry.js +213 -0
- package/dist/token-counter.d.ts +51 -0
- package/dist/token-counter.js +145 -0
- package/dist/usecases/get-dashboard-data.usecase.d.ts +52 -0
- package/dist/usecases/get-dashboard-data.usecase.js +61 -0
- package/dist/usecases/get-suggestions.usecase.d.ts +100 -0
- package/dist/usecases/get-suggestions.usecase.js +153 -0
- package/dist/user-normalizer.d.ts +41 -0
- package/dist/user-normalizer.js +141 -0
- package/dist/validators/phi-constants.d.ts +97 -0
- package/dist/validators/phi-constants.js +152 -0
- package/dist/validators/phi-scanner.d.ts +58 -0
- package/dist/validators/phi-scanner.js +215 -0
- package/dist/worktree-ownership.d.ts +50 -0
- package/dist/worktree-ownership.js +74 -0
- package/dist/worktree-scanner.d.ts +103 -0
- package/dist/worktree-scanner.js +168 -0
- package/dist/worktree-symlink.d.ts +99 -0
- package/dist/worktree-symlink.js +359 -0
- package/dist/wu-backlog-updater.d.ts +17 -0
- package/dist/wu-backlog-updater.js +37 -0
- package/dist/wu-checkpoint.d.ts +124 -0
- package/dist/wu-checkpoint.js +233 -0
- package/dist/wu-claim-helpers.d.ts +26 -0
- package/dist/wu-claim-helpers.js +63 -0
- package/dist/wu-claim-resume.d.ts +106 -0
- package/dist/wu-claim-resume.js +276 -0
- package/dist/wu-consistency-checker.d.ts +95 -0
- package/dist/wu-consistency-checker.js +567 -0
- package/dist/wu-constants.d.ts +1275 -0
- package/dist/wu-constants.js +1382 -0
- package/dist/wu-create-validators.d.ts +42 -0
- package/dist/wu-create-validators.js +93 -0
- package/dist/wu-done-branch-only.d.ts +63 -0
- package/dist/wu-done-branch-only.js +191 -0
- package/dist/wu-done-messages.d.ts +119 -0
- package/dist/wu-done-messages.js +185 -0
- package/dist/wu-done-pr.d.ts +72 -0
- package/dist/wu-done-pr.js +174 -0
- package/dist/wu-done-retry-helpers.d.ts +85 -0
- package/dist/wu-done-retry-helpers.js +172 -0
- package/dist/wu-done-ui.d.ts +37 -0
- package/dist/wu-done-ui.js +69 -0
- package/dist/wu-done-validators.d.ts +411 -0
- package/dist/wu-done-validators.js +1229 -0
- package/dist/wu-done-worktree.d.ts +182 -0
- package/dist/wu-done-worktree.js +1097 -0
- package/dist/wu-helpers.d.ts +128 -0
- package/dist/wu-helpers.js +248 -0
- package/dist/wu-lint.d.ts +70 -0
- package/dist/wu-lint.js +234 -0
- package/dist/wu-paths.d.ts +171 -0
- package/dist/wu-paths.js +178 -0
- package/dist/wu-preflight-validators.d.ts +86 -0
- package/dist/wu-preflight-validators.js +251 -0
- package/dist/wu-recovery.d.ts +138 -0
- package/dist/wu-recovery.js +341 -0
- package/dist/wu-repair-core.d.ts +131 -0
- package/dist/wu-repair-core.js +669 -0
- package/dist/wu-schema-normalization.d.ts +17 -0
- package/dist/wu-schema-normalization.js +82 -0
- package/dist/wu-schema.d.ts +793 -0
- package/dist/wu-schema.js +881 -0
- package/dist/wu-spawn-helpers.d.ts +121 -0
- package/dist/wu-spawn-helpers.js +271 -0
- package/dist/wu-spawn.d.ts +158 -0
- package/dist/wu-spawn.js +1306 -0
- package/dist/wu-state-schema.d.ts +213 -0
- package/dist/wu-state-schema.js +156 -0
- package/dist/wu-state-store.d.ts +264 -0
- package/dist/wu-state-store.js +691 -0
- package/dist/wu-status-transition.d.ts +63 -0
- package/dist/wu-status-transition.js +382 -0
- package/dist/wu-status-updater.d.ts +25 -0
- package/dist/wu-status-updater.js +116 -0
- package/dist/wu-transaction-collectors.d.ts +116 -0
- package/dist/wu-transaction-collectors.js +272 -0
- package/dist/wu-transaction.d.ts +170 -0
- package/dist/wu-transaction.js +273 -0
- package/dist/wu-validation-constants.d.ts +60 -0
- package/dist/wu-validation-constants.js +66 -0
- package/dist/wu-validation.d.ts +118 -0
- package/dist/wu-validation.js +243 -0
- package/dist/wu-validator.d.ts +62 -0
- package/dist/wu-validator.js +325 -0
- package/dist/wu-yaml-fixer.d.ts +97 -0
- package/dist/wu-yaml-fixer.js +264 -0
- package/dist/wu-yaml.d.ts +86 -0
- package/dist/wu-yaml.js +222 -0
- package/package.json +114 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Orphan Worktree Detector (WU-1476)
|
|
3
|
+
*
|
|
4
|
+
* Detects orphan directories in worktrees/ that are not tracked by git worktree.
|
|
5
|
+
* Orphan directories can occur when wu:done fails mid-workflow due to:
|
|
6
|
+
* - Backlog sync issues
|
|
7
|
+
* - Formatting errors
|
|
8
|
+
* - Typecheck failures
|
|
9
|
+
* - Recovery mode interruptions
|
|
10
|
+
*
|
|
11
|
+
* Multi-layer defense strategy:
|
|
12
|
+
* - Layer 1: Explicit cleanup in git-adapter.worktreeRemove()
|
|
13
|
+
* - Layer 2: Orphan detection in wu:prune (this module)
|
|
14
|
+
* - Layer 3: Pre-flight check in wu:claim
|
|
15
|
+
* - Layer 4: Manual utility wu:cleanup-orphans
|
|
16
|
+
*
|
|
17
|
+
* @see {@link tools/wu-prune.mjs} - Primary consumer
|
|
18
|
+
* @see {@link tools/wu-claim.mjs} - Pre-flight orphan check
|
|
19
|
+
* @see {@link tools/lib/git-adapter.mjs} - worktreeRemove with cleanup
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Result of orphan detection
|
|
23
|
+
* @typedef {object} OrphanDetectionResult
|
|
24
|
+
* @property {string[]} orphans - List of orphan directory paths (absolute)
|
|
25
|
+
* @property {string[]} tracked - List of tracked worktree paths (absolute)
|
|
26
|
+
* @property {string[]} errors - List of error messages encountered during detection
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* Parsed worktree entry from git worktree list --porcelain
|
|
30
|
+
*/
|
|
31
|
+
interface WorktreeEntry {
|
|
32
|
+
/** Absolute path to worktree */
|
|
33
|
+
path: string;
|
|
34
|
+
/** HEAD commit SHA */
|
|
35
|
+
head?: string;
|
|
36
|
+
/** Branch name (without refs/heads/ prefix) */
|
|
37
|
+
branch?: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Parse git worktree list --porcelain output into structured entries
|
|
41
|
+
*
|
|
42
|
+
* @param {string} porcelainOutput - Output from git worktree list --porcelain
|
|
43
|
+
* @returns {WorktreeEntry[]} Parsed worktree entries
|
|
44
|
+
*/
|
|
45
|
+
export declare function parseWorktreeList(porcelainOutput: string): WorktreeEntry[];
|
|
46
|
+
/**
|
|
47
|
+
* Get set of tracked worktree paths from git
|
|
48
|
+
*
|
|
49
|
+
* @param {string} [projectRoot] - Project root directory (defaults to cwd)
|
|
50
|
+
* @returns {Promise<Set<string>>} Set of absolute paths tracked by git worktree
|
|
51
|
+
*/
|
|
52
|
+
export declare function getTrackedWorktreePaths(projectRoot: any): Promise<Set<string>>;
|
|
53
|
+
/**
|
|
54
|
+
* Get list of directories in worktrees/ folder
|
|
55
|
+
*
|
|
56
|
+
* @param {string} projectRoot - Absolute path to project root
|
|
57
|
+
* @returns {string[]} List of absolute paths to directories in worktrees/
|
|
58
|
+
*/
|
|
59
|
+
export declare function getWorktreeDirectories(projectRoot: any): string[];
|
|
60
|
+
/**
|
|
61
|
+
* Detect orphan worktree directories
|
|
62
|
+
*
|
|
63
|
+
* Compares directories in worktrees/ against git worktree list to find
|
|
64
|
+
* directories that exist on disk but are not tracked by git.
|
|
65
|
+
*
|
|
66
|
+
* @param {string} [projectRoot] - Project root directory (defaults to cwd)
|
|
67
|
+
* @returns {Promise<OrphanDetectionResult>} Detection result with orphans and tracked paths
|
|
68
|
+
*/
|
|
69
|
+
export declare function detectOrphanWorktrees(projectRoot: any): Promise<{
|
|
70
|
+
orphans: string[];
|
|
71
|
+
tracked: any[];
|
|
72
|
+
errors: any[];
|
|
73
|
+
}>;
|
|
74
|
+
/**
|
|
75
|
+
* Check if a specific worktree path is an orphan
|
|
76
|
+
*
|
|
77
|
+
* Used by wu:claim for pre-flight checks before creating a worktree.
|
|
78
|
+
*
|
|
79
|
+
* @param {string} worktreePath - Absolute path to check
|
|
80
|
+
* @param {string} [projectRoot] - Project root directory (defaults to cwd)
|
|
81
|
+
* @returns {Promise<boolean>} True if the path exists but is not tracked by git
|
|
82
|
+
*/
|
|
83
|
+
export declare function isOrphanWorktree(worktreePath: any, projectRoot: any): Promise<boolean>;
|
|
84
|
+
/**
|
|
85
|
+
* Options for removing orphan directories
|
|
86
|
+
*/
|
|
87
|
+
export interface RemoveOrphanDirectoryOptions {
|
|
88
|
+
/** If true, don't actually remove */
|
|
89
|
+
dryRun?: boolean;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Remove an orphan directory
|
|
93
|
+
*
|
|
94
|
+
* Safely removes an orphan worktree directory from disk.
|
|
95
|
+
* Only removes directories that are confirmed orphans.
|
|
96
|
+
*
|
|
97
|
+
* @param {string} orphanPath - Absolute path to orphan directory
|
|
98
|
+
* @param {RemoveOrphanDirectoryOptions} [options] - Options
|
|
99
|
+
* @returns {Promise<{removed: boolean, path: string, error?: string}>} Result
|
|
100
|
+
*/
|
|
101
|
+
export declare function removeOrphanDirectory(orphanPath: any, options?: RemoveOrphanDirectoryOptions): Promise<{
|
|
102
|
+
removed: boolean;
|
|
103
|
+
path: any;
|
|
104
|
+
error: string;
|
|
105
|
+
dryRun?: undefined;
|
|
106
|
+
} | {
|
|
107
|
+
removed: boolean;
|
|
108
|
+
path: any;
|
|
109
|
+
dryRun: boolean;
|
|
110
|
+
error?: undefined;
|
|
111
|
+
} | {
|
|
112
|
+
removed: boolean;
|
|
113
|
+
path: any;
|
|
114
|
+
error?: undefined;
|
|
115
|
+
dryRun?: undefined;
|
|
116
|
+
}>;
|
|
117
|
+
/**
|
|
118
|
+
* Clean up all orphan directories
|
|
119
|
+
*
|
|
120
|
+
* Detects and removes all orphan worktree directories.
|
|
121
|
+
*
|
|
122
|
+
* @param {string} [projectRoot] - Project root directory (defaults to cwd)
|
|
123
|
+
* @param {RemoveOrphanDirectoryOptions} [options] - Options
|
|
124
|
+
* @returns {Promise<{detected: number, removed: number, errors: string[]}>} Summary
|
|
125
|
+
*/
|
|
126
|
+
export declare function cleanupOrphanDirectories(projectRoot: any, options?: RemoveOrphanDirectoryOptions): Promise<{
|
|
127
|
+
detected: number;
|
|
128
|
+
removed: number;
|
|
129
|
+
errors: any[];
|
|
130
|
+
}>;
|
|
131
|
+
export {};
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Orphan Worktree Detector (WU-1476)
|
|
3
|
+
*
|
|
4
|
+
* Detects orphan directories in worktrees/ that are not tracked by git worktree.
|
|
5
|
+
* Orphan directories can occur when wu:done fails mid-workflow due to:
|
|
6
|
+
* - Backlog sync issues
|
|
7
|
+
* - Formatting errors
|
|
8
|
+
* - Typecheck failures
|
|
9
|
+
* - Recovery mode interruptions
|
|
10
|
+
*
|
|
11
|
+
* Multi-layer defense strategy:
|
|
12
|
+
* - Layer 1: Explicit cleanup in git-adapter.worktreeRemove()
|
|
13
|
+
* - Layer 2: Orphan detection in wu:prune (this module)
|
|
14
|
+
* - Layer 3: Pre-flight check in wu:claim
|
|
15
|
+
* - Layer 4: Manual utility wu:cleanup-orphans
|
|
16
|
+
*
|
|
17
|
+
* @see {@link tools/wu-prune.mjs} - Primary consumer
|
|
18
|
+
* @see {@link tools/wu-claim.mjs} - Pre-flight orphan check
|
|
19
|
+
* @see {@link tools/lib/git-adapter.mjs} - worktreeRemove with cleanup
|
|
20
|
+
*/
|
|
21
|
+
import { readdirSync, statSync, existsSync, rmSync } from 'node:fs';
|
|
22
|
+
import path from 'node:path';
|
|
23
|
+
import { getGitForCwd, createGitForPath } from './git-adapter.js';
|
|
24
|
+
import { STRING_LITERALS, DEFAULTS } from './wu-constants.js';
|
|
25
|
+
/**
|
|
26
|
+
* Parse git worktree list --porcelain output into structured entries
|
|
27
|
+
*
|
|
28
|
+
* @param {string} porcelainOutput - Output from git worktree list --porcelain
|
|
29
|
+
* @returns {WorktreeEntry[]} Parsed worktree entries
|
|
30
|
+
*/
|
|
31
|
+
export function parseWorktreeList(porcelainOutput) {
|
|
32
|
+
if (!porcelainOutput || porcelainOutput.trim() === '') {
|
|
33
|
+
return [];
|
|
34
|
+
}
|
|
35
|
+
const worktrees = [];
|
|
36
|
+
const lines = porcelainOutput.split(STRING_LITERALS.NEWLINE);
|
|
37
|
+
let current = {};
|
|
38
|
+
for (const line of lines) {
|
|
39
|
+
if (line.startsWith('worktree ')) {
|
|
40
|
+
if (current.path) {
|
|
41
|
+
worktrees.push(current);
|
|
42
|
+
}
|
|
43
|
+
current = { path: line.substring(9).trim() };
|
|
44
|
+
}
|
|
45
|
+
else if (line.startsWith('HEAD ')) {
|
|
46
|
+
current.head = line.substring(5).trim();
|
|
47
|
+
}
|
|
48
|
+
else if (line.startsWith('branch ')) {
|
|
49
|
+
const fullRef = line.substring(7).trim();
|
|
50
|
+
// Extract branch name from refs/heads/...
|
|
51
|
+
current.branch = fullRef.replace(/^refs\/heads\//, '');
|
|
52
|
+
}
|
|
53
|
+
else if (line === '') {
|
|
54
|
+
if (current.path) {
|
|
55
|
+
worktrees.push(current);
|
|
56
|
+
}
|
|
57
|
+
current = {};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// Handle final entry without trailing newline
|
|
61
|
+
if (current.path) {
|
|
62
|
+
worktrees.push(current);
|
|
63
|
+
}
|
|
64
|
+
return worktrees;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Get set of tracked worktree paths from git
|
|
68
|
+
*
|
|
69
|
+
* @param {string} [projectRoot] - Project root directory (defaults to cwd)
|
|
70
|
+
* @returns {Promise<Set<string>>} Set of absolute paths tracked by git worktree
|
|
71
|
+
*/
|
|
72
|
+
export async function getTrackedWorktreePaths(projectRoot) {
|
|
73
|
+
const git = projectRoot ? createGitForPath(projectRoot) : getGitForCwd();
|
|
74
|
+
const output = await git.worktreeList();
|
|
75
|
+
const entries = parseWorktreeList(output);
|
|
76
|
+
return new Set(entries.map((e) => e.path));
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Get list of directories in worktrees/ folder
|
|
80
|
+
*
|
|
81
|
+
* @param {string} projectRoot - Absolute path to project root
|
|
82
|
+
* @returns {string[]} List of absolute paths to directories in worktrees/
|
|
83
|
+
*/
|
|
84
|
+
export function getWorktreeDirectories(projectRoot) {
|
|
85
|
+
const worktreesDir = path.join(projectRoot, DEFAULTS.WORKTREES_DIR);
|
|
86
|
+
if (!existsSync(worktreesDir)) {
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
const entries = readdirSync(worktreesDir, { withFileTypes: true });
|
|
91
|
+
return entries
|
|
92
|
+
.filter((entry) => entry.isDirectory())
|
|
93
|
+
.map((entry) => path.join(worktreesDir, entry.name));
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Detect orphan worktree directories
|
|
101
|
+
*
|
|
102
|
+
* Compares directories in worktrees/ against git worktree list to find
|
|
103
|
+
* directories that exist on disk but are not tracked by git.
|
|
104
|
+
*
|
|
105
|
+
* @param {string} [projectRoot] - Project root directory (defaults to cwd)
|
|
106
|
+
* @returns {Promise<OrphanDetectionResult>} Detection result with orphans and tracked paths
|
|
107
|
+
*/
|
|
108
|
+
export async function detectOrphanWorktrees(projectRoot) {
|
|
109
|
+
const errors = [];
|
|
110
|
+
const root = projectRoot || process.cwd();
|
|
111
|
+
// Get paths tracked by git worktree
|
|
112
|
+
let trackedPaths;
|
|
113
|
+
try {
|
|
114
|
+
trackedPaths = await getTrackedWorktreePaths(root);
|
|
115
|
+
}
|
|
116
|
+
catch (err) {
|
|
117
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
118
|
+
errors.push(`Failed to get git worktree list: ${message}`);
|
|
119
|
+
return { orphans: [], tracked: [], errors };
|
|
120
|
+
}
|
|
121
|
+
// Get directories on disk
|
|
122
|
+
const diskDirectories = getWorktreeDirectories(root);
|
|
123
|
+
// Find orphans: directories that exist but aren't tracked
|
|
124
|
+
const orphans = diskDirectories.filter((dir) => !trackedPaths.has(dir));
|
|
125
|
+
return {
|
|
126
|
+
orphans,
|
|
127
|
+
tracked: [...trackedPaths],
|
|
128
|
+
errors,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Check if a specific worktree path is an orphan
|
|
133
|
+
*
|
|
134
|
+
* Used by wu:claim for pre-flight checks before creating a worktree.
|
|
135
|
+
*
|
|
136
|
+
* @param {string} worktreePath - Absolute path to check
|
|
137
|
+
* @param {string} [projectRoot] - Project root directory (defaults to cwd)
|
|
138
|
+
* @returns {Promise<boolean>} True if the path exists but is not tracked by git
|
|
139
|
+
*/
|
|
140
|
+
export async function isOrphanWorktree(worktreePath, projectRoot) {
|
|
141
|
+
// If directory doesn't exist, it's not an orphan
|
|
142
|
+
if (!existsSync(worktreePath)) {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
// Check if path is in the tracked set
|
|
146
|
+
const trackedPaths = await getTrackedWorktreePaths(projectRoot);
|
|
147
|
+
return !trackedPaths.has(worktreePath);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Remove an orphan directory
|
|
151
|
+
*
|
|
152
|
+
* Safely removes an orphan worktree directory from disk.
|
|
153
|
+
* Only removes directories that are confirmed orphans.
|
|
154
|
+
*
|
|
155
|
+
* @param {string} orphanPath - Absolute path to orphan directory
|
|
156
|
+
* @param {RemoveOrphanDirectoryOptions} [options] - Options
|
|
157
|
+
* @returns {Promise<{removed: boolean, path: string, error?: string}>} Result
|
|
158
|
+
*/
|
|
159
|
+
export async function removeOrphanDirectory(orphanPath, options = {}) {
|
|
160
|
+
const { dryRun = false } = options;
|
|
161
|
+
// Verify it exists
|
|
162
|
+
if (!existsSync(orphanPath)) {
|
|
163
|
+
return { removed: false, path: orphanPath, error: 'Directory does not exist' };
|
|
164
|
+
}
|
|
165
|
+
// Verify it's a directory
|
|
166
|
+
try {
|
|
167
|
+
const stat = statSync(orphanPath);
|
|
168
|
+
if (!stat.isDirectory()) {
|
|
169
|
+
return { removed: false, path: orphanPath, error: 'Path is not a directory' };
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
catch (err) {
|
|
173
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
174
|
+
return { removed: false, path: orphanPath, error: `Failed to stat: ${message}` };
|
|
175
|
+
}
|
|
176
|
+
// Verify it's in worktrees/ directory (safety check)
|
|
177
|
+
const basename = path.basename(path.dirname(orphanPath));
|
|
178
|
+
if (basename !== DEFAULTS.WORKTREES_DIR) {
|
|
179
|
+
return {
|
|
180
|
+
removed: false,
|
|
181
|
+
path: orphanPath,
|
|
182
|
+
error: `Safety check failed: not in ${DEFAULTS.WORKTREES_DIR}/ directory`,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
if (dryRun) {
|
|
186
|
+
return { removed: false, path: orphanPath, dryRun: true };
|
|
187
|
+
}
|
|
188
|
+
// Remove the directory
|
|
189
|
+
try {
|
|
190
|
+
rmSync(orphanPath, { recursive: true, force: true });
|
|
191
|
+
return { removed: true, path: orphanPath };
|
|
192
|
+
}
|
|
193
|
+
catch (err) {
|
|
194
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
195
|
+
return { removed: false, path: orphanPath, error: `Failed to remove: ${message}` };
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Clean up all orphan directories
|
|
200
|
+
*
|
|
201
|
+
* Detects and removes all orphan worktree directories.
|
|
202
|
+
*
|
|
203
|
+
* @param {string} [projectRoot] - Project root directory (defaults to cwd)
|
|
204
|
+
* @param {RemoveOrphanDirectoryOptions} [options] - Options
|
|
205
|
+
* @returns {Promise<{detected: number, removed: number, errors: string[]}>} Summary
|
|
206
|
+
*/
|
|
207
|
+
export async function cleanupOrphanDirectories(projectRoot, options = {}) {
|
|
208
|
+
const { dryRun = false } = options;
|
|
209
|
+
const result = await detectOrphanWorktrees(projectRoot);
|
|
210
|
+
const errors = [...result.errors];
|
|
211
|
+
let removed = 0;
|
|
212
|
+
for (const orphanPath of result.orphans) {
|
|
213
|
+
const removeResult = await removeOrphanDirectory(orphanPath, { dryRun });
|
|
214
|
+
if (removeResult.removed) {
|
|
215
|
+
removed++;
|
|
216
|
+
}
|
|
217
|
+
else if (removeResult.error) {
|
|
218
|
+
errors.push(`${orphanPath}: ${removeResult.error}`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return {
|
|
222
|
+
detected: result.orphans.length,
|
|
223
|
+
removed,
|
|
224
|
+
errors,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Path Classification Utilities for WU Tooling
|
|
4
|
+
*
|
|
5
|
+
* WU-1255: Classifies file paths to determine test scoping.
|
|
6
|
+
* Uses string methods (no regex) for path prefix matching.
|
|
7
|
+
*
|
|
8
|
+
* Paths that skip web app tests:
|
|
9
|
+
* - Documentation: docs/, ai/, .claude/, README*, CLAUDE*.md
|
|
10
|
+
* - Tooling: tools/, scripts/
|
|
11
|
+
*
|
|
12
|
+
* @see {@link tools/lib/wu-done-validators.mjs} - Consumer for detectDocsOnlyByPaths
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Prefixes for paths that should skip web app tests.
|
|
16
|
+
* These are paths for docs, tooling, and configuration that don't affect app functionality.
|
|
17
|
+
*
|
|
18
|
+
* @constant {string[]}
|
|
19
|
+
*/
|
|
20
|
+
export declare const SKIP_TESTS_PREFIXES: readonly string[];
|
|
21
|
+
/**
|
|
22
|
+
* Root file patterns that should skip web app tests.
|
|
23
|
+
* These are files in the root directory that are documentation.
|
|
24
|
+
*
|
|
25
|
+
* @constant {string[]}
|
|
26
|
+
*/
|
|
27
|
+
export declare const SKIP_TESTS_ROOT_FILES: readonly string[];
|
|
28
|
+
/**
|
|
29
|
+
* Check if a single file path should skip web app tests.
|
|
30
|
+
*
|
|
31
|
+
* Uses string methods for matching:
|
|
32
|
+
* - `startsWith` for directory prefixes (docs/, tools/, etc.)
|
|
33
|
+
* - `toLowerCase` for case-insensitive root file matching
|
|
34
|
+
*
|
|
35
|
+
* @param {string|null|undefined} filePath - File path to check
|
|
36
|
+
* @returns {boolean} True if path should skip web app tests
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* isSkipWebTestsPath('docs/README.md') // true
|
|
40
|
+
* isSkipWebTestsPath('tools/wu-done.js') // true
|
|
41
|
+
* isSkipWebTestsPath('apps/web/src/page.tsx') // false
|
|
42
|
+
*/
|
|
43
|
+
export declare function isSkipWebTestsPath(filePath: any): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Check if ALL code paths should skip web app tests.
|
|
46
|
+
*
|
|
47
|
+
* Returns true only if EVERY path in the array is a skip-tests path.
|
|
48
|
+
* This is the aggregate check for WU YAML code_paths array.
|
|
49
|
+
*
|
|
50
|
+
* @param {string[]|null|undefined} codePaths - Array of file paths from WU YAML
|
|
51
|
+
* @returns {boolean} True if all paths should skip web app tests
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* shouldSkipWebTests(['docs/README.md', 'tools/wu-done.js']) // true
|
|
55
|
+
* shouldSkipWebTests(['docs/README.md', 'apps/web/src/page.tsx']) // false
|
|
56
|
+
*/
|
|
57
|
+
export declare function shouldSkipWebTests(codePaths: any): boolean;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Path Classification Utilities for WU Tooling
|
|
4
|
+
*
|
|
5
|
+
* WU-1255: Classifies file paths to determine test scoping.
|
|
6
|
+
* Uses string methods (no regex) for path prefix matching.
|
|
7
|
+
*
|
|
8
|
+
* Paths that skip web app tests:
|
|
9
|
+
* - Documentation: docs/, ai/, .claude/, README*, CLAUDE*.md
|
|
10
|
+
* - Tooling: tools/, scripts/
|
|
11
|
+
*
|
|
12
|
+
* @see {@link tools/lib/wu-done-validators.mjs} - Consumer for detectDocsOnlyByPaths
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Prefixes for paths that should skip web app tests.
|
|
16
|
+
* These are paths for docs, tooling, and configuration that don't affect app functionality.
|
|
17
|
+
*
|
|
18
|
+
* @constant {string[]}
|
|
19
|
+
*/
|
|
20
|
+
export const SKIP_TESTS_PREFIXES = Object.freeze([
|
|
21
|
+
'docs/',
|
|
22
|
+
'ai/',
|
|
23
|
+
'.claude/',
|
|
24
|
+
'tools/', // WU-1255: Tooling WUs
|
|
25
|
+
'scripts/', // WU-1255: Scripts WUs
|
|
26
|
+
]);
|
|
27
|
+
/**
|
|
28
|
+
* Root file patterns that should skip web app tests.
|
|
29
|
+
* These are files in the root directory that are documentation.
|
|
30
|
+
*
|
|
31
|
+
* @constant {string[]}
|
|
32
|
+
*/
|
|
33
|
+
export const SKIP_TESTS_ROOT_FILES = Object.freeze([
|
|
34
|
+
'readme', // Case-insensitive match
|
|
35
|
+
'claude', // CLAUDE.md, CLAUDE-core.md, etc.
|
|
36
|
+
]);
|
|
37
|
+
/**
|
|
38
|
+
* Check if a single file path should skip web app tests.
|
|
39
|
+
*
|
|
40
|
+
* Uses string methods for matching:
|
|
41
|
+
* - `startsWith` for directory prefixes (docs/, tools/, etc.)
|
|
42
|
+
* - `toLowerCase` for case-insensitive root file matching
|
|
43
|
+
*
|
|
44
|
+
* @param {string|null|undefined} filePath - File path to check
|
|
45
|
+
* @returns {boolean} True if path should skip web app tests
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* isSkipWebTestsPath('docs/README.md') // true
|
|
49
|
+
* isSkipWebTestsPath('tools/wu-done.js') // true
|
|
50
|
+
* isSkipWebTestsPath('apps/web/src/page.tsx') // false
|
|
51
|
+
*/
|
|
52
|
+
export function isSkipWebTestsPath(filePath) {
|
|
53
|
+
if (!filePath || typeof filePath !== 'string') {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
const path = filePath.trim();
|
|
57
|
+
if (path.length === 0) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
// Check directory prefixes (docs/, ai/, .claude/, tools/, scripts/)
|
|
61
|
+
for (const prefix of SKIP_TESTS_PREFIXES) {
|
|
62
|
+
if (path.startsWith(prefix)) {
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Check root file patterns (README*, CLAUDE*.md)
|
|
67
|
+
const lowerPath = path.toLowerCase();
|
|
68
|
+
for (const pattern of SKIP_TESTS_ROOT_FILES) {
|
|
69
|
+
if (lowerPath.startsWith(pattern)) {
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Check if ALL code paths should skip web app tests.
|
|
77
|
+
*
|
|
78
|
+
* Returns true only if EVERY path in the array is a skip-tests path.
|
|
79
|
+
* This is the aggregate check for WU YAML code_paths array.
|
|
80
|
+
*
|
|
81
|
+
* @param {string[]|null|undefined} codePaths - Array of file paths from WU YAML
|
|
82
|
+
* @returns {boolean} True if all paths should skip web app tests
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* shouldSkipWebTests(['docs/README.md', 'tools/wu-done.js']) // true
|
|
86
|
+
* shouldSkipWebTests(['docs/README.md', 'apps/web/src/page.tsx']) // false
|
|
87
|
+
*/
|
|
88
|
+
export function shouldSkipWebTests(codePaths) {
|
|
89
|
+
if (!codePaths || !Array.isArray(codePaths) || codePaths.length === 0) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
return codePaths.every((path) => isSkipWebTestsPath(path));
|
|
93
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WU-2278: Piped Command Detection
|
|
3
|
+
*
|
|
4
|
+
* Detects when pnpm dependency commands are being executed with piped input,
|
|
5
|
+
* which can bypass interactive prompts and cause security issues.
|
|
6
|
+
*
|
|
7
|
+
* Note: No external library exists for this specific shell command analysis.
|
|
8
|
+
*
|
|
9
|
+
* @module piped-command-detector
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Check if a command is a piped pnpm dependency command
|
|
13
|
+
*
|
|
14
|
+
* Detects patterns like:
|
|
15
|
+
* - echo "y" | pnpm add foo
|
|
16
|
+
* - yes | pnpm install
|
|
17
|
+
* - pnpm add foo < /dev/null
|
|
18
|
+
* - pnpm install <<< "y"
|
|
19
|
+
*
|
|
20
|
+
* Does NOT flag:
|
|
21
|
+
* - pnpm test | grep foo (pnpm not receiving input)
|
|
22
|
+
* - pnpm add foo (no pipe)
|
|
23
|
+
*
|
|
24
|
+
* @param {string} command - Shell command to analyze
|
|
25
|
+
* @returns {boolean} True if command is a piped pnpm dependency command
|
|
26
|
+
*/
|
|
27
|
+
export declare function isPipedPnpmCommand(command: any): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Check if a command contains any dependency-mutating pnpm subcommand
|
|
30
|
+
*
|
|
31
|
+
* @param {string} command - Shell command to analyze
|
|
32
|
+
* @returns {boolean} True if command contains pnpm dependency mutation
|
|
33
|
+
*/
|
|
34
|
+
export declare function containsPnpmDependencyCommand(command: any): boolean;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WU-2278: Piped Command Detection
|
|
3
|
+
*
|
|
4
|
+
* Detects when pnpm dependency commands are being executed with piped input,
|
|
5
|
+
* which can bypass interactive prompts and cause security issues.
|
|
6
|
+
*
|
|
7
|
+
* Note: No external library exists for this specific shell command analysis.
|
|
8
|
+
*
|
|
9
|
+
* @module piped-command-detector
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* List of pnpm commands that mutate dependencies
|
|
13
|
+
* @constant {string[]}
|
|
14
|
+
*/
|
|
15
|
+
const DEPENDENCY_COMMANDS = ['add', 'install', 'i', 'remove', 'rm', 'uninstall', 'update', 'up'];
|
|
16
|
+
/**
|
|
17
|
+
* Check if a command is a piped pnpm dependency command
|
|
18
|
+
*
|
|
19
|
+
* Detects patterns like:
|
|
20
|
+
* - echo "y" | pnpm add foo
|
|
21
|
+
* - yes | pnpm install
|
|
22
|
+
* - pnpm add foo < /dev/null
|
|
23
|
+
* - pnpm install <<< "y"
|
|
24
|
+
*
|
|
25
|
+
* Does NOT flag:
|
|
26
|
+
* - pnpm test | grep foo (pnpm not receiving input)
|
|
27
|
+
* - pnpm add foo (no pipe)
|
|
28
|
+
*
|
|
29
|
+
* @param {string} command - Shell command to analyze
|
|
30
|
+
* @returns {boolean} True if command is a piped pnpm dependency command
|
|
31
|
+
*/
|
|
32
|
+
export function isPipedPnpmCommand(command) {
|
|
33
|
+
if (!command || typeof command !== 'string') {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
// Pattern 1: Something piped TO pnpm (before pnpm in command)
|
|
37
|
+
// e.g., "echo y | pnpm add", "yes | pnpm install"
|
|
38
|
+
const pipeToPattern = /\|\s*pnpm\s+(add|install|i|remove|rm|uninstall|update|up)\b/i;
|
|
39
|
+
if (pipeToPattern.test(command)) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
// Pattern 2: Input redirection (< or <<<)
|
|
43
|
+
// e.g., "pnpm add foo < /dev/null", "pnpm install <<< y"
|
|
44
|
+
const redirectPattern = /pnpm\s+(add|install|i|remove|rm|uninstall|update|up)\b[^|]*<(?!<)/i;
|
|
45
|
+
const heredocPattern = /pnpm\s+(add|install|i|remove|rm|uninstall|update|up)\b[^|]*<<</i;
|
|
46
|
+
if (redirectPattern.test(command) || heredocPattern.test(command)) {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Check if a command contains any dependency-mutating pnpm subcommand
|
|
53
|
+
*
|
|
54
|
+
* @param {string} command - Shell command to analyze
|
|
55
|
+
* @returns {boolean} True if command contains pnpm dependency mutation
|
|
56
|
+
*/
|
|
57
|
+
export function containsPnpmDependencyCommand(command) {
|
|
58
|
+
if (!command || typeof command !== 'string') {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
// eslint-disable-next-line security/detect-non-literal-regexp -- Pattern built from const array, not user input
|
|
62
|
+
const pattern = new RegExp(`pnpm\\s+(${DEPENDENCY_COMMANDS.join('|')})\\b`, 'i');
|
|
63
|
+
return pattern.test(command);
|
|
64
|
+
}
|