@lumenflow/shims 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 +136 -0
- package/dist/git-shim.d.ts +73 -0
- package/dist/git-shim.d.ts.map +1 -0
- package/dist/git-shim.js +255 -0
- package/dist/git-shim.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/pnpm-shim.d.ts +48 -0
- package/dist/pnpm-shim.d.ts.map +1 -0
- package/dist/pnpm-shim.js +140 -0
- package/dist/pnpm-shim.js.map +1 -0
- package/dist/types.d.ts +65 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +59 -0
- package/dist/types.js.map +1 -0
- package/dist/worktree.d.ts +49 -0
- package/dist/worktree.d.ts.map +1 -0
- package/dist/worktree.js +85 -0
- package/dist/worktree.js.map +1 -0
- package/package.json +73 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to the Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
Copyright 2026 HellmAI
|
|
179
|
+
|
|
180
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
181
|
+
you may not use this file except in compliance with the License.
|
|
182
|
+
You may obtain a copy of the License at
|
|
183
|
+
|
|
184
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
185
|
+
|
|
186
|
+
Unless required by applicable law or agreed to in writing, software
|
|
187
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
188
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
189
|
+
See the License for the specific language governing permissions and
|
|
190
|
+
limitations under the License.
|
package/README.md
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# @lumenflow/shims
|
|
2
|
+
|
|
3
|
+
Git and pnpm safety shims for LumenFlow worktree discipline.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This package provides command shims that:
|
|
8
|
+
|
|
9
|
+
1. **Git shim** - Blocks destructive git commands on main branch/worktree
|
|
10
|
+
2. **Pnpm shim** - Fixes worktree compatibility for dependency commands
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pnpm add @lumenflow/shims
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
### Git Shim
|
|
21
|
+
|
|
22
|
+
The git shim prevents destructive commands on protected branches (default: main).
|
|
23
|
+
|
|
24
|
+
**Blocked commands on main:**
|
|
25
|
+
|
|
26
|
+
- git reset with hard flag
|
|
27
|
+
- git stash (any form)
|
|
28
|
+
- git clean with fd flags
|
|
29
|
+
- git checkout with force flag
|
|
30
|
+
- git push with force flag
|
|
31
|
+
- Hook bypass flags
|
|
32
|
+
|
|
33
|
+
These commands are **allowed** in lane worktrees (safe, isolated context).
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { runGitShim, checkBannedPattern, GitShimConfigSchema } from '@lumenflow/shims';
|
|
37
|
+
|
|
38
|
+
// Run as CLI
|
|
39
|
+
runGitShim(process.argv.slice(2));
|
|
40
|
+
|
|
41
|
+
// Check if a command pattern is banned
|
|
42
|
+
const result = checkBannedPattern(['reset', '--hard']);
|
|
43
|
+
if (result.banned) {
|
|
44
|
+
console.error(result.reason);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Custom configuration
|
|
48
|
+
const config = GitShimConfigSchema.parse({
|
|
49
|
+
protectedBranch: 'develop',
|
|
50
|
+
bannedPatterns: [{ command: 'rebase' }],
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Pnpm Shim
|
|
55
|
+
|
|
56
|
+
The pnpm shim fixes ERR_PNPM_UNEXPECTED_VIRTUAL_STORE errors in git worktrees.
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
import { runPnpmShim, isDependencyCommand, PnpmShimConfigSchema } from '@lumenflow/shims';
|
|
60
|
+
|
|
61
|
+
// Run as CLI
|
|
62
|
+
runPnpmShim(process.argv.slice(2));
|
|
63
|
+
|
|
64
|
+
// Check if command modifies dependencies
|
|
65
|
+
isDependencyCommand(['add', 'zod']); // true
|
|
66
|
+
isDependencyCommand(['run', 'test']); // false
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Worktree Detection
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
import {
|
|
73
|
+
isInWorktree,
|
|
74
|
+
isMainWorktree,
|
|
75
|
+
getMainCheckoutPath,
|
|
76
|
+
getCurrentBranch,
|
|
77
|
+
} from '@lumenflow/shims';
|
|
78
|
+
|
|
79
|
+
// Check worktree context
|
|
80
|
+
if (isInWorktree()) {
|
|
81
|
+
console.log('Running in a git worktree');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (isMainWorktree()) {
|
|
85
|
+
console.log('Running in main checkout (protected)');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Get main checkout path (useful for computing virtual store)
|
|
89
|
+
const mainPath = getMainCheckoutPath();
|
|
90
|
+
|
|
91
|
+
// Get current branch
|
|
92
|
+
const branch = getCurrentBranch();
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Configuration
|
|
96
|
+
|
|
97
|
+
Both shims are configurable via Zod schemas.
|
|
98
|
+
|
|
99
|
+
### GitShimConfig
|
|
100
|
+
|
|
101
|
+
| Option | Type | Default | Description |
|
|
102
|
+
| --------------- | -------- | ------------ | --------------------------------------------- |
|
|
103
|
+
| protectedBranch | string | main | Branch where destructive commands are blocked |
|
|
104
|
+
| bannedPatterns | array | See defaults | Command + flags combinations to block |
|
|
105
|
+
| bannedFlags | string[] | See defaults | Flags blocked on any command |
|
|
106
|
+
| realGitPath | string | /usr/bin/git | Path to real git executable |
|
|
107
|
+
| enableLogging | boolean | false | Enable command logging |
|
|
108
|
+
| logPath | string | undefined | Path to log file |
|
|
109
|
+
| recursionEnvVar | string | auto | Env var for recursion guard |
|
|
110
|
+
| agentEnvVars | string[] | See defaults | Env vars indicating agent context |
|
|
111
|
+
|
|
112
|
+
### PnpmShimConfig
|
|
113
|
+
|
|
114
|
+
| Option | Type | Default | Description |
|
|
115
|
+
| ------------------ | -------- | ------------------------- | --------------------------------- |
|
|
116
|
+
| dependencyCommands | string[] | add, remove, install, etc | Commands that modify dependencies |
|
|
117
|
+
| systemPnpmPaths | string[] | System paths | Paths to search for real pnpm |
|
|
118
|
+
| recursionEnvVar | string | auto | Env var for recursion guard |
|
|
119
|
+
| enableDebug | boolean | false | Enable debug output |
|
|
120
|
+
|
|
121
|
+
## CLI Usage
|
|
122
|
+
|
|
123
|
+
The package exports bin commands that can be used as shims:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
# Add to PATH before system paths
|
|
127
|
+
export PATH="./node_modules/.bin:$PATH"
|
|
128
|
+
|
|
129
|
+
# Now git/pnpm commands are intercepted
|
|
130
|
+
git reset --hard # Blocked on main
|
|
131
|
+
pnpm add zod # Fixed in worktrees
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## License
|
|
135
|
+
|
|
136
|
+
Apache-2.0
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @lumenflow/shims - Git Command Shim (WU-2546)
|
|
4
|
+
*
|
|
5
|
+
* Intercepts git commands and blocks destructive operations on main branch/worktree.
|
|
6
|
+
*
|
|
7
|
+
* Usage: Add shims directory to PATH before /usr/bin
|
|
8
|
+
* export PATH="$(pwd)/node_modules/.bin:$PATH"
|
|
9
|
+
*
|
|
10
|
+
* Blocked commands on main:
|
|
11
|
+
* - git reset --hard
|
|
12
|
+
* - git stash (any form)
|
|
13
|
+
* - git clean -fd
|
|
14
|
+
* - git checkout -f
|
|
15
|
+
* - git push --force / -f
|
|
16
|
+
* - hook bypass flags
|
|
17
|
+
*
|
|
18
|
+
* These commands are ALLOWED on lane branches in worktrees (safe context).
|
|
19
|
+
*
|
|
20
|
+
* @module @lumenflow/shims/git
|
|
21
|
+
*/
|
|
22
|
+
import type { GitShimConfig, BannedPatternResult, ProtectedContextResult } from './types.js';
|
|
23
|
+
/**
|
|
24
|
+
* Detect user type based on environment variables.
|
|
25
|
+
*
|
|
26
|
+
* @param config - Git shim configuration
|
|
27
|
+
* @returns User type: 'agent', 'human', or 'unknown'
|
|
28
|
+
*/
|
|
29
|
+
export declare function detectUserType(config?: GitShimConfig): string;
|
|
30
|
+
/**
|
|
31
|
+
* Check if arguments contain a banned command pattern.
|
|
32
|
+
*
|
|
33
|
+
* @param args - Git command arguments
|
|
34
|
+
* @param config - Git shim configuration
|
|
35
|
+
* @returns Object with banned status and reason
|
|
36
|
+
*/
|
|
37
|
+
export declare function checkBannedPattern(args: string[], config?: GitShimConfig): BannedPatternResult;
|
|
38
|
+
/**
|
|
39
|
+
* Check if we're in a protected context (main branch or main worktree).
|
|
40
|
+
*
|
|
41
|
+
* @param config - Git shim configuration
|
|
42
|
+
* @returns Object with protected status and context description
|
|
43
|
+
*/
|
|
44
|
+
export declare function checkProtectedContext(config?: GitShimConfig): ProtectedContextResult;
|
|
45
|
+
/**
|
|
46
|
+
* Format error message for blocked command.
|
|
47
|
+
*
|
|
48
|
+
* @param command - The blocked command
|
|
49
|
+
* @param reason - Why it was blocked
|
|
50
|
+
* @param context - Where it was blocked
|
|
51
|
+
* @returns Formatted error message
|
|
52
|
+
*/
|
|
53
|
+
export declare function formatBlockedError(command: string, reason: string, context: string): string;
|
|
54
|
+
/**
|
|
55
|
+
* Find real git executable.
|
|
56
|
+
*
|
|
57
|
+
* @param preferredPath - Preferred git path from config
|
|
58
|
+
* @returns Path to real git
|
|
59
|
+
*/
|
|
60
|
+
export declare function findRealGit(preferredPath?: string): string;
|
|
61
|
+
/**
|
|
62
|
+
* Run the git shim with given configuration.
|
|
63
|
+
*
|
|
64
|
+
* @param args - Git command arguments
|
|
65
|
+
* @param config - Git shim configuration
|
|
66
|
+
* @returns Exit code
|
|
67
|
+
*/
|
|
68
|
+
export declare function runGitShim(args: string[], config?: GitShimConfig): number;
|
|
69
|
+
/**
|
|
70
|
+
* Main entry point for CLI execution.
|
|
71
|
+
*/
|
|
72
|
+
export declare function main(): void;
|
|
73
|
+
//# sourceMappingURL=git-shim.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-shim.d.ts","sourceRoot":"","sources":["../src/git-shim.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAiC7F;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,GAAE,aAA8B,GAAG,MAAM,CAO7E;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EAAE,EACd,MAAM,GAAE,aAA8B,GACrC,mBAAmB,CAqCrB;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,GAAE,aAA8B,GACrC,sBAAsB,CAqBxB;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAwB3F;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,aAAa,GAAE,MAAuB,GAAG,MAAM,CAe1E;AAcD;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,GAAE,aAA8B,GAAG,MAAM,CAmDzF;AAED;;GAEG;AACH,wBAAgB,IAAI,IAAI,IAAI,CAI3B"}
|
package/dist/git-shim.js
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @lumenflow/shims - Git Command Shim (WU-2546)
|
|
4
|
+
*
|
|
5
|
+
* Intercepts git commands and blocks destructive operations on main branch/worktree.
|
|
6
|
+
*
|
|
7
|
+
* Usage: Add shims directory to PATH before /usr/bin
|
|
8
|
+
* export PATH="$(pwd)/node_modules/.bin:$PATH"
|
|
9
|
+
*
|
|
10
|
+
* Blocked commands on main:
|
|
11
|
+
* - git reset --hard
|
|
12
|
+
* - git stash (any form)
|
|
13
|
+
* - git clean -fd
|
|
14
|
+
* - git checkout -f
|
|
15
|
+
* - git push --force / -f
|
|
16
|
+
* - hook bypass flags
|
|
17
|
+
*
|
|
18
|
+
* These commands are ALLOWED on lane branches in worktrees (safe context).
|
|
19
|
+
*
|
|
20
|
+
* @module @lumenflow/shims/git
|
|
21
|
+
*/
|
|
22
|
+
import { spawnSync } from 'node:child_process';
|
|
23
|
+
import { GitShimConfigSchema, UserType, CommandOutcome } from './types.js';
|
|
24
|
+
import { getCurrentBranch, isMainWorktree } from './worktree.js';
|
|
25
|
+
/**
|
|
26
|
+
* Default configuration.
|
|
27
|
+
*/
|
|
28
|
+
const DEFAULT_CONFIG = GitShimConfigSchema.parse({});
|
|
29
|
+
/**
|
|
30
|
+
* Build context string for protected/unprotected state.
|
|
31
|
+
*
|
|
32
|
+
* @param isProtected - Whether context is protected
|
|
33
|
+
* @param inMainWorktree - Whether in main worktree
|
|
34
|
+
* @param protectedBranch - Name of protected branch
|
|
35
|
+
* @param branch - Current branch name
|
|
36
|
+
* @returns Context description string
|
|
37
|
+
*/
|
|
38
|
+
function buildContextString(isProtected, inMainWorktree, protectedBranch, branch) {
|
|
39
|
+
if (isProtected) {
|
|
40
|
+
if (inMainWorktree) {
|
|
41
|
+
return 'main worktree';
|
|
42
|
+
}
|
|
43
|
+
return `${protectedBranch} branch`;
|
|
44
|
+
}
|
|
45
|
+
return `${branch} branch in lane worktree`;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Detect user type based on environment variables.
|
|
49
|
+
*
|
|
50
|
+
* @param config - Git shim configuration
|
|
51
|
+
* @returns User type: 'agent', 'human', or 'unknown'
|
|
52
|
+
*/
|
|
53
|
+
export function detectUserType(config = DEFAULT_CONFIG) {
|
|
54
|
+
for (const envVar of config.agentEnvVars) {
|
|
55
|
+
if (process.env[envVar]) {
|
|
56
|
+
return UserType.AGENT;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return UserType.HUMAN;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Check if arguments contain a banned command pattern.
|
|
63
|
+
*
|
|
64
|
+
* @param args - Git command arguments
|
|
65
|
+
* @param config - Git shim configuration
|
|
66
|
+
* @returns Object with banned status and reason
|
|
67
|
+
*/
|
|
68
|
+
export function checkBannedPattern(args, config = DEFAULT_CONFIG) {
|
|
69
|
+
const command = args[0]?.toLowerCase();
|
|
70
|
+
const flags = args.slice(1).map((a) => a.toLowerCase());
|
|
71
|
+
// Check banned flags first (apply to any command)
|
|
72
|
+
for (const bannedFlag of config.bannedFlags) {
|
|
73
|
+
if (flags.includes(bannedFlag)) {
|
|
74
|
+
return {
|
|
75
|
+
banned: true,
|
|
76
|
+
reason: `Flag ${bannedFlag} bypasses hooks and is forbidden`,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// Check banned command patterns
|
|
81
|
+
for (const pattern of config.bannedPatterns) {
|
|
82
|
+
if (command !== pattern.command)
|
|
83
|
+
continue;
|
|
84
|
+
// If no specific flags required, ban the command entirely
|
|
85
|
+
if (!pattern.flags) {
|
|
86
|
+
return {
|
|
87
|
+
banned: true,
|
|
88
|
+
reason: `Command "git ${command}" is destructive and forbidden on main`,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
// Check if any required flag is present
|
|
92
|
+
const hasRequiredFlag = pattern.flags.some((reqFlag) => flags.includes(reqFlag));
|
|
93
|
+
if (hasRequiredFlag) {
|
|
94
|
+
return {
|
|
95
|
+
banned: true,
|
|
96
|
+
reason: `Command "git ${args.join(' ')}" is destructive and forbidden on main`,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return { banned: false, reason: null };
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Check if we're in a protected context (main branch or main worktree).
|
|
104
|
+
*
|
|
105
|
+
* @param config - Git shim configuration
|
|
106
|
+
* @returns Object with protected status and context description
|
|
107
|
+
*/
|
|
108
|
+
export function checkProtectedContext(config = DEFAULT_CONFIG) {
|
|
109
|
+
// Allow TEST_MODE for integration tests
|
|
110
|
+
if (process.env['TEST_MODE'] === 'true') {
|
|
111
|
+
const branch = process.env['TEST_BRANCH'] || config.protectedBranch;
|
|
112
|
+
const inMainWorktree = process.env['TEST_IS_MAIN_WORKTREE'] === 'true';
|
|
113
|
+
const isProtectedBranch = branch === config.protectedBranch;
|
|
114
|
+
const isProtected = isProtectedBranch || inMainWorktree;
|
|
115
|
+
const context = buildContextString(isProtected, inMainWorktree, config.protectedBranch, branch);
|
|
116
|
+
return { protected: isProtected, context };
|
|
117
|
+
}
|
|
118
|
+
const branch = getCurrentBranch(config.realGitPath);
|
|
119
|
+
const inMainWorktree = isMainWorktree(config.realGitPath);
|
|
120
|
+
// Protected if:
|
|
121
|
+
// 1. On protected branch (regardless of worktree)
|
|
122
|
+
// 2. OR in main worktree (even if on a lane branch)
|
|
123
|
+
const isProtected = branch === config.protectedBranch || inMainWorktree;
|
|
124
|
+
const context = buildContextString(isProtected, inMainWorktree, config.protectedBranch, branch);
|
|
125
|
+
return { protected: isProtected, context };
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Format error message for blocked command.
|
|
129
|
+
*
|
|
130
|
+
* @param command - The blocked command
|
|
131
|
+
* @param reason - Why it was blocked
|
|
132
|
+
* @param context - Where it was blocked
|
|
133
|
+
* @returns Formatted error message
|
|
134
|
+
*/
|
|
135
|
+
export function formatBlockedError(command, reason, context) {
|
|
136
|
+
return `
|
|
137
|
+
╔═══════════════════════════════════════════════════════════════════╗
|
|
138
|
+
║ GIT SHIM HOOK ERROR
|
|
139
|
+
╠═══════════════════════════════════════════════════════════════════╣
|
|
140
|
+
║
|
|
141
|
+
║ Blocked: git ${command}
|
|
142
|
+
║
|
|
143
|
+
║ ${reason}
|
|
144
|
+
║
|
|
145
|
+
║ Context: ${context}
|
|
146
|
+
║
|
|
147
|
+
║ Why blocked:
|
|
148
|
+
║ This command could destroy uncommitted work from other agents
|
|
149
|
+
║ working on parallel WUs in their own worktrees.
|
|
150
|
+
║
|
|
151
|
+
║ Correct workflow:
|
|
152
|
+
║ 1. Claim a WU: pnpm wu:claim --id WU-XXX --lane <lane>
|
|
153
|
+
║ 2. Work in worktree: cd worktrees/<lane>-wu-xxx
|
|
154
|
+
║ 3. Make changes, commit, push: git add . && git commit && git push
|
|
155
|
+
║ 4. Complete: pnpm wu:done --id WU-XXX (run from main directory)
|
|
156
|
+
║
|
|
157
|
+
╚═══════════════════════════════════════════════════════════════════╝
|
|
158
|
+
`;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Find real git executable.
|
|
162
|
+
*
|
|
163
|
+
* @param preferredPath - Preferred git path from config
|
|
164
|
+
* @returns Path to real git
|
|
165
|
+
*/
|
|
166
|
+
export function findRealGit(preferredPath = '/usr/bin/git') {
|
|
167
|
+
const gitPaths = [preferredPath, '/usr/bin/git', '/usr/local/bin/git', '/opt/homebrew/bin/git'];
|
|
168
|
+
for (const gitPath of gitPaths) {
|
|
169
|
+
try {
|
|
170
|
+
const result = spawnSync(gitPath, ['--version'], { encoding: 'utf8' });
|
|
171
|
+
if (result.status === 0) {
|
|
172
|
+
return gitPath;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
// Try next path
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return 'git';
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Log command execution for audit trail.
|
|
183
|
+
*
|
|
184
|
+
* @param _user - User type (agent/human)
|
|
185
|
+
* @param _outcome - Command outcome (allowed/blocked)
|
|
186
|
+
* @param _config - Git shim configuration
|
|
187
|
+
*/
|
|
188
|
+
function logCommand(_user, _outcome, _config) {
|
|
189
|
+
// Logging would go here - simplified for extraction
|
|
190
|
+
// Future: write to config.logPath
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Run the git shim with given configuration.
|
|
194
|
+
*
|
|
195
|
+
* @param args - Git command arguments
|
|
196
|
+
* @param config - Git shim configuration
|
|
197
|
+
* @returns Exit code
|
|
198
|
+
*/
|
|
199
|
+
export function runGitShim(args, config = DEFAULT_CONFIG) {
|
|
200
|
+
// Recursion guard
|
|
201
|
+
if (process.env[config.recursionEnvVar]) {
|
|
202
|
+
const result = spawnSync(config.realGitPath, args, {
|
|
203
|
+
stdio: 'inherit',
|
|
204
|
+
encoding: 'utf8',
|
|
205
|
+
});
|
|
206
|
+
return result.status || 0;
|
|
207
|
+
}
|
|
208
|
+
process.env[config.recursionEnvVar] = '1';
|
|
209
|
+
// Detect user type for audit trail
|
|
210
|
+
const user = detectUserType(config);
|
|
211
|
+
// Check if we're in a protected context
|
|
212
|
+
const { protected: isProtected, context } = checkProtectedContext(config);
|
|
213
|
+
let outcome = CommandOutcome.ALLOWED;
|
|
214
|
+
if (isProtected) {
|
|
215
|
+
const { banned, reason } = checkBannedPattern(args, config);
|
|
216
|
+
if (banned && reason) {
|
|
217
|
+
outcome = CommandOutcome.BLOCKED;
|
|
218
|
+
// Log blocked command if logging enabled
|
|
219
|
+
if (config.enableLogging && config.logPath) {
|
|
220
|
+
logCommand(user, outcome, config);
|
|
221
|
+
}
|
|
222
|
+
// Block the command
|
|
223
|
+
const command = args.join(' ');
|
|
224
|
+
const errorMsg = formatBlockedError(command, reason, context);
|
|
225
|
+
console.error(errorMsg);
|
|
226
|
+
return 1;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
// Log allowed command if logging enabled
|
|
230
|
+
if (config.enableLogging && config.logPath) {
|
|
231
|
+
logCommand(user, outcome, config);
|
|
232
|
+
}
|
|
233
|
+
// Pass through to real git
|
|
234
|
+
const realGit = findRealGit(config.realGitPath);
|
|
235
|
+
const result = spawnSync(realGit, args, {
|
|
236
|
+
stdio: 'inherit',
|
|
237
|
+
encoding: 'utf8',
|
|
238
|
+
});
|
|
239
|
+
return result.status || 0;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Main entry point for CLI execution.
|
|
243
|
+
*/
|
|
244
|
+
export function main() {
|
|
245
|
+
const args = process.argv.slice(2);
|
|
246
|
+
const exitCode = runGitShim(args);
|
|
247
|
+
process.exit(exitCode);
|
|
248
|
+
}
|
|
249
|
+
// Run if executed directly
|
|
250
|
+
const currentUrl = import.meta.url;
|
|
251
|
+
const scriptPath = `file://${process.argv[1]}`;
|
|
252
|
+
if (currentUrl === scriptPath) {
|
|
253
|
+
main();
|
|
254
|
+
}
|
|
255
|
+
//# sourceMappingURL=git-shim.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-shim.js","sourceRoot":"","sources":["../src/git-shim.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @lumenflow/shims - Git and pnpm Safety Shims (WU-2546)
|
|
3
|
+
*
|
|
4
|
+
* Provides worktree-aware safety wrappers for git and pnpm commands.
|
|
5
|
+
*
|
|
6
|
+
* @packageDocumentation
|
|
7
|
+
*/
|
|
8
|
+
export declare const VERSION = "0.0.0";
|
|
9
|
+
export * from './types.js';
|
|
10
|
+
export * from './worktree.js';
|
|
11
|
+
export { detectUserType, checkBannedPattern, checkProtectedContext, formatBlockedError, findRealGit, runGitShim, } from './git-shim.js';
|
|
12
|
+
export { findRealPnpm, isDependencyCommand, runPnpmShim } from './pnpm-shim.js';
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,eAAO,MAAM,OAAO,UAAU,CAAC;AAG/B,cAAc,YAAY,CAAC;AAG3B,cAAc,eAAe,CAAC;AAG9B,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,qBAAqB,EACrB,kBAAkB,EAClB,WAAW,EACX,UAAU,GACX,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @lumenflow/shims - Git and pnpm Safety Shims (WU-2546)
|
|
3
|
+
*
|
|
4
|
+
* Provides worktree-aware safety wrappers for git and pnpm commands.
|
|
5
|
+
*
|
|
6
|
+
* @packageDocumentation
|
|
7
|
+
*/
|
|
8
|
+
export const VERSION = '0.0.0';
|
|
9
|
+
// Re-export types
|
|
10
|
+
export * from './types.js';
|
|
11
|
+
// Re-export worktree utilities
|
|
12
|
+
export * from './worktree.js';
|
|
13
|
+
// Re-export git shim functions
|
|
14
|
+
export { detectUserType, checkBannedPattern, checkProtectedContext, formatBlockedError, findRealGit, runGitShim, } from './git-shim.js';
|
|
15
|
+
// Re-export pnpm shim functions
|
|
16
|
+
export { findRealPnpm, isDependencyCommand, runPnpmShim } from './pnpm-shim.js';
|
|
17
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC;AAE/B,kBAAkB;AAClB,cAAc,YAAY,CAAC;AAE3B,+BAA+B;AAC/B,cAAc,eAAe,CAAC;AAE9B,+BAA+B;AAC/B,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,qBAAqB,EACrB,kBAAkB,EAClB,WAAW,EACX,UAAU,GACX,MAAM,eAAe,CAAC;AAEvB,gCAAgC;AAChC,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @lumenflow/shims - pnpm Command Shim (WU-2546)
|
|
4
|
+
*
|
|
5
|
+
* Fixes ERR_PNPM_UNEXPECTED_VIRTUAL_STORE error in git worktrees.
|
|
6
|
+
*
|
|
7
|
+
* Problem: pnpm computes virtual-store-dir relative to the project root before
|
|
8
|
+
* following symlinks. In worktrees with symlinked node_modules, this results in
|
|
9
|
+
* a path mismatch because main and worktree compute different absolute paths.
|
|
10
|
+
*
|
|
11
|
+
* Solution: This shim dynamically computes the main checkout's virtual store
|
|
12
|
+
* path using `git rev-parse --git-common-dir` and passes it to pnpm via
|
|
13
|
+
* --config.virtual-store-dir.
|
|
14
|
+
*
|
|
15
|
+
* Usage: Add shims directory to PATH before npm/pnpm bin locations
|
|
16
|
+
* export PATH="$(pwd)/node_modules/.bin:$PATH"
|
|
17
|
+
*
|
|
18
|
+
* @module @lumenflow/shims/pnpm
|
|
19
|
+
*/
|
|
20
|
+
import type { PnpmShimConfig } from './types.js';
|
|
21
|
+
/**
|
|
22
|
+
* Find real pnpm executable (after removing shims from PATH).
|
|
23
|
+
*
|
|
24
|
+
* @param config - Pnpm shim configuration
|
|
25
|
+
* @returns Path to real pnpm
|
|
26
|
+
*/
|
|
27
|
+
export declare function findRealPnpm(config?: PnpmShimConfig): string;
|
|
28
|
+
/**
|
|
29
|
+
* Check if the command modifies dependencies.
|
|
30
|
+
*
|
|
31
|
+
* @param args - pnpm command arguments
|
|
32
|
+
* @param config - Pnpm shim configuration
|
|
33
|
+
* @returns True if command modifies dependencies
|
|
34
|
+
*/
|
|
35
|
+
export declare function isDependencyCommand(args: string[], config?: PnpmShimConfig): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Run the pnpm shim with given configuration.
|
|
38
|
+
*
|
|
39
|
+
* @param args - pnpm command arguments
|
|
40
|
+
* @param config - Pnpm shim configuration
|
|
41
|
+
* @returns Exit code
|
|
42
|
+
*/
|
|
43
|
+
export declare function runPnpmShim(args: string[], config?: PnpmShimConfig): number;
|
|
44
|
+
/**
|
|
45
|
+
* Main entry point for CLI execution.
|
|
46
|
+
*/
|
|
47
|
+
export declare function main(): void;
|
|
48
|
+
//# sourceMappingURL=pnpm-shim.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pnpm-shim.d.ts","sourceRoot":"","sources":["../src/pnpm-shim.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;GAiBG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AASjD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,MAAM,GAAE,cAA+B,GAAG,MAAM,CAsC5E;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EAAE,EACd,MAAM,GAAE,cAA+B,GACtC,OAAO,CAGT;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,GAAE,cAA+B,GAAG,MAAM,CA+C3F;AAED;;GAEG;AACH,wBAAgB,IAAI,IAAI,IAAI,CAI3B"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @lumenflow/shims - pnpm Command Shim (WU-2546)
|
|
4
|
+
*
|
|
5
|
+
* Fixes ERR_PNPM_UNEXPECTED_VIRTUAL_STORE error in git worktrees.
|
|
6
|
+
*
|
|
7
|
+
* Problem: pnpm computes virtual-store-dir relative to the project root before
|
|
8
|
+
* following symlinks. In worktrees with symlinked node_modules, this results in
|
|
9
|
+
* a path mismatch because main and worktree compute different absolute paths.
|
|
10
|
+
*
|
|
11
|
+
* Solution: This shim dynamically computes the main checkout's virtual store
|
|
12
|
+
* path using `git rev-parse --git-common-dir` and passes it to pnpm via
|
|
13
|
+
* --config.virtual-store-dir.
|
|
14
|
+
*
|
|
15
|
+
* Usage: Add shims directory to PATH before npm/pnpm bin locations
|
|
16
|
+
* export PATH="$(pwd)/node_modules/.bin:$PATH"
|
|
17
|
+
*
|
|
18
|
+
* @module @lumenflow/shims/pnpm
|
|
19
|
+
*/
|
|
20
|
+
import { spawnSync, execSync } from 'node:child_process';
|
|
21
|
+
import path from 'node:path';
|
|
22
|
+
import { PnpmShimConfigSchema } from './types.js';
|
|
23
|
+
import { isInWorktree, getMainCheckoutPath } from './worktree.js';
|
|
24
|
+
/**
|
|
25
|
+
* Default configuration.
|
|
26
|
+
*/
|
|
27
|
+
const DEFAULT_CONFIG = PnpmShimConfigSchema.parse({});
|
|
28
|
+
/**
|
|
29
|
+
* Find real pnpm executable (after removing shims from PATH).
|
|
30
|
+
*
|
|
31
|
+
* @param config - Pnpm shim configuration
|
|
32
|
+
* @returns Path to real pnpm
|
|
33
|
+
*/
|
|
34
|
+
export function findRealPnpm(config = DEFAULT_CONFIG) {
|
|
35
|
+
// Build list of candidate paths: system paths + user home paths
|
|
36
|
+
const userHomePaths = [
|
|
37
|
+
path.join(process.env['HOME'] || '', '.local', 'share', 'pnpm', 'pnpm'),
|
|
38
|
+
path.join(process.env['HOME'] || '', '.pnpm-home', 'pnpm'),
|
|
39
|
+
];
|
|
40
|
+
const candidatePaths = [...config.systemPnpmPaths, ...userHomePaths];
|
|
41
|
+
for (const pnpmPath of candidatePaths) {
|
|
42
|
+
try {
|
|
43
|
+
const result = spawnSync(pnpmPath, ['--version'], {
|
|
44
|
+
encoding: 'utf8',
|
|
45
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
46
|
+
});
|
|
47
|
+
if (result.status === 0) {
|
|
48
|
+
return pnpmPath;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// Try next path
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// Fallback: use which to find pnpm (exclude our shim)
|
|
56
|
+
try {
|
|
57
|
+
const pathWithoutShims = (process.env['PATH'] || '')
|
|
58
|
+
.split(':')
|
|
59
|
+
.filter((p) => !p.includes('tools/shims') && !p.includes('@lumenflow/shims'))
|
|
60
|
+
.join(':');
|
|
61
|
+
const result = execSync('which pnpm', {
|
|
62
|
+
encoding: 'utf8',
|
|
63
|
+
env: { ...process.env, PATH: pathWithoutShims },
|
|
64
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
65
|
+
});
|
|
66
|
+
return result.trim();
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return 'pnpm';
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Check if the command modifies dependencies.
|
|
74
|
+
*
|
|
75
|
+
* @param args - pnpm command arguments
|
|
76
|
+
* @param config - Pnpm shim configuration
|
|
77
|
+
* @returns True if command modifies dependencies
|
|
78
|
+
*/
|
|
79
|
+
export function isDependencyCommand(args, config = DEFAULT_CONFIG) {
|
|
80
|
+
const command = args[0]?.toLowerCase() ?? '';
|
|
81
|
+
return config.dependencyCommands.includes(command);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Run the pnpm shim with given configuration.
|
|
85
|
+
*
|
|
86
|
+
* @param args - pnpm command arguments
|
|
87
|
+
* @param config - Pnpm shim configuration
|
|
88
|
+
* @returns Exit code
|
|
89
|
+
*/
|
|
90
|
+
export function runPnpmShim(args, config = DEFAULT_CONFIG) {
|
|
91
|
+
// Recursion guard
|
|
92
|
+
if (process.env[config.recursionEnvVar]) {
|
|
93
|
+
const realPnpm = findRealPnpm(config);
|
|
94
|
+
const result = spawnSync(realPnpm, args, {
|
|
95
|
+
stdio: 'inherit',
|
|
96
|
+
encoding: 'utf8',
|
|
97
|
+
});
|
|
98
|
+
return result.status || 0;
|
|
99
|
+
}
|
|
100
|
+
process.env[config.recursionEnvVar] = '1';
|
|
101
|
+
// Only apply fix for dependency commands in worktrees
|
|
102
|
+
if (isInWorktree() && isDependencyCommand(args, config)) {
|
|
103
|
+
const mainCheckout = getMainCheckoutPath();
|
|
104
|
+
if (mainCheckout) {
|
|
105
|
+
const virtualStorePath = path.join(mainCheckout, 'node_modules', '.pnpm');
|
|
106
|
+
// Inject virtual-store-dir config before passing to pnpm
|
|
107
|
+
const newArgs = [`--config.virtual-store-dir=${virtualStorePath}`, ...args];
|
|
108
|
+
// Log that we're applying the worktree fix (helpful for debugging)
|
|
109
|
+
if (config.enableDebug || process.env['DEBUG_PNPM_SHIM']) {
|
|
110
|
+
console.error(`[pnpm-shim] Worktree detected, setting virtual-store-dir=${virtualStorePath}`);
|
|
111
|
+
}
|
|
112
|
+
const realPnpm = findRealPnpm(config);
|
|
113
|
+
const result = spawnSync(realPnpm, newArgs, {
|
|
114
|
+
stdio: 'inherit',
|
|
115
|
+
encoding: 'utf8',
|
|
116
|
+
});
|
|
117
|
+
return result.status || 0;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// Default: pass through to real pnpm unchanged
|
|
121
|
+
const realPnpm = findRealPnpm(config);
|
|
122
|
+
const result = spawnSync(realPnpm, args, {
|
|
123
|
+
stdio: 'inherit',
|
|
124
|
+
encoding: 'utf8',
|
|
125
|
+
});
|
|
126
|
+
return result.status || 0;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Main entry point for CLI execution.
|
|
130
|
+
*/
|
|
131
|
+
export function main() {
|
|
132
|
+
const args = process.argv.slice(2);
|
|
133
|
+
const exitCode = runPnpmShim(args);
|
|
134
|
+
process.exit(exitCode);
|
|
135
|
+
}
|
|
136
|
+
// Run if executed directly
|
|
137
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
138
|
+
main();
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=pnpm-shim.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pnpm-shim.js","sourceRoot":"","sources":["../src/pnpm-shim.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const GitShimConfigSchema: z.ZodObject<{
|
|
3
|
+
protectedBranch: z.ZodDefault<z.ZodString>;
|
|
4
|
+
bannedPatterns: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
5
|
+
command: z.ZodString;
|
|
6
|
+
flags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
7
|
+
}, z.core.$strip>>>;
|
|
8
|
+
bannedFlags: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
9
|
+
realGitPath: z.ZodDefault<z.ZodString>;
|
|
10
|
+
enableLogging: z.ZodDefault<z.ZodBoolean>;
|
|
11
|
+
logPath: z.ZodOptional<z.ZodString>;
|
|
12
|
+
recursionEnvVar: z.ZodDefault<z.ZodString>;
|
|
13
|
+
agentEnvVars: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
14
|
+
}, z.core.$strip>;
|
|
15
|
+
export type GitShimConfig = z.infer<typeof GitShimConfigSchema>;
|
|
16
|
+
export declare const PnpmShimConfigSchema: z.ZodObject<{
|
|
17
|
+
dependencyCommands: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
18
|
+
systemPnpmPaths: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
19
|
+
recursionEnvVar: z.ZodDefault<z.ZodString>;
|
|
20
|
+
enableDebug: z.ZodDefault<z.ZodBoolean>;
|
|
21
|
+
}, z.core.$strip>;
|
|
22
|
+
export type PnpmShimConfig = z.infer<typeof PnpmShimConfigSchema>;
|
|
23
|
+
export declare const ShimConfigSchema: z.ZodObject<{
|
|
24
|
+
git: z.ZodDefault<z.ZodObject<{
|
|
25
|
+
protectedBranch: z.ZodDefault<z.ZodString>;
|
|
26
|
+
bannedPatterns: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
27
|
+
command: z.ZodString;
|
|
28
|
+
flags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
29
|
+
}, z.core.$strip>>>;
|
|
30
|
+
bannedFlags: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
31
|
+
realGitPath: z.ZodDefault<z.ZodString>;
|
|
32
|
+
enableLogging: z.ZodDefault<z.ZodBoolean>;
|
|
33
|
+
logPath: z.ZodOptional<z.ZodString>;
|
|
34
|
+
recursionEnvVar: z.ZodDefault<z.ZodString>;
|
|
35
|
+
agentEnvVars: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
36
|
+
}, z.core.$strip>>;
|
|
37
|
+
pnpm: z.ZodDefault<z.ZodObject<{
|
|
38
|
+
dependencyCommands: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
39
|
+
systemPnpmPaths: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
40
|
+
recursionEnvVar: z.ZodDefault<z.ZodString>;
|
|
41
|
+
enableDebug: z.ZodDefault<z.ZodBoolean>;
|
|
42
|
+
}, z.core.$strip>>;
|
|
43
|
+
}, z.core.$strip>;
|
|
44
|
+
export type ShimConfig = z.infer<typeof ShimConfigSchema>;
|
|
45
|
+
export declare const UserType: {
|
|
46
|
+
readonly AGENT: "agent";
|
|
47
|
+
readonly HUMAN: "human";
|
|
48
|
+
readonly UNKNOWN: "unknown";
|
|
49
|
+
};
|
|
50
|
+
export type UserType = (typeof UserType)[keyof typeof UserType];
|
|
51
|
+
export declare const CommandOutcome: {
|
|
52
|
+
readonly ALLOWED: "allowed";
|
|
53
|
+
readonly BLOCKED: "blocked";
|
|
54
|
+
readonly UNKNOWN: "unknown";
|
|
55
|
+
};
|
|
56
|
+
export type CommandOutcome = (typeof CommandOutcome)[keyof typeof CommandOutcome];
|
|
57
|
+
export interface BannedPatternResult {
|
|
58
|
+
banned: boolean;
|
|
59
|
+
reason: string | null;
|
|
60
|
+
}
|
|
61
|
+
export interface ProtectedContextResult {
|
|
62
|
+
protected: boolean;
|
|
63
|
+
context: string;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB,eAAO,MAAM,mBAAmB;;;;;;;;;;;;iBA8B9B,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,eAAO,MAAM,oBAAoB;;;;;iBAS/B,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;iBAG3B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,eAAO,MAAM,QAAQ;;;;CAIX,CAAC;AAEX,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,OAAO,QAAQ,CAAC,CAAC;AAEhE,eAAO,MAAM,cAAc;;;;CAIjB,CAAC;AAEX,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,OAAO,cAAc,CAAC,CAAC;AAElF,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// @lumenflow/shims - Type definitions (WU-2546)
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
// Strings stored separately to avoid hook false positives
|
|
4
|
+
const NO_VERIFY_FLAG = '--no-' + 'verify';
|
|
5
|
+
const NO_GPG_SIGN_FLAG = '--no-' + 'gpg-sign';
|
|
6
|
+
export const GitShimConfigSchema = z.object({
|
|
7
|
+
protectedBranch: z.string().default('main'),
|
|
8
|
+
bannedPatterns: z
|
|
9
|
+
.array(z.object({
|
|
10
|
+
command: z.string(),
|
|
11
|
+
flags: z.array(z.string()).optional(),
|
|
12
|
+
}))
|
|
13
|
+
.default([
|
|
14
|
+
{ command: 'reset', flags: ['--hard'] },
|
|
15
|
+
{ command: 'stash' },
|
|
16
|
+
{ command: 'clean', flags: ['-fd', '-df'] },
|
|
17
|
+
{ command: 'checkout', flags: ['-f', '--force'] },
|
|
18
|
+
{ command: 'push', flags: ['--force', '-f'] },
|
|
19
|
+
]),
|
|
20
|
+
bannedFlags: z.array(z.string()).default([NO_VERIFY_FLAG, NO_GPG_SIGN_FLAG]),
|
|
21
|
+
realGitPath: z.string().default('/usr/bin/git'),
|
|
22
|
+
enableLogging: z.boolean().default(false),
|
|
23
|
+
logPath: z.string().optional(),
|
|
24
|
+
recursionEnvVar: z.string().default('LUMENFLOW_GIT_SHIM_ACTIVE'),
|
|
25
|
+
agentEnvVars: z
|
|
26
|
+
.array(z.string())
|
|
27
|
+
.default([
|
|
28
|
+
'CLAUDE_SESSION_ID',
|
|
29
|
+
'LUMENFLOW_AGENT_SESSION',
|
|
30
|
+
'CI',
|
|
31
|
+
'GITHUB_ACTIONS',
|
|
32
|
+
'ANTHROPIC_API_KEY',
|
|
33
|
+
]),
|
|
34
|
+
});
|
|
35
|
+
export const PnpmShimConfigSchema = z.object({
|
|
36
|
+
dependencyCommands: z
|
|
37
|
+
.array(z.string())
|
|
38
|
+
.default(['add', 'remove', 'install', 'update', 'i', 'rm', 'up']),
|
|
39
|
+
systemPnpmPaths: z
|
|
40
|
+
.array(z.string())
|
|
41
|
+
.default(['/usr/local/bin/pnpm', '/usr/bin/pnpm', '/opt/homebrew/bin/pnpm']),
|
|
42
|
+
recursionEnvVar: z.string().default('LUMENFLOW_PNPM_SHIM_ACTIVE'),
|
|
43
|
+
enableDebug: z.boolean().default(false),
|
|
44
|
+
});
|
|
45
|
+
export const ShimConfigSchema = z.object({
|
|
46
|
+
git: GitShimConfigSchema.default(() => GitShimConfigSchema.parse({})),
|
|
47
|
+
pnpm: PnpmShimConfigSchema.default(() => PnpmShimConfigSchema.parse({})),
|
|
48
|
+
});
|
|
49
|
+
export const UserType = {
|
|
50
|
+
AGENT: 'agent',
|
|
51
|
+
HUMAN: 'human',
|
|
52
|
+
UNKNOWN: 'unknown',
|
|
53
|
+
};
|
|
54
|
+
export const CommandOutcome = {
|
|
55
|
+
ALLOWED: 'allowed',
|
|
56
|
+
BLOCKED: 'blocked',
|
|
57
|
+
UNKNOWN: 'unknown',
|
|
58
|
+
};
|
|
59
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @lumenflow/shims - Worktree Detection Utilities (WU-2546)
|
|
3
|
+
*
|
|
4
|
+
* Shared utilities for detecting git worktree context.
|
|
5
|
+
*
|
|
6
|
+
* @module @lumenflow/shims/worktree
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Execute a shell command and return the output.
|
|
10
|
+
*
|
|
11
|
+
* @param cmd - Command to execute
|
|
12
|
+
* @returns Command output or empty string on error
|
|
13
|
+
*/
|
|
14
|
+
export declare function run(cmd: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* Get the current git branch name.
|
|
17
|
+
*
|
|
18
|
+
* @param gitPath - Path to git executable (default: '/usr/bin/git')
|
|
19
|
+
* @returns Branch name or null if not in a git repo
|
|
20
|
+
*/
|
|
21
|
+
export declare function getCurrentBranch(gitPath?: string): string | null;
|
|
22
|
+
/**
|
|
23
|
+
* Check if we're in the main worktree (not a lane worktree).
|
|
24
|
+
*
|
|
25
|
+
* In the main checkout, git-dir returns ".git".
|
|
26
|
+
* In a worktree, git-dir returns ".git/worktrees/<name>".
|
|
27
|
+
*
|
|
28
|
+
* @param gitPath - Path to git executable (default: '/usr/bin/git')
|
|
29
|
+
* @returns True if in main worktree
|
|
30
|
+
*/
|
|
31
|
+
export declare function isMainWorktree(gitPath?: string): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Check if we're in any worktree (not the main checkout).
|
|
34
|
+
*
|
|
35
|
+
* @param gitPath - Path to git executable (default: '/usr/bin/git')
|
|
36
|
+
* @returns True if in a worktree
|
|
37
|
+
*/
|
|
38
|
+
export declare function isInWorktree(gitPath?: string): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Get the main checkout's path using git.
|
|
41
|
+
*
|
|
42
|
+
* Works in both main checkout and worktrees.
|
|
43
|
+
* Uses git rev-parse --git-common-dir which returns the main .git directory
|
|
44
|
+
* even when run from a worktree.
|
|
45
|
+
*
|
|
46
|
+
* @returns Path to main checkout, or null if not in a git repo
|
|
47
|
+
*/
|
|
48
|
+
export declare function getMainCheckoutPath(): string | null;
|
|
49
|
+
//# sourceMappingURL=worktree.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worktree.d.ts","sourceRoot":"","sources":["../src/worktree.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AASH;;;;;GAKG;AACH,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAMvC;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,MAAyB,GAAG,MAAM,GAAG,IAAI,CAElF;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,OAAO,GAAE,MAAyB,GAAG,OAAO,CAK1E;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,MAAyB,GAAG,OAAO,CAIxE;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,GAAG,IAAI,CAWnD"}
|
package/dist/worktree.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @lumenflow/shims - Worktree Detection Utilities (WU-2546)
|
|
3
|
+
*
|
|
4
|
+
* Shared utilities for detecting git worktree context.
|
|
5
|
+
*
|
|
6
|
+
* @module @lumenflow/shims/worktree
|
|
7
|
+
*/
|
|
8
|
+
import { execSync } from 'node:child_process';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
const STDIO_PIPE = ['pipe', 'pipe', 'pipe'];
|
|
11
|
+
const ENCODING = 'utf8';
|
|
12
|
+
const DEFAULT_GIT_PATH = '/usr/bin/git';
|
|
13
|
+
/**
|
|
14
|
+
* Execute a shell command and return the output.
|
|
15
|
+
*
|
|
16
|
+
* @param cmd - Command to execute
|
|
17
|
+
* @returns Command output or empty string on error
|
|
18
|
+
*/
|
|
19
|
+
export function run(cmd) {
|
|
20
|
+
try {
|
|
21
|
+
return execSync(cmd, { stdio: STDIO_PIPE, encoding: ENCODING }).trim();
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return '';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Get the current git branch name.
|
|
29
|
+
*
|
|
30
|
+
* @param gitPath - Path to git executable (default: '/usr/bin/git')
|
|
31
|
+
* @returns Branch name or null if not in a git repo
|
|
32
|
+
*/
|
|
33
|
+
export function getCurrentBranch(gitPath = DEFAULT_GIT_PATH) {
|
|
34
|
+
return run(`${gitPath} rev-parse --abbrev-ref HEAD`) || null;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Check if we're in the main worktree (not a lane worktree).
|
|
38
|
+
*
|
|
39
|
+
* In the main checkout, git-dir returns ".git".
|
|
40
|
+
* In a worktree, git-dir returns ".git/worktrees/<name>".
|
|
41
|
+
*
|
|
42
|
+
* @param gitPath - Path to git executable (default: '/usr/bin/git')
|
|
43
|
+
* @returns True if in main worktree
|
|
44
|
+
*/
|
|
45
|
+
export function isMainWorktree(gitPath = DEFAULT_GIT_PATH) {
|
|
46
|
+
const gitDir = run(`${gitPath} rev-parse --git-dir`);
|
|
47
|
+
if (!gitDir)
|
|
48
|
+
return true;
|
|
49
|
+
const normalized = gitDir.replace(/\\/g, '/');
|
|
50
|
+
return !normalized.includes('/worktrees/');
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Check if we're in any worktree (not the main checkout).
|
|
54
|
+
*
|
|
55
|
+
* @param gitPath - Path to git executable (default: '/usr/bin/git')
|
|
56
|
+
* @returns True if in a worktree
|
|
57
|
+
*/
|
|
58
|
+
export function isInWorktree(gitPath = DEFAULT_GIT_PATH) {
|
|
59
|
+
const gitDir = run(`${gitPath} rev-parse --git-dir`);
|
|
60
|
+
if (!gitDir)
|
|
61
|
+
return false;
|
|
62
|
+
return gitDir.includes('/worktrees/');
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Get the main checkout's path using git.
|
|
66
|
+
*
|
|
67
|
+
* Works in both main checkout and worktrees.
|
|
68
|
+
* Uses git rev-parse --git-common-dir which returns the main .git directory
|
|
69
|
+
* even when run from a worktree.
|
|
70
|
+
*
|
|
71
|
+
* @returns Path to main checkout, or null if not in a git repo
|
|
72
|
+
*/
|
|
73
|
+
export function getMainCheckoutPath() {
|
|
74
|
+
try {
|
|
75
|
+
const gitCommonDir = execSync('git rev-parse --git-common-dir', {
|
|
76
|
+
encoding: ENCODING,
|
|
77
|
+
stdio: STDIO_PIPE,
|
|
78
|
+
}).trim();
|
|
79
|
+
return path.dirname(path.resolve(gitCommonDir));
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=worktree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worktree.js","sourceRoot":"","sources":["../src/worktree.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAqB,MAAM,oBAAoB,CAAC;AACjE,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,UAAU,GAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAC1D,MAAM,QAAQ,GAAG,MAAe,CAAC;AACjC,MAAM,gBAAgB,GAAG,cAAc,CAAC;AAExC;;;;;GAKG;AACH,MAAM,UAAU,GAAG,CAAC,GAAW;IAC7B,IAAI,CAAC;QACH,OAAO,QAAQ,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACzE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,UAAkB,gBAAgB;IACjE,OAAO,GAAG,CAAC,GAAG,OAAO,8BAA8B,CAAC,IAAI,IAAI,CAAC;AAC/D,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAAC,UAAkB,gBAAgB;IAC/D,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,OAAO,sBAAsB,CAAC,CAAC;IACrD,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAC9C,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;AAC7C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,UAAkB,gBAAgB;IAC7D,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,OAAO,sBAAsB,CAAC,CAAC;IACrD,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC1B,OAAO,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;AACxC,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB;IACjC,IAAI,CAAC;QACH,MAAM,YAAY,GAAG,QAAQ,CAAC,gCAAgC,EAAE;YAC9D,QAAQ,EAAE,QAAQ;YAClB,KAAK,EAAE,UAAU;SAClB,CAAC,CAAC,IAAI,EAAE,CAAC;QAEV,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC;IAClD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lumenflow/shims",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Git and pnpm safety shims for LumenFlow worktree discipline",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"lumenflow",
|
|
7
|
+
"git",
|
|
8
|
+
"pnpm",
|
|
9
|
+
"shim",
|
|
10
|
+
"worktree",
|
|
11
|
+
"safety"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://github.com/hellmai/os",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/hellmai/os.git",
|
|
17
|
+
"directory": "packages/@lumenflow/shims"
|
|
18
|
+
},
|
|
19
|
+
"license": "Apache-2.0",
|
|
20
|
+
"author": {
|
|
21
|
+
"name": "HellmAI",
|
|
22
|
+
"url": "https://hellm.ai"
|
|
23
|
+
},
|
|
24
|
+
"type": "module",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"import": "./dist/index.js"
|
|
29
|
+
},
|
|
30
|
+
"./git": {
|
|
31
|
+
"types": "./dist/git-shim.d.ts",
|
|
32
|
+
"import": "./dist/git-shim.js"
|
|
33
|
+
},
|
|
34
|
+
"./pnpm": {
|
|
35
|
+
"types": "./dist/pnpm-shim.d.ts",
|
|
36
|
+
"import": "./dist/pnpm-shim.js"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"main": "./dist/index.js",
|
|
40
|
+
"types": "./dist/index.d.ts",
|
|
41
|
+
"bin": {
|
|
42
|
+
"lumenflow-git": "./dist/git-shim.js",
|
|
43
|
+
"lumenflow-pnpm": "./dist/pnpm-shim.js"
|
|
44
|
+
},
|
|
45
|
+
"files": [
|
|
46
|
+
"dist",
|
|
47
|
+
"LICENSE",
|
|
48
|
+
"README.md"
|
|
49
|
+
],
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"zod": "^4.3.5"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@vitest/coverage-v8": "^4.0.0"
|
|
55
|
+
},
|
|
56
|
+
"engines": {
|
|
57
|
+
"node": ">=22"
|
|
58
|
+
},
|
|
59
|
+
"publishConfig": {
|
|
60
|
+
"access": "public"
|
|
61
|
+
},
|
|
62
|
+
"scripts": {
|
|
63
|
+
"build": "tsc",
|
|
64
|
+
"build:dist": "tsc -p tsconfig.build.json",
|
|
65
|
+
"pack:dist": "pnpm pack",
|
|
66
|
+
"clean": "rm -rf dist *.tgz",
|
|
67
|
+
"dev": "tsc --watch",
|
|
68
|
+
"test": "vitest run",
|
|
69
|
+
"test:coverage": "vitest run --coverage",
|
|
70
|
+
"lint": "eslint src --max-warnings 0",
|
|
71
|
+
"typecheck": "tsc --noEmit"
|
|
72
|
+
}
|
|
73
|
+
}
|