@junheep/gwt 0.3.0 → 0.4.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/README.md +19 -6
- package/bin/gwt.mjs +180 -27
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -29,9 +29,10 @@ gwt shell install zsh
|
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
The installer shows the line it will add to `~/.zshrc` and asks for
|
|
32
|
-
confirmation. The integration also provides Zsh completion
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
confirmation. The integration also provides Zsh completion and automatically
|
|
33
|
+
loads assigned ports and configured environment variables when Zsh enters a
|
|
34
|
+
managed worktree. Previous values are restored when Zsh leaves it. Normal
|
|
35
|
+
environment synchronization produces no output.
|
|
35
36
|
|
|
36
37
|
## Coding agents
|
|
37
38
|
|
|
@@ -90,6 +91,9 @@ Repositories without a remote use their canonical path.
|
|
|
90
91
|
"WEB_PORT",
|
|
91
92
|
"SERVER_PORT"
|
|
92
93
|
],
|
|
94
|
+
"env": {
|
|
95
|
+
"NEXT_PUBLIC_API_ENDPOINT": "http://127.0.0.1:${SERVER_PORT}"
|
|
96
|
+
},
|
|
93
97
|
"postCreate": "hooks/worktree-setup",
|
|
94
98
|
"preRemove": "hooks/worktree-cleanup"
|
|
95
99
|
}
|
|
@@ -121,6 +125,9 @@ The project file contains the configuration fields directly:
|
|
|
121
125
|
"WEB_PORT",
|
|
122
126
|
"SERVER_PORT"
|
|
123
127
|
],
|
|
128
|
+
"env": {
|
|
129
|
+
"NEXT_PUBLIC_API_ENDPOINT": "http://127.0.0.1:${SERVER_PORT}"
|
|
130
|
+
},
|
|
124
131
|
"postCreate": "./scripts/worktree-setup",
|
|
125
132
|
"preRemove": "./scripts/worktree-cleanup"
|
|
126
133
|
}
|
|
@@ -145,6 +152,10 @@ current commit as their base and run no setup actions.
|
|
|
145
152
|
overwriting an existing destination.
|
|
146
153
|
- `ports`: Environment variable names assigned stable ports in the range
|
|
147
154
|
20000–39999.
|
|
155
|
+
- `env`: Environment variables loaded alongside assigned ports. Values are
|
|
156
|
+
literal strings with optional `${PORT_NAME}` references to names declared in
|
|
157
|
+
`ports`. Shell expressions and references to arbitrary process variables are
|
|
158
|
+
not evaluated.
|
|
148
159
|
- `postCreate`: Executable run after files and ports are prepared.
|
|
149
160
|
- `preRemove`: Executable run before removal.
|
|
150
161
|
|
|
@@ -170,6 +181,7 @@ GWT_PATH
|
|
|
170
181
|
GWT_PRIMARY_PATH
|
|
171
182
|
GWT_BRANCH
|
|
172
183
|
<each name declared in ports>
|
|
184
|
+
<each name declared in env>
|
|
173
185
|
```
|
|
174
186
|
|
|
175
187
|
Example `postCreate` hook:
|
|
@@ -187,9 +199,10 @@ Hook paths in user config are resolved relative to the directory containing
|
|
|
187
199
|
worktree. Both run with the target worktree as their working directory, and
|
|
188
200
|
their standard output and errors are streamed directly to the terminal.
|
|
189
201
|
|
|
190
|
-
|
|
191
|
-
from a committed `.gwt.json` require explicit trust
|
|
192
|
-
repository
|
|
202
|
+
User configuration is trusted because the user added it directly. Ports and
|
|
203
|
+
environment variables from a committed `.gwt.json` require explicit trust
|
|
204
|
+
because they automatically change the shell; repository hooks require the same
|
|
205
|
+
approval because they execute code:
|
|
193
206
|
|
|
194
207
|
```sh
|
|
195
208
|
gwt trust
|
package/bin/gwt.mjs
CHANGED
|
@@ -28,7 +28,8 @@ const PORT_MIN = 20_000
|
|
|
28
28
|
const PORT_MAX = 39_999
|
|
29
29
|
const PICKER_ESCAPE_CODE_TIMEOUT_MS = 50
|
|
30
30
|
const ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/
|
|
31
|
-
const
|
|
31
|
+
const SHELL_ENV_STATE = "GWT_SHELL_ENV_STATE"
|
|
32
|
+
const DEFAULT_CONFIG = { copyFiles: [], ports: [], env: {} }
|
|
32
33
|
const SKILL_DIRECTORIES = { claude: ".claude", codex: ".agents" }
|
|
33
34
|
const SKILL_USAGE = `Usage: gwt skill install <${Object.keys(SKILL_DIRECTORIES).join("|")}> [--project] [--dry-run] [--yes]`
|
|
34
35
|
|
|
@@ -127,7 +128,7 @@ function validateRelativePath(value, field) {
|
|
|
127
128
|
|
|
128
129
|
function validateConfig(parsed, label) {
|
|
129
130
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new CliError(`${label} must contain an object`)
|
|
130
|
-
const allowed = new Set(["base", "worktreeDirectory", "copyFiles", "ports", "postCreate", "preRemove"])
|
|
131
|
+
const allowed = new Set(["base", "worktreeDirectory", "copyFiles", "ports", "env", "postCreate", "preRemove"])
|
|
131
132
|
for (const key of Object.keys(parsed)) {
|
|
132
133
|
if (!allowed.has(key)) throw new CliError(`${label} contains an unknown field: ${key}`)
|
|
133
134
|
}
|
|
@@ -150,11 +151,33 @@ function validateConfig(parsed, label) {
|
|
|
150
151
|
if (!Array.isArray(parsed.ports ?? [])) throw new CliError("ports must be an array")
|
|
151
152
|
const ports = (parsed.ports ?? []).map((name, index) => {
|
|
152
153
|
if (typeof name !== "string" || !ENV_NAME.test(name)) throw new CliError(`ports[${index}] is not a valid environment variable name`)
|
|
154
|
+
if (name.startsWith("GWT_")) throw new CliError(`ports[${index}] cannot use the reserved GWT_ prefix`)
|
|
153
155
|
return name
|
|
154
156
|
})
|
|
155
157
|
if (new Set(ports).size !== ports.length) throw new CliError("ports cannot contain duplicates")
|
|
156
158
|
if (ports.length > 100) throw new CliError("ports cannot contain more than 100 entries")
|
|
157
159
|
|
|
160
|
+
if (!parsed.env || typeof parsed.env !== "object" || Array.isArray(parsed.env)) {
|
|
161
|
+
if (parsed.env !== undefined) throw new CliError("env must be an object")
|
|
162
|
+
}
|
|
163
|
+
const envEntries = Object.entries(parsed.env ?? {})
|
|
164
|
+
if (envEntries.length > 100) throw new CliError("env cannot contain more than 100 entries")
|
|
165
|
+
const env = Object.fromEntries(envEntries.map(([name, value]) => {
|
|
166
|
+
if (!ENV_NAME.test(name)) throw new CliError(`env.${name} is not a valid environment variable name`)
|
|
167
|
+
if (name.startsWith("GWT_")) throw new CliError(`env.${name} cannot use the reserved GWT_ prefix`)
|
|
168
|
+
if (ports.includes(name)) throw new CliError(`env.${name} conflicts with a configured port`)
|
|
169
|
+
if (typeof value !== "string") throw new CliError(`env.${name} must be a string`)
|
|
170
|
+
|
|
171
|
+
const remainder = value.replace(/\$\{([^}]*)\}/g, (_, reference) => {
|
|
172
|
+
if (!ENV_NAME.test(reference) || !ports.includes(reference)) {
|
|
173
|
+
throw new CliError(`env.${name} references unknown port ${reference || "(empty)"}`)
|
|
174
|
+
}
|
|
175
|
+
return ""
|
|
176
|
+
})
|
|
177
|
+
if (remainder.includes("${")) throw new CliError(`env.${name} contains an invalid port reference`)
|
|
178
|
+
return [name, value]
|
|
179
|
+
}))
|
|
180
|
+
|
|
158
181
|
for (const hook of ["postCreate", "preRemove"]) {
|
|
159
182
|
if (parsed[hook] !== undefined) validateRelativePath(parsed[hook], hook)
|
|
160
183
|
}
|
|
@@ -163,11 +186,24 @@ function validateConfig(parsed, label) {
|
|
|
163
186
|
...parsed,
|
|
164
187
|
copyFiles,
|
|
165
188
|
ports,
|
|
189
|
+
env,
|
|
166
190
|
}
|
|
167
191
|
if (worktreeDirectory !== undefined) config.worktreeDirectory = worktreeDirectory
|
|
168
192
|
return config
|
|
169
193
|
}
|
|
170
194
|
|
|
195
|
+
function resolveConfiguredEnv(config, ports) {
|
|
196
|
+
return Object.fromEntries(Object.entries(config.env).map(([name, template]) => [
|
|
197
|
+
name,
|
|
198
|
+
template.replace(/\$\{([^}]*)\}/g, (_, reference) => {
|
|
199
|
+
if (!Object.hasOwn(ports, reference)) {
|
|
200
|
+
throw new CliError(`Cannot resolve env.${name}: this worktree has no assigned ${reference}`)
|
|
201
|
+
}
|
|
202
|
+
return String(ports[reference])
|
|
203
|
+
}),
|
|
204
|
+
]))
|
|
205
|
+
}
|
|
206
|
+
|
|
171
207
|
function configHome() {
|
|
172
208
|
return process.env.XDG_CONFIG_HOME || join(homedir(), ".config")
|
|
173
209
|
}
|
|
@@ -447,7 +483,7 @@ function hookPaths(configDocument, worktreePath) {
|
|
|
447
483
|
|
|
448
484
|
function trustFingerprint(repository, configDocument, worktreePath) {
|
|
449
485
|
const hooks = hookPaths(configDocument, worktreePath)
|
|
450
|
-
if (hooks.length === 0) return null
|
|
486
|
+
if (hooks.length === 0 && configDocument.value.ports.length === 0 && Object.keys(configDocument.value.env).length === 0) return null
|
|
451
487
|
const hash = createHash("sha256")
|
|
452
488
|
hash.update(repository.primaryPath)
|
|
453
489
|
hash.update("\0")
|
|
@@ -517,20 +553,24 @@ async function ensureTrusted(repository, configDocument, worktreePath) {
|
|
|
517
553
|
if (!fingerprint || isTrusted(repository, fingerprint)) return
|
|
518
554
|
|
|
519
555
|
const hooks = hookPaths(configDocument, worktreePath)
|
|
520
|
-
console.error("This repository wants to
|
|
556
|
+
console.error("This repository wants to configure your development environment:")
|
|
521
557
|
for (const hook of hooks) console.error(` ${hook.name}: ${hook.configuredPath}`)
|
|
558
|
+
for (const name of configDocument.value.ports) console.error(` port: ${name}`)
|
|
559
|
+
for (const name of Object.keys(configDocument.value.env)) console.error(` env: ${name}`)
|
|
522
560
|
const allowed = await ask("Allow and remember? [y/N] ")
|
|
523
|
-
if (!allowed) throw new CliError("Project
|
|
561
|
+
if (!allowed) throw new CliError("Project configuration is not trusted. Run 'gwt trust' to approve it")
|
|
524
562
|
saveTrust(repository, fingerprint)
|
|
525
563
|
}
|
|
526
564
|
|
|
527
|
-
function hookContext(repository, worktree, metadata) {
|
|
565
|
+
function hookContext(repository, config, worktree, metadata) {
|
|
566
|
+
const ports = metadata?.ports ?? {}
|
|
528
567
|
return {
|
|
529
568
|
id: metadata?.id ?? "",
|
|
530
569
|
path: canonical(worktree.path),
|
|
531
570
|
primaryPath: repository.primaryPath,
|
|
532
571
|
branch: worktree.branch ?? "",
|
|
533
|
-
ports
|
|
572
|
+
ports,
|
|
573
|
+
environment: resolveConfiguredEnv(config, ports),
|
|
534
574
|
}
|
|
535
575
|
}
|
|
536
576
|
|
|
@@ -538,7 +578,7 @@ function runHook(name, repository, configDocument, worktree, metadata) {
|
|
|
538
578
|
const configuredPath = configDocument.value[name]
|
|
539
579
|
if (!configuredPath) return
|
|
540
580
|
const hook = hookPaths(configDocument, canonical(worktree.path)).find((item) => item.name === name)
|
|
541
|
-
const context = hookContext(repository, worktree, metadata)
|
|
581
|
+
const context = hookContext(repository, configDocument.value, worktree, metadata)
|
|
542
582
|
const env = {
|
|
543
583
|
...process.env,
|
|
544
584
|
GWT_ID: context.id,
|
|
@@ -546,6 +586,7 @@ function runHook(name, repository, configDocument, worktree, metadata) {
|
|
|
546
586
|
GWT_PRIMARY_PATH: context.primaryPath,
|
|
547
587
|
GWT_BRANCH: context.branch,
|
|
548
588
|
...Object.fromEntries(Object.entries(context.ports).map(([key, value]) => [key, String(value)])),
|
|
589
|
+
...context.environment,
|
|
549
590
|
}
|
|
550
591
|
console.log(`Running ${name}...`)
|
|
551
592
|
const result = run(hook.path, [], {
|
|
@@ -1062,21 +1103,21 @@ function commandTrust(args) {
|
|
|
1062
1103
|
const configDocument = loadConfig(repository)
|
|
1063
1104
|
if (!configDocument.requiresTrust) {
|
|
1064
1105
|
console.log(configDocument.source === "user"
|
|
1065
|
-
? "User
|
|
1106
|
+
? "User configuration is trusted automatically"
|
|
1066
1107
|
: "This repository has no project config to approve")
|
|
1067
1108
|
return
|
|
1068
1109
|
}
|
|
1069
1110
|
const fingerprint = trustFingerprint(repository, configDocument, canonical(current.path))
|
|
1070
1111
|
if (!fingerprint) {
|
|
1071
|
-
console.log("This repository has no project
|
|
1112
|
+
console.log("This repository has no project configuration that requires approval")
|
|
1072
1113
|
return
|
|
1073
1114
|
}
|
|
1074
1115
|
saveTrust(repository, fingerprint)
|
|
1075
|
-
console.log(`Trusted project
|
|
1116
|
+
console.log(`Trusted project configuration for ${repository.primaryPath}`)
|
|
1076
1117
|
}
|
|
1077
1118
|
|
|
1078
1119
|
function configScaffold() {
|
|
1079
|
-
return { copyFiles: [], ports: [] }
|
|
1120
|
+
return { copyFiles: [], ports: [], env: {} }
|
|
1080
1121
|
}
|
|
1081
1122
|
|
|
1082
1123
|
function commandConfigCreate(args) {
|
|
@@ -1141,6 +1182,94 @@ function commandConfig(args) {
|
|
|
1141
1182
|
throw new CliError("Usage: gwt config <create [--project]|show>")
|
|
1142
1183
|
}
|
|
1143
1184
|
|
|
1185
|
+
function configuredShellEnvironment() {
|
|
1186
|
+
const insideRepository = git(["rev-parse", "--is-inside-work-tree"], process.cwd(), { allowFailure: true })
|
|
1187
|
+
if (insideRepository.status !== 0) return {}
|
|
1188
|
+
|
|
1189
|
+
const repository = discoverRepository()
|
|
1190
|
+
const worktree = currentWorktree(repository)
|
|
1191
|
+
if (!worktree || resolve(worktree.path) === resolve(repository.primaryPath)) return {}
|
|
1192
|
+
|
|
1193
|
+
const metadata = metadataForWorktree(repository, worktree)
|
|
1194
|
+
if (!metadata) return {}
|
|
1195
|
+
|
|
1196
|
+
const configDocument = loadConfig(repository)
|
|
1197
|
+
if (configDocument.requiresTrust) {
|
|
1198
|
+
const fingerprint = trustFingerprint(repository, configDocument, canonical(worktree.path))
|
|
1199
|
+
if (!isTrusted(repository, fingerprint)) return {}
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
const ports = Object.fromEntries(configDocument.value.ports.map((name) => {
|
|
1203
|
+
if (!Object.hasOwn(metadata.ports ?? {}, name)) {
|
|
1204
|
+
throw new CliError(`This worktree has no assigned ${name}; recreate it after changing ports`)
|
|
1205
|
+
}
|
|
1206
|
+
return [name, String(metadata.ports[name])]
|
|
1207
|
+
}))
|
|
1208
|
+
return { ...ports, ...resolveConfiguredEnv(configDocument.value, metadata.ports ?? {}) }
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
function readShellEnvironmentState() {
|
|
1212
|
+
const encoded = process.env[SHELL_ENV_STATE]
|
|
1213
|
+
if (!encoded) return { originals: {} }
|
|
1214
|
+
|
|
1215
|
+
try {
|
|
1216
|
+
const parsed = JSON.parse(Buffer.from(encoded, "base64url").toString("utf8"))
|
|
1217
|
+
if (!parsed || typeof parsed.originals !== "object" || Array.isArray(parsed.originals)) throw new Error()
|
|
1218
|
+
const originals = Object.fromEntries(Object.entries(parsed.originals).map(([name, original]) => {
|
|
1219
|
+
if (!ENV_NAME.test(name) || name.startsWith("GWT_")) throw new Error()
|
|
1220
|
+
if (!original || typeof original !== "object" || typeof original.present !== "boolean") throw new Error()
|
|
1221
|
+
if (original.present && typeof original.value !== "string") throw new Error()
|
|
1222
|
+
return [name, original.present ? { present: true, value: original.value } : { present: false }]
|
|
1223
|
+
}))
|
|
1224
|
+
return { originals }
|
|
1225
|
+
} catch {
|
|
1226
|
+
return { originals: {} }
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
function quoteZsh(value) {
|
|
1231
|
+
return `'${String(value).replaceAll("'", `'\\''`)}'`
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
function shellEnvironmentCommands(environment) {
|
|
1235
|
+
const previous = readShellEnvironmentState()
|
|
1236
|
+
const names = new Set([...Object.keys(previous.originals), ...Object.keys(environment)])
|
|
1237
|
+
const originals = {}
|
|
1238
|
+
const commands = []
|
|
1239
|
+
|
|
1240
|
+
for (const name of names) {
|
|
1241
|
+
const original = previous.originals[name] ?? (Object.hasOwn(process.env, name)
|
|
1242
|
+
? { present: true, value: process.env[name] }
|
|
1243
|
+
: { present: false })
|
|
1244
|
+
|
|
1245
|
+
if (Object.hasOwn(environment, name)) {
|
|
1246
|
+
originals[name] = original
|
|
1247
|
+
commands.push(`export ${name}=${quoteZsh(environment[name])}`)
|
|
1248
|
+
} else if (original.present) {
|
|
1249
|
+
commands.push(`export ${name}=${quoteZsh(original.value)}`)
|
|
1250
|
+
} else {
|
|
1251
|
+
commands.push(`unset ${name}`)
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
if (Object.keys(originals).length === 0) {
|
|
1256
|
+
commands.push(`unset ${SHELL_ENV_STATE}`)
|
|
1257
|
+
} else {
|
|
1258
|
+
const state = Buffer.from(JSON.stringify({ originals })).toString("base64url")
|
|
1259
|
+
commands.push(`export ${SHELL_ENV_STATE}=${quoteZsh(state)}`)
|
|
1260
|
+
}
|
|
1261
|
+
return commands.join("\n")
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
function commandShellEnvironment(args) {
|
|
1265
|
+
if (args.length !== 1 || args[0] !== "zsh") throw new CliError("Invalid shell environment request")
|
|
1266
|
+
let environment = {}
|
|
1267
|
+
try {
|
|
1268
|
+
environment = configuredShellEnvironment()
|
|
1269
|
+
} catch {}
|
|
1270
|
+
console.log(shellEnvironmentCommands(environment))
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1144
1273
|
function zshIntegration() {
|
|
1145
1274
|
return `# gwt shell integration for zsh
|
|
1146
1275
|
if command -v gwt >/dev/null 2>&1; then
|
|
@@ -1152,9 +1281,18 @@ if command -v gwt >/dev/null 2>&1; then
|
|
|
1152
1281
|
builtin cd -- "$(<"$cd_file")" || exit_code=$?
|
|
1153
1282
|
fi
|
|
1154
1283
|
rm -f -- "$cd_file"
|
|
1284
|
+
if [[ $exit_code -eq 0 ]]; then
|
|
1285
|
+
_gwt_sync_env
|
|
1286
|
+
fi
|
|
1155
1287
|
return $exit_code
|
|
1156
1288
|
}
|
|
1157
1289
|
|
|
1290
|
+
_gwt_sync_env() {
|
|
1291
|
+
local commands
|
|
1292
|
+
commands="$(command gwt __shell_env zsh 2>/dev/null)" || return 0
|
|
1293
|
+
[[ -n "$commands" ]] && eval "$commands"
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1158
1296
|
_gwt_worktrees() {
|
|
1159
1297
|
local -a targets
|
|
1160
1298
|
targets=("\${(@f)$(command gwt __complete worktrees 2>/dev/null)}")
|
|
@@ -1252,6 +1390,12 @@ if command -v gwt >/dev/null 2>&1; then
|
|
|
1252
1390
|
if (( $+functions[compdef] )); then
|
|
1253
1391
|
compdef _gwt gwt
|
|
1254
1392
|
fi
|
|
1393
|
+
|
|
1394
|
+
typeset -ga chpwd_functions
|
|
1395
|
+
if (( ! \${chpwd_functions[(I)_gwt_sync_env]} )); then
|
|
1396
|
+
chpwd_functions+=(_gwt_sync_env)
|
|
1397
|
+
fi
|
|
1398
|
+
_gwt_sync_env
|
|
1255
1399
|
fi`
|
|
1256
1400
|
}
|
|
1257
1401
|
|
|
@@ -1280,13 +1424,16 @@ stay accurate across versions.
|
|
|
1280
1424
|
|
|
1281
1425
|
## What the help does not make obvious
|
|
1282
1426
|
|
|
1283
|
-
-
|
|
1284
|
-
approved with \`gwt trust\`. Approval
|
|
1285
|
-
hook changes, so a repository that
|
|
1427
|
+
- Ports, environment variables, and hooks declared by a committed \`.gwt.json\`
|
|
1428
|
+
are not applied until the repository is approved with \`gwt trust\`. Approval
|
|
1429
|
+
is invalidated whenever the config or a hook changes, so a repository that
|
|
1430
|
+
worked before can start asking again.
|
|
1286
1431
|
- A failed setup keeps the worktree and records the failure. Retry it with
|
|
1287
1432
|
\`gwt setup <id>\` rather than removing and recreating the worktree.
|
|
1288
|
-
- Ports are assigned per worktree.
|
|
1289
|
-
|
|
1433
|
+
- Ports are assigned per worktree. With shell integration installed, assigned
|
|
1434
|
+
ports and configured environment variables load automatically. Read ports
|
|
1435
|
+
from \`gwt info\` instead of assuming a project default; two worktrees of the
|
|
1436
|
+
same project never share a port.
|
|
1290
1437
|
- \`gwt switch\` changes the shell's directory only when the shell integration is
|
|
1291
1438
|
installed. Otherwise it just prints the path.
|
|
1292
1439
|
- \`gwt switch\` with no target opens an interactive picker, so always pass an
|
|
@@ -1387,11 +1534,12 @@ function commandComplete(args) {
|
|
|
1387
1534
|
const repository = discoverRepository()
|
|
1388
1535
|
const values = ["primary"]
|
|
1389
1536
|
for (const worktree of repository.worktrees) {
|
|
1537
|
+
if (resolve(worktree.path) === resolve(repository.primaryPath)) continue
|
|
1390
1538
|
const metadata = metadataForWorktree(repository, worktree)
|
|
1391
|
-
|
|
1392
|
-
if (
|
|
1539
|
+
const selector = worktree.branch ?? metadata?.id
|
|
1540
|
+
if (selector) values.push(selector)
|
|
1393
1541
|
}
|
|
1394
|
-
console.log(
|
|
1542
|
+
console.log(values.join("\n"))
|
|
1395
1543
|
return
|
|
1396
1544
|
}
|
|
1397
1545
|
|
|
@@ -1430,7 +1578,7 @@ Commands:
|
|
|
1430
1578
|
switch Switch the current shell to a worktree
|
|
1431
1579
|
info Show worktree details and assigned ports
|
|
1432
1580
|
remove Safely remove a worktree and optionally its branch
|
|
1433
|
-
trust Approve or revoke repository project
|
|
1581
|
+
trust Approve or revoke repository project configuration
|
|
1434
1582
|
config Create or inspect configuration
|
|
1435
1583
|
shell Install shell integration
|
|
1436
1584
|
skill Install the gwt skill for coding agents
|
|
@@ -1575,7 +1723,7 @@ Examples:
|
|
|
1575
1723
|
gwt remove
|
|
1576
1724
|
gwt remove feature/auth --keep-branch
|
|
1577
1725
|
gwt remove a1b2c3d4 --discard --yes`,
|
|
1578
|
-
trust: `Approve or revoke
|
|
1726
|
+
trust: `Approve or revoke active configuration declared by the repository's .gwt.json.
|
|
1579
1727
|
|
|
1580
1728
|
Usage:
|
|
1581
1729
|
gwt trust [--revoke]
|
|
@@ -1584,9 +1732,10 @@ Options:
|
|
|
1584
1732
|
--revoke Remove the stored approval for this repository.
|
|
1585
1733
|
-h, --help Show help for this command.
|
|
1586
1734
|
|
|
1587
|
-
Approval is
|
|
1588
|
-
|
|
1589
|
-
|
|
1735
|
+
Approval is required before repository-defined ports or environment variables
|
|
1736
|
+
are applied, or repository hooks run. It is tied to the configuration and hook
|
|
1737
|
+
contents, so changing either requires approval again. User configuration is
|
|
1738
|
+
trusted automatically.
|
|
1590
1739
|
|
|
1591
1740
|
Examples:
|
|
1592
1741
|
gwt trust
|
|
@@ -1653,7 +1802,8 @@ Options:
|
|
|
1653
1802
|
-h, --help Show help for this command.
|
|
1654
1803
|
|
|
1655
1804
|
The integration lets gwt change the current shell's directory after new,
|
|
1656
|
-
switch, and removal of the current worktree. It also
|
|
1805
|
+
switch, and removal of the current worktree. It also loads the worktree's
|
|
1806
|
+
assigned ports and configured environment, and installs completion.
|
|
1657
1807
|
|
|
1658
1808
|
Example:
|
|
1659
1809
|
gwt shell install zsh`,
|
|
@@ -1668,7 +1818,9 @@ Options:
|
|
|
1668
1818
|
-h, --help Show help for this command.
|
|
1669
1819
|
|
|
1670
1820
|
The command adds one initialization line to ~/.zshrc, or to $ZDOTDIR/.zshrc
|
|
1671
|
-
when ZDOTDIR is set. Restart Zsh or source the file after installation.
|
|
1821
|
+
when ZDOTDIR is set. Restart Zsh or source the file after installation. The
|
|
1822
|
+
integration updates the environment when Zsh starts or changes directory and
|
|
1823
|
+
restores previous values after leaving a managed worktree.
|
|
1672
1824
|
|
|
1673
1825
|
Examples:
|
|
1674
1826
|
gwt shell install zsh
|
|
@@ -1745,6 +1897,7 @@ async function main() {
|
|
|
1745
1897
|
if (command === "shell") return commandShell(args)
|
|
1746
1898
|
if (command === "skill") return commandSkill(args)
|
|
1747
1899
|
if (command === "__complete") return commandComplete(args)
|
|
1900
|
+
if (command === "__shell_env") return commandShellEnvironment(args)
|
|
1748
1901
|
throw new CliError(`Unknown command: ${command}`)
|
|
1749
1902
|
}
|
|
1750
1903
|
|