feat-forge 1.0.2 → 1.1.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 +10 -0
- package/dist/cli.js +33 -1
- package/dist/commands/AliasCommands.js +245 -0
- package/dist/commands/BranchCommands.js +12 -0
- package/dist/commands/CompletionCommands.js +830 -290
- package/dist/commands/SubBranchCommands.js +4 -0
- package/dist/foundation/PortAllocator.js +10 -0
- package/package.json +1 -1
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { readdir } from 'fs/promises';
|
|
2
|
-
import { pathExists } from '../lib/fs.js';
|
|
3
1
|
import { AbstractCommands } from './AbstractCommands.js';
|
|
4
2
|
/**
|
|
5
3
|
* Commands for managing shell completion/autocomplete
|
|
@@ -13,12 +11,6 @@ export class CompletionCommands extends AbstractCommands {
|
|
|
13
11
|
// ============================================================================
|
|
14
12
|
// PUBLIC COMMAND METHODS
|
|
15
13
|
// ============================================================================
|
|
16
|
-
/**
|
|
17
|
-
* Generate and display shell completion script for the specified shell.
|
|
18
|
-
* Outputs only the script to stdout for piping or sourcing.
|
|
19
|
-
*
|
|
20
|
-
* @param shell - The target shell type (bash, zsh, or fish)
|
|
21
|
-
*/
|
|
22
14
|
/**
|
|
23
15
|
* Generate and display shell completion script for the specified shell.
|
|
24
16
|
* Outputs only the script to stdout for piping or sourcing.
|
|
@@ -34,9 +26,6 @@ export class CompletionCommands extends AbstractCommands {
|
|
|
34
26
|
// ============================================================================
|
|
35
27
|
/**
|
|
36
28
|
* Generate the appropriate completion script based on shell type.
|
|
37
|
-
*
|
|
38
|
-
* @param shell - The target shell type
|
|
39
|
-
* @returns The generated completion script as a string
|
|
40
29
|
*/
|
|
41
30
|
async generateCompletionScript(shell) {
|
|
42
31
|
switch (shell) {
|
|
@@ -53,33 +42,8 @@ export class CompletionCommands extends AbstractCommands {
|
|
|
53
42
|
throw new Error(`Unsupported shell: ${shell}`);
|
|
54
43
|
}
|
|
55
44
|
}
|
|
56
|
-
/**
|
|
57
|
-
* Get list of available feature slugs for contextual completion.
|
|
58
|
-
* Returns empty array if features directory doesn't exist or if there's an error.
|
|
59
|
-
*
|
|
60
|
-
* @returns Array of feature slugs
|
|
61
|
-
*/
|
|
62
|
-
async getAvailableFeatures() {
|
|
63
|
-
try {
|
|
64
|
-
if (!(await pathExists(this.context.paths.worktreesRoot))) {
|
|
65
|
-
return [];
|
|
66
|
-
}
|
|
67
|
-
const entries = await readdir(this.context.paths.worktreesRoot, { withFileTypes: true });
|
|
68
|
-
return entries
|
|
69
|
-
.filter((entry) => entry.isDirectory())
|
|
70
|
-
.map((entry) => entry.name)
|
|
71
|
-
.sort();
|
|
72
|
-
}
|
|
73
|
-
catch {
|
|
74
|
-
return [];
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
45
|
/**
|
|
78
46
|
* Extract command information from Commander.js program.
|
|
79
|
-
* Recursively extracts all commands and their subcommands.
|
|
80
|
-
*
|
|
81
|
-
* @param command - Commander.js Command object
|
|
82
|
-
* @returns Structured command information
|
|
83
47
|
*/
|
|
84
48
|
extractCommandInfo(command) {
|
|
85
49
|
const name = command.name();
|
|
@@ -90,82 +54,175 @@ export class CompletionCommands extends AbstractCommands {
|
|
|
90
54
|
}
|
|
91
55
|
/**
|
|
92
56
|
* Get all main commands from the program.
|
|
93
|
-
*
|
|
94
|
-
* @returns Array of command information
|
|
95
57
|
*/
|
|
96
58
|
getMainCommands() {
|
|
97
59
|
return this.program.commands.filter((cmd) => !cmd.name().includes('help')).map((cmd) => this.extractCommandInfo(cmd));
|
|
98
60
|
}
|
|
99
61
|
/**
|
|
100
62
|
* Find a specific command by name.
|
|
101
|
-
*
|
|
102
|
-
* @param commandName - Name of the command to find
|
|
103
|
-
* @returns Command information or undefined
|
|
104
63
|
*/
|
|
105
64
|
findCommand(commandName) {
|
|
106
65
|
return this.getMainCommands().find((cmd) => cmd.name === commandName);
|
|
107
66
|
}
|
|
108
|
-
|
|
109
|
-
|
|
67
|
+
/**
|
|
68
|
+
* Centralized extraction of all completion metadata.
|
|
69
|
+
*/
|
|
70
|
+
getCompletionData() {
|
|
71
|
+
const mainCommands = this.getMainCommands();
|
|
72
|
+
return {
|
|
73
|
+
mainCommands,
|
|
74
|
+
featureCmd: this.findCommand('feature'),
|
|
75
|
+
fixCmd: this.findCommand('fix'),
|
|
76
|
+
releaseCmd: this.findCommand('release'),
|
|
77
|
+
servicesCmd: this.findCommand('services'),
|
|
78
|
+
envCmd: this.findCommand('env'),
|
|
79
|
+
maintenanceCmd: this.findCommand('maintenance'),
|
|
80
|
+
modeCmd: this.findCommand('mode'),
|
|
81
|
+
agentCmd: this.findCommand('agent'),
|
|
82
|
+
slugSubcommands: ['stop', 'start', 'archive', 'resync', 'merge', 'rebase', 'open', 'path'],
|
|
83
|
+
rootSlugCommands: ['stop', 'start', 'archive', 'resync', 'merge', 'rebase', 'open', 'path'],
|
|
84
|
+
};
|
|
110
85
|
}
|
|
111
|
-
|
|
112
|
-
|
|
86
|
+
/**
|
|
87
|
+
* Generate the Node.js config-reading snippet used by bash/zsh.
|
|
88
|
+
* Returns variables: _FORGE_WORKTREES_ROOT, _FORGE_FEATURE_PREFIX, _FORGE_FIX_PREFIX, _FORGE_RELEASE_PREFIX, _FORGE_MODES
|
|
89
|
+
*/
|
|
90
|
+
nodeConfigSnippet() {
|
|
91
|
+
return `'const fs=require("fs");const path=require("path");
|
|
92
|
+
try{
|
|
93
|
+
const file=process.argv[1];
|
|
94
|
+
const configRoot=path.dirname(file);
|
|
95
|
+
const data=JSON.parse(fs.readFileSync(file,"utf8"));
|
|
96
|
+
let rootDir=data.rootDir;
|
|
97
|
+
if(rootDir){ if(!path.isAbsolute(rootDir)) rootDir=path.join(configRoot, rootDir); }
|
|
98
|
+
else { rootDir=configRoot; }
|
|
99
|
+
const opts=data.options||{};
|
|
100
|
+
const folders=opts.folders||data.folders||{};
|
|
101
|
+
const git=opts.git||data.git||{};
|
|
102
|
+
const worktrees=folders.worktrees||"worktrees";
|
|
103
|
+
const fp=git.featureBranchPrefix||"feature/";
|
|
104
|
+
const xp=git.fixBranchPrefix||"fix/";
|
|
105
|
+
const rp=git.releaseBranchPrefix||"release/";
|
|
106
|
+
const modes=(data.modes||[]).map(m=>m.name||path.basename(m.agentFile||"",".md")).filter(Boolean).join(" ");
|
|
107
|
+
process.stdout.write("_FORGE_WORKTREES_ROOT="+path.join(rootDir, worktrees)+"\\n");
|
|
108
|
+
process.stdout.write("_FORGE_FEATURE_PREFIX="+fp+"\\n");
|
|
109
|
+
process.stdout.write("_FORGE_FIX_PREFIX="+xp+"\\n");
|
|
110
|
+
process.stdout.write("_FORGE_RELEASE_PREFIX="+rp+"\\n");
|
|
111
|
+
process.stdout.write("_FORGE_MODES="+modes+"\\n");
|
|
112
|
+
}catch(e){}'`;
|
|
113
113
|
}
|
|
114
114
|
/**
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
* @returns Array of command names
|
|
115
|
+
* Generate the Node.js config-reading snippet for fish shell.
|
|
116
|
+
* Outputs `set -g` commands instead of VAR= assignments.
|
|
118
117
|
*/
|
|
119
|
-
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
118
|
+
nodeConfigSnippetFish() {
|
|
119
|
+
return `'const fs=require("fs");const path=require("path");
|
|
120
|
+
try{
|
|
121
|
+
const file=process.argv[1];
|
|
122
|
+
const configRoot=path.dirname(file);
|
|
123
|
+
const data=JSON.parse(fs.readFileSync(file,"utf8"));
|
|
124
|
+
let rootDir=data.rootDir;
|
|
125
|
+
if(rootDir){ if(!path.isAbsolute(rootDir)) rootDir=path.join(configRoot, rootDir); }
|
|
126
|
+
else { rootDir=configRoot; }
|
|
127
|
+
const opts=data.options||{};
|
|
128
|
+
const folders=opts.folders||data.folders||{};
|
|
129
|
+
const git=opts.git||data.git||{};
|
|
130
|
+
const worktrees=folders.worktrees||"worktrees";
|
|
131
|
+
const fp=git.featureBranchPrefix||"feature/";
|
|
132
|
+
const xp=git.fixBranchPrefix||"fix/";
|
|
133
|
+
const rp=git.releaseBranchPrefix||"release/";
|
|
134
|
+
const modes=(data.modes||[]).map(m=>m.name||path.basename(m.agentFile||"",".md")).filter(Boolean).join(" ");
|
|
135
|
+
process.stdout.write("set -g _FORGE_WORKTREES_ROOT "+path.join(rootDir, worktrees)+"\\n");
|
|
136
|
+
process.stdout.write("set -g _FORGE_FEATURE_PREFIX "+fp+"\\n");
|
|
137
|
+
process.stdout.write("set -g _FORGE_FIX_PREFIX "+xp+"\\n");
|
|
138
|
+
process.stdout.write("set -g _FORGE_RELEASE_PREFIX "+rp+"\\n");
|
|
139
|
+
process.stdout.write("set -g _FORGE_MODES "+modes+"\\n");
|
|
140
|
+
}catch(e){}'`;
|
|
130
141
|
}
|
|
131
142
|
/**
|
|
132
|
-
* Generate
|
|
133
|
-
*
|
|
134
|
-
* @returns Bash completion script content
|
|
143
|
+
* Generate the Node.js config-reading snippet for PowerShell.
|
|
144
|
+
* Outputs a JSON object parsable with ConvertFrom-Json.
|
|
135
145
|
*/
|
|
146
|
+
nodeConfigSnippetPowerShell() {
|
|
147
|
+
return `'const fs=require("fs");const path=require("path");
|
|
148
|
+
try{
|
|
149
|
+
const file=process.argv[1];
|
|
150
|
+
const configRoot=path.dirname(file);
|
|
151
|
+
const data=JSON.parse(fs.readFileSync(file,"utf8"));
|
|
152
|
+
let rootDir=data.rootDir;
|
|
153
|
+
if(rootDir){ if(!path.isAbsolute(rootDir)) rootDir=path.join(configRoot, rootDir); }
|
|
154
|
+
else { rootDir=configRoot; }
|
|
155
|
+
const opts=data.options||{};
|
|
156
|
+
const folders=opts.folders||data.folders||{};
|
|
157
|
+
const git=opts.git||data.git||{};
|
|
158
|
+
const worktrees=folders.worktrees||"worktrees";
|
|
159
|
+
const fp=git.featureBranchPrefix||"feature/";
|
|
160
|
+
const xp=git.fixBranchPrefix||"fix/";
|
|
161
|
+
const rp=git.releaseBranchPrefix||"release/";
|
|
162
|
+
const modes=(data.modes||[]).map(m=>m.name||path.basename(m.agentFile||"",".md")).filter(Boolean);
|
|
163
|
+
process.stdout.write(JSON.stringify({worktreesRoot:path.join(rootDir,worktrees),featurePrefix:fp,fixPrefix:xp,releasePrefix:rp,modes:modes}));
|
|
164
|
+
}catch(e){process.stdout.write("{}");}'`;
|
|
165
|
+
}
|
|
166
|
+
// ============================================================================
|
|
167
|
+
// BASH COMPLETION
|
|
168
|
+
// ============================================================================
|
|
136
169
|
generateBashCompletion() {
|
|
137
|
-
const
|
|
138
|
-
const
|
|
139
|
-
const
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
const
|
|
143
|
-
const
|
|
144
|
-
const
|
|
145
|
-
const agentCommands = agentCmd?.subcommands.map((cmd) => cmd.name).join(' ') || '';
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
170
|
+
const data = this.getCompletionData();
|
|
171
|
+
const commands = data.mainCommands.map((cmd) => cmd.name).join(' ');
|
|
172
|
+
const featureCommands = data.featureCmd?.subcommands.map((cmd) => cmd.name).join(' ') || '';
|
|
173
|
+
const fixCommands = data.fixCmd?.subcommands.map((cmd) => cmd.name).join(' ') || '';
|
|
174
|
+
const releaseCommands = data.releaseCmd?.subcommands.map((cmd) => cmd.name).join(' ') || '';
|
|
175
|
+
const servicesCommands = data.servicesCmd?.subcommands.map((cmd) => cmd.name).join(' ') || '';
|
|
176
|
+
const envCommands = data.envCmd?.subcommands.map((cmd) => cmd.name).join(' ') || '';
|
|
177
|
+
const maintenanceCommands = data.maintenanceCmd?.subcommands.map((cmd) => cmd.name).join(' ') || '';
|
|
178
|
+
const agentCommands = data.agentCmd?.subcommands.map((cmd) => cmd.name).join(' ') || '';
|
|
179
|
+
const featureSlugCmds = data.featureCmd?.subcommands
|
|
180
|
+
.filter((cmd) => data.slugSubcommands.includes(cmd.name))
|
|
181
|
+
.map((cmd) => cmd.name) || [];
|
|
182
|
+
const fixSlugCmds = data.fixCmd?.subcommands
|
|
183
|
+
.filter((cmd) => data.slugSubcommands.includes(cmd.name))
|
|
184
|
+
.map((cmd) => cmd.name) || [];
|
|
185
|
+
const releaseSlugCmds = data.releaseCmd?.subcommands
|
|
186
|
+
.filter((cmd) => data.slugSubcommands.includes(cmd.name))
|
|
187
|
+
.map((cmd) => cmd.name) || [];
|
|
188
|
+
const mainSlugCmds = data.mainCommands
|
|
189
|
+
.filter((cmd) => data.rootSlugCommands.includes(cmd.name))
|
|
190
|
+
.map((cmd) => cmd.name);
|
|
191
|
+
const featureSlugCase = featureSlugCmds.length > 0
|
|
192
|
+
? ` ${featureSlugCmds.join('|')})
|
|
193
|
+
if [[ \${cword} -eq 3 ]]; then
|
|
194
|
+
local slugs="\$(_forge_feature_slugs)"
|
|
195
|
+
COMPREPLY=( \$(compgen -W "\${slugs}" -- "\${cur}") )
|
|
196
|
+
return 0
|
|
197
|
+
fi
|
|
198
|
+
;;
|
|
199
|
+
`
|
|
200
|
+
: '';
|
|
201
|
+
const fixSlugCase = fixSlugCmds.length > 0
|
|
202
|
+
? ` ${fixSlugCmds.join('|')})
|
|
203
|
+
if [[ \${cword} -eq 3 ]]; then
|
|
204
|
+
local slugs="\$(_forge_fix_slugs)"
|
|
205
|
+
COMPREPLY=( \$(compgen -W "\${slugs}" -- "\${cur}") )
|
|
206
|
+
return 0
|
|
207
|
+
fi
|
|
208
|
+
;;
|
|
209
|
+
`
|
|
210
|
+
: '';
|
|
211
|
+
const releaseSlugCase = releaseSlugCmds.length > 0
|
|
212
|
+
? ` ${releaseSlugCmds.join('|')})
|
|
153
213
|
if [[ \${cword} -eq 3 ]]; then
|
|
154
|
-
local
|
|
155
|
-
|
|
156
|
-
COMPREPLY=( \$(compgen -W "\${features}" -- "\${cur}") )
|
|
214
|
+
local slugs="\$(_forge_release_slugs)"
|
|
215
|
+
COMPREPLY=( \$(compgen -W "\${slugs}" -- "\${cur}") )
|
|
157
216
|
return 0
|
|
158
217
|
fi
|
|
159
218
|
;;
|
|
160
219
|
`
|
|
161
220
|
: '';
|
|
162
|
-
const mainSlugCase =
|
|
163
|
-
? ` ${
|
|
164
|
-
# Suggest available features for commands with slug argument
|
|
221
|
+
const mainSlugCase = mainSlugCmds.length > 0
|
|
222
|
+
? ` ${mainSlugCmds.join('|')})
|
|
165
223
|
if [[ \${cword} -eq 2 ]]; then
|
|
166
|
-
local
|
|
167
|
-
|
|
168
|
-
COMPREPLY=( \$(compgen -W "\${features}" -- "\${cur}") )
|
|
224
|
+
local branches="\$(_forge_all_branches)"
|
|
225
|
+
COMPREPLY=( \$(compgen -W "\${branches}" -- "\${cur}") )
|
|
169
226
|
return 0
|
|
170
227
|
fi
|
|
171
228
|
;;
|
|
@@ -185,57 +242,125 @@ _forge_find_config() {
|
|
|
185
242
|
return 1
|
|
186
243
|
}
|
|
187
244
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
245
|
+
# Load forge config once and cache variables for the current completion
|
|
246
|
+
_forge_config_loaded=0
|
|
247
|
+
_forge_load_config() {
|
|
248
|
+
if [[ \${_forge_config_loaded} -eq 1 ]]; then
|
|
191
249
|
return 0
|
|
192
250
|
fi
|
|
251
|
+
_FORGE_WORKTREES_ROOT=""
|
|
252
|
+
_FORGE_FEATURE_PREFIX="feature/"
|
|
253
|
+
_FORGE_FIX_PREFIX="fix/"
|
|
254
|
+
_FORGE_RELEASE_PREFIX="release/"
|
|
255
|
+
_FORGE_MODES=""
|
|
256
|
+
|
|
257
|
+
if [[ -n "\${FORGE_WORKTREES_ROOT}" ]]; then
|
|
258
|
+
_FORGE_WORKTREES_ROOT="\${FORGE_WORKTREES_ROOT}"
|
|
259
|
+
fi
|
|
260
|
+
|
|
193
261
|
local start="\${PWD}"
|
|
194
262
|
local config_root
|
|
195
263
|
config_root="\$(_forge_find_config "\${start}")" || true
|
|
196
264
|
if [[ -n "\${config_root}" ]]; then
|
|
197
|
-
local
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
let rootDir=data.rootDir;
|
|
204
|
-
if(rootDir){ if(!path.isAbsolute(rootDir)) rootDir=path.join(configRoot, rootDir); }
|
|
205
|
-
else { rootDir=configRoot; }
|
|
206
|
-
const folders=(data.options && data.options.folders) || data.folders || {};
|
|
207
|
-
const worktrees=folders.worktrees || "features";
|
|
208
|
-
process.stdout.write(path.join(rootDir, worktrees));
|
|
209
|
-
}catch(e){}
|
|
210
|
-
' "\${config_root}/.feat-forge.json" 2>/dev/null)
|
|
211
|
-
if [[ -n "\${worktrees}" ]]; then
|
|
212
|
-
echo "\${worktrees}"
|
|
213
|
-
return 0
|
|
265
|
+
local output
|
|
266
|
+
output=\$(node -e ${this.nodeConfigSnippet()} "\${config_root}/.feat-forge.json" 2>/dev/null)
|
|
267
|
+
if [[ -n "\${output}" ]]; then
|
|
268
|
+
eval "\${output}"
|
|
269
|
+
elif [[ -z "\${_FORGE_WORKTREES_ROOT}" ]]; then
|
|
270
|
+
_FORGE_WORKTREES_ROOT="\${config_root}/worktrees"
|
|
214
271
|
fi
|
|
215
|
-
|
|
216
|
-
|
|
272
|
+
elif [[ -z "\${_FORGE_WORKTREES_ROOT}" ]]; then
|
|
273
|
+
_FORGE_WORKTREES_ROOT="worktrees"
|
|
217
274
|
fi
|
|
218
|
-
|
|
275
|
+
_forge_config_loaded=1
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
_forge_feature_slugs() {
|
|
279
|
+
_forge_load_config
|
|
280
|
+
local prefix_dir="\${_FORGE_FEATURE_PREFIX%/}"
|
|
281
|
+
local dir="\${_FORGE_WORKTREES_ROOT}/\${prefix_dir}"
|
|
282
|
+
if [[ -d "\${dir}" ]]; then
|
|
283
|
+
find "\${dir}" -mindepth 1 -maxdepth 1 -type d -exec basename {} \\; 2>/dev/null
|
|
284
|
+
fi
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
_forge_fix_slugs() {
|
|
288
|
+
_forge_load_config
|
|
289
|
+
local prefix_dir="\${_FORGE_FIX_PREFIX%/}"
|
|
290
|
+
local dir="\${_FORGE_WORKTREES_ROOT}/\${prefix_dir}"
|
|
291
|
+
if [[ -d "\${dir}" ]]; then
|
|
292
|
+
find "\${dir}" -mindepth 1 -maxdepth 1 -type d -exec basename {} \\; 2>/dev/null
|
|
293
|
+
fi
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
_forge_release_slugs() {
|
|
297
|
+
_forge_load_config
|
|
298
|
+
local prefix_dir="\${_FORGE_RELEASE_PREFIX%/}"
|
|
299
|
+
local dir="\${_FORGE_WORKTREES_ROOT}/\${prefix_dir}"
|
|
300
|
+
if [[ -d "\${dir}" ]]; then
|
|
301
|
+
find "\${dir}" -mindepth 1 -maxdepth 1 -type d -exec basename {} \\; 2>/dev/null
|
|
302
|
+
fi
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
_forge_all_branches() {
|
|
306
|
+
_forge_load_config
|
|
307
|
+
local root="\${_FORGE_WORKTREES_ROOT}"
|
|
308
|
+
if [[ ! -d "\${root}" ]]; then
|
|
309
|
+
return
|
|
310
|
+
fi
|
|
311
|
+
local prefix_dirs=()
|
|
312
|
+
for p in "\${_FORGE_FEATURE_PREFIX%/}" "\${_FORGE_FIX_PREFIX%/}" "\${_FORGE_RELEASE_PREFIX%/}"; do
|
|
313
|
+
if [[ -n "\${p}" ]]; then
|
|
314
|
+
prefix_dirs+=("\${p}")
|
|
315
|
+
fi
|
|
316
|
+
done
|
|
317
|
+
for entry in "\${root}"/*/; do
|
|
318
|
+
[[ -d "\${entry}" ]] || continue
|
|
319
|
+
local name="\$(basename "\${entry}")"
|
|
320
|
+
local is_prefix=0
|
|
321
|
+
for pd in "\${prefix_dirs[@]}"; do
|
|
322
|
+
if [[ "\${name}" == "\${pd}" ]]; then
|
|
323
|
+
is_prefix=1
|
|
324
|
+
break
|
|
325
|
+
fi
|
|
326
|
+
done
|
|
327
|
+
if [[ \${is_prefix} -eq 1 ]]; then
|
|
328
|
+
for sub in "\${entry}"*/; do
|
|
329
|
+
[[ -d "\${sub}" ]] || continue
|
|
330
|
+
echo "\${name}/\$(basename "\${sub}")"
|
|
331
|
+
done
|
|
332
|
+
else
|
|
333
|
+
echo "\${name}"
|
|
334
|
+
fi
|
|
335
|
+
done
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
_forge_modes() {
|
|
339
|
+
_forge_load_config
|
|
340
|
+
echo "\${_FORGE_MODES}"
|
|
219
341
|
}
|
|
220
342
|
|
|
221
343
|
_forge_completion() {
|
|
222
344
|
local cur prev words cword
|
|
223
345
|
_init_completion || return
|
|
224
346
|
|
|
225
|
-
#
|
|
347
|
+
# Reset config cache for each completion
|
|
348
|
+
_forge_config_loaded=0
|
|
349
|
+
|
|
226
350
|
local commands="${commands}"
|
|
227
351
|
|
|
228
|
-
# Subcommands for each main command
|
|
229
352
|
local feature_commands="${featureCommands}"
|
|
230
|
-
local
|
|
353
|
+
local fix_commands="${fixCommands}"
|
|
354
|
+
local release_commands="${releaseCommands}"
|
|
355
|
+
local services_commands="${servicesCommands}"
|
|
356
|
+
local env_commands="${envCommands}"
|
|
357
|
+
local maintenance_commands="${maintenanceCommands}"
|
|
231
358
|
local agent_commands="${agentCommands}"
|
|
232
359
|
|
|
233
|
-
# Get previous word for context
|
|
234
360
|
case "\${words[1]}" in
|
|
235
361
|
feature)
|
|
236
362
|
case "\${words[2]}" in
|
|
237
363
|
${featureSlugCase} *)
|
|
238
|
-
# Suggest feature subcommands
|
|
239
364
|
if [[ \${cword} -eq 2 ]]; then
|
|
240
365
|
COMPREPLY=( \$(compgen -W "\${feature_commands}" -- "\${cur}") )
|
|
241
366
|
return 0
|
|
@@ -243,29 +368,75 @@ ${featureSlugCase} *)
|
|
|
243
368
|
;;
|
|
244
369
|
esac
|
|
245
370
|
;;
|
|
371
|
+
fix)
|
|
372
|
+
case "\${words[2]}" in
|
|
373
|
+
${fixSlugCase} *)
|
|
374
|
+
if [[ \${cword} -eq 2 ]]; then
|
|
375
|
+
COMPREPLY=( \$(compgen -W "\${fix_commands}" -- "\${cur}") )
|
|
376
|
+
return 0
|
|
377
|
+
fi
|
|
378
|
+
;;
|
|
379
|
+
esac
|
|
380
|
+
;;
|
|
381
|
+
release)
|
|
382
|
+
case "\${words[2]}" in
|
|
383
|
+
${releaseSlugCase} *)
|
|
384
|
+
if [[ \${cword} -eq 2 ]]; then
|
|
385
|
+
COMPREPLY=( \$(compgen -W "\${release_commands}" -- "\${cur}") )
|
|
386
|
+
return 0
|
|
387
|
+
fi
|
|
388
|
+
;;
|
|
389
|
+
esac
|
|
390
|
+
;;
|
|
391
|
+
services)
|
|
392
|
+
if [[ \${cword} -eq 2 ]]; then
|
|
393
|
+
COMPREPLY=( \$(compgen -W "\${services_commands}" -- "\${cur}") )
|
|
394
|
+
return 0
|
|
395
|
+
fi
|
|
396
|
+
;;
|
|
397
|
+
env)
|
|
398
|
+
if [[ \${cword} -eq 2 ]]; then
|
|
399
|
+
COMPREPLY=( \$(compgen -W "\${env_commands}" -- "\${cur}") )
|
|
400
|
+
return 0
|
|
401
|
+
fi
|
|
402
|
+
;;
|
|
403
|
+
maintenance)
|
|
404
|
+
if [[ \${cword} -eq 2 ]]; then
|
|
405
|
+
COMPREPLY=( \$(compgen -W "\${maintenance_commands}" -- "\${cur}") )
|
|
406
|
+
return 0
|
|
407
|
+
fi
|
|
408
|
+
if [[ \${cword} -eq 3 ]]; then
|
|
409
|
+
local branches="\$(_forge_all_branches)"
|
|
410
|
+
COMPREPLY=( \$(compgen -W "\${branches}" -- "\${cur}") )
|
|
411
|
+
return 0
|
|
412
|
+
fi
|
|
413
|
+
;;
|
|
246
414
|
mode)
|
|
247
|
-
# Suggest mode subcommands
|
|
248
415
|
if [[ \${cword} -eq 2 ]]; then
|
|
249
|
-
|
|
416
|
+
local modes="\$(_forge_modes)"
|
|
417
|
+
COMPREPLY=( \$(compgen -W "\${modes}" -- "\${cur}") )
|
|
250
418
|
return 0
|
|
251
419
|
fi
|
|
252
420
|
;;
|
|
253
421
|
agent)
|
|
254
|
-
# Suggest agent subcommands
|
|
255
422
|
if [[ \${cword} -eq 2 ]]; then
|
|
256
423
|
COMPREPLY=( \$(compgen -W "\${agent_commands}" -- "\${cur}") )
|
|
257
424
|
return 0
|
|
258
425
|
fi
|
|
259
426
|
;;
|
|
260
427
|
${mainSlugCase} completion)
|
|
261
|
-
|
|
428
|
+
if [[ \${cword} -eq 2 ]]; then
|
|
429
|
+
COMPREPLY=( \$(compgen -W "bash zsh fish powershell pwsh" -- "\${cur}") )
|
|
430
|
+
return 0
|
|
431
|
+
fi
|
|
432
|
+
;;
|
|
433
|
+
alias)
|
|
262
434
|
if [[ \${cword} -eq 2 ]]; then
|
|
263
435
|
COMPREPLY=( \$(compgen -W "bash zsh fish powershell pwsh" -- "\${cur}") )
|
|
264
436
|
return 0
|
|
265
437
|
fi
|
|
266
438
|
;;
|
|
267
439
|
*)
|
|
268
|
-
# Suggest main commands at root level
|
|
269
440
|
if [[ \${cword} -eq 1 ]]; then
|
|
270
441
|
COMPREPLY=( \$(compgen -W "\${commands}" -- "\${cur}") )
|
|
271
442
|
return 0
|
|
@@ -284,44 +455,64 @@ complete -F _forge_completion forge
|
|
|
284
455
|
# Option 2 - Save to file:
|
|
285
456
|
# forge completion bash > ~/.local/share/bash-completion/completions/forge
|
|
286
457
|
# # Or system-wide: /etc/bash_completion.d/forge
|
|
458
|
+
#
|
|
459
|
+
# Tip: Run 'forge alias bash' to generate shell aliases for quick access.
|
|
287
460
|
`;
|
|
288
461
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
* @returns Zsh completion script content
|
|
293
|
-
*/
|
|
462
|
+
// ============================================================================
|
|
463
|
+
// ZSH COMPLETION
|
|
464
|
+
// ============================================================================
|
|
294
465
|
generateZshCompletion() {
|
|
295
|
-
const
|
|
296
|
-
const
|
|
297
|
-
const
|
|
298
|
-
const
|
|
299
|
-
|
|
300
|
-
const
|
|
301
|
-
const
|
|
302
|
-
const
|
|
303
|
-
const agentArray = agentCmd?.subcommands.map((cmd) => ` '${cmd.name}:${cmd.description.replace(/'/g, "''")}'`).join('\n') || '';
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
const
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
466
|
+
const data = this.getCompletionData();
|
|
467
|
+
const commandsArray = data.mainCommands.map((cmd) => ` '${cmd.name}:${cmd.description.replace(/'/g, "''")}'`).join('\n');
|
|
468
|
+
const featureArray = data.featureCmd?.subcommands.map((cmd) => ` '${cmd.name}:${cmd.description.replace(/'/g, "''")}'`).join('\n') || '';
|
|
469
|
+
const fixArray = data.fixCmd?.subcommands.map((cmd) => ` '${cmd.name}:${cmd.description.replace(/'/g, "''")}'`).join('\n') || '';
|
|
470
|
+
const releaseArray = data.releaseCmd?.subcommands.map((cmd) => ` '${cmd.name}:${cmd.description.replace(/'/g, "''")}'`).join('\n') || '';
|
|
471
|
+
const servicesArray = data.servicesCmd?.subcommands.map((cmd) => ` '${cmd.name}:${cmd.description.replace(/'/g, "''")}'`).join('\n') || '';
|
|
472
|
+
const envArray = data.envCmd?.subcommands.map((cmd) => ` '${cmd.name}:${cmd.description.replace(/'/g, "''")}'`).join('\n') || '';
|
|
473
|
+
const maintenanceArray = data.maintenanceCmd?.subcommands.map((cmd) => ` '${cmd.name}:${cmd.description.replace(/'/g, "''")}'`).join('\n') || '';
|
|
474
|
+
const agentArray = data.agentCmd?.subcommands.map((cmd) => ` '${cmd.name}:${cmd.description.replace(/'/g, "''")}'`).join('\n') || '';
|
|
475
|
+
const featureSlugCmds = data.featureCmd?.subcommands
|
|
476
|
+
.filter((cmd) => data.slugSubcommands.includes(cmd.name))
|
|
477
|
+
.map((cmd) => cmd.name) || [];
|
|
478
|
+
const fixSlugCmds = data.fixCmd?.subcommands
|
|
479
|
+
.filter((cmd) => data.slugSubcommands.includes(cmd.name))
|
|
480
|
+
.map((cmd) => cmd.name) || [];
|
|
481
|
+
const releaseSlugCmds = data.releaseCmd?.subcommands
|
|
482
|
+
.filter((cmd) => data.slugSubcommands.includes(cmd.name))
|
|
483
|
+
.map((cmd) => cmd.name) || [];
|
|
484
|
+
const mainSlugCmds = data.mainCommands
|
|
485
|
+
.filter((cmd) => data.rootSlugCommands.includes(cmd.name))
|
|
486
|
+
.map((cmd) => cmd.name);
|
|
487
|
+
const featureSlugCase = featureSlugCmds.length > 0
|
|
488
|
+
? ` ${featureSlugCmds.join('|')})
|
|
489
|
+
local -a slugs
|
|
490
|
+
slugs=(\${(f)"\$(_forge_feature_slugs)"})
|
|
491
|
+
_describe 'feature slug' slugs
|
|
492
|
+
;;
|
|
493
|
+
`
|
|
494
|
+
: '';
|
|
495
|
+
const fixSlugCase = fixSlugCmds.length > 0
|
|
496
|
+
? ` ${fixSlugCmds.join('|')})
|
|
497
|
+
local -a slugs
|
|
498
|
+
slugs=(\${(f)"\$(_forge_fix_slugs)"})
|
|
499
|
+
_describe 'fix slug' slugs
|
|
500
|
+
;;
|
|
501
|
+
`
|
|
502
|
+
: '';
|
|
503
|
+
const releaseSlugCase = releaseSlugCmds.length > 0
|
|
504
|
+
? ` ${releaseSlugCmds.join('|')})
|
|
505
|
+
local -a slugs
|
|
506
|
+
slugs=(\${(f)"\$(_forge_release_slugs)"})
|
|
507
|
+
_describe 'release slug' slugs
|
|
315
508
|
;;
|
|
316
509
|
`
|
|
317
510
|
: '';
|
|
318
|
-
const mainSlugCase =
|
|
319
|
-
? ` ${
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
features=(\${(f)"\$(find "\${worktrees_root}" -mindepth 1 -maxdepth 1 -type d -exec basename {} \\; 2>/dev/null)"})
|
|
324
|
-
_describe 'feature slug' features
|
|
511
|
+
const mainSlugCase = mainSlugCmds.length > 0
|
|
512
|
+
? ` ${mainSlugCmds.join('|')})
|
|
513
|
+
local -a branches
|
|
514
|
+
branches=(\${(f)"\$(_forge_all_branches)"})
|
|
515
|
+
_describe 'branch name' branches
|
|
325
516
|
;;
|
|
326
517
|
`
|
|
327
518
|
: '';
|
|
@@ -340,41 +531,111 @@ _forge_find_config() {
|
|
|
340
531
|
return 1
|
|
341
532
|
}
|
|
342
533
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
534
|
+
# Load forge config once and cache variables for the current completion
|
|
535
|
+
_forge_config_loaded=0
|
|
536
|
+
_forge_load_config() {
|
|
537
|
+
if [[ \${_forge_config_loaded} -eq 1 ]]; then
|
|
346
538
|
return 0
|
|
347
539
|
fi
|
|
540
|
+
_FORGE_WORKTREES_ROOT=""
|
|
541
|
+
_FORGE_FEATURE_PREFIX="feature/"
|
|
542
|
+
_FORGE_FIX_PREFIX="fix/"
|
|
543
|
+
_FORGE_RELEASE_PREFIX="release/"
|
|
544
|
+
_FORGE_MODES=""
|
|
545
|
+
|
|
546
|
+
if [[ -n "\${FORGE_WORKTREES_ROOT}" ]]; then
|
|
547
|
+
_FORGE_WORKTREES_ROOT="\${FORGE_WORKTREES_ROOT}"
|
|
548
|
+
fi
|
|
549
|
+
|
|
348
550
|
local start="\${PWD}"
|
|
349
551
|
local config_root
|
|
350
552
|
config_root="\$(_forge_find_config "\${start}")" || true
|
|
351
553
|
if [[ -n "\${config_root}" ]]; then
|
|
352
|
-
local
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
let rootDir=data.rootDir;
|
|
359
|
-
if(rootDir){ if(!path.isAbsolute(rootDir)) rootDir=path.join(configRoot, rootDir); }
|
|
360
|
-
else { rootDir=configRoot; }
|
|
361
|
-
const folders=(data.options && data.options.folders) || data.folders || {};
|
|
362
|
-
const worktrees=folders.worktrees || "features";
|
|
363
|
-
process.stdout.write(path.join(rootDir, worktrees));
|
|
364
|
-
}catch(e){}
|
|
365
|
-
' "\${config_root}/.feat-forge.json" 2>/dev/null)
|
|
366
|
-
if [[ -n "\${worktrees}" ]]; then
|
|
367
|
-
echo "\${worktrees}"
|
|
368
|
-
return 0
|
|
554
|
+
local output
|
|
555
|
+
output=\$(node -e ${this.nodeConfigSnippet()} "\${config_root}/.feat-forge.json" 2>/dev/null)
|
|
556
|
+
if [[ -n "\${output}" ]]; then
|
|
557
|
+
eval "\${output}"
|
|
558
|
+
elif [[ -z "\${_FORGE_WORKTREES_ROOT}" ]]; then
|
|
559
|
+
_FORGE_WORKTREES_ROOT="\${config_root}/worktrees"
|
|
369
560
|
fi
|
|
370
|
-
|
|
371
|
-
|
|
561
|
+
elif [[ -z "\${_FORGE_WORKTREES_ROOT}" ]]; then
|
|
562
|
+
_FORGE_WORKTREES_ROOT="worktrees"
|
|
372
563
|
fi
|
|
373
|
-
|
|
564
|
+
_forge_config_loaded=1
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
_forge_feature_slugs() {
|
|
568
|
+
_forge_load_config
|
|
569
|
+
local prefix_dir="\${_FORGE_FEATURE_PREFIX%/}"
|
|
570
|
+
local dir="\${_FORGE_WORKTREES_ROOT}/\${prefix_dir}"
|
|
571
|
+
if [[ -d "\${dir}" ]]; then
|
|
572
|
+
find "\${dir}" -mindepth 1 -maxdepth 1 -type d -exec basename {} \\; 2>/dev/null
|
|
573
|
+
fi
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
_forge_fix_slugs() {
|
|
577
|
+
_forge_load_config
|
|
578
|
+
local prefix_dir="\${_FORGE_FIX_PREFIX%/}"
|
|
579
|
+
local dir="\${_FORGE_WORKTREES_ROOT}/\${prefix_dir}"
|
|
580
|
+
if [[ -d "\${dir}" ]]; then
|
|
581
|
+
find "\${dir}" -mindepth 1 -maxdepth 1 -type d -exec basename {} \\; 2>/dev/null
|
|
582
|
+
fi
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
_forge_release_slugs() {
|
|
586
|
+
_forge_load_config
|
|
587
|
+
local prefix_dir="\${_FORGE_RELEASE_PREFIX%/}"
|
|
588
|
+
local dir="\${_FORGE_WORKTREES_ROOT}/\${prefix_dir}"
|
|
589
|
+
if [[ -d "\${dir}" ]]; then
|
|
590
|
+
find "\${dir}" -mindepth 1 -maxdepth 1 -type d -exec basename {} \\; 2>/dev/null
|
|
591
|
+
fi
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
_forge_all_branches() {
|
|
595
|
+
_forge_load_config
|
|
596
|
+
local root="\${_FORGE_WORKTREES_ROOT}"
|
|
597
|
+
if [[ ! -d "\${root}" ]]; then
|
|
598
|
+
return
|
|
599
|
+
fi
|
|
600
|
+
local prefix_dirs=()
|
|
601
|
+
for p in "\${_FORGE_FEATURE_PREFIX%/}" "\${_FORGE_FIX_PREFIX%/}" "\${_FORGE_RELEASE_PREFIX%/}"; do
|
|
602
|
+
if [[ -n "\${p}" ]]; then
|
|
603
|
+
prefix_dirs+=("\${p}")
|
|
604
|
+
fi
|
|
605
|
+
done
|
|
606
|
+
for entry in "\${root}"/*/; do
|
|
607
|
+
[[ -d "\${entry}" ]] || continue
|
|
608
|
+
local name="\$(basename "\${entry}")"
|
|
609
|
+
local is_prefix=0
|
|
610
|
+
for pd in "\${prefix_dirs[@]}"; do
|
|
611
|
+
if [[ "\${name}" == "\${pd}" ]]; then
|
|
612
|
+
is_prefix=1
|
|
613
|
+
break
|
|
614
|
+
fi
|
|
615
|
+
done
|
|
616
|
+
if [[ \${is_prefix} -eq 1 ]]; then
|
|
617
|
+
for sub in "\${entry}"*/; do
|
|
618
|
+
[[ -d "\${sub}" ]] || continue
|
|
619
|
+
echo "\${name}/\$(basename "\${sub}")"
|
|
620
|
+
done
|
|
621
|
+
else
|
|
622
|
+
echo "\${name}"
|
|
623
|
+
fi
|
|
624
|
+
done
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
_forge_modes() {
|
|
628
|
+
_forge_load_config
|
|
629
|
+
echo "\${_FORGE_MODES}"
|
|
374
630
|
}
|
|
375
631
|
|
|
376
632
|
_forge() {
|
|
377
|
-
local -a commands feature_commands
|
|
633
|
+
local -a commands feature_commands fix_commands release_commands
|
|
634
|
+
local -a services_commands env_commands maintenance_commands
|
|
635
|
+
local -a agent_commands
|
|
636
|
+
|
|
637
|
+
# Reset config cache for each completion
|
|
638
|
+
_forge_config_loaded=0
|
|
378
639
|
|
|
379
640
|
commands=(
|
|
380
641
|
${commandsArray}
|
|
@@ -384,8 +645,24 @@ ${commandsArray}
|
|
|
384
645
|
${featureArray}
|
|
385
646
|
)
|
|
386
647
|
|
|
387
|
-
|
|
388
|
-
${
|
|
648
|
+
fix_commands=(
|
|
649
|
+
${fixArray}
|
|
650
|
+
)
|
|
651
|
+
|
|
652
|
+
release_commands=(
|
|
653
|
+
${releaseArray}
|
|
654
|
+
)
|
|
655
|
+
|
|
656
|
+
services_commands=(
|
|
657
|
+
${servicesArray}
|
|
658
|
+
)
|
|
659
|
+
|
|
660
|
+
env_commands=(
|
|
661
|
+
${envArray}
|
|
662
|
+
)
|
|
663
|
+
|
|
664
|
+
maintenance_commands=(
|
|
665
|
+
${maintenanceArray}
|
|
389
666
|
)
|
|
390
667
|
|
|
391
668
|
agent_commands=(
|
|
@@ -409,14 +686,52 @@ ${featureSlugCase} *)
|
|
|
409
686
|
;;
|
|
410
687
|
esac
|
|
411
688
|
;;
|
|
689
|
+
fix)
|
|
690
|
+
case \${words[2]} in
|
|
691
|
+
${fixSlugCase} *)
|
|
692
|
+
_describe 'fix command' fix_commands
|
|
693
|
+
;;
|
|
694
|
+
esac
|
|
695
|
+
;;
|
|
696
|
+
release)
|
|
697
|
+
case \${words[2]} in
|
|
698
|
+
${releaseSlugCase} *)
|
|
699
|
+
_describe 'release command' release_commands
|
|
700
|
+
;;
|
|
701
|
+
esac
|
|
702
|
+
;;
|
|
703
|
+
services)
|
|
704
|
+
_describe 'services command' services_commands
|
|
705
|
+
;;
|
|
706
|
+
env)
|
|
707
|
+
_describe 'env command' env_commands
|
|
708
|
+
;;
|
|
709
|
+
maintenance)
|
|
710
|
+
if (( CURRENT == 2 )); then
|
|
711
|
+
_describe 'maintenance command' maintenance_commands
|
|
712
|
+
else
|
|
713
|
+
local -a branches
|
|
714
|
+
branches=(\${(f)"\$(_forge_all_branches)"})
|
|
715
|
+
_describe 'branch name' branches
|
|
716
|
+
fi
|
|
717
|
+
;;
|
|
412
718
|
mode)
|
|
413
|
-
|
|
719
|
+
local -a modes
|
|
720
|
+
modes=(\${(f)"\$(_forge_modes)"})
|
|
721
|
+
if [[ \${#modes} -gt 0 ]]; then
|
|
722
|
+
_describe 'mode name' modes
|
|
723
|
+
fi
|
|
414
724
|
;;
|
|
415
725
|
agent)
|
|
416
726
|
_describe 'agent command' agent_commands
|
|
417
727
|
;;
|
|
418
728
|
${mainSlugCase} completion)
|
|
419
|
-
local shells
|
|
729
|
+
local -a shells
|
|
730
|
+
shells=('bash' 'zsh' 'fish' 'powershell' 'pwsh')
|
|
731
|
+
_describe 'shell type' shells
|
|
732
|
+
;;
|
|
733
|
+
alias)
|
|
734
|
+
local -a shells
|
|
420
735
|
shells=('bash' 'zsh' 'fish' 'powershell' 'pwsh')
|
|
421
736
|
_describe 'shell type' shells
|
|
422
737
|
;;
|
|
@@ -436,50 +751,60 @@ compdef _forge forge
|
|
|
436
751
|
# forge completion zsh > ~/.zsh/completions/_forge
|
|
437
752
|
# # Add to .zshrc: fpath=(~/.zsh/completions $fpath)
|
|
438
753
|
# # Then run: autoload -Uz compinit && compinit
|
|
754
|
+
#
|
|
755
|
+
# Tip: Run 'forge alias zsh' to generate shell aliases for quick access.
|
|
439
756
|
`;
|
|
440
757
|
}
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
* @returns Fish completion script content
|
|
445
|
-
*/
|
|
758
|
+
// ============================================================================
|
|
759
|
+
// FISH COMPLETION
|
|
760
|
+
// ============================================================================
|
|
446
761
|
generateFishCompletion() {
|
|
447
|
-
const
|
|
448
|
-
|
|
449
|
-
const
|
|
450
|
-
const agentCmd = this.findCommand('agent');
|
|
451
|
-
// Generate main commands
|
|
452
|
-
const mainCommandsLines = mainCommands
|
|
762
|
+
const data = this.getCompletionData();
|
|
763
|
+
// Main commands
|
|
764
|
+
const mainCommandsLines = data.mainCommands
|
|
453
765
|
.map((cmd) => `complete -c forge -n "__fish_use_subcommand" -a ${cmd.name} -d "${cmd.description.replace(/"/g, '\\"')}"`)
|
|
454
766
|
.join('\n');
|
|
455
|
-
//
|
|
456
|
-
const
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
.
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
const
|
|
467
|
-
const
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
const
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
.
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
767
|
+
// Helper to generate subcommand completions for a group
|
|
768
|
+
const genSubCmds = (parentName, cmd) => {
|
|
769
|
+
if (!cmd || !cmd.subcommands.length)
|
|
770
|
+
return '';
|
|
771
|
+
const subNames = cmd.subcommands.map((c) => c.name).join(' ');
|
|
772
|
+
return cmd.subcommands
|
|
773
|
+
.map((c) => `complete -c forge -n "__fish_seen_subcommand_from ${parentName}; and not __fish_seen_subcommand_from ${subNames}" -a ${c.name} -d "${c.description.replace(/"/g, '\\"')}"`)
|
|
774
|
+
.join('\n');
|
|
775
|
+
};
|
|
776
|
+
const featureSubCmds = genSubCmds('feature', data.featureCmd);
|
|
777
|
+
const fixSubCmds = genSubCmds('fix', data.fixCmd);
|
|
778
|
+
const releaseSubCmds = genSubCmds('release', data.releaseCmd);
|
|
779
|
+
const servicesSubCmds = genSubCmds('services', data.servicesCmd);
|
|
780
|
+
const envSubCmds = genSubCmds('env', data.envCmd);
|
|
781
|
+
const maintenanceSubCmds = genSubCmds('maintenance', data.maintenanceCmd);
|
|
782
|
+
const agentSubCmds = genSubCmds('agent', data.agentCmd);
|
|
783
|
+
// Slug completions for feature/fix/release
|
|
784
|
+
const genSlugCompletions = (parentName, cmd, slugFn) => {
|
|
785
|
+
if (!cmd)
|
|
786
|
+
return '';
|
|
787
|
+
const slugCmds = cmd.subcommands.filter((c) => data.slugSubcommands.includes(c.name));
|
|
788
|
+
return slugCmds
|
|
789
|
+
.map((c) => `complete -c forge -n "__fish_seen_subcommand_from ${parentName}; and __fish_seen_subcommand_from ${c.name}" -a "(${slugFn})"`)
|
|
790
|
+
.join('\n');
|
|
791
|
+
};
|
|
792
|
+
const featureSlugCompletions = genSlugCompletions('feature', data.featureCmd, '__forge_feature_slugs');
|
|
793
|
+
const fixSlugCompletions = genSlugCompletions('fix', data.fixCmd, '__forge_fix_slugs');
|
|
794
|
+
const releaseSlugCompletions = genSlugCompletions('release', data.releaseCmd, '__forge_release_slugs');
|
|
795
|
+
// Root-level slug commands
|
|
796
|
+
const mainSlugCmds = data.mainCommands
|
|
797
|
+
.filter((cmd) => data.rootSlugCommands.includes(cmd.name));
|
|
798
|
+
const mainSlugCompletions = mainSlugCmds
|
|
799
|
+
.map((cmd) => `complete -c forge -n "__fish_seen_subcommand_from ${cmd.name}" -a "(__forge_all_branches)"`)
|
|
479
800
|
.join('\n');
|
|
801
|
+
// Maintenance slug completion at position 3
|
|
802
|
+
const maintenanceSlugCompletion = data.maintenanceCmd?.subcommands
|
|
803
|
+
.map((c) => `complete -c forge -n "__fish_seen_subcommand_from maintenance; and __fish_seen_subcommand_from ${c.name}" -a "(__forge_all_branches)"`)
|
|
804
|
+
.join('\n') || '';
|
|
480
805
|
return `# forge fish completion script
|
|
481
806
|
|
|
482
|
-
# Helper functions
|
|
807
|
+
# Helper functions
|
|
483
808
|
function __forge_find_config_root
|
|
484
809
|
set -l dir $PWD
|
|
485
810
|
while test -n "$dir" -a "$dir" != "/"
|
|
@@ -492,45 +817,107 @@ function __forge_find_config_root
|
|
|
492
817
|
return 1
|
|
493
818
|
end
|
|
494
819
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
820
|
+
set -g _forge_config_loaded 0
|
|
821
|
+
|
|
822
|
+
function __forge_load_config
|
|
823
|
+
if test $_forge_config_loaded -eq 1
|
|
498
824
|
return 0
|
|
499
825
|
end
|
|
826
|
+
set -g _FORGE_WORKTREES_ROOT ""
|
|
827
|
+
set -g _FORGE_FEATURE_PREFIX "feature/"
|
|
828
|
+
set -g _FORGE_FIX_PREFIX "fix/"
|
|
829
|
+
set -g _FORGE_RELEASE_PREFIX "release/"
|
|
830
|
+
set -g _FORGE_MODES ""
|
|
831
|
+
|
|
832
|
+
if test -n "$FORGE_WORKTREES_ROOT"
|
|
833
|
+
set -g _FORGE_WORKTREES_ROOT $FORGE_WORKTREES_ROOT
|
|
834
|
+
end
|
|
835
|
+
|
|
500
836
|
set -l config_root (__forge_find_config_root)
|
|
501
837
|
if test -n "$config_root"
|
|
502
|
-
set -l
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
838
|
+
set -l output (node -e ${this.nodeConfigSnippetFish()} "$config_root/.feat-forge.json" 2>/dev/null)
|
|
839
|
+
if test -n "$output"
|
|
840
|
+
eval $output
|
|
841
|
+
else if test -z "$_FORGE_WORKTREES_ROOT"
|
|
842
|
+
set -g _FORGE_WORKTREES_ROOT "$config_root/worktrees"
|
|
843
|
+
end
|
|
844
|
+
else if test -z "$_FORGE_WORKTREES_ROOT"
|
|
845
|
+
set -g _FORGE_WORKTREES_ROOT "worktrees"
|
|
846
|
+
end
|
|
847
|
+
set -g _forge_config_loaded 1
|
|
848
|
+
end
|
|
849
|
+
|
|
850
|
+
function __forge_feature_slugs
|
|
851
|
+
__forge_load_config
|
|
852
|
+
set -l prefix_dir (string trim --right --chars=/ $_FORGE_FEATURE_PREFIX)
|
|
853
|
+
set -l dir "$_FORGE_WORKTREES_ROOT/$prefix_dir"
|
|
854
|
+
if test -d "$dir"
|
|
855
|
+
for entry in $dir/*/
|
|
856
|
+
basename $entry
|
|
857
|
+
end
|
|
858
|
+
end
|
|
859
|
+
end
|
|
860
|
+
|
|
861
|
+
function __forge_fix_slugs
|
|
862
|
+
__forge_load_config
|
|
863
|
+
set -l prefix_dir (string trim --right --chars=/ $_FORGE_FIX_PREFIX)
|
|
864
|
+
set -l dir "$_FORGE_WORKTREES_ROOT/$prefix_dir"
|
|
865
|
+
if test -d "$dir"
|
|
866
|
+
for entry in $dir/*/
|
|
867
|
+
basename $entry
|
|
868
|
+
end
|
|
869
|
+
end
|
|
870
|
+
end
|
|
871
|
+
|
|
872
|
+
function __forge_release_slugs
|
|
873
|
+
__forge_load_config
|
|
874
|
+
set -l prefix_dir (string trim --right --chars=/ $_FORGE_RELEASE_PREFIX)
|
|
875
|
+
set -l dir "$_FORGE_WORKTREES_ROOT/$prefix_dir"
|
|
876
|
+
if test -d "$dir"
|
|
877
|
+
for entry in $dir/*/
|
|
878
|
+
basename $entry
|
|
518
879
|
end
|
|
519
|
-
echo "$config_root/features"
|
|
520
|
-
return 0
|
|
521
880
|
end
|
|
522
|
-
echo "features"
|
|
523
881
|
end
|
|
524
882
|
|
|
525
|
-
function
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
883
|
+
function __forge_all_branches
|
|
884
|
+
__forge_load_config
|
|
885
|
+
set -l root $_FORGE_WORKTREES_ROOT
|
|
886
|
+
if not test -d "$root"
|
|
887
|
+
return
|
|
888
|
+
end
|
|
889
|
+
set -l prefix_dirs (string trim --right --chars=/ $_FORGE_FEATURE_PREFIX) (string trim --right --chars=/ $_FORGE_FIX_PREFIX) (string trim --right --chars=/ $_FORGE_RELEASE_PREFIX)
|
|
890
|
+
for entry in $root/*/
|
|
891
|
+
set -l name (basename $entry)
|
|
892
|
+
set -l is_prefix 0
|
|
893
|
+
for pd in $prefix_dirs
|
|
894
|
+
if test "$name" = "$pd"
|
|
895
|
+
set is_prefix 1
|
|
896
|
+
break
|
|
897
|
+
end
|
|
898
|
+
end
|
|
899
|
+
if test $is_prefix -eq 1
|
|
900
|
+
for sub in $entry*/
|
|
901
|
+
if test -d "$sub"
|
|
902
|
+
echo "$name/"(basename $sub)
|
|
903
|
+
end
|
|
904
|
+
end
|
|
905
|
+
else
|
|
906
|
+
echo $name
|
|
530
907
|
end
|
|
531
908
|
end
|
|
532
909
|
end
|
|
533
910
|
|
|
911
|
+
function __forge_modes
|
|
912
|
+
__forge_load_config
|
|
913
|
+
string split " " $_FORGE_MODES
|
|
914
|
+
end
|
|
915
|
+
|
|
916
|
+
# Reset config cache on each completion invocation
|
|
917
|
+
function __forge_reset_config --on-event fish_prompt
|
|
918
|
+
set -g _forge_config_loaded 0
|
|
919
|
+
end
|
|
920
|
+
|
|
534
921
|
# Disable file completion by default
|
|
535
922
|
complete -c forge -f
|
|
536
923
|
|
|
@@ -540,18 +927,42 @@ ${mainCommandsLines}
|
|
|
540
927
|
# Feature subcommands
|
|
541
928
|
${featureSubCmds}
|
|
542
929
|
|
|
543
|
-
# Feature
|
|
930
|
+
# Feature slug completions
|
|
544
931
|
${featureSlugCompletions}
|
|
545
932
|
|
|
546
|
-
#
|
|
547
|
-
${
|
|
933
|
+
# Fix subcommands
|
|
934
|
+
${fixSubCmds}
|
|
935
|
+
|
|
936
|
+
# Fix slug completions
|
|
937
|
+
${fixSlugCompletions}
|
|
938
|
+
|
|
939
|
+
# Release subcommands
|
|
940
|
+
${releaseSubCmds}
|
|
941
|
+
|
|
942
|
+
# Release slug completions
|
|
943
|
+
${releaseSlugCompletions}
|
|
944
|
+
|
|
945
|
+
# Services subcommands
|
|
946
|
+
${servicesSubCmds}
|
|
947
|
+
|
|
948
|
+
# Env subcommands
|
|
949
|
+
${envSubCmds}
|
|
950
|
+
|
|
951
|
+
# Maintenance subcommands
|
|
952
|
+
${maintenanceSubCmds}
|
|
953
|
+
|
|
954
|
+
# Maintenance slug completions
|
|
955
|
+
${maintenanceSlugCompletion}
|
|
548
956
|
|
|
549
957
|
# Agent subcommands
|
|
550
958
|
${agentSubCmds}
|
|
551
959
|
|
|
552
|
-
#
|
|
960
|
+
# Root-level commands with branch completion
|
|
553
961
|
${mainSlugCompletions}
|
|
554
962
|
|
|
963
|
+
# Mode command with mode name completion
|
|
964
|
+
complete -c forge -n "__fish_seen_subcommand_from mode" -a "(__forge_modes)"
|
|
965
|
+
|
|
555
966
|
# Completion command with shell types
|
|
556
967
|
complete -c forge -n "__fish_seen_subcommand_from completion" -a bash -d "Generate bash completion"
|
|
557
968
|
complete -c forge -n "__fish_seen_subcommand_from completion" -a zsh -d "Generate zsh completion"
|
|
@@ -559,33 +970,49 @@ complete -c forge -n "__fish_seen_subcommand_from completion" -a fish -d "Genera
|
|
|
559
970
|
complete -c forge -n "__fish_seen_subcommand_from completion" -a powershell -d "Generate PowerShell completion"
|
|
560
971
|
complete -c forge -n "__fish_seen_subcommand_from completion" -a pwsh -d "Generate PowerShell completion"
|
|
561
972
|
|
|
973
|
+
# Alias command with shell types
|
|
974
|
+
complete -c forge -n "__fish_seen_subcommand_from alias" -a bash -d "Generate bash aliases"
|
|
975
|
+
complete -c forge -n "__fish_seen_subcommand_from alias" -a zsh -d "Generate zsh aliases"
|
|
976
|
+
complete -c forge -n "__fish_seen_subcommand_from alias" -a fish -d "Generate fish aliases"
|
|
977
|
+
complete -c forge -n "__fish_seen_subcommand_from alias" -a powershell -d "Generate PowerShell aliases"
|
|
978
|
+
complete -c forge -n "__fish_seen_subcommand_from alias" -a pwsh -d "Generate PowerShell aliases"
|
|
979
|
+
|
|
562
980
|
# Installation instructions:
|
|
563
981
|
# Option 1 - Add to ~/.config/fish/config.fish:
|
|
564
982
|
# forge completion fish | source
|
|
565
983
|
#
|
|
566
984
|
# Option 2 - Save to completions directory:
|
|
567
985
|
# forge completion fish > ~/.config/fish/completions/forge.fish
|
|
986
|
+
#
|
|
987
|
+
# Tip: Run 'forge alias fish' to generate shell aliases for quick access.
|
|
568
988
|
`;
|
|
569
989
|
}
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
* @returns PowerShell completion script content
|
|
574
|
-
*/
|
|
990
|
+
// ============================================================================
|
|
991
|
+
// POWERSHELL COMPLETION
|
|
992
|
+
// ============================================================================
|
|
575
993
|
generatePowerShellCompletion() {
|
|
576
|
-
const
|
|
577
|
-
const featureCmd = this.findCommand('feature');
|
|
578
|
-
const modeCmd = this.findCommand('mode');
|
|
579
|
-
const agentCmd = this.findCommand('agent');
|
|
994
|
+
const data = this.getCompletionData();
|
|
580
995
|
const toPsArray = (items) => items.length > 0 ? items.map((item) => `'${item.replace(/'/g, "''")}'`).join(', ') : '';
|
|
581
|
-
const mainCommandsList = toPsArray(mainCommands.map((cmd) => cmd.name));
|
|
582
|
-
const featureCommandsList = toPsArray(featureCmd?.subcommands.map((cmd) => cmd.name) || []);
|
|
583
|
-
const
|
|
584
|
-
const
|
|
585
|
-
const
|
|
586
|
-
const
|
|
587
|
-
const
|
|
588
|
-
const
|
|
996
|
+
const mainCommandsList = toPsArray(data.mainCommands.map((cmd) => cmd.name));
|
|
997
|
+
const featureCommandsList = toPsArray(data.featureCmd?.subcommands.map((cmd) => cmd.name) || []);
|
|
998
|
+
const fixCommandsList = toPsArray(data.fixCmd?.subcommands.map((cmd) => cmd.name) || []);
|
|
999
|
+
const releaseCommandsList = toPsArray(data.releaseCmd?.subcommands.map((cmd) => cmd.name) || []);
|
|
1000
|
+
const servicesCommandsList = toPsArray(data.servicesCmd?.subcommands.map((cmd) => cmd.name) || []);
|
|
1001
|
+
const envCommandsList = toPsArray(data.envCmd?.subcommands.map((cmd) => cmd.name) || []);
|
|
1002
|
+
const maintenanceCommandsList = toPsArray(data.maintenanceCmd?.subcommands.map((cmd) => cmd.name) || []);
|
|
1003
|
+
const agentCommandsList = toPsArray(data.agentCmd?.subcommands.map((cmd) => cmd.name) || []);
|
|
1004
|
+
const featureSlugCommandsList = toPsArray(data.featureCmd?.subcommands
|
|
1005
|
+
.filter((cmd) => data.slugSubcommands.includes(cmd.name))
|
|
1006
|
+
.map((cmd) => cmd.name) || []);
|
|
1007
|
+
const fixSlugCommandsList = toPsArray(data.fixCmd?.subcommands
|
|
1008
|
+
.filter((cmd) => data.slugSubcommands.includes(cmd.name))
|
|
1009
|
+
.map((cmd) => cmd.name) || []);
|
|
1010
|
+
const releaseSlugCommandsList = toPsArray(data.releaseCmd?.subcommands
|
|
1011
|
+
.filter((cmd) => data.slugSubcommands.includes(cmd.name))
|
|
1012
|
+
.map((cmd) => cmd.name) || []);
|
|
1013
|
+
const mainSlugCommandsList = toPsArray(data.mainCommands
|
|
1014
|
+
.filter((cmd) => data.rootSlugCommands.includes(cmd.name))
|
|
1015
|
+
.map((cmd) => cmd.name));
|
|
589
1016
|
const completionShellsList = toPsArray(['bash', 'zsh', 'fish', 'powershell', 'pwsh']);
|
|
590
1017
|
return `# forge PowerShell completion script
|
|
591
1018
|
|
|
@@ -600,40 +1027,100 @@ function __ForgeFindConfigRoot {
|
|
|
600
1027
|
return $null
|
|
601
1028
|
}
|
|
602
1029
|
|
|
603
|
-
|
|
604
|
-
|
|
1030
|
+
$script:__ForgeConfigLoaded = $false
|
|
1031
|
+
$script:__ForgeConfig = $null
|
|
1032
|
+
|
|
1033
|
+
function __ForgeLoadConfig {
|
|
1034
|
+
if ($script:__ForgeConfigLoaded) { return }
|
|
1035
|
+
|
|
1036
|
+
$script:__ForgeConfig = @{
|
|
1037
|
+
WorktreesRoot = ""
|
|
1038
|
+
FeaturePrefix = "feature/"
|
|
1039
|
+
FixPrefix = "fix/"
|
|
1040
|
+
ReleasePrefix = "release/"
|
|
1041
|
+
Modes = @()
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
if ($env:FORGE_WORKTREES_ROOT) {
|
|
1045
|
+
$script:__ForgeConfig.WorktreesRoot = $env:FORGE_WORKTREES_ROOT
|
|
1046
|
+
}
|
|
1047
|
+
|
|
605
1048
|
$configRoot = __ForgeFindConfigRoot
|
|
606
1049
|
if ($configRoot) {
|
|
607
1050
|
try {
|
|
608
|
-
$
|
|
609
|
-
$
|
|
610
|
-
|
|
611
|
-
if (
|
|
612
|
-
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
$
|
|
616
|
-
}
|
|
617
|
-
if ($json.options -and $json.options.folders -and $json.options.folders.worktrees) {
|
|
618
|
-
return (Join-Path $rootDir $json.options.folders.worktrees)
|
|
1051
|
+
$output = node -e ${this.nodeConfigSnippetPowerShell()} (Join-Path $configRoot ".feat-forge.json") 2>$null
|
|
1052
|
+
if ($output) {
|
|
1053
|
+
$parsed = $output | ConvertFrom-Json
|
|
1054
|
+
if ($parsed.worktreesRoot) { $script:__ForgeConfig.WorktreesRoot = $parsed.worktreesRoot }
|
|
1055
|
+
if ($parsed.featurePrefix) { $script:__ForgeConfig.FeaturePrefix = $parsed.featurePrefix }
|
|
1056
|
+
if ($parsed.fixPrefix) { $script:__ForgeConfig.FixPrefix = $parsed.fixPrefix }
|
|
1057
|
+
if ($parsed.releasePrefix) { $script:__ForgeConfig.ReleasePrefix = $parsed.releasePrefix }
|
|
1058
|
+
if ($parsed.modes) { $script:__ForgeConfig.Modes = $parsed.modes }
|
|
619
1059
|
}
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
} catch {
|
|
1060
|
+
} catch {}
|
|
1061
|
+
if (-not $script:__ForgeConfig.WorktreesRoot) {
|
|
1062
|
+
$script:__ForgeConfig.WorktreesRoot = Join-Path $configRoot "worktrees"
|
|
624
1063
|
}
|
|
625
|
-
return (Join-Path $rootDir "features")
|
|
626
1064
|
}
|
|
627
|
-
|
|
1065
|
+
if (-not $script:__ForgeConfig.WorktreesRoot) {
|
|
1066
|
+
$script:__ForgeConfig.WorktreesRoot = "worktrees"
|
|
1067
|
+
}
|
|
1068
|
+
$script:__ForgeConfigLoaded = $true
|
|
628
1069
|
}
|
|
629
1070
|
|
|
630
1071
|
function __ForgeFeatureSlugs {
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
1072
|
+
__ForgeLoadConfig
|
|
1073
|
+
$prefix = $script:__ForgeConfig.FeaturePrefix.TrimEnd('/')
|
|
1074
|
+
$dir = Join-Path $script:__ForgeConfig.WorktreesRoot $prefix
|
|
1075
|
+
if (Test-Path $dir) {
|
|
1076
|
+
Get-ChildItem -Directory -Path $dir | ForEach-Object { $_.Name }
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
function __ForgeFixSlugs {
|
|
1081
|
+
__ForgeLoadConfig
|
|
1082
|
+
$prefix = $script:__ForgeConfig.FixPrefix.TrimEnd('/')
|
|
1083
|
+
$dir = Join-Path $script:__ForgeConfig.WorktreesRoot $prefix
|
|
1084
|
+
if (Test-Path $dir) {
|
|
1085
|
+
Get-ChildItem -Directory -Path $dir | ForEach-Object { $_.Name }
|
|
634
1086
|
}
|
|
635
1087
|
}
|
|
636
1088
|
|
|
1089
|
+
function __ForgeReleaseSlugs {
|
|
1090
|
+
__ForgeLoadConfig
|
|
1091
|
+
$prefix = $script:__ForgeConfig.ReleasePrefix.TrimEnd('/')
|
|
1092
|
+
$dir = Join-Path $script:__ForgeConfig.WorktreesRoot $prefix
|
|
1093
|
+
if (Test-Path $dir) {
|
|
1094
|
+
Get-ChildItem -Directory -Path $dir | ForEach-Object { $_.Name }
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
function __ForgeAllBranches {
|
|
1099
|
+
__ForgeLoadConfig
|
|
1100
|
+
$root = $script:__ForgeConfig.WorktreesRoot
|
|
1101
|
+
if (-not (Test-Path $root)) { return }
|
|
1102
|
+
$prefixDirs = @(
|
|
1103
|
+
$script:__ForgeConfig.FeaturePrefix.TrimEnd('/'),
|
|
1104
|
+
$script:__ForgeConfig.FixPrefix.TrimEnd('/'),
|
|
1105
|
+
$script:__ForgeConfig.ReleasePrefix.TrimEnd('/')
|
|
1106
|
+
)
|
|
1107
|
+
Get-ChildItem -Directory -Path $root | ForEach-Object {
|
|
1108
|
+
$name = $_.Name
|
|
1109
|
+
if ($prefixDirs -contains $name) {
|
|
1110
|
+
Get-ChildItem -Directory -Path $_.FullName | ForEach-Object {
|
|
1111
|
+
"$name/$($_.Name)"
|
|
1112
|
+
}
|
|
1113
|
+
} else {
|
|
1114
|
+
$name
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
function __ForgeModes {
|
|
1120
|
+
__ForgeLoadConfig
|
|
1121
|
+
$script:__ForgeConfig.Modes
|
|
1122
|
+
}
|
|
1123
|
+
|
|
637
1124
|
function __ForgeCompletionResults {
|
|
638
1125
|
param([string[]]$Items, [string]$Word)
|
|
639
1126
|
if (-not $Items) { return }
|
|
@@ -644,15 +1131,24 @@ function __ForgeCompletionResults {
|
|
|
644
1131
|
|
|
645
1132
|
$mainCommands = @(${mainCommandsList})
|
|
646
1133
|
$featureCommands = @(${featureCommandsList})
|
|
647
|
-
$
|
|
1134
|
+
$fixCommands = @(${fixCommandsList})
|
|
1135
|
+
$releaseCommands = @(${releaseCommandsList})
|
|
1136
|
+
$servicesCommands = @(${servicesCommandsList})
|
|
1137
|
+
$envCommands = @(${envCommandsList})
|
|
1138
|
+
$maintenanceCommands = @(${maintenanceCommandsList})
|
|
648
1139
|
$agentCommands = @(${agentCommandsList})
|
|
649
1140
|
$featureSlugCommands = @(${featureSlugCommandsList})
|
|
1141
|
+
$fixSlugCommands = @(${fixSlugCommandsList})
|
|
1142
|
+
$releaseSlugCommands = @(${releaseSlugCommandsList})
|
|
650
1143
|
$mainSlugCommands = @(${mainSlugCommandsList})
|
|
651
1144
|
$completionShells = @(${completionShellsList})
|
|
652
1145
|
|
|
653
1146
|
Register-ArgumentCompleter -CommandName forge -ScriptBlock {
|
|
654
1147
|
param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
|
|
655
1148
|
|
|
1149
|
+
# Reset config cache for each completion
|
|
1150
|
+
$script:__ForgeConfigLoaded = $false
|
|
1151
|
+
|
|
656
1152
|
$elements = $commandAst.CommandElements | ForEach-Object { $_.Value }
|
|
657
1153
|
|
|
658
1154
|
if ($elements.Count -le 1) {
|
|
@@ -673,8 +1169,46 @@ Register-ArgumentCompleter -CommandName forge -ScriptBlock {
|
|
|
673
1169
|
return
|
|
674
1170
|
}
|
|
675
1171
|
}
|
|
1172
|
+
'fix' {
|
|
1173
|
+
if ($elements.Count -le 2) {
|
|
1174
|
+
__ForgeCompletionResults -Items $fixCommands -Word $wordToComplete
|
|
1175
|
+
return
|
|
1176
|
+
}
|
|
1177
|
+
$sub = $elements[2]
|
|
1178
|
+
if ($fixSlugCommands -contains $sub) {
|
|
1179
|
+
__ForgeCompletionResults -Items (__ForgeFixSlugs) -Word $wordToComplete
|
|
1180
|
+
return
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
'release' {
|
|
1184
|
+
if ($elements.Count -le 2) {
|
|
1185
|
+
__ForgeCompletionResults -Items $releaseCommands -Word $wordToComplete
|
|
1186
|
+
return
|
|
1187
|
+
}
|
|
1188
|
+
$sub = $elements[2]
|
|
1189
|
+
if ($releaseSlugCommands -contains $sub) {
|
|
1190
|
+
__ForgeCompletionResults -Items (__ForgeReleaseSlugs) -Word $wordToComplete
|
|
1191
|
+
return
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
'services' {
|
|
1195
|
+
__ForgeCompletionResults -Items $servicesCommands -Word $wordToComplete
|
|
1196
|
+
return
|
|
1197
|
+
}
|
|
1198
|
+
'env' {
|
|
1199
|
+
__ForgeCompletionResults -Items $envCommands -Word $wordToComplete
|
|
1200
|
+
return
|
|
1201
|
+
}
|
|
1202
|
+
'maintenance' {
|
|
1203
|
+
if ($elements.Count -le 2) {
|
|
1204
|
+
__ForgeCompletionResults -Items $maintenanceCommands -Word $wordToComplete
|
|
1205
|
+
return
|
|
1206
|
+
}
|
|
1207
|
+
__ForgeCompletionResults -Items (__ForgeAllBranches) -Word $wordToComplete
|
|
1208
|
+
return
|
|
1209
|
+
}
|
|
676
1210
|
'mode' {
|
|
677
|
-
__ForgeCompletionResults -Items
|
|
1211
|
+
__ForgeCompletionResults -Items (__ForgeModes) -Word $wordToComplete
|
|
678
1212
|
return
|
|
679
1213
|
}
|
|
680
1214
|
'agent' {
|
|
@@ -685,9 +1219,13 @@ Register-ArgumentCompleter -CommandName forge -ScriptBlock {
|
|
|
685
1219
|
__ForgeCompletionResults -Items $completionShells -Word $wordToComplete
|
|
686
1220
|
return
|
|
687
1221
|
}
|
|
1222
|
+
'alias' {
|
|
1223
|
+
__ForgeCompletionResults -Items $completionShells -Word $wordToComplete
|
|
1224
|
+
return
|
|
1225
|
+
}
|
|
688
1226
|
default {
|
|
689
1227
|
if ($mainSlugCommands -contains $first) {
|
|
690
|
-
__ForgeCompletionResults -Items (
|
|
1228
|
+
__ForgeCompletionResults -Items (__ForgeAllBranches) -Word $wordToComplete
|
|
691
1229
|
return
|
|
692
1230
|
}
|
|
693
1231
|
}
|
|
@@ -695,8 +1233,10 @@ Register-ArgumentCompleter -CommandName forge -ScriptBlock {
|
|
|
695
1233
|
}
|
|
696
1234
|
|
|
697
1235
|
# Installation instructions:
|
|
698
|
-
# Option 1 - Add to
|
|
1236
|
+
# Option 1 - Add to \$PROFILE:
|
|
699
1237
|
# forge completion powershell | Out-String | Invoke-Expression
|
|
1238
|
+
#
|
|
1239
|
+
# Tip: Run 'forge alias powershell' to generate shell aliases for quick access.
|
|
700
1240
|
`;
|
|
701
1241
|
}
|
|
702
1242
|
}
|