@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,649 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file git-adapter.mjs
|
|
3
|
+
* @description Git operations adapter using simple-git library
|
|
4
|
+
* WU-1082: Extract shared utilities (eliminate run() duplication)
|
|
5
|
+
* WU-1213: Refactor to use simple-git library (eliminate execSync)
|
|
6
|
+
* WU-2242: Add runtime type assertions to prevent silent API misuse
|
|
7
|
+
*
|
|
8
|
+
* Replaces run() function in:
|
|
9
|
+
* - tools/wu-claim.mjs
|
|
10
|
+
* - tools/wu-done.mjs
|
|
11
|
+
* - tools/wu-block.mjs
|
|
12
|
+
* - tools/wu-unblock.mjs
|
|
13
|
+
* - tools/wu-create.mjs
|
|
14
|
+
* - tools/wu-cleanup.mjs
|
|
15
|
+
* - tools/gates-pre-commit.mjs
|
|
16
|
+
* - tools/validate.mjs
|
|
17
|
+
* - tools/guard-worktree-commit.mjs
|
|
18
|
+
*/
|
|
19
|
+
import { simpleGit } from 'simple-git';
|
|
20
|
+
import { existsSync, rmSync } from 'node:fs';
|
|
21
|
+
import { GIT_FLAGS } from './wu-constants.js';
|
|
22
|
+
// WU-2242: Runtime assertion helpers
|
|
23
|
+
/**
|
|
24
|
+
* Assert that a value is a non-empty string
|
|
25
|
+
* @param {*} value - Value to check
|
|
26
|
+
* @param {string} name - Parameter name for error message
|
|
27
|
+
* @throws {TypeError} If value is not a string
|
|
28
|
+
* @throws {Error} If value is an empty string
|
|
29
|
+
*/
|
|
30
|
+
function assertNonEmptyString(value, name) {
|
|
31
|
+
if (typeof value !== 'string') {
|
|
32
|
+
throw new TypeError(`${name} must be a string, got ${typeof value}`);
|
|
33
|
+
}
|
|
34
|
+
if (value === '') {
|
|
35
|
+
throw new Error(`${name} must be a non-empty string`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Assert that a value is a string (if provided)
|
|
40
|
+
* @param {*} value - Value to check
|
|
41
|
+
* @param {string} name - Parameter name for error message
|
|
42
|
+
* @throws {TypeError} If value is not a string (and not null/undefined)
|
|
43
|
+
*/
|
|
44
|
+
function assertOptionalString(value, name) {
|
|
45
|
+
if (value !== undefined && value !== null && typeof value !== 'string') {
|
|
46
|
+
throw new TypeError(`${name} must be a string, got ${typeof value}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Assert that a value is a string or array of strings
|
|
51
|
+
* @param {*} value - Value to check
|
|
52
|
+
* @param {string} name - Parameter name for error message
|
|
53
|
+
* @throws {TypeError} If value is not a string or array
|
|
54
|
+
* @throws {Error} If value is empty string or empty array
|
|
55
|
+
*/
|
|
56
|
+
function assertStringOrArray(value, name) {
|
|
57
|
+
if (typeof value === 'string') {
|
|
58
|
+
if (value === '') {
|
|
59
|
+
throw new Error(`${name} must be a non-empty string or array`);
|
|
60
|
+
}
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (Array.isArray(value)) {
|
|
64
|
+
if (value.length === 0) {
|
|
65
|
+
throw new Error(`${name} must be a non-empty string or array`);
|
|
66
|
+
}
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
throw new TypeError(`${name} must be a string or array, got ${typeof value}`);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Assert that a value is an array
|
|
73
|
+
* @param {*} value - Value to check
|
|
74
|
+
* @param {string} name - Parameter name for error message
|
|
75
|
+
* @throws {TypeError} If value is not an array
|
|
76
|
+
*/
|
|
77
|
+
function assertArray(value, name) {
|
|
78
|
+
if (!Array.isArray(value)) {
|
|
79
|
+
throw new TypeError(`${name} must be an array, got ${typeof value}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Git operations adapter with dependency injection support
|
|
84
|
+
* @class GitAdapter
|
|
85
|
+
*/
|
|
86
|
+
export class GitAdapter {
|
|
87
|
+
git;
|
|
88
|
+
/**
|
|
89
|
+
* Create a GitAdapter instance
|
|
90
|
+
* @param {object} [options] - Configuration options
|
|
91
|
+
* @param {object} [options.git] - simple-git instance (for testing)
|
|
92
|
+
* @param {string} [options.baseDir] - Base directory for git operations
|
|
93
|
+
*/
|
|
94
|
+
constructor(options = {}) {
|
|
95
|
+
if (options.baseDir && process.env.DEBUG) {
|
|
96
|
+
console.log(`[git-adapter] DEBUG: GitAdapter constructor with baseDir=${options.baseDir}`);
|
|
97
|
+
}
|
|
98
|
+
this.git = options.git || simpleGit(options.baseDir);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Get current branch name
|
|
102
|
+
* @returns {Promise<string>} Current branch name
|
|
103
|
+
* @example
|
|
104
|
+
* await git.getCurrentBranch(); // "lane/operations/wu-1213"
|
|
105
|
+
*/
|
|
106
|
+
async getCurrentBranch() {
|
|
107
|
+
const result = await this.git.revparse([GIT_FLAGS.ABBREV_REF, 'HEAD']);
|
|
108
|
+
return result.trim();
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Get git status (porcelain format string for compatibility)
|
|
112
|
+
* @returns {Promise<string>} Status output in porcelain format
|
|
113
|
+
* @example
|
|
114
|
+
* await git.getStatus(); // " M file.txt\n?? untracked.txt"
|
|
115
|
+
*/
|
|
116
|
+
async getStatus() {
|
|
117
|
+
const result = await this.git.raw(['status', GIT_FLAGS.PORCELAIN]);
|
|
118
|
+
return result.trim();
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Check if a branch exists
|
|
122
|
+
* @param {string} branch - Branch name
|
|
123
|
+
* @returns {Promise<boolean>} True if branch exists
|
|
124
|
+
* @throws {TypeError} If branch is not a string
|
|
125
|
+
* @throws {Error} If branch is empty
|
|
126
|
+
* @example
|
|
127
|
+
* await git.branchExists('main'); // true
|
|
128
|
+
* await git.branchExists('nonexistent'); // false
|
|
129
|
+
*/
|
|
130
|
+
async branchExists(branch) {
|
|
131
|
+
assertNonEmptyString(branch, 'branch');
|
|
132
|
+
try {
|
|
133
|
+
await this.git.raw(['rev-parse', '--verify', branch]);
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Fetch from remote
|
|
142
|
+
* @param {string} [remote] - Remote name (defaults to fetching all)
|
|
143
|
+
* @param {string} [branch] - Branch name
|
|
144
|
+
* @throws {TypeError} If remote or branch is not a string (when provided)
|
|
145
|
+
* @example
|
|
146
|
+
* await git.fetch('origin', 'main');
|
|
147
|
+
* await git.fetch(); // Fetch all remotes
|
|
148
|
+
*/
|
|
149
|
+
async fetch(remote, branch) {
|
|
150
|
+
assertOptionalString(remote, 'remote');
|
|
151
|
+
assertOptionalString(branch, 'branch');
|
|
152
|
+
if (remote && branch) {
|
|
153
|
+
await this.git.fetch(remote, branch);
|
|
154
|
+
}
|
|
155
|
+
else if (remote) {
|
|
156
|
+
await this.git.fetch(remote);
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
await this.git.fetch();
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Pull from remote branch
|
|
164
|
+
* @param {string} remote - Remote name
|
|
165
|
+
* @param {string} branch - Branch name
|
|
166
|
+
* @throws {TypeError} If remote or branch is not a string
|
|
167
|
+
* @example
|
|
168
|
+
* await git.pull('origin', 'main');
|
|
169
|
+
*/
|
|
170
|
+
async pull(remote, branch) {
|
|
171
|
+
assertNonEmptyString(remote, 'remote');
|
|
172
|
+
assertNonEmptyString(branch, 'branch');
|
|
173
|
+
await this.git.pull(remote, branch);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Get git config value
|
|
177
|
+
* @param {string} key - Config key (e.g., 'user.email')
|
|
178
|
+
* @returns {Promise<string>} Config value
|
|
179
|
+
* @example
|
|
180
|
+
* await git.getConfigValue('user.email'); // "user@example.com"
|
|
181
|
+
*/
|
|
182
|
+
async getConfigValue(key) {
|
|
183
|
+
const result = await this.git.raw(['config', key]);
|
|
184
|
+
return result.trim();
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Check if working tree is clean (no uncommitted changes)
|
|
188
|
+
* @returns {Promise<boolean>} True if clean
|
|
189
|
+
* @example
|
|
190
|
+
* await git.isClean(); // true or false
|
|
191
|
+
*/
|
|
192
|
+
async isClean() {
|
|
193
|
+
const status = await this.getStatus();
|
|
194
|
+
return status === '';
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Add files to staging area
|
|
198
|
+
* @param {string|string[]} files - Files to add
|
|
199
|
+
* @throws {TypeError} If files is not a string or array
|
|
200
|
+
* @throws {Error} If files is empty string or empty array
|
|
201
|
+
* @example
|
|
202
|
+
* await git.add('file.txt');
|
|
203
|
+
* await git.add(['file1.txt', 'file2.txt']);
|
|
204
|
+
* await git.add('.'); // Add all
|
|
205
|
+
*/
|
|
206
|
+
async add(files) {
|
|
207
|
+
assertStringOrArray(files, 'files');
|
|
208
|
+
await this.git.add(files);
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Add files to staging area including deletions
|
|
212
|
+
*
|
|
213
|
+
* WU-1813: Stages modifications, additions, AND deletions for specified files.
|
|
214
|
+
* Unlike add(), this uses `git add -A` which properly handles tracked file deletions.
|
|
215
|
+
*
|
|
216
|
+
* When files array is empty, stages all changes in the working directory (git add -A .)
|
|
217
|
+
* to catch any deletions that might not have been explicitly listed.
|
|
218
|
+
*
|
|
219
|
+
* @param {string[]} files - Files to add (empty array = add all)
|
|
220
|
+
* @example
|
|
221
|
+
* await git.addWithDeletions(['modified.txt', 'deleted.txt']);
|
|
222
|
+
* await git.addWithDeletions([]); // Add all changes including deletions
|
|
223
|
+
*/
|
|
224
|
+
async addWithDeletions(files) {
|
|
225
|
+
if (files && files.length > 0) {
|
|
226
|
+
// Stage specific files with -A flag to include deletions
|
|
227
|
+
// Using -- separator for safety with paths that might look like flags
|
|
228
|
+
await this.git.raw(['add', '-A', '--', ...files]);
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
// Stage all changes including deletions
|
|
232
|
+
await this.git.raw(['add', '-A', '.']);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Commit changes
|
|
237
|
+
* @param {string} message - Commit message
|
|
238
|
+
* @throws {TypeError} If message is not a string
|
|
239
|
+
* @throws {Error} If message is empty
|
|
240
|
+
* @example
|
|
241
|
+
* await git.commit('feat: add new feature');
|
|
242
|
+
*/
|
|
243
|
+
async commit(message) {
|
|
244
|
+
assertNonEmptyString(message, 'message');
|
|
245
|
+
await this.git.commit(message);
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Push to remote
|
|
249
|
+
* @param {string} [remote='origin'] - Remote name
|
|
250
|
+
* @param {string} [branch] - Branch name (uses current branch if not specified)
|
|
251
|
+
* @param {object} [options] - Push options
|
|
252
|
+
* @param {boolean} [options.setUpstream] - Set upstream tracking (-u flag)
|
|
253
|
+
* @throws {TypeError} If remote or branch is not a string (when provided)
|
|
254
|
+
* @example
|
|
255
|
+
* await git.push('origin', 'main');
|
|
256
|
+
* await git.push('origin', 'feature', { setUpstream: true });
|
|
257
|
+
*/
|
|
258
|
+
async push(remote = 'origin', branch, options = {}) {
|
|
259
|
+
assertOptionalString(remote, 'remote');
|
|
260
|
+
assertOptionalString(branch, 'branch');
|
|
261
|
+
const pushOptions = {};
|
|
262
|
+
if (options.setUpstream) {
|
|
263
|
+
pushOptions[GIT_FLAGS.UPSTREAM] = null;
|
|
264
|
+
}
|
|
265
|
+
await this.git.push(remote, branch, pushOptions);
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Push using refspec to push local ref to different remote ref
|
|
269
|
+
*
|
|
270
|
+
* WU-1435: Enables push-only pattern to keep local main pristine.
|
|
271
|
+
* Pushes directly to origin/main without updating local main.
|
|
272
|
+
*
|
|
273
|
+
* @param {string} remote - Remote name (e.g., 'origin')
|
|
274
|
+
* @param {string} localRef - Local ref to push (e.g., 'tmp/wu-claim/wu-123')
|
|
275
|
+
* @param {string} remoteRef - Remote ref to update (e.g., 'main')
|
|
276
|
+
* @example
|
|
277
|
+
* await git.pushRefspec('origin', 'tmp/wu-claim/wu-123', 'main');
|
|
278
|
+
* // Equivalent to: git push origin tmp/wu-claim/wu-123:main
|
|
279
|
+
*/
|
|
280
|
+
async pushRefspec(remote, localRef, remoteRef) {
|
|
281
|
+
const refspec = `${localRef}:${remoteRef}`;
|
|
282
|
+
await this.git.push(remote, refspec);
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Create and checkout a new branch
|
|
286
|
+
* @param {string} branch - Branch name
|
|
287
|
+
* @param {string} [startPoint] - Starting commit (defaults to HEAD)
|
|
288
|
+
* @throws {TypeError} If branch is not a string, or startPoint is not a string (when provided)
|
|
289
|
+
* @throws {Error} If branch is empty
|
|
290
|
+
* @example
|
|
291
|
+
* await git.createBranch('feature/new-feature');
|
|
292
|
+
* await git.createBranch('hotfix/bug', 'main');
|
|
293
|
+
*/
|
|
294
|
+
async createBranch(branch, startPoint) {
|
|
295
|
+
assertNonEmptyString(branch, 'branch');
|
|
296
|
+
assertOptionalString(startPoint, 'startPoint');
|
|
297
|
+
const args = [GIT_FLAGS.BRANCH, branch];
|
|
298
|
+
if (startPoint) {
|
|
299
|
+
args.push(startPoint);
|
|
300
|
+
}
|
|
301
|
+
await this.git.checkout(args);
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Checkout a branch
|
|
305
|
+
* @param {string} branch - Branch name
|
|
306
|
+
* @throws {TypeError} If branch is not a string
|
|
307
|
+
* @throws {Error} If branch is empty
|
|
308
|
+
* @example
|
|
309
|
+
* await git.checkout('main');
|
|
310
|
+
*/
|
|
311
|
+
async checkout(branch) {
|
|
312
|
+
assertNonEmptyString(branch, 'branch');
|
|
313
|
+
await this.git.checkout(branch);
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Get commit hash
|
|
317
|
+
* @param {string} [ref='HEAD'] - Git ref
|
|
318
|
+
* @returns {Promise<string>} Commit hash
|
|
319
|
+
* @example
|
|
320
|
+
* await git.getCommitHash(); // "a1b2c3d..."
|
|
321
|
+
* await git.getCommitHash('main'); // "e4f5g6h..."
|
|
322
|
+
*/
|
|
323
|
+
async getCommitHash(ref = 'HEAD') {
|
|
324
|
+
const result = await this.git.revparse([ref]);
|
|
325
|
+
return result.trim();
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Merge a branch
|
|
329
|
+
*
|
|
330
|
+
* WU-1749: Bug 1 fix - Return success status and handle false positive failures.
|
|
331
|
+
* simple-git's merge() returns a MergeSummary that we now properly handle.
|
|
332
|
+
*
|
|
333
|
+
* @param {string} branch - Branch to merge
|
|
334
|
+
* @param {object} [options] - Merge options
|
|
335
|
+
* @param {boolean} [options.ffOnly] - Fast-forward only merge
|
|
336
|
+
* @returns {Promise<{success: boolean}>} Merge result
|
|
337
|
+
* @throws {TypeError} If branch is not a string
|
|
338
|
+
* @throws {Error} If branch is empty
|
|
339
|
+
* @example
|
|
340
|
+
* await git.merge('feature-branch');
|
|
341
|
+
* await git.merge('feature-branch', { ffOnly: true });
|
|
342
|
+
*/
|
|
343
|
+
async merge(branch, options = {}) {
|
|
344
|
+
assertNonEmptyString(branch, 'branch');
|
|
345
|
+
const args = [];
|
|
346
|
+
if (options.ffOnly) {
|
|
347
|
+
args.push(GIT_FLAGS.FF_ONLY);
|
|
348
|
+
}
|
|
349
|
+
args.push(branch);
|
|
350
|
+
await this.git.merge(args);
|
|
351
|
+
// If we get here without throwing, merge succeeded
|
|
352
|
+
return { success: true };
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Get commit log
|
|
356
|
+
*
|
|
357
|
+
* WU-1749: Bug 4 fix - Add log() method for counting commits.
|
|
358
|
+
* Used by wu-done-retry-helpers.mjs to count previous completion attempts.
|
|
359
|
+
*
|
|
360
|
+
* @param {object} [options] - Log options
|
|
361
|
+
* @param {number} [options.maxCount] - Maximum number of commits to return
|
|
362
|
+
* @returns {Promise<{all: Array<{hash: string, message: string}>, total: number}>} Log result
|
|
363
|
+
* @example
|
|
364
|
+
* await git.log({ maxCount: 50 });
|
|
365
|
+
*/
|
|
366
|
+
async log(options = {}) {
|
|
367
|
+
return await this.git.log(options);
|
|
368
|
+
}
|
|
369
|
+
// New semantic methods for wu- scripts (WU-1213)
|
|
370
|
+
/**
|
|
371
|
+
* Find common ancestor (merge base) of two refs
|
|
372
|
+
* @param {string} ref1 - First ref
|
|
373
|
+
* @param {string} ref2 - Second ref
|
|
374
|
+
* @returns {Promise<string>} Common ancestor commit hash
|
|
375
|
+
* @throws {TypeError} If ref1 or ref2 is not a string
|
|
376
|
+
* @example
|
|
377
|
+
* await git.mergeBase('main', 'feature'); // "abc123def456"
|
|
378
|
+
*/
|
|
379
|
+
async mergeBase(ref1, ref2) {
|
|
380
|
+
assertNonEmptyString(ref1, 'ref1');
|
|
381
|
+
assertNonEmptyString(ref2, 'ref2');
|
|
382
|
+
const result = await this.git.raw(['merge-base', ref1, ref2]);
|
|
383
|
+
return result.trim();
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Simulate merge and detect conflicts without touching working tree
|
|
387
|
+
* @param {string} base - Base commit hash
|
|
388
|
+
* @param {string} ref1 - First ref to merge
|
|
389
|
+
* @param {string} ref2 - Second ref to merge
|
|
390
|
+
* @returns {Promise<string>} Merge tree output (contains conflict markers if conflicts exist)
|
|
391
|
+
* @example
|
|
392
|
+
* await git.mergeTree('base123', 'main', 'feature');
|
|
393
|
+
*/
|
|
394
|
+
async mergeTree(base, ref1, ref2) {
|
|
395
|
+
const result = await this.git.raw(['merge-tree', base, ref1, ref2]);
|
|
396
|
+
return result;
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* List commits with various options
|
|
400
|
+
* @param {string[]} args - Arguments to pass to git rev-list
|
|
401
|
+
* @returns {Promise<string>} Rev-list output
|
|
402
|
+
* @example
|
|
403
|
+
* await git.revList(['--count', '--left-right', 'main...feature']); // "5\t0"
|
|
404
|
+
*/
|
|
405
|
+
async revList(args) {
|
|
406
|
+
const result = await this.git.raw(['rev-list', ...args]);
|
|
407
|
+
return result.trim();
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Add a worktree with new branch
|
|
411
|
+
* @param {string} path - Worktree path
|
|
412
|
+
* @param {string} branch - Branch name
|
|
413
|
+
* @param {string} [startPoint] - Starting commit (defaults to HEAD)
|
|
414
|
+
* @throws {TypeError} If path or branch is not a string
|
|
415
|
+
* @throws {Error} If path or branch is empty
|
|
416
|
+
* @example
|
|
417
|
+
* await git.worktreeAdd('worktrees/feature', 'feature-branch', 'main');
|
|
418
|
+
*/
|
|
419
|
+
async worktreeAdd(path, branch, startPoint) {
|
|
420
|
+
assertNonEmptyString(path, 'path');
|
|
421
|
+
assertNonEmptyString(branch, 'branch');
|
|
422
|
+
assertOptionalString(startPoint, 'startPoint');
|
|
423
|
+
const args = ['worktree', 'add', path, GIT_FLAGS.BRANCH, branch];
|
|
424
|
+
if (startPoint) {
|
|
425
|
+
args.push(startPoint);
|
|
426
|
+
}
|
|
427
|
+
await this.git.raw(args);
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Remove a worktree
|
|
431
|
+
*
|
|
432
|
+
* WU-1476: Layer 1 defense - explicit directory cleanup after git worktree remove.
|
|
433
|
+
* Git worktree remove can leave orphan directories when:
|
|
434
|
+
* - The worktree was forcefully removed
|
|
435
|
+
* - Git worktree metadata was corrupted
|
|
436
|
+
* - Previous wu:done failed mid-workflow
|
|
437
|
+
*
|
|
438
|
+
* @param {string} worktreePath - Worktree path
|
|
439
|
+
* @param {object} [options] - Remove options
|
|
440
|
+
* @param {boolean} [options.force] - Force removal
|
|
441
|
+
* @example
|
|
442
|
+
* await git.worktreeRemove('worktrees/feature');
|
|
443
|
+
* await git.worktreeRemove('worktrees/feature', { force: true });
|
|
444
|
+
*/
|
|
445
|
+
async worktreeRemove(worktreePath, options = {}) {
|
|
446
|
+
const args = ['worktree', 'remove'];
|
|
447
|
+
if (options.force) {
|
|
448
|
+
args.push(GIT_FLAGS.FORCE);
|
|
449
|
+
}
|
|
450
|
+
args.push(worktreePath);
|
|
451
|
+
// Attempt git worktree remove
|
|
452
|
+
try {
|
|
453
|
+
await this.git.raw(args);
|
|
454
|
+
}
|
|
455
|
+
catch (err) {
|
|
456
|
+
// If git fails, we still want to clean up the directory
|
|
457
|
+
// Re-throw after cleanup attempt to report the original error
|
|
458
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool with validated worktree path
|
|
459
|
+
if (existsSync(worktreePath)) {
|
|
460
|
+
rmSync(worktreePath, { recursive: true, force: true });
|
|
461
|
+
}
|
|
462
|
+
throw err;
|
|
463
|
+
}
|
|
464
|
+
// Layer 1 defense (WU-1476): Explicit cleanup if directory still exists
|
|
465
|
+
// This handles edge cases where git worktree remove succeeds but leaves the directory
|
|
466
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool with validated worktree path
|
|
467
|
+
if (existsSync(worktreePath)) {
|
|
468
|
+
rmSync(worktreePath, { recursive: true, force: true });
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* List all worktrees
|
|
473
|
+
* @returns {Promise<string>} Worktree list in porcelain format
|
|
474
|
+
* @example
|
|
475
|
+
* await git.worktreeList();
|
|
476
|
+
*/
|
|
477
|
+
async worktreeList() {
|
|
478
|
+
const result = await this.git.raw(['worktree', 'list', GIT_FLAGS.PORCELAIN]);
|
|
479
|
+
return result;
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Delete a branch
|
|
483
|
+
* @param {string} branch - Branch name
|
|
484
|
+
* @param {object} [options] - Delete options
|
|
485
|
+
* @param {boolean} [options.force] - Force delete (use -D instead of -d)
|
|
486
|
+
* @throws {TypeError} If branch is not a string
|
|
487
|
+
* @throws {Error} If branch is empty
|
|
488
|
+
* @example
|
|
489
|
+
* await git.deleteBranch('feature');
|
|
490
|
+
* await git.deleteBranch('feature', { force: true });
|
|
491
|
+
*/
|
|
492
|
+
async deleteBranch(branch, options = {}) {
|
|
493
|
+
assertNonEmptyString(branch, 'branch');
|
|
494
|
+
const flag = options.force ? GIT_FLAGS.DELETE_FORCE : GIT_FLAGS.DELETE;
|
|
495
|
+
await this.git.branch([flag, branch]);
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* Create a branch WITHOUT switching to it (WU-1262)
|
|
499
|
+
* Used for micro-worktree pattern where main checkout stays on main
|
|
500
|
+
* @param {string} branch - Branch name to create
|
|
501
|
+
* @param {string} [startPoint] - Starting commit (defaults to HEAD)
|
|
502
|
+
* @example
|
|
503
|
+
* await git.createBranchNoCheckout('tmp/wu-create/wu-123', 'main');
|
|
504
|
+
*/
|
|
505
|
+
async createBranchNoCheckout(branch, startPoint) {
|
|
506
|
+
const args = ['branch', branch];
|
|
507
|
+
if (startPoint) {
|
|
508
|
+
args.push(startPoint);
|
|
509
|
+
}
|
|
510
|
+
await this.git.raw(args);
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Add a worktree for an EXISTING branch (WU-1262)
|
|
514
|
+
* Unlike worktreeAdd, this doesn't create a new branch
|
|
515
|
+
* @param {string} path - Worktree path
|
|
516
|
+
* @param {string} branch - Existing branch name
|
|
517
|
+
* @example
|
|
518
|
+
* await git.worktreeAddExisting('/tmp/wu-create-xyz', 'tmp/wu-create/wu-123');
|
|
519
|
+
*/
|
|
520
|
+
async worktreeAddExisting(path, branch) {
|
|
521
|
+
await this.git.raw(['worktree', 'add', path, branch]);
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Rebase current branch onto target (WU-1262)
|
|
525
|
+
* Used in micro-worktree to rebase temp branch when main moves
|
|
526
|
+
* @param {string} onto - Target ref to rebase onto
|
|
527
|
+
* @throws {TypeError} If onto is not a string
|
|
528
|
+
* @throws {Error} If onto is empty
|
|
529
|
+
* @example
|
|
530
|
+
* await git.rebase('main');
|
|
531
|
+
*/
|
|
532
|
+
async rebase(onto) {
|
|
533
|
+
assertNonEmptyString(onto, 'onto');
|
|
534
|
+
const gitWithEditor = this.git.env({ ...process.env, GIT_EDITOR: 'true' });
|
|
535
|
+
await gitWithEditor.rebase([onto]);
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* Reset HEAD to specified commit
|
|
539
|
+
* @param {string} [ref] - Commit ref to reset to (defaults to HEAD)
|
|
540
|
+
* @param {object} [options] - Reset options
|
|
541
|
+
* @param {boolean} [options.hard] - Hard reset (--hard flag)
|
|
542
|
+
* @example
|
|
543
|
+
* await git.reset('abc123', { hard: true });
|
|
544
|
+
*/
|
|
545
|
+
async reset(ref, options = {}) {
|
|
546
|
+
const args = ['reset'];
|
|
547
|
+
if (options.hard) {
|
|
548
|
+
args.push(GIT_FLAGS.HARD);
|
|
549
|
+
}
|
|
550
|
+
if (ref) {
|
|
551
|
+
args.push(ref);
|
|
552
|
+
}
|
|
553
|
+
await this.git.raw(args);
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* Execute arbitrary git command via raw()
|
|
557
|
+
* @param {string[]} args - Git command arguments
|
|
558
|
+
* @returns {Promise<string>} Command output
|
|
559
|
+
* @throws {TypeError} If args is not an array
|
|
560
|
+
* @example
|
|
561
|
+
* await git.raw(['status', '--porcelain']);
|
|
562
|
+
*/
|
|
563
|
+
async raw(args) {
|
|
564
|
+
assertArray(args, 'args');
|
|
565
|
+
const result = await this.git.raw(args);
|
|
566
|
+
return result;
|
|
567
|
+
}
|
|
568
|
+
// Deprecated methods (for backward compatibility during migration)
|
|
569
|
+
/**
|
|
570
|
+
* @deprecated Use async methods directly instead
|
|
571
|
+
* Legacy method for backward compatibility
|
|
572
|
+
* Execute a git command and return trimmed output
|
|
573
|
+
* @param {string} cmd - Command to execute
|
|
574
|
+
* @returns {string} Trimmed command output
|
|
575
|
+
*/
|
|
576
|
+
run(cmd) {
|
|
577
|
+
throw new Error('GitAdapter.run() is deprecated (WU-1213). Use async methods instead. ' +
|
|
578
|
+
`Attempted to run: ${cmd}`);
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* @deprecated Use worktreeAdd() instead
|
|
582
|
+
*/
|
|
583
|
+
addWorktree(path, branch, startPoint) {
|
|
584
|
+
return this.worktreeAdd(path, branch, startPoint);
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* @deprecated Use worktreeRemove() instead
|
|
588
|
+
*/
|
|
589
|
+
removeWorktree(path, options) {
|
|
590
|
+
return this.worktreeRemove(path, options);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
// WU-1235: Factory functions for explicit directory control
|
|
594
|
+
/**
|
|
595
|
+
* Create a GitAdapter for a specific directory
|
|
596
|
+
* Use this when you need git operations in an explicit path (e.g., worktree vs main)
|
|
597
|
+
* @param {string} baseDir - Directory for git operations
|
|
598
|
+
* @returns {GitAdapter} New GitAdapter instance for the specified directory
|
|
599
|
+
* @example
|
|
600
|
+
* const gitWorktree = createGitForPath('/path/to/worktree');
|
|
601
|
+
* const gitMain = createGitForPath('/path/to/main');
|
|
602
|
+
*/
|
|
603
|
+
export function createGitForPath(baseDir) {
|
|
604
|
+
return new GitAdapter({ baseDir });
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* Create a GitAdapter for the current working directory
|
|
608
|
+
* Captures process.cwd() at call time (not import time)
|
|
609
|
+
* Use this after process.chdir() to get an adapter for the new directory
|
|
610
|
+
* @returns {GitAdapter} New GitAdapter instance for current process.cwd()
|
|
611
|
+
* @example
|
|
612
|
+
* process.chdir(worktreePath);
|
|
613
|
+
* const git = getGitForCwd(); // Uses new directory
|
|
614
|
+
*/
|
|
615
|
+
export function getGitForCwd() {
|
|
616
|
+
const cwd = process.cwd();
|
|
617
|
+
if (process.env.DEBUG) {
|
|
618
|
+
console.log(`[git-adapter] DEBUG: getGitForCwd() creating adapter with baseDir=${cwd}`);
|
|
619
|
+
}
|
|
620
|
+
return new GitAdapter({ baseDir: cwd });
|
|
621
|
+
}
|
|
622
|
+
// Singleton deprecation tracking
|
|
623
|
+
let singletonWarned = false;
|
|
624
|
+
/**
|
|
625
|
+
* Reset singleton warning flag (for testing only)
|
|
626
|
+
* @internal
|
|
627
|
+
*/
|
|
628
|
+
export function _resetSingletonWarning() {
|
|
629
|
+
singletonWarned = false;
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* @deprecated Use createGitForPath() or getGitForCwd() instead
|
|
633
|
+
* Singleton GitAdapter instance - captured cwd at module load time
|
|
634
|
+
* WARNING: Does not respect process.chdir() - use factory functions for worktree operations
|
|
635
|
+
* @type {GitAdapter}
|
|
636
|
+
*/
|
|
637
|
+
const gitSingleton = new GitAdapter();
|
|
638
|
+
export const git = new Proxy(gitSingleton, {
|
|
639
|
+
get(target, prop) {
|
|
640
|
+
if (!singletonWarned) {
|
|
641
|
+
console.warn('[DEPRECATED] git singleton captured cwd at import time. ' +
|
|
642
|
+
'Use createGitForPath(path) or getGitForCwd() for explicit directory control.');
|
|
643
|
+
singletonWarned = true;
|
|
644
|
+
}
|
|
645
|
+
const value = target[prop];
|
|
646
|
+
// Bind methods to preserve 'this' context
|
|
647
|
+
return typeof value === 'function' ? value.bind(target) : value;
|
|
648
|
+
},
|
|
649
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git Staged Files Validator
|
|
3
|
+
*
|
|
4
|
+
* Centralized validation for staged files requirement.
|
|
5
|
+
* Extracted from duplicate implementations in wu-block and wu-unblock (WU-1341).
|
|
6
|
+
*
|
|
7
|
+
* Used in --no-auto mode to enforce that required files are staged before commit.
|
|
8
|
+
*
|
|
9
|
+
* @module git-staged-validator
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Ensure all required paths are staged in git index
|
|
13
|
+
*
|
|
14
|
+
* Validates that specified files/directories are staged for commit.
|
|
15
|
+
* Supports exact path matching and directory prefix matching.
|
|
16
|
+
*
|
|
17
|
+
* @param {Array<string|null|undefined>} paths - Paths to check (null/undefined values filtered out)
|
|
18
|
+
* @throws {Error} If any required paths are not staged
|
|
19
|
+
* @returns {Array<string>} List of all staged files
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* // All files staged - success
|
|
23
|
+
* ensureStaged(['docs/file.md', 'tools/script.js']);
|
|
24
|
+
*
|
|
25
|
+
* // Directory prefix - matches all files under directory
|
|
26
|
+
* ensureStaged(['docs/04-operations/']);
|
|
27
|
+
*
|
|
28
|
+
* // Missing files - throws error
|
|
29
|
+
* ensureStaged(['docs/file1.md', 'docs/file2.md']);
|
|
30
|
+
* // Error: Stage updates for: docs/file2.md
|
|
31
|
+
*/
|
|
32
|
+
export declare function ensureStaged(paths: Array<string | null | undefined>): string[];
|