feat-forge 1.0.3 → 1.2.3
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 +36 -5
- 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 +809 -290
- package/dist/commands/SubBranchCommands.js +4 -0
- package/dist/foundation/PortAllocator.js +2 -0
- package/dist/foundation/Proxy.js +5 -2
- package/dist/foundation/types/Services.js +6 -0
- package/dist/lib/proxy-dashboard.js +51 -27
- package/dist/lib/services.js +2 -0
- package/dist/templates/agent/CONTEXT.code.md +7 -7
- package/dist/templates/agent/CONTEXT.spec.md +9 -9
- package/dist/templates/agent/Copilot/Specs.agent.md +3 -3
- package/dist/templates/agent/Copilot/SpecsCommit.agent.md +1 -1
- package/dist/templates/agent/Copilot/TODO-Reader.agent.md +2 -2
- package/package.json +18 -15
|
@@ -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,167 @@ 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
|
-
const
|
|
148
|
-
const
|
|
149
|
-
const
|
|
150
|
-
const featureSlugCase =
|
|
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.filter((cmd) => data.slugSubcommands.includes(cmd.name)).map((cmd) => cmd.name) || [];
|
|
180
|
+
const fixSlugCmds = data.fixCmd?.subcommands.filter((cmd) => data.slugSubcommands.includes(cmd.name)).map((cmd) => cmd.name) || [];
|
|
181
|
+
const releaseSlugCmds = data.releaseCmd?.subcommands.filter((cmd) => data.slugSubcommands.includes(cmd.name)).map((cmd) => cmd.name) || [];
|
|
182
|
+
const mainSlugCmds = data.mainCommands.filter((cmd) => data.rootSlugCommands.includes(cmd.name)).map((cmd) => cmd.name);
|
|
183
|
+
const featureSlugCase = featureSlugCmds.length > 0
|
|
184
|
+
? ` ${featureSlugCmds.join('|')})
|
|
185
|
+
if [[ \${cword} -eq 3 ]]; then
|
|
186
|
+
local slugs="\$(_forge_feature_slugs)"
|
|
187
|
+
COMPREPLY=( \$(compgen -W "\${slugs}" -- "\${cur}") )
|
|
188
|
+
return 0
|
|
189
|
+
fi
|
|
190
|
+
;;
|
|
191
|
+
`
|
|
192
|
+
: '';
|
|
193
|
+
const fixSlugCase = fixSlugCmds.length > 0
|
|
194
|
+
? ` ${fixSlugCmds.join('|')})
|
|
195
|
+
if [[ \${cword} -eq 3 ]]; then
|
|
196
|
+
local slugs="\$(_forge_fix_slugs)"
|
|
197
|
+
COMPREPLY=( \$(compgen -W "\${slugs}" -- "\${cur}") )
|
|
198
|
+
return 0
|
|
199
|
+
fi
|
|
200
|
+
;;
|
|
201
|
+
`
|
|
202
|
+
: '';
|
|
203
|
+
const releaseSlugCase = releaseSlugCmds.length > 0
|
|
204
|
+
? ` ${releaseSlugCmds.join('|')})
|
|
153
205
|
if [[ \${cword} -eq 3 ]]; then
|
|
154
|
-
local
|
|
155
|
-
|
|
156
|
-
COMPREPLY=( \$(compgen -W "\${features}" -- "\${cur}") )
|
|
206
|
+
local slugs="\$(_forge_release_slugs)"
|
|
207
|
+
COMPREPLY=( \$(compgen -W "\${slugs}" -- "\${cur}") )
|
|
157
208
|
return 0
|
|
158
209
|
fi
|
|
159
210
|
;;
|
|
160
211
|
`
|
|
161
212
|
: '';
|
|
162
|
-
const mainSlugCase =
|
|
163
|
-
? ` ${
|
|
164
|
-
# Suggest available features for commands with slug argument
|
|
213
|
+
const mainSlugCase = mainSlugCmds.length > 0
|
|
214
|
+
? ` ${mainSlugCmds.join('|')})
|
|
165
215
|
if [[ \${cword} -eq 2 ]]; then
|
|
166
|
-
local
|
|
167
|
-
|
|
168
|
-
COMPREPLY=( \$(compgen -W "\${features}" -- "\${cur}") )
|
|
216
|
+
local branches="\$(_forge_all_branches)"
|
|
217
|
+
COMPREPLY=( \$(compgen -W "\${branches}" -- "\${cur}") )
|
|
169
218
|
return 0
|
|
170
219
|
fi
|
|
171
220
|
;;
|
|
@@ -185,57 +234,125 @@ _forge_find_config() {
|
|
|
185
234
|
return 1
|
|
186
235
|
}
|
|
187
236
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
237
|
+
# Load forge config once and cache variables for the current completion
|
|
238
|
+
_forge_config_loaded=0
|
|
239
|
+
_forge_load_config() {
|
|
240
|
+
if [[ \${_forge_config_loaded} -eq 1 ]]; then
|
|
191
241
|
return 0
|
|
192
242
|
fi
|
|
243
|
+
_FORGE_WORKTREES_ROOT=""
|
|
244
|
+
_FORGE_FEATURE_PREFIX="feature/"
|
|
245
|
+
_FORGE_FIX_PREFIX="fix/"
|
|
246
|
+
_FORGE_RELEASE_PREFIX="release/"
|
|
247
|
+
_FORGE_MODES=""
|
|
248
|
+
|
|
249
|
+
if [[ -n "\${FORGE_WORKTREES_ROOT}" ]]; then
|
|
250
|
+
_FORGE_WORKTREES_ROOT="\${FORGE_WORKTREES_ROOT}"
|
|
251
|
+
fi
|
|
252
|
+
|
|
193
253
|
local start="\${PWD}"
|
|
194
254
|
local config_root
|
|
195
255
|
config_root="\$(_forge_find_config "\${start}")" || true
|
|
196
256
|
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
|
|
257
|
+
local output
|
|
258
|
+
output=\$(node -e ${this.nodeConfigSnippet()} "\${config_root}/.feat-forge.json" 2>/dev/null)
|
|
259
|
+
if [[ -n "\${output}" ]]; then
|
|
260
|
+
eval "\${output}"
|
|
261
|
+
elif [[ -z "\${_FORGE_WORKTREES_ROOT}" ]]; then
|
|
262
|
+
_FORGE_WORKTREES_ROOT="\${config_root}/worktrees"
|
|
214
263
|
fi
|
|
215
|
-
|
|
216
|
-
|
|
264
|
+
elif [[ -z "\${_FORGE_WORKTREES_ROOT}" ]]; then
|
|
265
|
+
_FORGE_WORKTREES_ROOT="worktrees"
|
|
217
266
|
fi
|
|
218
|
-
|
|
267
|
+
_forge_config_loaded=1
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
_forge_feature_slugs() {
|
|
271
|
+
_forge_load_config
|
|
272
|
+
local prefix_dir="\${_FORGE_FEATURE_PREFIX%/}"
|
|
273
|
+
local dir="\${_FORGE_WORKTREES_ROOT}/\${prefix_dir}"
|
|
274
|
+
if [[ -d "\${dir}" ]]; then
|
|
275
|
+
find "\${dir}" -mindepth 1 -maxdepth 1 -type d -exec basename {} \\; 2>/dev/null
|
|
276
|
+
fi
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
_forge_fix_slugs() {
|
|
280
|
+
_forge_load_config
|
|
281
|
+
local prefix_dir="\${_FORGE_FIX_PREFIX%/}"
|
|
282
|
+
local dir="\${_FORGE_WORKTREES_ROOT}/\${prefix_dir}"
|
|
283
|
+
if [[ -d "\${dir}" ]]; then
|
|
284
|
+
find "\${dir}" -mindepth 1 -maxdepth 1 -type d -exec basename {} \\; 2>/dev/null
|
|
285
|
+
fi
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
_forge_release_slugs() {
|
|
289
|
+
_forge_load_config
|
|
290
|
+
local prefix_dir="\${_FORGE_RELEASE_PREFIX%/}"
|
|
291
|
+
local dir="\${_FORGE_WORKTREES_ROOT}/\${prefix_dir}"
|
|
292
|
+
if [[ -d "\${dir}" ]]; then
|
|
293
|
+
find "\${dir}" -mindepth 1 -maxdepth 1 -type d -exec basename {} \\; 2>/dev/null
|
|
294
|
+
fi
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
_forge_all_branches() {
|
|
298
|
+
_forge_load_config
|
|
299
|
+
local root="\${_FORGE_WORKTREES_ROOT}"
|
|
300
|
+
if [[ ! -d "\${root}" ]]; then
|
|
301
|
+
return
|
|
302
|
+
fi
|
|
303
|
+
local prefix_dirs=()
|
|
304
|
+
for p in "\${_FORGE_FEATURE_PREFIX%/}" "\${_FORGE_FIX_PREFIX%/}" "\${_FORGE_RELEASE_PREFIX%/}"; do
|
|
305
|
+
if [[ -n "\${p}" ]]; then
|
|
306
|
+
prefix_dirs+=("\${p}")
|
|
307
|
+
fi
|
|
308
|
+
done
|
|
309
|
+
for entry in "\${root}"/*/; do
|
|
310
|
+
[[ -d "\${entry}" ]] || continue
|
|
311
|
+
local name="\$(basename "\${entry}")"
|
|
312
|
+
local is_prefix=0
|
|
313
|
+
for pd in "\${prefix_dirs[@]}"; do
|
|
314
|
+
if [[ "\${name}" == "\${pd}" ]]; then
|
|
315
|
+
is_prefix=1
|
|
316
|
+
break
|
|
317
|
+
fi
|
|
318
|
+
done
|
|
319
|
+
if [[ \${is_prefix} -eq 1 ]]; then
|
|
320
|
+
for sub in "\${entry}"*/; do
|
|
321
|
+
[[ -d "\${sub}" ]] || continue
|
|
322
|
+
echo "\${name}/\$(basename "\${sub}")"
|
|
323
|
+
done
|
|
324
|
+
else
|
|
325
|
+
echo "\${name}"
|
|
326
|
+
fi
|
|
327
|
+
done
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
_forge_modes() {
|
|
331
|
+
_forge_load_config
|
|
332
|
+
echo "\${_FORGE_MODES}"
|
|
219
333
|
}
|
|
220
334
|
|
|
221
335
|
_forge_completion() {
|
|
222
336
|
local cur prev words cword
|
|
223
337
|
_init_completion || return
|
|
224
338
|
|
|
225
|
-
#
|
|
339
|
+
# Reset config cache for each completion
|
|
340
|
+
_forge_config_loaded=0
|
|
341
|
+
|
|
226
342
|
local commands="${commands}"
|
|
227
343
|
|
|
228
|
-
# Subcommands for each main command
|
|
229
344
|
local feature_commands="${featureCommands}"
|
|
230
|
-
local
|
|
345
|
+
local fix_commands="${fixCommands}"
|
|
346
|
+
local release_commands="${releaseCommands}"
|
|
347
|
+
local services_commands="${servicesCommands}"
|
|
348
|
+
local env_commands="${envCommands}"
|
|
349
|
+
local maintenance_commands="${maintenanceCommands}"
|
|
231
350
|
local agent_commands="${agentCommands}"
|
|
232
351
|
|
|
233
|
-
# Get previous word for context
|
|
234
352
|
case "\${words[1]}" in
|
|
235
353
|
feature)
|
|
236
354
|
case "\${words[2]}" in
|
|
237
355
|
${featureSlugCase} *)
|
|
238
|
-
# Suggest feature subcommands
|
|
239
356
|
if [[ \${cword} -eq 2 ]]; then
|
|
240
357
|
COMPREPLY=( \$(compgen -W "\${feature_commands}" -- "\${cur}") )
|
|
241
358
|
return 0
|
|
@@ -243,29 +360,75 @@ ${featureSlugCase} *)
|
|
|
243
360
|
;;
|
|
244
361
|
esac
|
|
245
362
|
;;
|
|
363
|
+
fix)
|
|
364
|
+
case "\${words[2]}" in
|
|
365
|
+
${fixSlugCase} *)
|
|
366
|
+
if [[ \${cword} -eq 2 ]]; then
|
|
367
|
+
COMPREPLY=( \$(compgen -W "\${fix_commands}" -- "\${cur}") )
|
|
368
|
+
return 0
|
|
369
|
+
fi
|
|
370
|
+
;;
|
|
371
|
+
esac
|
|
372
|
+
;;
|
|
373
|
+
release)
|
|
374
|
+
case "\${words[2]}" in
|
|
375
|
+
${releaseSlugCase} *)
|
|
376
|
+
if [[ \${cword} -eq 2 ]]; then
|
|
377
|
+
COMPREPLY=( \$(compgen -W "\${release_commands}" -- "\${cur}") )
|
|
378
|
+
return 0
|
|
379
|
+
fi
|
|
380
|
+
;;
|
|
381
|
+
esac
|
|
382
|
+
;;
|
|
383
|
+
services)
|
|
384
|
+
if [[ \${cword} -eq 2 ]]; then
|
|
385
|
+
COMPREPLY=( \$(compgen -W "\${services_commands}" -- "\${cur}") )
|
|
386
|
+
return 0
|
|
387
|
+
fi
|
|
388
|
+
;;
|
|
389
|
+
env)
|
|
390
|
+
if [[ \${cword} -eq 2 ]]; then
|
|
391
|
+
COMPREPLY=( \$(compgen -W "\${env_commands}" -- "\${cur}") )
|
|
392
|
+
return 0
|
|
393
|
+
fi
|
|
394
|
+
;;
|
|
395
|
+
maintenance)
|
|
396
|
+
if [[ \${cword} -eq 2 ]]; then
|
|
397
|
+
COMPREPLY=( \$(compgen -W "\${maintenance_commands}" -- "\${cur}") )
|
|
398
|
+
return 0
|
|
399
|
+
fi
|
|
400
|
+
if [[ \${cword} -eq 3 ]]; then
|
|
401
|
+
local branches="\$(_forge_all_branches)"
|
|
402
|
+
COMPREPLY=( \$(compgen -W "\${branches}" -- "\${cur}") )
|
|
403
|
+
return 0
|
|
404
|
+
fi
|
|
405
|
+
;;
|
|
246
406
|
mode)
|
|
247
|
-
# Suggest mode subcommands
|
|
248
407
|
if [[ \${cword} -eq 2 ]]; then
|
|
249
|
-
|
|
408
|
+
local modes="\$(_forge_modes)"
|
|
409
|
+
COMPREPLY=( \$(compgen -W "\${modes}" -- "\${cur}") )
|
|
250
410
|
return 0
|
|
251
411
|
fi
|
|
252
412
|
;;
|
|
253
413
|
agent)
|
|
254
|
-
# Suggest agent subcommands
|
|
255
414
|
if [[ \${cword} -eq 2 ]]; then
|
|
256
415
|
COMPREPLY=( \$(compgen -W "\${agent_commands}" -- "\${cur}") )
|
|
257
416
|
return 0
|
|
258
417
|
fi
|
|
259
418
|
;;
|
|
260
419
|
${mainSlugCase} completion)
|
|
261
|
-
|
|
420
|
+
if [[ \${cword} -eq 2 ]]; then
|
|
421
|
+
COMPREPLY=( \$(compgen -W "bash zsh fish powershell pwsh" -- "\${cur}") )
|
|
422
|
+
return 0
|
|
423
|
+
fi
|
|
424
|
+
;;
|
|
425
|
+
alias)
|
|
262
426
|
if [[ \${cword} -eq 2 ]]; then
|
|
263
427
|
COMPREPLY=( \$(compgen -W "bash zsh fish powershell pwsh" -- "\${cur}") )
|
|
264
428
|
return 0
|
|
265
429
|
fi
|
|
266
430
|
;;
|
|
267
431
|
*)
|
|
268
|
-
# Suggest main commands at root level
|
|
269
432
|
if [[ \${cword} -eq 1 ]]; then
|
|
270
433
|
COMPREPLY=( \$(compgen -W "\${commands}" -- "\${cur}") )
|
|
271
434
|
return 0
|
|
@@ -284,44 +447,60 @@ complete -F _forge_completion forge
|
|
|
284
447
|
# Option 2 - Save to file:
|
|
285
448
|
# forge completion bash > ~/.local/share/bash-completion/completions/forge
|
|
286
449
|
# # Or system-wide: /etc/bash_completion.d/forge
|
|
450
|
+
#
|
|
451
|
+
# Tip: Run 'forge alias bash' to generate shell aliases for quick access.
|
|
287
452
|
`;
|
|
288
453
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
* @returns Zsh completion script content
|
|
293
|
-
*/
|
|
454
|
+
// ============================================================================
|
|
455
|
+
// ZSH COMPLETION
|
|
456
|
+
// ============================================================================
|
|
294
457
|
generateZshCompletion() {
|
|
295
|
-
const
|
|
296
|
-
const
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
const
|
|
301
|
-
const
|
|
302
|
-
const
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
const
|
|
306
|
-
|
|
307
|
-
const
|
|
308
|
-
const
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
458
|
+
const data = this.getCompletionData();
|
|
459
|
+
const commandsArray = data.mainCommands
|
|
460
|
+
.map((cmd) => ` '${cmd.name}:${cmd.description.replace(/'/g, "''")}'`)
|
|
461
|
+
.join('\n');
|
|
462
|
+
const featureArray = data.featureCmd?.subcommands.map((cmd) => ` '${cmd.name}:${cmd.description.replace(/'/g, "''")}'`).join('\n') || '';
|
|
463
|
+
const fixArray = data.fixCmd?.subcommands.map((cmd) => ` '${cmd.name}:${cmd.description.replace(/'/g, "''")}'`).join('\n') || '';
|
|
464
|
+
const releaseArray = data.releaseCmd?.subcommands.map((cmd) => ` '${cmd.name}:${cmd.description.replace(/'/g, "''")}'`).join('\n') || '';
|
|
465
|
+
const servicesArray = data.servicesCmd?.subcommands.map((cmd) => ` '${cmd.name}:${cmd.description.replace(/'/g, "''")}'`).join('\n') ||
|
|
466
|
+
'';
|
|
467
|
+
const envArray = data.envCmd?.subcommands.map((cmd) => ` '${cmd.name}:${cmd.description.replace(/'/g, "''")}'`).join('\n') || '';
|
|
468
|
+
const maintenanceArray = data.maintenanceCmd?.subcommands.map((cmd) => ` '${cmd.name}:${cmd.description.replace(/'/g, "''")}'`).join('\n') ||
|
|
469
|
+
'';
|
|
470
|
+
const agentArray = data.agentCmd?.subcommands.map((cmd) => ` '${cmd.name}:${cmd.description.replace(/'/g, "''")}'`).join('\n') || '';
|
|
471
|
+
const featureSlugCmds = data.featureCmd?.subcommands.filter((cmd) => data.slugSubcommands.includes(cmd.name)).map((cmd) => cmd.name) || [];
|
|
472
|
+
const fixSlugCmds = data.fixCmd?.subcommands.filter((cmd) => data.slugSubcommands.includes(cmd.name)).map((cmd) => cmd.name) || [];
|
|
473
|
+
const releaseSlugCmds = data.releaseCmd?.subcommands.filter((cmd) => data.slugSubcommands.includes(cmd.name)).map((cmd) => cmd.name) || [];
|
|
474
|
+
const mainSlugCmds = data.mainCommands.filter((cmd) => data.rootSlugCommands.includes(cmd.name)).map((cmd) => cmd.name);
|
|
475
|
+
const featureSlugCase = featureSlugCmds.length > 0
|
|
476
|
+
? ` ${featureSlugCmds.join('|')})
|
|
477
|
+
local -a slugs
|
|
478
|
+
slugs=(\${(f)"\$(_forge_feature_slugs)"})
|
|
479
|
+
_describe 'feature slug' slugs
|
|
315
480
|
;;
|
|
316
481
|
`
|
|
317
482
|
: '';
|
|
318
|
-
const
|
|
319
|
-
? `
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
483
|
+
const fixSlugCase = fixSlugCmds.length > 0
|
|
484
|
+
? ` ${fixSlugCmds.join('|')})
|
|
485
|
+
local -a slugs
|
|
486
|
+
slugs=(\${(f)"\$(_forge_fix_slugs)"})
|
|
487
|
+
_describe 'fix slug' slugs
|
|
488
|
+
;;
|
|
489
|
+
`
|
|
490
|
+
: '';
|
|
491
|
+
const releaseSlugCase = releaseSlugCmds.length > 0
|
|
492
|
+
? ` ${releaseSlugCmds.join('|')})
|
|
493
|
+
local -a slugs
|
|
494
|
+
slugs=(\${(f)"\$(_forge_release_slugs)"})
|
|
495
|
+
_describe 'release slug' slugs
|
|
496
|
+
;;
|
|
497
|
+
`
|
|
498
|
+
: '';
|
|
499
|
+
const mainSlugCase = mainSlugCmds.length > 0
|
|
500
|
+
? ` ${mainSlugCmds.join('|')})
|
|
501
|
+
local -a branches
|
|
502
|
+
branches=(\${(f)"\$(_forge_all_branches)"})
|
|
503
|
+
_describe 'branch name' branches
|
|
325
504
|
;;
|
|
326
505
|
`
|
|
327
506
|
: '';
|
|
@@ -340,41 +519,111 @@ _forge_find_config() {
|
|
|
340
519
|
return 1
|
|
341
520
|
}
|
|
342
521
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
522
|
+
# Load forge config once and cache variables for the current completion
|
|
523
|
+
_forge_config_loaded=0
|
|
524
|
+
_forge_load_config() {
|
|
525
|
+
if [[ \${_forge_config_loaded} -eq 1 ]]; then
|
|
346
526
|
return 0
|
|
347
527
|
fi
|
|
528
|
+
_FORGE_WORKTREES_ROOT=""
|
|
529
|
+
_FORGE_FEATURE_PREFIX="feature/"
|
|
530
|
+
_FORGE_FIX_PREFIX="fix/"
|
|
531
|
+
_FORGE_RELEASE_PREFIX="release/"
|
|
532
|
+
_FORGE_MODES=""
|
|
533
|
+
|
|
534
|
+
if [[ -n "\${FORGE_WORKTREES_ROOT}" ]]; then
|
|
535
|
+
_FORGE_WORKTREES_ROOT="\${FORGE_WORKTREES_ROOT}"
|
|
536
|
+
fi
|
|
537
|
+
|
|
348
538
|
local start="\${PWD}"
|
|
349
539
|
local config_root
|
|
350
540
|
config_root="\$(_forge_find_config "\${start}")" || true
|
|
351
541
|
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
|
|
542
|
+
local output
|
|
543
|
+
output=\$(node -e ${this.nodeConfigSnippet()} "\${config_root}/.feat-forge.json" 2>/dev/null)
|
|
544
|
+
if [[ -n "\${output}" ]]; then
|
|
545
|
+
eval "\${output}"
|
|
546
|
+
elif [[ -z "\${_FORGE_WORKTREES_ROOT}" ]]; then
|
|
547
|
+
_FORGE_WORKTREES_ROOT="\${config_root}/worktrees"
|
|
369
548
|
fi
|
|
370
|
-
|
|
371
|
-
|
|
549
|
+
elif [[ -z "\${_FORGE_WORKTREES_ROOT}" ]]; then
|
|
550
|
+
_FORGE_WORKTREES_ROOT="worktrees"
|
|
372
551
|
fi
|
|
373
|
-
|
|
552
|
+
_forge_config_loaded=1
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
_forge_feature_slugs() {
|
|
556
|
+
_forge_load_config
|
|
557
|
+
local prefix_dir="\${_FORGE_FEATURE_PREFIX%/}"
|
|
558
|
+
local dir="\${_FORGE_WORKTREES_ROOT}/\${prefix_dir}"
|
|
559
|
+
if [[ -d "\${dir}" ]]; then
|
|
560
|
+
find "\${dir}" -mindepth 1 -maxdepth 1 -type d -exec basename {} \\; 2>/dev/null
|
|
561
|
+
fi
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
_forge_fix_slugs() {
|
|
565
|
+
_forge_load_config
|
|
566
|
+
local prefix_dir="\${_FORGE_FIX_PREFIX%/}"
|
|
567
|
+
local dir="\${_FORGE_WORKTREES_ROOT}/\${prefix_dir}"
|
|
568
|
+
if [[ -d "\${dir}" ]]; then
|
|
569
|
+
find "\${dir}" -mindepth 1 -maxdepth 1 -type d -exec basename {} \\; 2>/dev/null
|
|
570
|
+
fi
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
_forge_release_slugs() {
|
|
574
|
+
_forge_load_config
|
|
575
|
+
local prefix_dir="\${_FORGE_RELEASE_PREFIX%/}"
|
|
576
|
+
local dir="\${_FORGE_WORKTREES_ROOT}/\${prefix_dir}"
|
|
577
|
+
if [[ -d "\${dir}" ]]; then
|
|
578
|
+
find "\${dir}" -mindepth 1 -maxdepth 1 -type d -exec basename {} \\; 2>/dev/null
|
|
579
|
+
fi
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
_forge_all_branches() {
|
|
583
|
+
_forge_load_config
|
|
584
|
+
local root="\${_FORGE_WORKTREES_ROOT}"
|
|
585
|
+
if [[ ! -d "\${root}" ]]; then
|
|
586
|
+
return
|
|
587
|
+
fi
|
|
588
|
+
local prefix_dirs=()
|
|
589
|
+
for p in "\${_FORGE_FEATURE_PREFIX%/}" "\${_FORGE_FIX_PREFIX%/}" "\${_FORGE_RELEASE_PREFIX%/}"; do
|
|
590
|
+
if [[ -n "\${p}" ]]; then
|
|
591
|
+
prefix_dirs+=("\${p}")
|
|
592
|
+
fi
|
|
593
|
+
done
|
|
594
|
+
for entry in "\${root}"/*/; do
|
|
595
|
+
[[ -d "\${entry}" ]] || continue
|
|
596
|
+
local name="\$(basename "\${entry}")"
|
|
597
|
+
local is_prefix=0
|
|
598
|
+
for pd in "\${prefix_dirs[@]}"; do
|
|
599
|
+
if [[ "\${name}" == "\${pd}" ]]; then
|
|
600
|
+
is_prefix=1
|
|
601
|
+
break
|
|
602
|
+
fi
|
|
603
|
+
done
|
|
604
|
+
if [[ \${is_prefix} -eq 1 ]]; then
|
|
605
|
+
for sub in "\${entry}"*/; do
|
|
606
|
+
[[ -d "\${sub}" ]] || continue
|
|
607
|
+
echo "\${name}/\$(basename "\${sub}")"
|
|
608
|
+
done
|
|
609
|
+
else
|
|
610
|
+
echo "\${name}"
|
|
611
|
+
fi
|
|
612
|
+
done
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
_forge_modes() {
|
|
616
|
+
_forge_load_config
|
|
617
|
+
echo "\${_FORGE_MODES}"
|
|
374
618
|
}
|
|
375
619
|
|
|
376
620
|
_forge() {
|
|
377
|
-
local -a commands feature_commands
|
|
621
|
+
local -a commands feature_commands fix_commands release_commands
|
|
622
|
+
local -a services_commands env_commands maintenance_commands
|
|
623
|
+
local -a agent_commands
|
|
624
|
+
|
|
625
|
+
# Reset config cache for each completion
|
|
626
|
+
_forge_config_loaded=0
|
|
378
627
|
|
|
379
628
|
commands=(
|
|
380
629
|
${commandsArray}
|
|
@@ -384,8 +633,24 @@ ${commandsArray}
|
|
|
384
633
|
${featureArray}
|
|
385
634
|
)
|
|
386
635
|
|
|
387
|
-
|
|
388
|
-
${
|
|
636
|
+
fix_commands=(
|
|
637
|
+
${fixArray}
|
|
638
|
+
)
|
|
639
|
+
|
|
640
|
+
release_commands=(
|
|
641
|
+
${releaseArray}
|
|
642
|
+
)
|
|
643
|
+
|
|
644
|
+
services_commands=(
|
|
645
|
+
${servicesArray}
|
|
646
|
+
)
|
|
647
|
+
|
|
648
|
+
env_commands=(
|
|
649
|
+
${envArray}
|
|
650
|
+
)
|
|
651
|
+
|
|
652
|
+
maintenance_commands=(
|
|
653
|
+
${maintenanceArray}
|
|
389
654
|
)
|
|
390
655
|
|
|
391
656
|
agent_commands=(
|
|
@@ -409,14 +674,52 @@ ${featureSlugCase} *)
|
|
|
409
674
|
;;
|
|
410
675
|
esac
|
|
411
676
|
;;
|
|
677
|
+
fix)
|
|
678
|
+
case \${words[2]} in
|
|
679
|
+
${fixSlugCase} *)
|
|
680
|
+
_describe 'fix command' fix_commands
|
|
681
|
+
;;
|
|
682
|
+
esac
|
|
683
|
+
;;
|
|
684
|
+
release)
|
|
685
|
+
case \${words[2]} in
|
|
686
|
+
${releaseSlugCase} *)
|
|
687
|
+
_describe 'release command' release_commands
|
|
688
|
+
;;
|
|
689
|
+
esac
|
|
690
|
+
;;
|
|
691
|
+
services)
|
|
692
|
+
_describe 'services command' services_commands
|
|
693
|
+
;;
|
|
694
|
+
env)
|
|
695
|
+
_describe 'env command' env_commands
|
|
696
|
+
;;
|
|
697
|
+
maintenance)
|
|
698
|
+
if (( CURRENT == 2 )); then
|
|
699
|
+
_describe 'maintenance command' maintenance_commands
|
|
700
|
+
else
|
|
701
|
+
local -a branches
|
|
702
|
+
branches=(\${(f)"\$(_forge_all_branches)"})
|
|
703
|
+
_describe 'branch name' branches
|
|
704
|
+
fi
|
|
705
|
+
;;
|
|
412
706
|
mode)
|
|
413
|
-
|
|
707
|
+
local -a modes
|
|
708
|
+
modes=(\${(f)"\$(_forge_modes)"})
|
|
709
|
+
if [[ \${#modes} -gt 0 ]]; then
|
|
710
|
+
_describe 'mode name' modes
|
|
711
|
+
fi
|
|
414
712
|
;;
|
|
415
713
|
agent)
|
|
416
714
|
_describe 'agent command' agent_commands
|
|
417
715
|
;;
|
|
418
716
|
${mainSlugCase} completion)
|
|
419
|
-
local shells
|
|
717
|
+
local -a shells
|
|
718
|
+
shells=('bash' 'zsh' 'fish' 'powershell' 'pwsh')
|
|
719
|
+
_describe 'shell type' shells
|
|
720
|
+
;;
|
|
721
|
+
alias)
|
|
722
|
+
local -a shells
|
|
420
723
|
shells=('bash' 'zsh' 'fish' 'powershell' 'pwsh')
|
|
421
724
|
_describe 'shell type' shells
|
|
422
725
|
;;
|
|
@@ -436,50 +739,59 @@ compdef _forge forge
|
|
|
436
739
|
# forge completion zsh > ~/.zsh/completions/_forge
|
|
437
740
|
# # Add to .zshrc: fpath=(~/.zsh/completions $fpath)
|
|
438
741
|
# # Then run: autoload -Uz compinit && compinit
|
|
742
|
+
#
|
|
743
|
+
# Tip: Run 'forge alias zsh' to generate shell aliases for quick access.
|
|
439
744
|
`;
|
|
440
745
|
}
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
* @returns Fish completion script content
|
|
445
|
-
*/
|
|
746
|
+
// ============================================================================
|
|
747
|
+
// FISH COMPLETION
|
|
748
|
+
// ============================================================================
|
|
446
749
|
generateFishCompletion() {
|
|
447
|
-
const
|
|
448
|
-
|
|
449
|
-
const
|
|
450
|
-
const agentCmd = this.findCommand('agent');
|
|
451
|
-
// Generate main commands
|
|
452
|
-
const mainCommandsLines = mainCommands
|
|
750
|
+
const data = this.getCompletionData();
|
|
751
|
+
// Main commands
|
|
752
|
+
const mainCommandsLines = data.mainCommands
|
|
453
753
|
.map((cmd) => `complete -c forge -n "__fish_use_subcommand" -a ${cmd.name} -d "${cmd.description.replace(/"/g, '\\"')}"`)
|
|
454
754
|
.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
|
-
|
|
755
|
+
// Helper to generate subcommand completions for a group
|
|
756
|
+
const genSubCmds = (parentName, cmd) => {
|
|
757
|
+
if (!cmd || !cmd.subcommands.length)
|
|
758
|
+
return '';
|
|
759
|
+
const subNames = cmd.subcommands.map((c) => c.name).join(' ');
|
|
760
|
+
return cmd.subcommands
|
|
761
|
+
.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, '\\"')}"`)
|
|
762
|
+
.join('\n');
|
|
763
|
+
};
|
|
764
|
+
const featureSubCmds = genSubCmds('feature', data.featureCmd);
|
|
765
|
+
const fixSubCmds = genSubCmds('fix', data.fixCmd);
|
|
766
|
+
const releaseSubCmds = genSubCmds('release', data.releaseCmd);
|
|
767
|
+
const servicesSubCmds = genSubCmds('services', data.servicesCmd);
|
|
768
|
+
const envSubCmds = genSubCmds('env', data.envCmd);
|
|
769
|
+
const maintenanceSubCmds = genSubCmds('maintenance', data.maintenanceCmd);
|
|
770
|
+
const agentSubCmds = genSubCmds('agent', data.agentCmd);
|
|
771
|
+
// Slug completions for feature/fix/release
|
|
772
|
+
const genSlugCompletions = (parentName, cmd, slugFn) => {
|
|
773
|
+
if (!cmd)
|
|
774
|
+
return '';
|
|
775
|
+
const slugCmds = cmd.subcommands.filter((c) => data.slugSubcommands.includes(c.name));
|
|
776
|
+
return slugCmds
|
|
777
|
+
.map((c) => `complete -c forge -n "__fish_seen_subcommand_from ${parentName}; and __fish_seen_subcommand_from ${c.name}" -a "(${slugFn})"`)
|
|
778
|
+
.join('\n');
|
|
779
|
+
};
|
|
780
|
+
const featureSlugCompletions = genSlugCompletions('feature', data.featureCmd, '__forge_feature_slugs');
|
|
781
|
+
const fixSlugCompletions = genSlugCompletions('fix', data.fixCmd, '__forge_fix_slugs');
|
|
782
|
+
const releaseSlugCompletions = genSlugCompletions('release', data.releaseCmd, '__forge_release_slugs');
|
|
783
|
+
// Root-level slug commands
|
|
784
|
+
const mainSlugCmds = data.mainCommands.filter((cmd) => data.rootSlugCommands.includes(cmd.name));
|
|
785
|
+
const mainSlugCompletions = mainSlugCmds
|
|
786
|
+
.map((cmd) => `complete -c forge -n "__fish_seen_subcommand_from ${cmd.name}" -a "(__forge_all_branches)"`)
|
|
479
787
|
.join('\n');
|
|
788
|
+
// Maintenance slug completion at position 3
|
|
789
|
+
const maintenanceSlugCompletion = data.maintenanceCmd?.subcommands
|
|
790
|
+
.map((c) => `complete -c forge -n "__fish_seen_subcommand_from maintenance; and __fish_seen_subcommand_from ${c.name}" -a "(__forge_all_branches)"`)
|
|
791
|
+
.join('\n') || '';
|
|
480
792
|
return `# forge fish completion script
|
|
481
793
|
|
|
482
|
-
# Helper functions
|
|
794
|
+
# Helper functions
|
|
483
795
|
function __forge_find_config_root
|
|
484
796
|
set -l dir $PWD
|
|
485
797
|
while test -n "$dir" -a "$dir" != "/"
|
|
@@ -492,45 +804,107 @@ function __forge_find_config_root
|
|
|
492
804
|
return 1
|
|
493
805
|
end
|
|
494
806
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
807
|
+
set -g _forge_config_loaded 0
|
|
808
|
+
|
|
809
|
+
function __forge_load_config
|
|
810
|
+
if test $_forge_config_loaded -eq 1
|
|
498
811
|
return 0
|
|
499
812
|
end
|
|
813
|
+
set -g _FORGE_WORKTREES_ROOT ""
|
|
814
|
+
set -g _FORGE_FEATURE_PREFIX "feature/"
|
|
815
|
+
set -g _FORGE_FIX_PREFIX "fix/"
|
|
816
|
+
set -g _FORGE_RELEASE_PREFIX "release/"
|
|
817
|
+
set -g _FORGE_MODES ""
|
|
818
|
+
|
|
819
|
+
if test -n "$FORGE_WORKTREES_ROOT"
|
|
820
|
+
set -g _FORGE_WORKTREES_ROOT $FORGE_WORKTREES_ROOT
|
|
821
|
+
end
|
|
822
|
+
|
|
500
823
|
set -l config_root (__forge_find_config_root)
|
|
501
824
|
if test -n "$config_root"
|
|
502
|
-
set -l
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
825
|
+
set -l output (node -e ${this.nodeConfigSnippetFish()} "$config_root/.feat-forge.json" 2>/dev/null)
|
|
826
|
+
if test -n "$output"
|
|
827
|
+
eval $output
|
|
828
|
+
else if test -z "$_FORGE_WORKTREES_ROOT"
|
|
829
|
+
set -g _FORGE_WORKTREES_ROOT "$config_root/worktrees"
|
|
830
|
+
end
|
|
831
|
+
else if test -z "$_FORGE_WORKTREES_ROOT"
|
|
832
|
+
set -g _FORGE_WORKTREES_ROOT "worktrees"
|
|
833
|
+
end
|
|
834
|
+
set -g _forge_config_loaded 1
|
|
835
|
+
end
|
|
836
|
+
|
|
837
|
+
function __forge_feature_slugs
|
|
838
|
+
__forge_load_config
|
|
839
|
+
set -l prefix_dir (string trim --right --chars=/ $_FORGE_FEATURE_PREFIX)
|
|
840
|
+
set -l dir "$_FORGE_WORKTREES_ROOT/$prefix_dir"
|
|
841
|
+
if test -d "$dir"
|
|
842
|
+
for entry in $dir/*/
|
|
843
|
+
basename $entry
|
|
518
844
|
end
|
|
519
|
-
echo "$config_root/features"
|
|
520
|
-
return 0
|
|
521
845
|
end
|
|
522
|
-
echo "features"
|
|
523
846
|
end
|
|
524
847
|
|
|
525
|
-
function
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
848
|
+
function __forge_fix_slugs
|
|
849
|
+
__forge_load_config
|
|
850
|
+
set -l prefix_dir (string trim --right --chars=/ $_FORGE_FIX_PREFIX)
|
|
851
|
+
set -l dir "$_FORGE_WORKTREES_ROOT/$prefix_dir"
|
|
852
|
+
if test -d "$dir"
|
|
853
|
+
for entry in $dir/*/
|
|
854
|
+
basename $entry
|
|
530
855
|
end
|
|
531
856
|
end
|
|
532
857
|
end
|
|
533
858
|
|
|
859
|
+
function __forge_release_slugs
|
|
860
|
+
__forge_load_config
|
|
861
|
+
set -l prefix_dir (string trim --right --chars=/ $_FORGE_RELEASE_PREFIX)
|
|
862
|
+
set -l dir "$_FORGE_WORKTREES_ROOT/$prefix_dir"
|
|
863
|
+
if test -d "$dir"
|
|
864
|
+
for entry in $dir/*/
|
|
865
|
+
basename $entry
|
|
866
|
+
end
|
|
867
|
+
end
|
|
868
|
+
end
|
|
869
|
+
|
|
870
|
+
function __forge_all_branches
|
|
871
|
+
__forge_load_config
|
|
872
|
+
set -l root $_FORGE_WORKTREES_ROOT
|
|
873
|
+
if not test -d "$root"
|
|
874
|
+
return
|
|
875
|
+
end
|
|
876
|
+
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)
|
|
877
|
+
for entry in $root/*/
|
|
878
|
+
set -l name (basename $entry)
|
|
879
|
+
set -l is_prefix 0
|
|
880
|
+
for pd in $prefix_dirs
|
|
881
|
+
if test "$name" = "$pd"
|
|
882
|
+
set is_prefix 1
|
|
883
|
+
break
|
|
884
|
+
end
|
|
885
|
+
end
|
|
886
|
+
if test $is_prefix -eq 1
|
|
887
|
+
for sub in $entry*/
|
|
888
|
+
if test -d "$sub"
|
|
889
|
+
echo "$name/"(basename $sub)
|
|
890
|
+
end
|
|
891
|
+
end
|
|
892
|
+
else
|
|
893
|
+
echo $name
|
|
894
|
+
end
|
|
895
|
+
end
|
|
896
|
+
end
|
|
897
|
+
|
|
898
|
+
function __forge_modes
|
|
899
|
+
__forge_load_config
|
|
900
|
+
string split " " $_FORGE_MODES
|
|
901
|
+
end
|
|
902
|
+
|
|
903
|
+
# Reset config cache on each completion invocation
|
|
904
|
+
function __forge_reset_config --on-event fish_prompt
|
|
905
|
+
set -g _forge_config_loaded 0
|
|
906
|
+
end
|
|
907
|
+
|
|
534
908
|
# Disable file completion by default
|
|
535
909
|
complete -c forge -f
|
|
536
910
|
|
|
@@ -540,18 +914,42 @@ ${mainCommandsLines}
|
|
|
540
914
|
# Feature subcommands
|
|
541
915
|
${featureSubCmds}
|
|
542
916
|
|
|
543
|
-
# Feature
|
|
917
|
+
# Feature slug completions
|
|
544
918
|
${featureSlugCompletions}
|
|
545
919
|
|
|
546
|
-
#
|
|
547
|
-
${
|
|
920
|
+
# Fix subcommands
|
|
921
|
+
${fixSubCmds}
|
|
922
|
+
|
|
923
|
+
# Fix slug completions
|
|
924
|
+
${fixSlugCompletions}
|
|
925
|
+
|
|
926
|
+
# Release subcommands
|
|
927
|
+
${releaseSubCmds}
|
|
928
|
+
|
|
929
|
+
# Release slug completions
|
|
930
|
+
${releaseSlugCompletions}
|
|
931
|
+
|
|
932
|
+
# Services subcommands
|
|
933
|
+
${servicesSubCmds}
|
|
934
|
+
|
|
935
|
+
# Env subcommands
|
|
936
|
+
${envSubCmds}
|
|
937
|
+
|
|
938
|
+
# Maintenance subcommands
|
|
939
|
+
${maintenanceSubCmds}
|
|
940
|
+
|
|
941
|
+
# Maintenance slug completions
|
|
942
|
+
${maintenanceSlugCompletion}
|
|
548
943
|
|
|
549
944
|
# Agent subcommands
|
|
550
945
|
${agentSubCmds}
|
|
551
946
|
|
|
552
|
-
#
|
|
947
|
+
# Root-level commands with branch completion
|
|
553
948
|
${mainSlugCompletions}
|
|
554
949
|
|
|
950
|
+
# Mode command with mode name completion
|
|
951
|
+
complete -c forge -n "__fish_seen_subcommand_from mode" -a "(__forge_modes)"
|
|
952
|
+
|
|
555
953
|
# Completion command with shell types
|
|
556
954
|
complete -c forge -n "__fish_seen_subcommand_from completion" -a bash -d "Generate bash completion"
|
|
557
955
|
complete -c forge -n "__fish_seen_subcommand_from completion" -a zsh -d "Generate zsh completion"
|
|
@@ -559,33 +957,41 @@ complete -c forge -n "__fish_seen_subcommand_from completion" -a fish -d "Genera
|
|
|
559
957
|
complete -c forge -n "__fish_seen_subcommand_from completion" -a powershell -d "Generate PowerShell completion"
|
|
560
958
|
complete -c forge -n "__fish_seen_subcommand_from completion" -a pwsh -d "Generate PowerShell completion"
|
|
561
959
|
|
|
960
|
+
# Alias command with shell types
|
|
961
|
+
complete -c forge -n "__fish_seen_subcommand_from alias" -a bash -d "Generate bash aliases"
|
|
962
|
+
complete -c forge -n "__fish_seen_subcommand_from alias" -a zsh -d "Generate zsh aliases"
|
|
963
|
+
complete -c forge -n "__fish_seen_subcommand_from alias" -a fish -d "Generate fish aliases"
|
|
964
|
+
complete -c forge -n "__fish_seen_subcommand_from alias" -a powershell -d "Generate PowerShell aliases"
|
|
965
|
+
complete -c forge -n "__fish_seen_subcommand_from alias" -a pwsh -d "Generate PowerShell aliases"
|
|
966
|
+
|
|
562
967
|
# Installation instructions:
|
|
563
968
|
# Option 1 - Add to ~/.config/fish/config.fish:
|
|
564
969
|
# forge completion fish | source
|
|
565
970
|
#
|
|
566
971
|
# Option 2 - Save to completions directory:
|
|
567
972
|
# forge completion fish > ~/.config/fish/completions/forge.fish
|
|
973
|
+
#
|
|
974
|
+
# Tip: Run 'forge alias fish' to generate shell aliases for quick access.
|
|
568
975
|
`;
|
|
569
976
|
}
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
* @returns PowerShell completion script content
|
|
574
|
-
*/
|
|
977
|
+
// ============================================================================
|
|
978
|
+
// POWERSHELL COMPLETION
|
|
979
|
+
// ============================================================================
|
|
575
980
|
generatePowerShellCompletion() {
|
|
576
|
-
const
|
|
577
|
-
const featureCmd = this.findCommand('feature');
|
|
578
|
-
const modeCmd = this.findCommand('mode');
|
|
579
|
-
const agentCmd = this.findCommand('agent');
|
|
981
|
+
const data = this.getCompletionData();
|
|
580
982
|
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
|
|
983
|
+
const mainCommandsList = toPsArray(data.mainCommands.map((cmd) => cmd.name));
|
|
984
|
+
const featureCommandsList = toPsArray(data.featureCmd?.subcommands.map((cmd) => cmd.name) || []);
|
|
985
|
+
const fixCommandsList = toPsArray(data.fixCmd?.subcommands.map((cmd) => cmd.name) || []);
|
|
986
|
+
const releaseCommandsList = toPsArray(data.releaseCmd?.subcommands.map((cmd) => cmd.name) || []);
|
|
987
|
+
const servicesCommandsList = toPsArray(data.servicesCmd?.subcommands.map((cmd) => cmd.name) || []);
|
|
988
|
+
const envCommandsList = toPsArray(data.envCmd?.subcommands.map((cmd) => cmd.name) || []);
|
|
989
|
+
const maintenanceCommandsList = toPsArray(data.maintenanceCmd?.subcommands.map((cmd) => cmd.name) || []);
|
|
990
|
+
const agentCommandsList = toPsArray(data.agentCmd?.subcommands.map((cmd) => cmd.name) || []);
|
|
991
|
+
const featureSlugCommandsList = toPsArray(data.featureCmd?.subcommands.filter((cmd) => data.slugSubcommands.includes(cmd.name)).map((cmd) => cmd.name) || []);
|
|
992
|
+
const fixSlugCommandsList = toPsArray(data.fixCmd?.subcommands.filter((cmd) => data.slugSubcommands.includes(cmd.name)).map((cmd) => cmd.name) || []);
|
|
993
|
+
const releaseSlugCommandsList = toPsArray(data.releaseCmd?.subcommands.filter((cmd) => data.slugSubcommands.includes(cmd.name)).map((cmd) => cmd.name) || []);
|
|
994
|
+
const mainSlugCommandsList = toPsArray(data.mainCommands.filter((cmd) => data.rootSlugCommands.includes(cmd.name)).map((cmd) => cmd.name));
|
|
589
995
|
const completionShellsList = toPsArray(['bash', 'zsh', 'fish', 'powershell', 'pwsh']);
|
|
590
996
|
return `# forge PowerShell completion script
|
|
591
997
|
|
|
@@ -600,40 +1006,100 @@ function __ForgeFindConfigRoot {
|
|
|
600
1006
|
return $null
|
|
601
1007
|
}
|
|
602
1008
|
|
|
603
|
-
|
|
604
|
-
|
|
1009
|
+
$script:__ForgeConfigLoaded = $false
|
|
1010
|
+
$script:__ForgeConfig = $null
|
|
1011
|
+
|
|
1012
|
+
function __ForgeLoadConfig {
|
|
1013
|
+
if ($script:__ForgeConfigLoaded) { return }
|
|
1014
|
+
|
|
1015
|
+
$script:__ForgeConfig = @{
|
|
1016
|
+
WorktreesRoot = ""
|
|
1017
|
+
FeaturePrefix = "feature/"
|
|
1018
|
+
FixPrefix = "fix/"
|
|
1019
|
+
ReleasePrefix = "release/"
|
|
1020
|
+
Modes = @()
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
if ($env:FORGE_WORKTREES_ROOT) {
|
|
1024
|
+
$script:__ForgeConfig.WorktreesRoot = $env:FORGE_WORKTREES_ROOT
|
|
1025
|
+
}
|
|
1026
|
+
|
|
605
1027
|
$configRoot = __ForgeFindConfigRoot
|
|
606
1028
|
if ($configRoot) {
|
|
607
1029
|
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)
|
|
1030
|
+
$output = node -e ${this.nodeConfigSnippetPowerShell()} (Join-Path $configRoot ".feat-forge.json") 2>$null
|
|
1031
|
+
if ($output) {
|
|
1032
|
+
$parsed = $output | ConvertFrom-Json
|
|
1033
|
+
if ($parsed.worktreesRoot) { $script:__ForgeConfig.WorktreesRoot = $parsed.worktreesRoot }
|
|
1034
|
+
if ($parsed.featurePrefix) { $script:__ForgeConfig.FeaturePrefix = $parsed.featurePrefix }
|
|
1035
|
+
if ($parsed.fixPrefix) { $script:__ForgeConfig.FixPrefix = $parsed.fixPrefix }
|
|
1036
|
+
if ($parsed.releasePrefix) { $script:__ForgeConfig.ReleasePrefix = $parsed.releasePrefix }
|
|
1037
|
+
if ($parsed.modes) { $script:__ForgeConfig.Modes = $parsed.modes }
|
|
619
1038
|
}
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
} catch {
|
|
1039
|
+
} catch {}
|
|
1040
|
+
if (-not $script:__ForgeConfig.WorktreesRoot) {
|
|
1041
|
+
$script:__ForgeConfig.WorktreesRoot = Join-Path $configRoot "worktrees"
|
|
624
1042
|
}
|
|
625
|
-
return (Join-Path $rootDir "features")
|
|
626
1043
|
}
|
|
627
|
-
|
|
1044
|
+
if (-not $script:__ForgeConfig.WorktreesRoot) {
|
|
1045
|
+
$script:__ForgeConfig.WorktreesRoot = "worktrees"
|
|
1046
|
+
}
|
|
1047
|
+
$script:__ForgeConfigLoaded = $true
|
|
628
1048
|
}
|
|
629
1049
|
|
|
630
1050
|
function __ForgeFeatureSlugs {
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
1051
|
+
__ForgeLoadConfig
|
|
1052
|
+
$prefix = $script:__ForgeConfig.FeaturePrefix.TrimEnd('/')
|
|
1053
|
+
$dir = Join-Path $script:__ForgeConfig.WorktreesRoot $prefix
|
|
1054
|
+
if (Test-Path $dir) {
|
|
1055
|
+
Get-ChildItem -Directory -Path $dir | ForEach-Object { $_.Name }
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
function __ForgeFixSlugs {
|
|
1060
|
+
__ForgeLoadConfig
|
|
1061
|
+
$prefix = $script:__ForgeConfig.FixPrefix.TrimEnd('/')
|
|
1062
|
+
$dir = Join-Path $script:__ForgeConfig.WorktreesRoot $prefix
|
|
1063
|
+
if (Test-Path $dir) {
|
|
1064
|
+
Get-ChildItem -Directory -Path $dir | ForEach-Object { $_.Name }
|
|
634
1065
|
}
|
|
635
1066
|
}
|
|
636
1067
|
|
|
1068
|
+
function __ForgeReleaseSlugs {
|
|
1069
|
+
__ForgeLoadConfig
|
|
1070
|
+
$prefix = $script:__ForgeConfig.ReleasePrefix.TrimEnd('/')
|
|
1071
|
+
$dir = Join-Path $script:__ForgeConfig.WorktreesRoot $prefix
|
|
1072
|
+
if (Test-Path $dir) {
|
|
1073
|
+
Get-ChildItem -Directory -Path $dir | ForEach-Object { $_.Name }
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
function __ForgeAllBranches {
|
|
1078
|
+
__ForgeLoadConfig
|
|
1079
|
+
$root = $script:__ForgeConfig.WorktreesRoot
|
|
1080
|
+
if (-not (Test-Path $root)) { return }
|
|
1081
|
+
$prefixDirs = @(
|
|
1082
|
+
$script:__ForgeConfig.FeaturePrefix.TrimEnd('/'),
|
|
1083
|
+
$script:__ForgeConfig.FixPrefix.TrimEnd('/'),
|
|
1084
|
+
$script:__ForgeConfig.ReleasePrefix.TrimEnd('/')
|
|
1085
|
+
)
|
|
1086
|
+
Get-ChildItem -Directory -Path $root | ForEach-Object {
|
|
1087
|
+
$name = $_.Name
|
|
1088
|
+
if ($prefixDirs -contains $name) {
|
|
1089
|
+
Get-ChildItem -Directory -Path $_.FullName | ForEach-Object {
|
|
1090
|
+
"$name/$($_.Name)"
|
|
1091
|
+
}
|
|
1092
|
+
} else {
|
|
1093
|
+
$name
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
function __ForgeModes {
|
|
1099
|
+
__ForgeLoadConfig
|
|
1100
|
+
$script:__ForgeConfig.Modes
|
|
1101
|
+
}
|
|
1102
|
+
|
|
637
1103
|
function __ForgeCompletionResults {
|
|
638
1104
|
param([string[]]$Items, [string]$Word)
|
|
639
1105
|
if (-not $Items) { return }
|
|
@@ -644,15 +1110,24 @@ function __ForgeCompletionResults {
|
|
|
644
1110
|
|
|
645
1111
|
$mainCommands = @(${mainCommandsList})
|
|
646
1112
|
$featureCommands = @(${featureCommandsList})
|
|
647
|
-
$
|
|
1113
|
+
$fixCommands = @(${fixCommandsList})
|
|
1114
|
+
$releaseCommands = @(${releaseCommandsList})
|
|
1115
|
+
$servicesCommands = @(${servicesCommandsList})
|
|
1116
|
+
$envCommands = @(${envCommandsList})
|
|
1117
|
+
$maintenanceCommands = @(${maintenanceCommandsList})
|
|
648
1118
|
$agentCommands = @(${agentCommandsList})
|
|
649
1119
|
$featureSlugCommands = @(${featureSlugCommandsList})
|
|
1120
|
+
$fixSlugCommands = @(${fixSlugCommandsList})
|
|
1121
|
+
$releaseSlugCommands = @(${releaseSlugCommandsList})
|
|
650
1122
|
$mainSlugCommands = @(${mainSlugCommandsList})
|
|
651
1123
|
$completionShells = @(${completionShellsList})
|
|
652
1124
|
|
|
653
1125
|
Register-ArgumentCompleter -CommandName forge -ScriptBlock {
|
|
654
1126
|
param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
|
|
655
1127
|
|
|
1128
|
+
# Reset config cache for each completion
|
|
1129
|
+
$script:__ForgeConfigLoaded = $false
|
|
1130
|
+
|
|
656
1131
|
$elements = $commandAst.CommandElements | ForEach-Object { $_.Value }
|
|
657
1132
|
|
|
658
1133
|
if ($elements.Count -le 1) {
|
|
@@ -673,8 +1148,46 @@ Register-ArgumentCompleter -CommandName forge -ScriptBlock {
|
|
|
673
1148
|
return
|
|
674
1149
|
}
|
|
675
1150
|
}
|
|
1151
|
+
'fix' {
|
|
1152
|
+
if ($elements.Count -le 2) {
|
|
1153
|
+
__ForgeCompletionResults -Items $fixCommands -Word $wordToComplete
|
|
1154
|
+
return
|
|
1155
|
+
}
|
|
1156
|
+
$sub = $elements[2]
|
|
1157
|
+
if ($fixSlugCommands -contains $sub) {
|
|
1158
|
+
__ForgeCompletionResults -Items (__ForgeFixSlugs) -Word $wordToComplete
|
|
1159
|
+
return
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
'release' {
|
|
1163
|
+
if ($elements.Count -le 2) {
|
|
1164
|
+
__ForgeCompletionResults -Items $releaseCommands -Word $wordToComplete
|
|
1165
|
+
return
|
|
1166
|
+
}
|
|
1167
|
+
$sub = $elements[2]
|
|
1168
|
+
if ($releaseSlugCommands -contains $sub) {
|
|
1169
|
+
__ForgeCompletionResults -Items (__ForgeReleaseSlugs) -Word $wordToComplete
|
|
1170
|
+
return
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
'services' {
|
|
1174
|
+
__ForgeCompletionResults -Items $servicesCommands -Word $wordToComplete
|
|
1175
|
+
return
|
|
1176
|
+
}
|
|
1177
|
+
'env' {
|
|
1178
|
+
__ForgeCompletionResults -Items $envCommands -Word $wordToComplete
|
|
1179
|
+
return
|
|
1180
|
+
}
|
|
1181
|
+
'maintenance' {
|
|
1182
|
+
if ($elements.Count -le 2) {
|
|
1183
|
+
__ForgeCompletionResults -Items $maintenanceCommands -Word $wordToComplete
|
|
1184
|
+
return
|
|
1185
|
+
}
|
|
1186
|
+
__ForgeCompletionResults -Items (__ForgeAllBranches) -Word $wordToComplete
|
|
1187
|
+
return
|
|
1188
|
+
}
|
|
676
1189
|
'mode' {
|
|
677
|
-
__ForgeCompletionResults -Items
|
|
1190
|
+
__ForgeCompletionResults -Items (__ForgeModes) -Word $wordToComplete
|
|
678
1191
|
return
|
|
679
1192
|
}
|
|
680
1193
|
'agent' {
|
|
@@ -685,9 +1198,13 @@ Register-ArgumentCompleter -CommandName forge -ScriptBlock {
|
|
|
685
1198
|
__ForgeCompletionResults -Items $completionShells -Word $wordToComplete
|
|
686
1199
|
return
|
|
687
1200
|
}
|
|
1201
|
+
'alias' {
|
|
1202
|
+
__ForgeCompletionResults -Items $completionShells -Word $wordToComplete
|
|
1203
|
+
return
|
|
1204
|
+
}
|
|
688
1205
|
default {
|
|
689
1206
|
if ($mainSlugCommands -contains $first) {
|
|
690
|
-
__ForgeCompletionResults -Items (
|
|
1207
|
+
__ForgeCompletionResults -Items (__ForgeAllBranches) -Word $wordToComplete
|
|
691
1208
|
return
|
|
692
1209
|
}
|
|
693
1210
|
}
|
|
@@ -695,8 +1212,10 @@ Register-ArgumentCompleter -CommandName forge -ScriptBlock {
|
|
|
695
1212
|
}
|
|
696
1213
|
|
|
697
1214
|
# Installation instructions:
|
|
698
|
-
# Option 1 - Add to
|
|
1215
|
+
# Option 1 - Add to \$PROFILE:
|
|
699
1216
|
# forge completion powershell | Out-String | Invoke-Expression
|
|
1217
|
+
#
|
|
1218
|
+
# Tip: Run 'forge alias powershell' to generate shell aliases for quick access.
|
|
700
1219
|
`;
|
|
701
1220
|
}
|
|
702
1221
|
}
|